LLVMSwift 是一个纯 Swift 接口,用于访问 LLVM API 及其相关的库。它提供了原生的、易于使用的组件,使编译器开发变得有趣。
LLVM IR 程序的基本组织单元是 Module
let module = Module(name: "main")
LLVM IR 的构建由 IRBuilder
对象处理。IRBuilder
是一个指向上下文的游标,因此它具有扩展上下文并在其中移动的方法。
定义函数并将游标移动到我们可以开始插入指令的位置,可以这样做:
let builder = IRBuilder(module: module)
let main = builder.addFunction("main",
type: FunctionType([], IntType.int64))
let entry = main.appendBasicBlock(named: "entry")
builder.positionAtEnd(of: entry)
插入指令会创建原生的 IRValue
占位符对象,这使我们能够像构建 Swift 程序一样构建 LLVM IR 程序
let constant = IntType.int64.constant(21)
let sum = builder.buildAdd(constant, constant)
builder.buildRet(sum)
这个简单的程序生成以下 IR:
// module.dump()
define i64 @main() {
entry:
ret i64 42
}
LLVM IR 是一种强类型的、静态类型的语言。因此,值和函数都标有它们的类型,并且它们之间的转换必须是显式的(参见 转换操作)。LLVMSwift 使用符合 IRType
协议的值来表示这一点,并定义了以下类型:
类型 | 代表 |
---|---|
VoidType | 无类型;没有大小 |
IntType | 整数和布尔值 (i1 ) |
FloatType | 浮点数值 |
FunctionType | 函数值 |
LabelType | 代码标签 |
TokenType | 与指令配对的值 |
MetadataType | 嵌入的元数据 |
X86MMXType | X86 MMX 值 |
PointerType | 指针值 |
VectorType | SIMD 数据 |
ArrayType | 同构值 |
Structure Type | 异构值 |
控制流通过无条件和条件 br
指令更改。
LLVM 还因一种称为 PHI 节点的特定于控制流的 IR 构造而闻名。由于 LLVM IR 中的所有指令都采用 SSA(静态单赋值)形式,因此当变量赋值的值取决于控制流通过程序的路径时,PHI 节点是必要的。例如,让我们尝试在 IR 中构建以下 Swift 程序:
func calculateFibs(_ backward : Bool) -> Double {
let retVal : Double
if !backward {
// the fibonacci series (sort of)
retVal = 1/89
} else {
// the fibonacci series (sort of) backwards
retVal = 1/109
}
return retVal
}
请注意,retVal
的值取决于控制流通过此程序的路径,因此我们必须发出一个 PHI 节点来正确初始化它
let function = builder.addFunction("calculateFibs",
type: FunctionType([IntType.int1],
FloatType.double))
let entryBB = function.appendBasicBlock(named: "entry")
builder.positionAtEnd(of: entryBB)
// allocate space for a local value
let local = builder.buildAlloca(type: FloatType.double, name: "local")
// Compare to the condition
let test = builder.buildICmp(function.parameters[0], IntType.int1.zero(), .equal)
// Create basic blocks for "then", "else", and "merge"
let thenBB = function.appendBasicBlock(named: "then")
let elseBB = function.appendBasicBlock(named: "else")
let mergeBB = function.appendBasicBlock(named: "merge")
builder.buildCondBr(condition: test, then: thenBB, else: elseBB)
// MARK: Then Block
builder.positionAtEnd(of: thenBB)
// local = 1/89, the fibonacci series (sort of)
let thenVal = FloatType.double.constant(1/89)
// Branch to the merge block
builder.buildBr(mergeBB)
// MARK: Else Block
builder.positionAtEnd(of: elseBB)
// local = 1/109, the fibonacci series (sort of) backwards
let elseVal = FloatType.double.constant(1/109)
// Branch to the merge block
builder.buildBr(mergeBB)
// MARK: Merge Block
builder.positionAtEnd(of: mergeBB)
let phi = builder.buildPhi(FloatType.double, name: "phi_example")
phi.addIncoming([
(thenVal, thenBB),
(elseVal, elseBB),
])
builder.buildStore(phi, to: local)
let ret = builder.buildLoad(local, type: FloatType.double, name: "ret")
builder.buildRet(ret)
此程序生成以下 IR:
define double @calculateFibs(i1) {
entry:
%local = alloca double
%1 = icmp ne i1 %0, false
br i1 %1, label %then, label %else
then: ; preds = %entry
br label %merge
else: ; preds = %entry
br label %merge
merge: ; preds = %else, %then
%phi_example = phi double [ 0x3F8702E05C0B8170, %then ], [ 0x3F82C9FB4D812CA0, %else ]
store double %phi_example, double* %local
%ret = load double, double* %local
ret double %ret
}
LLVMSwift 提供了一个 JIT 抽象,使在 LLVM 模块中执行代码变得快速而简单。让我们执行之前 PHI 节点的示例:
// Setup the JIT
let jit = try JIT(machine: TargetMachine())
typealias FnPtr = @convention(c) (Bool) -> Double
_ = try jit.addEagerlyCompiledIR(module) { (name) -> JIT.TargetAddress in
return JIT.TargetAddress()
}
// Retrieve a handle to the function we're going to invoke
let addr = try jit.address(of: "calculateFibs")
let fn = unsafeBitCast(addr, to: FnPtr.self)
// Call the function!
print(fn(true)) // 0.00917431192660551...
print(fn(false)) // 0.0112359550561798...
在构建 LLVMSwift 之前,您需要完成几个繁琐的步骤
brew install llvm@11
llvm-config
在您的 PATH
中/bin
文件夹中。swift utils/make-pkgconfig.swift
完成此操作后,您可以将 LLVMSwift 添加为您自己的 Swift 编译器项目的依赖项!
.package(url: "https://github.com/llvm-swift/LLVMSwift.git", from: "0.8.0")
我们真的建议将 SwiftPM 与 LLVMSwift 一起使用,但是如果您的项目结构使得使用 SwiftPM 不切实际或不可能,请使用以下说明
Sources/
中的文件添加到您的 Xcode 项目。Library Search Paths
下添加 llvm-config --libdir
的输出Header Search Paths
下添加 llvm-config --includedir
的输出Link Target with Libraries
下,拖入 /path/to/your/llvm/lib/libLLVM.dylib
Trill 的所有代码生成都使用了这个项目,Trill。
本项目根据 MIT 许可证发布,该许可证的副本在此仓库中提供。