Skip to content

Add accessors for inline assembly #168

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 1 commit into from
Jan 3, 2019
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
19 changes: 17 additions & 2 deletions Sources/LLVM/IRBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 21 additions & 0 deletions Sources/LLVM/Module.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
54 changes: 45 additions & 9 deletions Sources/LLVM/Operation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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]!
}
}
Expand Down Expand Up @@ -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,
Expand All @@ -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]!
}
}
Expand Down Expand Up @@ -207,7 +207,7 @@ public enum AtomicOrdering: Comparable {
}

/// Retrieves the corresponding `LLVMAtomicOrdering`.
public var llvm: LLVMAtomicOrdering {
var llvm: LLVMAtomicOrdering {
return AtomicOrdering.orderingMapping[self]!
}
}
Expand Down Expand Up @@ -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,
Expand All @@ -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]!
}
}
36 changes: 36 additions & 0 deletions Tests/LLVMTests/IRBuilderSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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"]) {
Expand Down