菜单

函数内联

相关源文件

函数内联是 Kotlin 编译器中的一项关键优化技术,它通过在调用点将函数调用替换为函数的实际主体来实现。本文档提供了 Kotlin 编译器中函数内联实现的详细技术概述,重点关注 IR(中间表示)阶段。有关编译阶段的总体信息,请参阅 Kotlin 编译器与工具概述

概述

Kotlin 中的函数内联系统具有多种用途:

  1. 减少函数调用开销
  2. 启用 reified 类型参数等高级功能
  3. 优化传递给内联函数的 lambda 表达式
  4. 传播常量值并启用进一步的优化

内联过程实现为一组作用于 IR 树的 IR 转换。它根据目标平台以多个阶段运行,并通过 Kotlin 源代码中的 inline 修饰符进行控制。

来源

核心组件

函数内联实现包含几个协同工作的关键组件,以执行内联转换。

FunctionInlining 类

The FunctionInlining 类是内联过程的主要入口点。它扩展了 IrTransformer<IrDeclaration> 并实现了 BodyLoweringPass 接口,使其成为 IR 降低流水线的一部分。

The FunctionInlining 构造函数接受

  • context:提供对编译器服务的访问权限的 LoweringContext
  • inlineFunctionResolver:负责确定应内联哪些函数的 InlineFunctionResolver
  • 用于自定义内联行为的其他参数

The main method is visitFunctionAccess,它在 IR 树中为每个函数调用调用。它确定函数是否应被内联并启动内联过程。

来源

InlineFunctionResolver

The InlineFunctionResolver determines which functions should be inlined. It provides the following key methods

  • needsInlining(symbol: IrFunctionSymbol): Determines if a function should be inlined
  • needsInlining(expression: IrFunctionAccessExpression): Determines if a function call should be inlined
  • getFunctionDeclaration(symbol: IrFunctionSymbol): Retrieves the declaration of the function to be inlined

The resolver operates in different modes (InlineMode)

  • PRIVATE_INLINE_FUNCTIONS: Only inline private functions
  • ALL_INLINE_FUNCTIONS: Inline all functions marked with the inline modifier
  • ALL_FUNCTIONS: Inline all functions (rarely used, mainly for testing)

Each target platform (JVM, JS, Native, Wasm) has its own implementation of the resolver to handle platform-specific concerns.

来源

CallInlining

The CallInlining inner class within FunctionInlining performs the actual inlining of a function. Its main methods include

  • inline(callSite: IrFunctionAccessExpression, parent: IrDeclarationParent): The entry point for inlining a function call
  • inlineFunction(callSite: IrFunctionAccessExpression, callee: IrFunction, originalInlinedElement: IrElement): Creates an IR block representing the inlined function
  • evaluateArguments(...): Evaluates function arguments and substitutes parameters

This class handles the complex logic of

  1. Creating temporary variables for function arguments
  2. Substituting parameter references in the function body
  3. Handling default parameter values
  4. Creating a returnable block for the inlined function body

来源

InlinePostprocessor

The InlinePostprocessor inner class within CallInlining transforms the inlined function body. It

  1. Transforms return statements to return to the correct target
  2. Substitutes parameter references with argument expressions
  3. Handles nested inline function calls, particularly inline lambdas

来源

Inlining Process

The function inlining process consists of several steps, outlined below.

Determining Which Functions to Inline

The process begins when FunctionInlining.visitFunctionAccess is called for a function call. It asks the InlineFunctionResolver if the function should be inlined

The resolver checks various conditions

  • Is the function marked with the inline modifier?
  • Does it match the current inlining mode (private only or all inline functions)?
  • Is it an external function (and are external functions allowed to be inlined)?

For example, in the first phase of inlining, only private inline functions are inlined

来源

Function Body Preprocessing

Before inlining, the function body is preprocessed to handle type substitution and other transformations

This processing substitutes type parameters in the function body with the actual type arguments from the call site.

来源

Argument Evaluation and Parameter Substitution

The evaluateArguments method in CallInlining handles

  1. Creating temporary variables for the function arguments
  2. Establishing a mapping from parameters to their substitutions
  3. Special handling for default parameter values and varargs

The substituteMap maps each parameter of the inlined function to the expression that should replace it in the inlined code.

来源

Return Statement Transformation

The InlinePostprocessor transforms return statements in the inlined function

This redirects the return to the returnable block that wraps the inlined function body, ensuring correct control flow.

来源

Parameter Reference Substitution

The InlinePostprocessor also transforms references to parameters

This replaces each reference to a parameter with the corresponding argument expression from the substituteMap.

来源

Handling Inline Lambdas

When an inline lambda is called within an inlined function, it's also inlined

This process is recursive - lambdas passed to inline functions are themselves inlined at their call sites, potentially multiple levels deep.

来源

特殊情况

Callable References

Callable references (e.g., ::function) passed to inline functions require special handling. They are first transformed to lambda expressions by the InlineCallableReferenceToLambdaPhase

The transformation creates a lambda that delegates to the original function

来源

挂起函数

由于挂起函数会转换为 continuation-passing 风格(CPS)的转换,因此在内联时需要特殊处理。AddContinuationLowering 类负责处理此问题。

这确保了在内联挂起函数时,continuation 参数能够被正确处理。

来源

合成访问器

从内联函数访问的私有成员需要合成访问器。SyntheticAccessorLowering 创建这些访问器。

这些访问器确保私有成员可以从内联代码中访问。

来源

特定于后端

每个后端(JVM、Native、JS、Wasm)在函数内联方面都有自己的方法,但它们都使用通用的 FunctionInlining 基础设施。

JVM 后端

JVM 后端通过 JvmIrInlinerJvmInlineFunctionResolver 实现内联。它处理 JVM 特定的问题,例如:

  • 内联代码的调试信息
  • 私有成员的合成访问器
  • 挂起函数的特殊处理
  • 内联类重新生成

来源

Native 后端

Native 后端使用 NativeIrInlinerNativeInlineFunctionResolver。它处理:

  • 从 KLIB 反序列化内联函数体
  • 特定于 Native 的引用处理
  • 反射兼容性

来源

JS 后端

JS 后端通过 JsLoweringPhases.kt 中的阶段实现内联。它处理:

  • 特定于 JS 的 lambda 处理
  • 与 JavaScript 代码的互操作
  • 可调用引用的 JavaScript 适配

来源

Wasm 后端

Wasm 后端使用 WasmFunctionInlining,其关注点与 JS 后端类似,但针对 WebAssembly 进行了调整。

  • 特定于 Wasm 的类型处理
  • Wasm 的函数引用包装
  • Wasm 内存模型兼容性

来源

Pipeline集成

函数内联被集成到 IR 降低管线中,作为一系列阶段。典型的顺序是:

  1. 准备代码进行内联(升级可调用引用、处理数组构造函数等)
  2. 首先内联私有函数
  3. 生成合成访问器并处理外部 this 引用
  4. 内联所有剩余的内联函数
  5. 验证并清理内联的 IR
  6. 执行特定于后端的后处理

确切的顺序因后端而异,并在特定平台的降低阶段文件中定义。

来源

调试支持

内联系统包含对内联代码调试的支持。这包括:

  1. 保留原始函数的源代码位置
  2. 创建假的局部变量来标记内联区域
  3. 在 IDE 调试会话中对内联函数调用的特殊处理

例如,在 JVM 后端中

这使得调试器在单步执行内联代码时能够正确显示源代码和变量。

来源

结论

函数内联是 Kotlin 编译器中一个复杂但至关重要的优化。它在 IR 级别进行操作,用函数体替换函数调用,同时处理许多特殊情况和特定于平台的要求。该系统设计得非常灵活,为各种目标平台提供了不同的阶段和解析器实现,确保了在所有支持的平台上进行高效且正确的内联。

对于任何从事 Kotlin 编译器工作或开发与内联代码交互的优化的人来说,理解这个系统至关重要。