LLVMSwift

Build Status Documentation Slack Invite

LLVMSwift 是一个纯 Swift 接口,用于访问 LLVM API 及其相关的库。它提供了原生的、易于使用的组件,使编译器开发变得有趣。

简介

LLVM IR

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
}

JIT

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 之前,您需要完成几个繁琐的步骤

完成此操作后,您可以将 LLVMSwift 添加为您自己的 Swift 编译器项目的依赖项!

使用 Swift Package Manager 安装

.package(url: "https://github.com/llvm-swift/LLVMSwift.git", from: "0.8.0")

不使用 Swift Package Manager 安装

我们真的建议将 SwiftPM 与 LLVMSwift 一起使用,但是如果您的项目结构使得使用 SwiftPM 不切实际或不可能,请使用以下说明

Trill 的所有代码生成都使用了这个项目,Trill

作者

许可

本项目根据 MIT 许可证发布,该许可证的副本在此仓库中提供。