菜单

IR (中间表示)

相关源文件

IR(中间表示)是 Kotlin 编译器管道中的一个关键组件,它作为前端(解析和分析)与后端(代码生成)之间的通用代码表示。本页面将介绍 IR 的结构和目的、如何对其应用转换,以及它如何实现 Kotlin 的跨平台编译。

IR 位于 Kotlin 编译器的中间阶段,在前端从源代码生成结构化表示之后,但在特定于后端的代码生成之前。有关前端 IR (FIR) 的信息,请参阅页面 FIR (前端 IR)

IR 结构和组织

IR 以树状结构表示 Kotlin 代码,该结构在保留所有必要代码生成信息的同时,抽象化了源代码的语法。

来源:compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/FunctionInlining.kt19-33 compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrInlineUtils.kt8-13

关键组件

  1. 声明:定义程序结构(类、函数、属性)

    • IrClass:表示类、接口、对象、枚举类
    • IrFunction:所有类函数声明的基类
    • IrProperty:具有关联的 getter/setter 的属性
    • IrVariable:局部变量
  2. 表达式:表示产生值的计算

    • IrCall:函数调用
    • IrGetValue/IrSetValue:变量访问/赋值
    • IrReturn:return 语句
    • IrWhen:条件表达式
  3. 符号:充当声明的引用

    • IrClassSymbol:类引用
    • IrFunctionSymbol:函数引用
    • 符号可以是“绑定的”(指向声明)或“未绑定的”(占位符)
  4. 类型:指定表达式产生的值的种类

    • IrSimpleType:常规类型(类、接口)
    • IrDynamicType:动态类型(主要用于 JavaScript)
  5. 签名:声明的唯一标识符

    • IdSignature:用于跨模块链接和序列化

来源:compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/DeclarationTable.kt12-59 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/signature/IdSignatureComputers.kt22-59

IR 转换和下推

IR 创建后,它会经历许多称为“下推”(lowerings)的转换,这些转换会修改 IR 树,逐步为代码生成做好准备。

来源:kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/NativeLoweringPhases.kt149-176 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt747-779 compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt149-186

函数内联

最重要的转换之一是函数内联,它在适当转换后用函数体替换对内联函数的调用。这分为两个阶段:

  1. 内联私有函数:处理私有内联函数的第一个阶段
  2. 内联所有函数:处理所有其他内联函数的第二个阶段

来源:compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/FunctionInlining.kt37-134 compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/InlineFunctionResolver.kt27-74 compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/CommonInlineCallableReferenceToLambdaPhase.kt7-14

IR 阶段的函数内联支持多项重要功能:

  • 跨模块内联(使用依赖项中的内联函数)
  • Reified 类型参数的类型特化
  • Lambda 参数内联
  • 正确处理 Lambda 中的 return 语句

来源:compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/InlineCallableReferenceToLambda.kt36-98 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeIrInliner.kt12-18

其他关键转换

除了函数内联,IR 还经历许多其他转换:

  • 默认参数:转换带默认参数的函数
  • 枚举处理:将枚举类转换为具有静态字段的常规类
  • 局部函数:将局部函数提升为顶层函数,支持闭包
  • 类型操作:实现类型检查、类型转换和 reified 类型操作
  • Tailrec:将尾递归函数转换为循环
  • Suspend 函数:将挂起函数转换为状态机
  • 属性访问器:处理属性的 getter 和 setter
  • 特定平台转换:为目标平台准备代码

来源:kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/NativeLoweringPhases.kt551-615

IR 序列化

IR 可以被序列化以支持增量编译、多模块项目和库分发。

来源:compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrFileSerializer.kt114-205 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/serializeModuleIntoKlib.kt1-40

序列化组件

  1. IrFileSerializer:将 IR 文件序列化为 protobuf 格式
  2. IrDeclarationSerializer:序列化声明(类、函数、属性)
  3. IrBodySerializer:序列化函数体(表达式和语句)
  4. Protocol Buffers:用于高效二进制表示的格式
  5. KLIB:Kotlin 库格式,包含序列化的 IR 和元数据

来源:compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrSerializationSettings.kt36-46 compiler/ir/serialization.common/src/KotlinIr.proto1-2

反序列化和链接

当消费序列化的 IR 时

  1. 声明结构:首先被重建
  2. 符号表:构建用于跟踪声明
  3. 延迟体反序列化:函数体按需加载
  4. 符号链接:引用连接到声明

对于内联函数,存在特殊的反序列化器,可以在需要内联时按需加载函数体。

来源:compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrFileDeserializer.kt1-6 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrBodyDeserializer.kt1-35 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/NonLinkingIrInlineFunctionDeserializer.kt24-35

多平台支持

IR 通过提供可独立于平台转换的通用表示,实现了 Kotlin 的多平台能力

  1. 平台无关的转换
  2. 由平台特定的优化进一步处理
  3. 序列化到可被不同后端使用的库中

来源:compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt80-109 native/native.tests/tests/org/jetbrains/kotlin/konan/test/NativeKlibSerializerFacade.kt40-61

每个后端(JVM、JS、Native、WebAssembly)都有处理平台特定要求的特定 IR 降低阶段,但它们都从相同的通用 IR 结构开始。

来源:kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FirNativeSerializer.kt1-5 js/js.tests/test/org/jetbrains/kotlin/benchmarks/GenerateIrRuntime.kt77-109 compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt34-61

函数内联过程详解

函数内联是 IR 阶段最复杂的转换之一。该过程包括

来源:compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/FunctionInlining.kt136-201 compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/FunctionInlining.kt37-61

函数内联的关键步骤

  1. 检测:识别内联函数的调用
  2. 解析:查找函数体(可能来自库)
  3. 转换:
    • 用实参替换形参
    • 处理 return 语句
    • 处理嵌套的内联函数
  4. 集成:将转换后的函数体插入调用点

函数内联分两个阶段进行,以正确处理依赖关系

  • 首先,仅内联 private 函数
  • 然后,如有必要,生成合成访问器
  • 最后,内联所有剩余的函数

来源:compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/CommonLoweringPhases.kt54-63 compiler/ir/ir.inline/src/org/jetbrains/kotlin/ir/inline/CommonLoweringPhases.kt103-140

IR 对 IDE 和工具的支持

虽然 IR 主要用于编译,但也支持工具

  1. 增量编译:仅重新编译已更改的内容
  2. IDE 功能:代码分析和重构
  3. 调试信息:行号和源映射
  4. 错误报告:精确的错误位置

IR 结构保留了足够多的关于原始源代码的信息,可以支持这些功能,同时仍然是一个可优化的中间表示。

来源:compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt204-208

结论

IR 是 Kotlin 编译器的骨干,它实现了

  1. 平台无关的优化
  2. 目标特定的代码生成
  3. 跨模块内联和其他高级功能
  4. 库格式,用于分发编译后的代码

它的设计允许 Kotlin 在所有目标平台保持相同的语言语义,同时仍然为每个平台生成最佳代码。

来源:compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/IrInlineUtils.kt25-64