From 46c92d5430a96742b44e473bec682e113ccd1e3c Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 25 Dec 2018 18:56:48 -0500 Subject: [PATCH 1/3] Improve alias bindings --- Sources/LLVM/Alias.swift | 18 ++++++++++++++++++ Sources/LLVM/Module.swift | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/Sources/LLVM/Alias.swift b/Sources/LLVM/Alias.swift index d2301d2b..52559f33 100644 --- a/Sources/LLVM/Alias.swift +++ b/Sources/LLVM/Alias.swift @@ -11,4 +11,22 @@ public struct Alias: IRGlobal { public func asLLVM() -> LLVMValueRef { return llvm } + + /// Access the target value of this alias. + public var aliasee: IRValue { + get { return LLVMAliasGetAliasee(llvm) } + set { LLVMAliasSetAliasee(llvm, newValue.asLLVM()) } + } + + /// Retrieves the previous alias in the module, if there is one. + public func previous() -> Alias? { + guard let previous = LLVMGetPreviousGlobalAlias(llvm) else { return nil } + return Alias(llvm: previous) + } + + /// Retrieves the next alias in the module, if there is one. + public func next() -> Alias? { + guard let next = LLVMGetNextGlobalAlias(llvm) else { return nil } + return Alias(llvm: next) + } } diff --git a/Sources/LLVM/Module.swift b/Sources/LLVM/Module.swift index c671be4f..c0e71c9d 100644 --- a/Sources/LLVM/Module.swift +++ b/Sources/LLVM/Module.swift @@ -230,6 +230,29 @@ public final class Module: CustomStringConvertible { } } + /// Retrieves the sequence of aliases that make up this module. + public var aliases: AnySequence { + var current = firstAlias + return AnySequence { + return AnyIterator { + defer { current = current?.next() } + return current + } + } + } + + /// Retrieves the first alias in this module, if there are any aliases. + public var firstAlias: Alias? { + guard let fn = LLVMGetFirstGlobalAlias(llvm) else { return nil } + return Alias(llvm: fn) + } + + /// Retrieves the last alias in this module, if there are any aliases. + public var lastAlias: Alias? { + guard let fn = LLVMGetLastGlobalAlias(llvm) else { return nil } + return Alias(llvm: fn) + } + /// The current debug metadata version number. public static var debugMetadataVersion: UInt32 { return LLVMDebugMetadataVersion(); @@ -307,6 +330,18 @@ extension Module { return Function(llvm: fn) } + /// Searches for and retrieves an alias with the given name in this module + /// if that name references an existing alias. + /// + /// - parameter name: The name of the alias to search for. + /// + /// - returns: A representation of an alias with the given + /// name or nil if no such named alias exists. + public func alias(named name: String) -> Alias? { + guard let alias = LLVMGetNamedGlobalAlias(llvm, name, name.count) else { return nil } + return Alias(llvm: alias) + } + /// 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. /// From bda8a393621a22e2b10af33cfb85c25585e04b50 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 25 Dec 2018 18:56:59 -0500 Subject: [PATCH 2/3] Add Loop unroll and Jam pass --- Sources/LLVM/PassManager.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sources/LLVM/PassManager.swift b/Sources/LLVM/PassManager.swift index 591d013b..abd10d1a 100644 --- a/Sources/LLVM/PassManager.swift +++ b/Sources/LLVM/PassManager.swift @@ -60,6 +60,8 @@ public enum FunctionPass { case loopReroll /// This pass is a simple loop unrolling pass. case loopUnroll + /// This pass is a simple loop unroll-and-jam pass. + case loopUnrollAndJam /// This pass is a simple loop unswitching pass. case loopUnswitch /// This pass performs optimizations related to eliminating `memcpy` calls @@ -198,6 +200,7 @@ public class FunctionPassManager { .loopRotate: LLVMAddLoopRotatePass, .loopReroll: LLVMAddLoopRerollPass, .loopUnroll: LLVMAddLoopUnrollPass, + .loopUnrollAndJam: LLVMAddLoopUnrollAndJamPass, .loopUnswitch: LLVMAddLoopUnswitchPass, .memCpyOpt: LLVMAddMemCpyOptPass, .partiallyInlineLibCalls: LLVMAddPartiallyInlineLibCallsPass, From 79f974ddcb0199c1b66884f6b2f928797f10acbc Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 25 Dec 2018 18:57:35 -0500 Subject: [PATCH 3/3] Add bindings for module flags --- Sources/LLVM/Module.swift | 155 ++++++++++++++++++++++- Tests/LLVMTests/ModuleMetadataSpec.swift | 67 ++++++++++ Tests/LinuxMain.swift | 1 + 3 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 Tests/LLVMTests/ModuleMetadataSpec.swift diff --git a/Sources/LLVM/Module.swift b/Sources/LLVM/Module.swift index c0e71c9d..2e66b2bf 100644 --- a/Sources/LLVM/Module.swift +++ b/Sources/LLVM/Module.swift @@ -324,7 +324,7 @@ extension Module { /// - 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. + /// 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) @@ -432,6 +432,159 @@ extension Module { } } +// MARK: Module Flags + +extension Module { + /// Represents flags that describe information about the module for use by + /// an external entity e.g. the dynamic linker. + /// + /// - Warning: Module flags are not a general runtime metadata infrastructure, + /// and may be stripped by LLVM. As of the current release, LLVM hardcodes + /// support for object-file emission of module flags related to + /// Objective-C. + public class Flags { + /// Enumerates the supported behaviors for resolving collisions when two + /// module flags share the same key. These collisions can occur when the + /// different flags are inserted under the same key, or when modules + /// containing flags under the same key are merged. + public enum Behavior { + /// Emits an error if two values disagree, otherwise the resulting value + /// is that of the operands. + case error + /// Emits a warning if two values disagree. The result value will be the + /// operand for the flag from the first module being linked. + case warning + /// Adds a requirement that another module flag be present and have a + /// specified value after linking is performed. The value must be a + /// metadata pair, where the first element of the pair is the ID of the + /// module flag to be restricted, and the second element of the pair is + /// the value the module flag should be restricted to. This behavior can + /// be used to restrict the allowable results (via triggering of an error) + /// of linking IDs with the **Override** behavior. + case require + /// Uses the specified value, regardless of the behavior or value of the + /// other module. If both modules specify **Override**, but the values + /// differ, an error will be emitted. + case override + /// Appends the two values, which are required to be metadata nodes. + case append + /// Appends the two values, which are required to be metadata + /// nodes. However, duplicate entries in the second list are dropped + /// during the append operation. + case appendUnique + + fileprivate init(raw: LLVMModuleFlagBehavior) { + switch raw { + case LLVMModuleFlagBehaviorError: + self = .error + case LLVMModuleFlagBehaviorWarning: + self = .warning + case LLVMModuleFlagBehaviorRequire: + self = .require + case LLVMModuleFlagBehaviorOverride: + self = .override + case LLVMModuleFlagBehaviorAppend: + self = .append + case LLVMModuleFlagBehaviorAppendUnique: + self = .appendUnique + default: + fatalError("Unknown behavior kind") + } + } + + fileprivate static let behaviorMapping: [Behavior: LLVMModuleFlagBehavior] = [ + .error: LLVMModuleFlagBehaviorError, + .warning: LLVMModuleFlagBehaviorWarning, + .require: LLVMModuleFlagBehaviorRequire, + .override: LLVMModuleFlagBehaviorOverride, + .append: LLVMModuleFlagBehaviorAppend, + .appendUnique: LLVMModuleFlagBehaviorAppendUnique, + ] + } + + /// Represents an entry in the module flags structure. + public struct Entry { + fileprivate let base: Flags + fileprivate let index: UInt32 + + /// The conflict behavior of this flag. + public var behavior: Behavior { + let raw = LLVMModuleFlagEntriesGetFlagBehavior(self.base.llvm, self.index) + return Behavior(raw: raw) + } + + /// The key this flag was inserted with. + public var key: String { + var count = 0 + guard let key = LLVMModuleFlagEntriesGetKey(self.base.llvm, self.index, &count) else { return "" } + return String(cString: key) + } + + /// The metadata value associated with this flag. + public var metadata: IRMetadata { + return AnyMetadata(llvm: LLVMModuleFlagEntriesGetMetadata(self.base.llvm, self.index)) + } + } + + private let llvm: OpaquePointer? + private let bounds: Int + fileprivate init(llvm: OpaquePointer?, bounds: Int) { + self.llvm = llvm + self.bounds = bounds + } + + deinit { + guard let ptr = llvm else { return } + LLVMDisposeModuleFlagsMetadata(ptr) + } + + /// Retrieves a flag at the given index. + /// + /// - Parameter index: The index to retrieve. + /// + /// - Returns: An entry describing the flag at the given index. + public subscript(_ index: Int) -> Entry { + precondition(index >= 0 && index < self.bounds, "Index out of bounds") + return Entry(base: self, index: UInt32(index)) + } + + public var count: Int { + return self.bounds + } + } + + /// Add a module-level flag to the module-level flags metadata. + /// + /// - Parameters: + /// - name: The key for this flag. + /// - value: The metadata node to insert as the value for this flag. + /// - behavior: The resolution strategy to apply should the key for this + /// flag conflict with an existing flag. + public func addFlag(named name: String, value: IRMetadata, behavior: Flags.Behavior) { + let raw = Flags.Behavior.behaviorMapping[behavior]! + LLVMAddModuleFlag(llvm, raw, name, name.count, value.asMetadata()) + } + + /// A convenience for inserting constant values as module-level flags. + /// + /// - Parameters: + /// - name: The key for this flag. + /// - value: The constant value to insert as the metadata for this flag. + /// - behavior: The resolution strategy to apply should the key for this + /// flag conflict with an existing flag. + public func addFlag(named name: String, constant: IRConstant, behavior: Flags.Behavior) { + let raw = Flags.Behavior.behaviorMapping[behavior]! + LLVMAddModuleFlag(llvm, raw, name, name.count, LLVMValueAsMetadata(constant.asLLVM())) + } + + /// Retrieves the module-level flags, if they exist. + public var flags: Flags? { + var len = 0 + guard let raw = LLVMCopyModuleFlagsMetadata(llvm, &len) else { return nil } + return Flags(llvm: raw, bounds: len) + } +} + extension Bool { internal var llvm: LLVMBool { return self ? 1 : 0 diff --git a/Tests/LLVMTests/ModuleMetadataSpec.swift b/Tests/LLVMTests/ModuleMetadataSpec.swift new file mode 100644 index 00000000..973fbc53 --- /dev/null +++ b/Tests/LLVMTests/ModuleMetadataSpec.swift @@ -0,0 +1,67 @@ +import LLVM +import XCTest +import FileCheck +import Foundation + +class ModuleMetadataSpec : XCTestCase { + func testAddModuleFlags() { + XCTAssertTrue(fileCheckOutput(of: .stderr, withPrefixes: ["MODULE-FLAGS"]) { + // MODULE-FLAGS: ; ModuleID = 'ModuleFlagsTest' + let module = Module(name: "ModuleFlagsTest") + // MODULE-FLAGS: !llvm.module.flags = !{!0, !1, !2, !3, !4, !5} + + // MODULE-FLAGS: !0 = !{i32 [[ERRVAL:\d+]], !"error", i32 [[ERRVAL]]} + module.addFlag(named: "error", constant: IntType.int32.constant(1), behavior: .error) + // MODULE-FLAGS-NEXT: !1 = !{i32 [[WARNVAL:\d+]], !"warning", i32 [[WARNVAL]]} + module.addFlag(named: "warning", constant: IntType.int32.constant(2), behavior: .warning) + // MODULE-FLAGS-NEXT: !2 = !{i32 [[REQUIREVAL:\d+]], !"require", i32 [[REQUIREVAL]]} + module.addFlag(named: "require", constant: IntType.int32.constant(3), behavior: .require) + // MODULE-FLAGS-NEXT: !3 = !{i32 [[OVERRIDEVAL:\d+]], !"override", i32 [[OVERRIDEVAL]]} + module.addFlag(named: "override", constant: IntType.int32.constant(4), behavior: .override) + // MODULE-FLAGS-NEXT: !4 = !{i32 [[APPVAL:\d+]], !"append", i32 [[APPVAL]]} + module.addFlag(named: "append", constant: IntType.int32.constant(5), behavior: .append) + // MODULE-FLAGS-NEXT: !5 = !{i32 [[APPUNIQVAL:\d+]], !"appendUnique", i32 [[APPUNIQVAL]]} + module.addFlag(named: "appendUnique", constant: IntType.int32.constant(6), behavior: .appendUnique) + + module.dump() + }) + } + + func testModuleRetrieveFlags() { + let module = Module(name: "ModuleFlagsTest") + module.addFlag(named: "error", constant: IntType.int32.constant(1), behavior: .error) + module.addFlag(named: "warning", constant: IntType.int32.constant(2), behavior: .warning) + module.addFlag(named: "require", constant: IntType.int32.constant(3), behavior: .require) + module.addFlag(named: "override", constant: IntType.int32.constant(4), behavior: .override) + module.addFlag(named: "append", constant: IntType.int32.constant(5), behavior: .append) + module.addFlag(named: "appendUnique", constant: IntType.int32.constant(6), behavior: .appendUnique) + + guard let flags = module.flags else { + XCTFail() + return + } + + XCTAssertEqual(flags.count, 6) + + XCTAssertEqual(flags[0].behavior, .error) + XCTAssertEqual(flags[1].behavior, .warning) + XCTAssertEqual(flags[2].behavior, .require) + XCTAssertEqual(flags[3].behavior, .override) + XCTAssertEqual(flags[4].behavior, .append) + XCTAssertEqual(flags[5].behavior, .appendUnique) + + XCTAssertEqual(flags[0].key, "error") + XCTAssertEqual(flags[1].key, "warning") + XCTAssertEqual(flags[2].key, "require") + XCTAssertEqual(flags[3].key, "override") + XCTAssertEqual(flags[4].key, "append") + XCTAssertEqual(flags[5].key, "appendUnique") + } + + #if !os(macOS) + static var allTests = testCase([ + ("testAddModuleFlags", testAddModuleFlags), + ("testModuleRetrieveFlags", testModuleRetrieveFlags), + ]) + #endif +} diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index a612e8a7..b94b4fcd 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -14,5 +14,6 @@ XCTMain([ // FIXME: These tests cannot run on Linux without SEGFAULT'ing. // JITSpec.allTests, ModuleLinkSpec.allTests, + ModuleMetadataSpec.allTests, ]) #endif