From b90baf4f0fce505957784637aacbe8b24c9d8266 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 29 Nov 2018 20:57:38 -0500 Subject: [PATCH] Add accessors for inline assembly --- Sources/LLVM/IRBuilder.swift | 19 ++++++++-- Sources/LLVM/Module.swift | 21 +++++++++++ Sources/LLVM/Operation.swift | 54 ++++++++++++++++++++++++----- Tests/LLVMTests/IRBuilderSpec.swift | 36 +++++++++++++++++++ 4 files changed, 119 insertions(+), 11 deletions(-) diff --git a/Sources/LLVM/IRBuilder.swift b/Sources/LLVM/IRBuilder.swift index 5d4a5b49..c2f79176 100644 --- a/Sources/LLVM/IRBuilder.swift +++ b/Sources/LLVM/IRBuilder.swift @@ -1599,11 +1599,26 @@ extension IRBuilder { /// /// - returns: A representation of the newly created inline assembly /// expression. - 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) + public func buildInlineAssembly( + _ asm: String, dialect: InlineAssemblyDialect, type: FunctionType, + constraints: String = "", + hasSideEffects: Bool = true, needsAlignedStack: Bool = true + ) -> IRValue { + var asm = asm.utf8CString + var constraints = constraints.utf8CString + return asm.withUnsafeMutableBufferPointer { asm in + return constraints.withUnsafeMutableBufferPointer { constraints in + return LLVMGetInlineAsm(type.asLLVM(), + asm.baseAddress, asm.count, + constraints.baseAddress, constraints.count, + hasSideEffects.llvm, needsAlignedStack.llvm, + dialect.llvm) + } + } } } + private func lowerVector(_ type: IRType) -> IRType { guard let vectorType = type as? VectorType else { return type diff --git a/Sources/LLVM/Module.swift b/Sources/LLVM/Module.swift index 2e66b2bf..24d03633 100644 --- a/Sources/LLVM/Module.swift +++ b/Sources/LLVM/Module.swift @@ -119,6 +119,18 @@ public final class Module: CustomStringConvertible { } } + /// Retrieves the inline assembly for this module, if any. + public var inlineAssembly: String { + get { + var length: Int = 0 + guard let id = LLVMGetModuleInlineAsm(llvm, &length) else { return "" } + return String(cString: id) + } + set { + LLVMSetModuleInlineAsm2(llvm, newValue, newValue.utf8.count) + } + } + /// 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 @@ -430,6 +442,15 @@ extension Module { public func addAlias(name: String, to aliasee: IRGlobal, type: IRType) -> Alias { return Alias(llvm: LLVMAddAlias(llvm, type.asLLVM(), aliasee.asLLVM(), name)) } + + /// Append to the module-scope inline assembly blocks. + /// + /// A trailing newline is added if the given string doesn't have one. + /// + /// - parameter asm: The inline assembly expression template string. + public func appendInlineAssembly(_ asm: String) { + LLVMAppendModuleInlineAsm(llvm, asm, asm.count) + } } // MARK: Module Flags diff --git a/Sources/LLVM/Operation.swift b/Sources/LLVM/Operation.swift index e78dc0f3..724b909c 100644 --- a/Sources/LLVM/Operation.swift +++ b/Sources/LLVM/Operation.swift @@ -51,16 +51,16 @@ public enum IntPredicate { /// less than or equal to the second. case signedLessThanOrEqual - static let predicateMapping: [IntPredicate: LLVMIntPredicate] = [ + private 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 { + var llvm: LLVMIntPredicate { return IntPredicate.predicateMapping[self]! } } @@ -100,7 +100,7 @@ public enum RealPredicate { /// No comparison, always returns `true`. case `true` - static let predicateMapping: [RealPredicate: LLVMRealPredicate] = [ + private static let predicateMapping: [RealPredicate: LLVMRealPredicate] = [ .false: LLVMRealPredicateFalse, .orderedEqual: LLVMRealOEQ, .orderedGreaterThan: LLVMRealOGT, .orderedGreaterThanOrEqual: LLVMRealOGE, .orderedLessThan: LLVMRealOLT, .orderedLessThanOrEqual: LLVMRealOLE, @@ -109,10 +109,10 @@ public enum RealPredicate { .unorderedGreaterThanOrEqual: LLVMRealUGE, .unorderedLessThan: LLVMRealULT, .unorderedLessThanOrEqual: LLVMRealULE, .unorderedNotEqual: LLVMRealUNE, .true: LLVMRealPredicateTrue, - ] + ] /// Retrieves the corresponding `LLVMRealPredicate`. - public var llvm: LLVMRealPredicate { + var llvm: LLVMRealPredicate { return RealPredicate.predicateMapping[self]! } } @@ -207,7 +207,7 @@ public enum AtomicOrdering: Comparable { } /// Retrieves the corresponding `LLVMAtomicOrdering`. - public var llvm: LLVMAtomicOrdering { + var llvm: LLVMAtomicOrdering { return AtomicOrdering.orderingMapping[self]! } } @@ -290,7 +290,7 @@ public enum AtomicReadModifyWriteOperation { /// ``` case umin - static let atomicRMWMapping: [AtomicReadModifyWriteOperation: LLVMAtomicRMWBinOp] = [ + private static let atomicRMWMapping: [AtomicReadModifyWriteOperation: LLVMAtomicRMWBinOp] = [ .xchg: LLVMAtomicRMWBinOpXchg, .add: LLVMAtomicRMWBinOpAdd, .sub: LLVMAtomicRMWBinOpSub, .and: LLVMAtomicRMWBinOpAnd, .nand: LLVMAtomicRMWBinOpNand, .or: LLVMAtomicRMWBinOpOr, @@ -300,7 +300,43 @@ public enum AtomicReadModifyWriteOperation { ] /// Retrieves the corresponding `LLVMAtomicRMWBinOp`. - public var llvm: LLVMAtomicRMWBinOp { + var llvm: LLVMAtomicRMWBinOp { return AtomicReadModifyWriteOperation.atomicRMWMapping[self]! } } + +/// Enumerates the dialects of inline assembly LLVM's parsers can handle. +public enum InlineAssemblyDialect { + /// The dialect of assembly created at Bell Labs by AT&T. + /// + /// AT&T syntax differs from Intel syntax in a number of ways. Notably: + /// + /// - The source operand is before the destination operand + /// - Immediate operands are prefixed by a dollar-sign (`$`) + /// - Register operands are preceded by a percent-sign (`%`) + /// - The size of memory operands is determined from the last character of the + /// the opcode name. Valid suffixes include `b` for "byte" (8-bit), + /// `w` for "word" (16-bit), `l` for "long-word" (32-bit), and `q` for + /// "quad-word" (64-bit) memory references + case att + /// The dialect of assembly created at Intel. + /// + /// Intel syntax differs from AT&T syntax in a number of ways. Notably: + /// + /// - The destination operand is before the source operand + /// - Immediate and register operands have no prefix. + /// - Memory operands are annotated with their sizes. Valid annotations + /// include `byte ptr` (8-bit), `word ptr` (16-bit), `dword ptr` (32-bit) and + /// `qword ptr` (64-bit). + case intel + + private static let dialectMapping: [InlineAssemblyDialect: LLVMInlineAsmDialect] = [ + .att: LLVMInlineAsmDialectATT, + .intel: LLVMInlineAsmDialectIntel, + ] + + /// Retrieves the corresponding `LLVMInlineAsmDialect`. + var llvm: LLVMInlineAsmDialect { + return InlineAssemblyDialect.dialectMapping[self]! + } +} diff --git a/Tests/LLVMTests/IRBuilderSpec.swift b/Tests/LLVMTests/IRBuilderSpec.swift index d3997933..b8d35ece 100644 --- a/Tests/LLVMTests/IRBuilderSpec.swift +++ b/Tests/LLVMTests/IRBuilderSpec.swift @@ -23,6 +23,42 @@ class IRBuilderSpec : XCTestCase { module.dump() }) + XCTAssert(fileCheckOutput(of: .stderr, withPrefixes: ["IRBUILDER-INLINE-ASM"]) { + // IRBUILDER-INLINE-ASM: ; ModuleID = '[[ModuleName:IRBuilderInlineAsmTest]]' + // IRBUILDER-INLINE-ASM-NEXT: source_filename = "[[ModuleName]]" + let module = Module(name: "IRBuilderInlineAsmTest") + let builder = IRBuilder(module: module) + + // IRBUILDER-INLINE-ASM: module asm "i32 (i32) asm \22bswap $0\22, \22=r,r\22" + module.appendInlineAssembly(""" + i32 (i32) asm "bswap $0", "=r,r" + """) + // IRBUILDER-INLINE-ASM-NEXT: %X = call i32 asm \22bswap $0\22, \22=r,r\22(i32 %Y) + module.appendInlineAssembly(""" + %X = call i32 asm "bswap $0", "=r,r"(i32 %Y) + """) + + // IRBUILDER-INLINE-ASM: @a = global i32 1 + let g1 = builder.addGlobal("a", type: IntType.int32) + g1.initializer = Int32(1) + + // IRBUILDER-INLINE-ASM: define void @main() { + let main = builder.addFunction("main", + type: FunctionType(argTypes: [], + returnType: VoidType())) + // IRBUILDER-INLINE-ASM-NEXT: entry: + let entry = main.appendBasicBlock(named: "entry") + builder.positionAtEnd(of: entry) + let ty = FunctionType(argTypes: [ PointerType(pointee: IntType.int32) ], returnType: VoidType()) + let emptyASM = builder.buildInlineAssembly("", dialect: .att, type: ty, constraints: "=r,0", hasSideEffects: true, needsAlignedStack: true) + // IRBUILDER-INLINE-ASM-NEXT: call void asm sideeffect alignstack "\00", "=r,0\00"(i32* @a) + _ = builder.buildCall(emptyASM, args: [ g1 ]) + // IRBUILDER-INLINE-ASM-NEXT: ret void + builder.buildRetVoid() + // IRBUILDER-INLINE-ASM-NEXT: } + module.dump() + }) + // MARK: Arithmetic Instructions XCTAssert(fileCheckOutput(of: .stderr, withPrefixes: ["IRBUILDERARITH"]) {