| # Basic step-by-step inlining example for proposed model |
| |
| Let's check the following code: |
| ```kotlin |
| // lib |
| fun process(x: Int) { /* some code here */ } |
| inline fun run(block: () -> Int) = process(block()) |
| // main |
| fun foo() { |
| run { 42 } |
| } |
| ``` |
| |
| We would dig deeper into the compilation of the main module, assuming lib is already compiled. |
| |
| |
| After frontend execution, we get something like that. Here we have all calls resolved. |
| Functions from dependencies (lib) are loaded as LazyIr. |
| |
| ```kotlin |
| fun foo() : Unit { |
| run( |
| lambda@{ return@lambda 42 } |
| ) |
| } |
| |
| // dependencies: |
| // lazyIR without bodies |
| fun process(x: Int): Unit |
| inline fun run(block: Function0<Int>) |
| // ... lazyIR for Int, Unit, Function and so on |
| ``` |
| |
| Then, pre-inline lowering happens. But as we have a very simple example, there is nothing to do. |
| |
| Next, we need to load Ir of run function. For that we need to run Deserializer. But we can't run linker, |
| so references inside function body wouldn't be resolved. |
| |
| ```kotlin |
| // IrFunction |
| // name = run |
| // isInline = true |
| // valueParameter0 |
| // irType |
| // classifier = Lazy class kotlin.Function0 (from Lazy run function) |
| // typeArgument0 = Lazy class kotlin.Int (from Lazy run function) |
| // returnType = Lazy class kotlin.Unit (from Lazy run function) |
| // body |
| // IrCall symbol = Unbound function symbol with signature "process(Int) : Unit" |
| // returnType = IrType classifier = Unbound class symbol with singnature kotlin.Unit |
| // valueArgument0 = |
| // IrCall symbol = Unbound function symbol with signature Function0.invoke() |
| // typeArgument0 = IrType classifier = Unbound class symbol with singnature kotlin.Int |
| // valueArguemnt0 = IrGet valueParamenter block |
| // type = Unbound class symbol with singnature kotlin.Int |
| // |
| inline fun run(block: Function0<Int>) : Unit { // note, that here we merged LazyIr of run function with deserialized body |
| process(block.invoke()) |
| } |
| ``` |
| |
| Now this function can be inlined to original. |
| |
| ```kotlin |
| // IrFunction |
| // name = foo |
| // returnType = Lazy class kotlin.Unit |
| // body |
| // IrReturnableBlock symbol=symbol1 |
| // type = Lazy class kotlin.Unit |
| // IrInlinedFunctionBlock required for debug information |
| // IrReturn |
| // target=symbol1 |
| // value = IrCall |
| // symbol = Unbound function symbol with signature "process(Int) : Unit" |
| // returnType = IrType classifier = Unbound class symbol with singnature kotlin.Unit |
| // valueArgument0 |
| // IrReturnableBlock symbol=symbol2 |
| // type = Lazy class for kotlin.Int |
| // IrInlinedFunctionBlock required for debug information |
| // IrReturn target=symbol2 value = IrConst<Int>(42) |
| fun foo() : Unit { |
| inlinedBlock@{ |
| return@inlinedBlock process(lambda@ { return@lambda 42 }) |
| } |
| } |
| ``` |
| |
| Several side notes on the result: |
| 1. We have both lazy references to kotlin.Unit/kotlin.Int and unbound ones in the tree now. |
| It is fine, as we only need to deserialize it now. |
| 2. While inlining we need to understand that we need special handling of |
| `Unbound function symbol with signature Function0.invoke()`, this must be done by signature only. |
| 3. IrReturnableBlock is represented like it works now, while IrInlinedFunctionBlock is significantly simplified. |
| Probably we need to redesign both to make them serializable. |