Skip to content

Commit b90baf4

Browse files
committed
Add accessors for inline assembly
1 parent 993c0f3 commit b90baf4

File tree

4 files changed

+119
-11
lines changed

4 files changed

+119
-11
lines changed

Sources/LLVM/IRBuilder.swift

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1599,11 +1599,26 @@ extension IRBuilder {
15991599
///
16001600
/// - returns: A representation of the newly created inline assembly
16011601
/// expression.
1602-
public func buildInlineAssembly(_ asm: String, type: FunctionType, constraints: String = "", hasSideEffects: Bool = true, needsAlignedStack: Bool = true) -> IRValue {
1603-
return LLVMConstInlineAsm(type.asLLVM(), asm, constraints, hasSideEffects.llvm, needsAlignedStack.llvm)
1602+
public func buildInlineAssembly(
1603+
_ asm: String, dialect: InlineAssemblyDialect, type: FunctionType,
1604+
constraints: String = "",
1605+
hasSideEffects: Bool = true, needsAlignedStack: Bool = true
1606+
) -> IRValue {
1607+
var asm = asm.utf8CString
1608+
var constraints = constraints.utf8CString
1609+
return asm.withUnsafeMutableBufferPointer { asm in
1610+
return constraints.withUnsafeMutableBufferPointer { constraints in
1611+
return LLVMGetInlineAsm(type.asLLVM(),
1612+
asm.baseAddress, asm.count,
1613+
constraints.baseAddress, constraints.count,
1614+
hasSideEffects.llvm, needsAlignedStack.llvm,
1615+
dialect.llvm)
1616+
}
1617+
}
16041618
}
16051619
}
16061620

1621+
16071622
private func lowerVector(_ type: IRType) -> IRType {
16081623
guard let vectorType = type as? VectorType else {
16091624
return type

Sources/LLVM/Module.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,18 @@ public final class Module: CustomStringConvertible {
119119
}
120120
}
121121

122+
/// Retrieves the inline assembly for this module, if any.
123+
public var inlineAssembly: String {
124+
get {
125+
var length: Int = 0
126+
guard let id = LLVMGetModuleInlineAsm(llvm, &length) else { return "" }
127+
return String(cString: id)
128+
}
129+
set {
130+
LLVMSetModuleInlineAsm2(llvm, newValue, newValue.utf8.count)
131+
}
132+
}
133+
122134
/// Print a representation of a module to a file at the given path.
123135
///
124136
/// If the provided path is not suitable for writing, this function will throw
@@ -430,6 +442,15 @@ extension Module {
430442
public func addAlias(name: String, to aliasee: IRGlobal, type: IRType) -> Alias {
431443
return Alias(llvm: LLVMAddAlias(llvm, type.asLLVM(), aliasee.asLLVM(), name))
432444
}
445+
446+
/// Append to the module-scope inline assembly blocks.
447+
///
448+
/// A trailing newline is added if the given string doesn't have one.
449+
///
450+
/// - parameter asm: The inline assembly expression template string.
451+
public func appendInlineAssembly(_ asm: String) {
452+
LLVMAppendModuleInlineAsm(llvm, asm, asm.count)
453+
}
433454
}
434455

435456
// MARK: Module Flags

Sources/LLVM/Operation.swift

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,16 +51,16 @@ public enum IntPredicate {
5151
/// less than or equal to the second.
5252
case signedLessThanOrEqual
5353

54-
static let predicateMapping: [IntPredicate: LLVMIntPredicate] = [
54+
private static let predicateMapping: [IntPredicate: LLVMIntPredicate] = [
5555
.equal: LLVMIntEQ, .notEqual: LLVMIntNE, .unsignedGreaterThan: LLVMIntUGT,
5656
.unsignedGreaterThanOrEqual: LLVMIntUGE, .unsignedLessThan: LLVMIntULT,
5757
.unsignedLessThanOrEqual: LLVMIntULE, .signedGreaterThan: LLVMIntSGT,
5858
.signedGreaterThanOrEqual: LLVMIntSGE, .signedLessThan: LLVMIntSLT,
5959
.signedLessThanOrEqual: LLVMIntSLE,
60-
]
60+
]
6161

6262
/// Retrieves the corresponding `LLVMIntPredicate`.
63-
public var llvm: LLVMIntPredicate {
63+
var llvm: LLVMIntPredicate {
6464
return IntPredicate.predicateMapping[self]!
6565
}
6666
}
@@ -100,7 +100,7 @@ public enum RealPredicate {
100100
/// No comparison, always returns `true`.
101101
case `true`
102102

103-
static let predicateMapping: [RealPredicate: LLVMRealPredicate] = [
103+
private static let predicateMapping: [RealPredicate: LLVMRealPredicate] = [
104104
.false: LLVMRealPredicateFalse, .orderedEqual: LLVMRealOEQ,
105105
.orderedGreaterThan: LLVMRealOGT, .orderedGreaterThanOrEqual: LLVMRealOGE,
106106
.orderedLessThan: LLVMRealOLT, .orderedLessThanOrEqual: LLVMRealOLE,
@@ -109,10 +109,10 @@ public enum RealPredicate {
109109
.unorderedGreaterThanOrEqual: LLVMRealUGE, .unorderedLessThan: LLVMRealULT,
110110
.unorderedLessThanOrEqual: LLVMRealULE, .unorderedNotEqual: LLVMRealUNE,
111111
.true: LLVMRealPredicateTrue,
112-
]
112+
]
113113

114114
/// Retrieves the corresponding `LLVMRealPredicate`.
115-
public var llvm: LLVMRealPredicate {
115+
var llvm: LLVMRealPredicate {
116116
return RealPredicate.predicateMapping[self]!
117117
}
118118
}
@@ -207,7 +207,7 @@ public enum AtomicOrdering: Comparable {
207207
}
208208

209209
/// Retrieves the corresponding `LLVMAtomicOrdering`.
210-
public var llvm: LLVMAtomicOrdering {
210+
var llvm: LLVMAtomicOrdering {
211211
return AtomicOrdering.orderingMapping[self]!
212212
}
213213
}
@@ -290,7 +290,7 @@ public enum AtomicReadModifyWriteOperation {
290290
/// ```
291291
case umin
292292

293-
static let atomicRMWMapping: [AtomicReadModifyWriteOperation: LLVMAtomicRMWBinOp] = [
293+
private static let atomicRMWMapping: [AtomicReadModifyWriteOperation: LLVMAtomicRMWBinOp] = [
294294
.xchg: LLVMAtomicRMWBinOpXchg, .add: LLVMAtomicRMWBinOpAdd,
295295
.sub: LLVMAtomicRMWBinOpSub, .and: LLVMAtomicRMWBinOpAnd,
296296
.nand: LLVMAtomicRMWBinOpNand, .or: LLVMAtomicRMWBinOpOr,
@@ -300,7 +300,43 @@ public enum AtomicReadModifyWriteOperation {
300300
]
301301

302302
/// Retrieves the corresponding `LLVMAtomicRMWBinOp`.
303-
public var llvm: LLVMAtomicRMWBinOp {
303+
var llvm: LLVMAtomicRMWBinOp {
304304
return AtomicReadModifyWriteOperation.atomicRMWMapping[self]!
305305
}
306306
}
307+
308+
/// Enumerates the dialects of inline assembly LLVM's parsers can handle.
309+
public enum InlineAssemblyDialect {
310+
/// The dialect of assembly created at Bell Labs by AT&T.
311+
///
312+
/// AT&T syntax differs from Intel syntax in a number of ways. Notably:
313+
///
314+
/// - The source operand is before the destination operand
315+
/// - Immediate operands are prefixed by a dollar-sign (`$`)
316+
/// - Register operands are preceded by a percent-sign (`%`)
317+
/// - The size of memory operands is determined from the last character of the
318+
/// the opcode name. Valid suffixes include `b` for "byte" (8-bit),
319+
/// `w` for "word" (16-bit), `l` for "long-word" (32-bit), and `q` for
320+
/// "quad-word" (64-bit) memory references
321+
case att
322+
/// The dialect of assembly created at Intel.
323+
///
324+
/// Intel syntax differs from AT&T syntax in a number of ways. Notably:
325+
///
326+
/// - The destination operand is before the source operand
327+
/// - Immediate and register operands have no prefix.
328+
/// - Memory operands are annotated with their sizes. Valid annotations
329+
/// include `byte ptr` (8-bit), `word ptr` (16-bit), `dword ptr` (32-bit) and
330+
/// `qword ptr` (64-bit).
331+
case intel
332+
333+
private static let dialectMapping: [InlineAssemblyDialect: LLVMInlineAsmDialect] = [
334+
.att: LLVMInlineAsmDialectATT,
335+
.intel: LLVMInlineAsmDialectIntel,
336+
]
337+
338+
/// Retrieves the corresponding `LLVMInlineAsmDialect`.
339+
var llvm: LLVMInlineAsmDialect {
340+
return InlineAssemblyDialect.dialectMapping[self]!
341+
}
342+
}

Tests/LLVMTests/IRBuilderSpec.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,42 @@ class IRBuilderSpec : XCTestCase {
2323
module.dump()
2424
})
2525

26+
XCTAssert(fileCheckOutput(of: .stderr, withPrefixes: ["IRBUILDER-INLINE-ASM"]) {
27+
// IRBUILDER-INLINE-ASM: ; ModuleID = '[[ModuleName:IRBuilderInlineAsmTest]]'
28+
// IRBUILDER-INLINE-ASM-NEXT: source_filename = "[[ModuleName]]"
29+
let module = Module(name: "IRBuilderInlineAsmTest")
30+
let builder = IRBuilder(module: module)
31+
32+
// IRBUILDER-INLINE-ASM: module asm "i32 (i32) asm \22bswap $0\22, \22=r,r\22"
33+
module.appendInlineAssembly("""
34+
i32 (i32) asm "bswap $0", "=r,r"
35+
""")
36+
// IRBUILDER-INLINE-ASM-NEXT: %X = call i32 asm \22bswap $0\22, \22=r,r\22(i32 %Y)
37+
module.appendInlineAssembly("""
38+
%X = call i32 asm "bswap $0", "=r,r"(i32 %Y)
39+
""")
40+
41+
// IRBUILDER-INLINE-ASM: @a = global i32 1
42+
let g1 = builder.addGlobal("a", type: IntType.int32)
43+
g1.initializer = Int32(1)
44+
45+
// IRBUILDER-INLINE-ASM: define void @main() {
46+
let main = builder.addFunction("main",
47+
type: FunctionType(argTypes: [],
48+
returnType: VoidType()))
49+
// IRBUILDER-INLINE-ASM-NEXT: entry:
50+
let entry = main.appendBasicBlock(named: "entry")
51+
builder.positionAtEnd(of: entry)
52+
let ty = FunctionType(argTypes: [ PointerType(pointee: IntType.int32) ], returnType: VoidType())
53+
let emptyASM = builder.buildInlineAssembly("", dialect: .att, type: ty, constraints: "=r,0", hasSideEffects: true, needsAlignedStack: true)
54+
// IRBUILDER-INLINE-ASM-NEXT: call void asm sideeffect alignstack "\00", "=r,0\00"(i32* @a)
55+
_ = builder.buildCall(emptyASM, args: [ g1 ])
56+
// IRBUILDER-INLINE-ASM-NEXT: ret void
57+
builder.buildRetVoid()
58+
// IRBUILDER-INLINE-ASM-NEXT: }
59+
module.dump()
60+
})
61+
2662
// MARK: Arithmetic Instructions
2763

2864
XCTAssert(fileCheckOutput(of: .stderr, withPrefixes: ["IRBUILDERARITH"]) {

0 commit comments

Comments
 (0)