[WASM] Lazy properties initialization

This commit is contained in:
Igor Yakovlev
2021-12-23 19:38:56 +01:00
committed by igoriakovlev
parent e58d4163ad
commit 82455c849d
15 changed files with 91 additions and 45 deletions
@@ -30,6 +30,7 @@ interface JsCommonBackendContext : CommonBackendContext {
override val mapping: JsMapping
val reflectionSymbols: ReflectionSymbols
val propertyLazyInitialization: PropertyLazyInitialization
override val inlineClassesUtils: JsCommonInlineClassesUtils
@@ -294,8 +294,6 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
val doNotIntrinsifyAnnotationSymbol = context.symbolTable.referenceClass(context.getJsInternalClass("DoNotIntrinsify"))
val jsFunAnnotationSymbol = context.symbolTable.referenceClass(context.getJsInternalClass("JsFun"))
val jsEagerInitializationAnnotationSymbol = context.symbolTable.referenceClass(context.getJsInternalClass("EagerInitialization"))
// TODO move CharSequence-related stiff to IntrinsifyCallsLowering
val charSequenceClassSymbol = context.symbolTable.referenceClass(context.getClass(FqName("kotlin.CharSequence")))
val charSequenceLengthPropertyGetterSymbol by context.lazy2 {
@@ -56,7 +56,7 @@ class JsIrBackendContext(
override val scriptMode: Boolean = false,
override val es6mode: Boolean = false,
val dceRuntimeDiagnostic: RuntimeDiagnostic? = null,
val propertyLazyInitialization: Boolean = false,
propertyLazyInitialization: Boolean = false,
val baseClassIntoMetadata: Boolean = false,
val safeExternalBoolean: Boolean = false,
val safeExternalBooleanDiagnostic: RuntimeDiagnostic? = null,
@@ -65,8 +65,6 @@ class JsIrBackendContext(
val icCompatibleIr2Js: Boolean = false,
) : JsCommonBackendContext {
val fileToInitializationFuns: MutableMap<IrFile, IrSimpleFunction?> = mutableMapOf()
val fileToInitializerPureness: MutableMap<IrFile, Boolean> = mutableMapOf()
val fieldToInitializer: MutableMap<IrField, IrExpression> = mutableMapOf()
val localClassNames: MutableMap<IrClass, String> = mutableMapOf()
@@ -142,6 +140,11 @@ class JsIrBackendContext(
val intrinsics: JsIntrinsics = JsIntrinsics(irBuiltIns, this)
override val reflectionSymbols: ReflectionSymbols get() = intrinsics.reflectionSymbols
override val propertyLazyInitialization: PropertyLazyInitialization = PropertyLazyInitialization(
enabled = propertyLazyInitialization,
eagerInitialization = symbolTable.referenceClass(getJsInternalClass("EagerInitialization"))
)
override val catchAllThrowableType: IrType
get() = dynamicType
@@ -207,7 +210,8 @@ class JsIrBackendContext(
override val getContinuation = symbolTable.referenceSimpleFunction(getJsInternalFunction("getContinuation"))
override val coroutineContextGetter = symbolTable.referenceSimpleFunction(context.coroutineSymbols.coroutineContextProperty.getter!!)
override val coroutineContextGetter =
symbolTable.referenceSimpleFunction(context.coroutineSymbols.coroutineContextProperty.getter!!)
override val suspendCoroutineUninterceptedOrReturn =
symbolTable.referenceSimpleFunction(getJsInternalFunction(COROUTINE_SUSPEND_OR_RETURN_JS_NAME))
@@ -0,0 +1,16 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.backend.js
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
class PropertyLazyInitialization(val enabled: Boolean, eagerInitialization: IrClassSymbol) {
val fileToInitializationFuns: MutableMap<IrFile, IrSimpleFunction?> = mutableMapOf()
val fileToInitializerPureness: MutableMap<IrFile, Boolean> = mutableMapOf()
val eagerInitialization: IrClassSymbol = eagerInitialization
}
@@ -13,8 +13,7 @@ import org.jetbrains.kotlin.backend.common.ir.isTopLevel
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities.INTERNAL
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrArithBuilder
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.prependFunctionCall
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
@@ -28,25 +27,23 @@ import kotlin.collections.component2
import kotlin.collections.set
class PropertyLazyInitLowering(
private val context: JsIrBackendContext
private val context: JsCommonBackendContext
) : BodyLoweringPass {
private val irBuiltIns
get() = context.irBuiltIns
private val calculator = JsIrArithBuilder(context)
private val irFactory
get() = context.irFactory
private val fileToInitializationFuns
get() = context.fileToInitializationFuns
get() = context.propertyLazyInitialization.fileToInitializationFuns
private val fileToInitializerPureness
get() = context.fileToInitializerPureness
get() = context.propertyLazyInitialization.fileToInitializerPureness
override fun lower(irBody: IrBody, container: IrDeclaration) {
if (!context.propertyLazyInitialization) {
if (!context.propertyLazyInitialization.enabled) {
return
}
@@ -132,32 +129,35 @@ class PropertyLazyInitLowering(
body = irFactory.createBlockBody(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
buildBodyWithIfGuard(initializers, initializedField)
listOf(buildBodyWithIfGuard(initializers, initializedField))
)
}
private fun buildBodyWithIfGuard(
initializers: Map<IrField, IrExpression>,
initializedField: IrField
): List<IrStatement> {
val statements = initializers
.map { (field, expression) ->
createIrSetField(field, expression)
): IrStatement {
val statements = buildList<IrStatement> {
val upGuard = createIrSetField(
initializedField,
JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, true)
)
add(upGuard)
initializers.forEach { (field, expression) ->
add(createIrSetField(field, expression))
}
val upGuard = createIrSetField(
initializedField,
JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, true)
)
add(JsIrBuilder.buildBlock(irBuiltIns.unitType))
}
return JsIrBuilder.buildIfElse(
type = irBuiltIns.unitType,
cond = calculator.not(createIrGetField(initializedField)),
thenBranch = JsIrBuilder.buildComposite(
cond = createIrGetField(initializedField),
thenBranch = JsIrBuilder.buildBlock(irBuiltIns.unitType),
elseBranch = JsIrBuilder.buildComposite(
type = irBuiltIns.unitType,
statements = mutableListOf(upGuard).apply { addAll(statements) }
statements = statements
)
).let { listOf(it) }
)
}
companion object {
@@ -188,14 +188,14 @@ private fun allFieldsInFilePure(fieldToInitializer: Collection<IrExpression>): B
}
class RemoveInitializersForLazyProperties(
private val context: JsIrBackendContext
private val context: JsCommonBackendContext
) : DeclarationTransformer {
private val fileToInitializerPureness
get() = context.fileToInitializerPureness
get() = context.propertyLazyInitialization.fileToInitializerPureness
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (!context.propertyLazyInitialization) {
if (!context.propertyLazyInitialization.enabled) {
return null
}
@@ -235,7 +235,10 @@ class RemoveInitializersForLazyProperties(
}
}
private fun calculateFieldToExpression(declarations: Collection<IrDeclaration>, context: JsIrBackendContext): Map<IrField, IrExpression> =
private fun calculateFieldToExpression(
declarations: Collection<IrDeclaration>,
context: JsCommonBackendContext
): Map<IrField, IrExpression> =
declarations
.asSequence()
.filter { it.isCompatibleDeclaration(context) }
@@ -276,9 +279,9 @@ private fun IrDeclaration.propertyWithPersistentSafe(transform: IrDeclaration.()
private fun <T> IrDeclaration.withPersistentSafe(transform: IrDeclaration.() -> T?): T? =
transform()
private fun IrDeclaration.isCompatibleDeclaration(context: JsIrBackendContext) =
private fun IrDeclaration.isCompatibleDeclaration(context: JsCommonBackendContext) =
correspondingProperty?.let {
it.isForLazyInit() && !it.hasAnnotation(context.intrinsics.jsEagerInitializationAnnotationSymbol)
it.isForLazyInit() && !it.hasAnnotation(context.propertyLazyInitialization.eagerInitialization)
} ?: true && withPersistentSafe { origin in compatibleOrigins } == true
private val compatibleOrigins = listOf(
@@ -35,6 +35,7 @@ class WasmBackendContext(
override val irBuiltIns: IrBuiltIns,
val symbolTable: SymbolTable,
val irModuleFragment: IrModuleFragment,
propertyLazyInitialization: Boolean,
override val configuration: CompilerConfiguration,
) : JsCommonBackendContext {
override val builtIns = module.builtIns
@@ -104,6 +105,9 @@ class WasmBackendContext(
val wasmSymbols: WasmSymbols = WasmSymbols(this@WasmBackendContext, symbolTable)
override val reflectionSymbols: ReflectionSymbols get() = wasmSymbols.reflectionSymbols
override val propertyLazyInitialization: PropertyLazyInitialization =
PropertyLazyInitialization(enabled = propertyLazyInitialization, eagerInitialization = wasmSymbols.eagerInitialization)
override val ir = object : Ir<WasmBackendContext>(this, irModuleFragment) {
override val symbols: Symbols<WasmBackendContext> = wasmSymbols
override fun shouldGenerateHandlerParameterForDefaultBodyFun() = true
@@ -496,6 +496,18 @@ private val forLoopsLoweringPhase = makeWasmModulePhase(
description = "[Optimization] For loops lowering"
)
private val propertyLazyInitLoweringPhase = makeWasmModulePhase(
::PropertyLazyInitLowering,
name = "PropertyLazyInitLowering",
description = "Make property init as lazy"
)
private val removeInitializersForLazyProperties = makeWasmModulePhase(
::RemoveInitializersForLazyProperties,
name = "RemoveInitializersForLazyProperties",
description = "Remove property initializers if they was initialized lazily"
)
private val propertyAccessorInlinerLoweringPhase = makeWasmModulePhase(
::PropertyAccessorInlineLowering,
name = "PropertyAccessorInlineLowering",
@@ -578,6 +590,8 @@ val wasmPhases = NamedCompilerPhase(
returnableBlockLoweringPhase then
forLoopsLoweringPhase then
propertyLazyInitLoweringPhase then
removeInitializersForLazyProperties then
propertyAccessorInlinerLoweringPhase then
stringConcatenationLowering then
@@ -63,6 +63,8 @@ class WasmSymbols(
internal val reflectionSymbols: WasmReflectionSymbols = WasmReflectionSymbols()
internal val eagerInitialization: IrClassSymbol = getIrClass(FqName("kotlin.EagerInitialization"))
override val throwNullPointerException = getInternalFunction("THROW_NPE")
override val throwISE = getInternalFunction("THROW_ISE")
override val throwTypeCastException = getInternalFunction("THROW_CCE")
@@ -29,6 +29,7 @@ fun compileWasm(
phaseConfig: PhaseConfig,
irFactory: IrFactory,
exportedDeclarations: Set<FqName> = emptySet(),
propertyLazyInitialization: Boolean,
emitNameSection: Boolean = false,
): WasmCompilerResult {
val mainModule = depsDescriptors.mainModule
@@ -46,7 +47,7 @@ fun compileWasm(
}
val moduleDescriptor = moduleFragment.descriptor
val context = WasmBackendContext(moduleDescriptor, irBuiltIns, symbolTable, moduleFragment, configuration)
val context = WasmBackendContext(moduleDescriptor, irBuiltIns, symbolTable, moduleFragment, propertyLazyInitialization, configuration)
// Load declarations referenced during `context` initialization
allModules.forEach {