From 7006eb938d56a824d2955ac2002231e45cdcd4ab Mon Sep 17 00:00:00 2001 From: Pavel Kunyavskiy Date: Fri, 8 Jul 2022 16:09:52 +0200 Subject: [PATCH] [K/N] Apply initializers lowering to any declaration container --- .../jetbrains/kotlin/backend/konan/Context.kt | 2 +- .../kotlin/backend/konan/KonanConfig.kt | 2 +- .../kotlin/backend/konan/ToplevelPhases.kt | 2 - .../backend/konan/llvm/BitcodePhases.kt | 4 +- .../kotlin/backend/konan/llvm/ContextUtils.kt | 29 ++-- .../kotlin/backend/konan/llvm/IrToBitcode.kt | 126 +++++++++--------- .../konan/lower/StaticInitializersLowering.kt | 65 ++++++--- .../backend/konan/optimizations/DFGBuilder.kt | 2 +- .../backend/konan/optimizations/DataFlowIR.kt | 8 +- .../optimizations/DevirtualizationAnalysis.kt | 5 +- .../StaticInitializersOptimization.kt | 102 +++++++------- 11 files changed, 184 insertions(+), 163 deletions(-) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 3d9c71b3e51..0f9c1e9c26c 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -163,7 +163,7 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config), Confi } fun needGlobalInit(field: IrField): Boolean { - if (field.descriptor.containingDeclaration !is PackageFragmentDescriptor) return false + if (field.descriptor.containingDeclaration !is PackageFragmentDescriptor) return field.isStatic // TODO: add some smartness here. Maybe if package of the field is in never accessed // assume its global init can be actually omitted. return true diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt index a5be34ed83d..a13113d8705 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt @@ -184,7 +184,7 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration configuration.get(BinaryOptions.mimallocUseDefaultOptions) ?: false } - val mimallocUseCompaction by lazy { + val mimallocUseCompaction: Boolean by lazy { // Turned off by default, because it slows down allocation. configuration.get(BinaryOptions.mimallocUseCompaction) ?: false } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt index 4f4bb20ba33..e0c82a911ac 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt @@ -573,8 +573,6 @@ internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) { } disableUnless(verifyBitcodePhase, config.needCompilerVerification || getBoolean(KonanConfigKeys.VERIFY_BITCODE)) - disableUnless(staticInitializersPhase, config.propertyLazyInitialization) - disableUnless(removeRedundantCallsToStaticInitializersPhase, config.propertyLazyInitialization) disableUnless(removeRedundantSafepointsPhase, config.memoryModel == MemoryModel.EXPERIMENTAL) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt index 0509d33e5e7..932962d5f26 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt @@ -101,11 +101,11 @@ internal val dcePhase = makeKonanModuleOpPhase( val referencedFunctions = mutableSetOf() callGraph.rootExternalFunctions.forEach { - if (!it.isTopLevelFieldInitializer) + if (!it.isStaticFieldInitializer) referencedFunctions.add(it.irFunction ?: error("No IR for: $it")) } for (node in callGraph.directEdges.values) { - if (!node.symbol.isTopLevelFieldInitializer) + if (!node.symbol.isStaticFieldInitializer) referencedFunctions.add(node.symbol.irFunction ?: error("No IR for: ${node.symbol}")) node.callSites.forEach { assert (!it.isVirtual) { "There should be no virtual calls in the call graph, but was: ${it.actualCallee}" } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index 02cbf3cb2b9..7484365468d 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -235,25 +235,30 @@ internal interface ContextUtils : RuntimeAware { */ internal fun stringAsBytes(str: String) = str.toByteArray(Charsets.UTF_8) -internal class InitializersGenerationState { - val fileGlobalInitStates = mutableMapOf() - val fileThreadLocalInitStates = mutableMapOf() - +internal class ScopeInitializersGenerationState { val topLevelFields = mutableListOf() var globalInitFunction: IrFunction? = null var globalInitState: LLVMValueRef? = null var threadLocalInitFunction: IrFunction? = null var threadLocalInitState: AddressAccess? = null + val globalSharedObjects = mutableSetOf() + fun isEmpty() = topLevelFields.isEmpty() && + globalInitState == null && + threadLocalInitState == null && + globalSharedObjects.isEmpty() +} - fun reset() { - topLevelFields.clear() - globalInitFunction = null - globalInitState = null - threadLocalInitFunction = null - threadLocalInitState = null +internal class InitializersGenerationState { + val fileGlobalInitStates = mutableMapOf() + val fileThreadLocalInitStates = mutableMapOf() + + var scopeState = ScopeInitializersGenerationState() + + fun reset(newState: ScopeInitializersGenerationState) : ScopeInitializersGenerationState { + val t = scopeState + scopeState = newState + return t } - - fun isEmpty() = topLevelFields.isEmpty() && globalInitState == null && threadLocalInitState == null } internal class ConstInt1(llvm: Llvm, val value: Boolean) : ConstValue { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index 7a5744762f5..e09213922b2 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -15,9 +15,9 @@ import org.jetbrains.kotlin.backend.konan.descriptors.* import org.jetbrains.kotlin.backend.konan.ir.* import org.jetbrains.kotlin.backend.konan.llvm.coverage.LLVMCoverageInstrumentation import org.jetbrains.kotlin.backend.konan.lower.* -import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_FILE_GLOBAL_INITIALIZER -import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_FILE_STANDALONE_THREAD_LOCAL_INITIALIZER -import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_FILE_THREAD_LOCAL_INITIALIZER +import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_STATIC_GLOBAL_INITIALIZER +import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_STATIC_STANDALONE_THREAD_LOCAL_INITIALIZER +import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_STATIC_THREAD_LOCAL_INITIALIZER import org.jetbrains.kotlin.builtins.UnsignedType import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.IrElement @@ -364,24 +364,16 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map Unit) { - // TODO: collect those two in one place. - llvm.fileUsesThreadLocalObjects = false - llvm.globalSharedObjects.clear() - - llvm.initializersGenerationState.reset() - - f() - - llvm.initializersGenerationState.globalInitFunction?.let { fileInitFunction -> + private fun buildInitializerFunctions(scopeState: ScopeInitializersGenerationState) { + scopeState.globalInitFunction?.let { fileInitFunction -> generateFunction(codegen, fileInitFunction, fileInitFunction.location(start = true), fileInitFunction.location(start = false)) { using(FunctionScope(fileInitFunction, this)) { val parameterScope = ParameterScope(fileInitFunction, functionGenerationContext) using(parameterScope) usingParameterScope@{ using(VariableScope()) usingVariableScope@{ - llvm.initializersGenerationState.topLevelFields + scopeState.topLevelFields .filter { it.storageKind(context) != FieldStorageKind.THREAD_LOCAL } - .filterNot { it.shouldBeInitializedEagerly } + .filterNot { context.shouldBeInitializedEagerly(it) } .forEach { initGlobalField(it) } ret(null) } @@ -390,15 +382,15 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map + scopeState.threadLocalInitFunction?.let { fileInitFunction -> generateFunction(codegen, fileInitFunction, fileInitFunction.location(start = true), fileInitFunction.location(start = false)) { using(FunctionScope(fileInitFunction, this)) { val parameterScope = ParameterScope(fileInitFunction, functionGenerationContext) using(parameterScope) usingParameterScope@{ using(VariableScope()) usingVariableScope@{ - llvm.initializersGenerationState.topLevelFields + scopeState.topLevelFields .filter { it.storageKind(context) == FieldStorageKind.THREAD_LOCAL } - .filterNot { it.shouldBeInitializedEagerly } + .filterNot { context.shouldBeInitializedEagerly(it) } .forEach { initThreadLocalField(it) } ret(null) } @@ -406,15 +398,17 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map Unit) { + val oldScopeState = llvm.initializersGenerationState.reset(ScopeInitializersGenerationState()) + f() + val scopeState = llvm.initializersGenerationState.reset(oldScopeState) + scopeState.takeIf { !it.isEmpty() }?.let { + buildInitializerFunctions(it) + val initNode = createInitNode(createInitBody(it)) + llvm.irStaticInitializers.add(IrStaticInitializer(konanLibrary, createInitCtor(initNode))) } - - // Create global initialization records. - val initNode = createInitNode(createInitBody()) - llvm.irStaticInitializers.add(IrStaticInitializer(konanLibrary, createInitCtor(initNode))) } //-------------------------------------------------------------------------// @@ -468,7 +462,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map if (irField.type.binaryTypeIsReference() && irField.storageKind(context) != FieldStorageKind.THREAD_LOCAL) { @@ -534,10 +523,10 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map + state.globalSharedObjects.forEach { address -> storeHeapRef(codegen.kNullObjHeaderPtr, address) } - llvm.initializersGenerationState.globalInitState?.let { + state.globalInitState?.let { store(llvm.int32(FILE_NOT_INITIALIZED), it) } ret(null) @@ -774,16 +763,24 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map "${fqName}\$${fileEntry.name}" + else -> fqNameForIrSerialization.asString() + } + + private fun getGlobalInitStateFor(container: IrDeclarationContainer): LLVMValueRef = + llvm.initializersGenerationState.fileGlobalInitStates.getOrPut(container) { + codegen.addGlobal("state_global$${container.initVariableSuffix}", llvm.int32Type, false).also { LLVMSetInitializer(it, llvm.int32(FILE_NOT_INITIALIZED)) + LLVMSetLinkage(it, LLVMLinkage.LLVMInternalLinkage) } } - private fun getThreadLocalInitStateFor(file: IrFile): AddressAccess = - llvm.initializersGenerationState.fileThreadLocalInitStates.getOrPut(file) { - codegen.addKotlinThreadLocal("state_thread_local$${file.fileEntry.name}", llvm.int32Type) + private fun getThreadLocalInitStateFor(container: IrDeclarationContainer): AddressAccess = + llvm.initializersGenerationState.fileThreadLocalInitStates.getOrPut(container) { + codegen.addKotlinThreadLocal("state_thread_local$${container.initVariableSuffix}", llvm.int32Type).also { + LLVMSetInitializer((it as GlobalAddressAccess).getAddress(null), llvm.int32(FILE_NOT_INITIALIZED)) + } } override fun visitFunction(declaration: IrFunction) { @@ -791,22 +788,23 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map intrinsicGenerator.evaluateCall(callee, args, resultSlot) function.isBuiltInOperator -> evaluateOperatorCall(callee, args) - function.origin == DECLARATION_ORIGIN_FILE_GLOBAL_INITIALIZER -> evaluateFileGlobalInitializerCall(function) - function.origin == DECLARATION_ORIGIN_FILE_THREAD_LOCAL_INITIALIZER -> evaluateFileThreadLocalInitializerCall(function) - function.origin == DECLARATION_ORIGIN_FILE_STANDALONE_THREAD_LOCAL_INITIALIZER -> evaluateFileStandaloneThreadLocalInitializerCall(function) + function.origin == DECLARATION_ORIGIN_STATIC_GLOBAL_INITIALIZER -> evaluateFileGlobalInitializerCall(function) + function.origin == DECLARATION_ORIGIN_STATIC_THREAD_LOCAL_INITIALIZER -> evaluateFileThreadLocalInitializerCall(function) + function.origin == DECLARATION_ORIGIN_STATIC_STANDALONE_THREAD_LOCAL_INITIALIZER -> evaluateFileStandaloneThreadLocalInitializerCall(function) else -> evaluateSimpleFunctionCall(function, args, resultLifetime, callee.superQualifierSymbol?.owner, resultSlot) } } private fun evaluateFileGlobalInitializerCall(fileInitializer: IrFunction) = with(functionGenerationContext) { - val statePtr = getGlobalInitStateFor(fileInitializer.parent as IrFile) + val statePtr = getGlobalInitStateFor(fileInitializer.parent as IrDeclarationContainer) val initializerPtr = with(codegen) { fileInitializer.llvmFunction.llvmValue } val bbInit = basicBlock("label_init", null) @@ -2426,8 +2426,8 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map lateinit var returnParameter: FunctionParameter - val isTopLevelFieldInitializer = attributes.and(FunctionAttributes.IS_TOP_LEVEL_FIELD_INITIALIZER) != 0 + val isStaticFieldInitializer = attributes.and(FunctionAttributes.IS_STATIC_FIELD_INITIALIZER) != 0 val returnsUnit = attributes.and(FunctionAttributes.RETURNS_UNIT) != 0 val returnsNothing = attributes.and(FunctionAttributes.RETURNS_NOTHING) != 0 val explicitlyExported = attributes.and(FunctionAttributes.EXPLICITLY_EXPORTED) != 0 @@ -656,8 +656,8 @@ internal object DataFlowIR { private fun mapPropertyInitializer(irField: IrField): FunctionSymbol { functionMap[irField]?.let { return it } - assert(irField.parent !is IrClass) { "All local properties initializers should've been lowered" } - val attributes = FunctionAttributes.IS_TOP_LEVEL_FIELD_INITIALIZER or FunctionAttributes.RETURNS_UNIT + assert(irField.isStatic) { "All local properties initializers should've been lowered" } + val attributes = FunctionAttributes.IS_STATIC_FIELD_INITIALIZER or FunctionAttributes.RETURNS_UNIT val symbol = FunctionSymbol.Private(privateFunIndex++, module, -1, attributes, irField, null, takeName { "${irField.computeSymbolName()}_init" }) functionMap[irField] = symbol diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DevirtualizationAnalysis.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DevirtualizationAnalysis.kt index 7b321ce832a..441e99e2928 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DevirtualizationAnalysis.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DevirtualizationAnalysis.kt @@ -13,7 +13,6 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.irBlock import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.ir.isBoxOrUnboxCall -import org.jetbrains.kotlin.backend.konan.optimizations.DevirtualizationAnalysis.irCoerce import org.jetbrains.kotlin.backend.konan.util.IntArrayList import org.jetbrains.kotlin.backend.konan.util.LongArrayList import org.jetbrains.kotlin.descriptors.ClassKind @@ -83,8 +82,8 @@ internal object DevirtualizationAnalysis { // TODO: Are globals initializers always called whether they are actually reachable from roots or not? // TODO: With the changed semantics of global initializers this is no longer the case - rework. val globalInitializers = - moduleDFG.symbolTable.functionMap.values.filter { it.isTopLevelFieldInitializer } + - externalModulesDFG.functionDFGs.keys.filter { it.isTopLevelFieldInitializer } + moduleDFG.symbolTable.functionMap.values.filter { it.isStaticFieldInitializer } + + externalModulesDFG.functionDFGs.keys.filter { it.isStaticFieldInitializer } val explicitlyExported = moduleDFG.symbolTable.functionMap.values.filter { it.explicitlyExported } + diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/StaticInitializersOptimization.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/StaticInitializersOptimization.kt index 1ac68fe05bb..027f1c56df8 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/StaticInitializersOptimization.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/StaticInitializersOptimization.kt @@ -58,13 +58,13 @@ internal object StaticInitializersOptimization { val callSitesRequiringGlobalInitializerCall: Set, val callSitesRequiringThreadLocalInitializerCall: Set) - private class InitializedFiles(val fileIds: Map) { + private class InitializedContainers(val containerIds: Map) { val afterCall = mutableMapOf() val beforeCallGlobal = mutableMapOf() val beforeCallThreadLocal = mutableMapOf() } - private val invalidFileId = 0 + private val invalidContainerId = 0 private class InterproceduralAnalysis(val context: Context, val callGraph: CallGraph, val rootSet: Set) { @@ -109,20 +109,18 @@ internal object StaticInitializersOptimization { +"" } - var fileId = invalidFileId - val fileIds = mutableMapOf() - for (node in callGraph.directEdges.values) { - val callerFile = node.symbol.irFile - if (callerFile != null && fileIds[callerFile] == null) - fileIds[callerFile] = ++fileId - for (callSite in node.callSites) { - val calleeFile = callSite.actualCallee.irFile - if (calleeFile != null && fileIds[calleeFile] == null) - fileIds[calleeFile] = ++fileId + val functions = buildSet { + callGraph.directEdges.values.forEach { + add(it.symbol.irFunction) + it.callSites.forEach { callSite -> add(callSite.actualCallee.irFunction) } } } + val containers = functions + .mapNotNull { it?.calledInitializer } + .distinct() + val containerIds = containers.indices.associate { containers[it] to it + 1 } - val initializedFiles = InitializedFiles(fileIds) + val initializedFiles = InitializedContainers(containerIds) context.log { "FIRST PHASE: compute initialized after call" } @@ -163,11 +161,10 @@ internal object StaticInitializersOptimization { ): Set { val result = mutableSetOf() initializedFiles.forEach { (function, functionInitializedFiles) -> - val irFile = function.fileOrNull + val containter = function.calledInitializer ?: return@forEach val backingField = (function as? IrSimpleFunction)?.correspondingPropertySymbol?.owner?.backingField val isDefaultAccessor = backingField != null && function.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR - if (irFile == null || - (!functionInitializedFiles.get(fileIds[irFile]!!) + if ((!functionInitializedFiles.get(containerIds[containter]!!) && function !in functionsWhoseInitializerCallCanBeExtractedToCallSites // Extract calls to file initializers off of default accessors to simplify their inlining. && (!isDefaultAccessor || function in rootSet)) @@ -194,7 +191,7 @@ internal object StaticInitializersOptimization { } private fun analyze(multiNode: DirectedGraphMultiNode, - initializedFiles: InitializedFiles, + initializedFiles: InitializedContainers, analysisGoal: AnalysisGoal) { val nodes = multiNode.nodes.toList() @@ -244,12 +241,12 @@ internal object StaticInitializersOptimization { CollectCallSites } - private fun IrFunction.callsFileInitializer() = - (body?.statements?.get(0) as? IrCall)?.symbol?.owner?.isFileInitializer == true + private val IrFunction.calledInitializer get() = + (body?.statements?.get(0) as? IrCall)?.symbol?.owner?.takeIf { it.isStaticInitializer }?.parent as IrDeclarationContainer? private fun intraproceduralAnalysis( node: CallGraphNode, - initializedFiles: InitializedFiles, + initializedContainers: InitializedContainers, analysisGoal: AnalysisGoal, callSitesRequiringGlobalInitializerCall: MutableSet = dummySet, callSitesRequiringThreadLocalInitializerCall: MutableSet = dummySet, @@ -257,7 +254,7 @@ internal object StaticInitializersOptimization { callSitesNotRequiringThreadLocalInitializerCall: MutableSet = dummySet ) { val irDeclaration = node.symbol.irDeclaration ?: return - val body = if (node.symbol.isTopLevelFieldInitializer) + val body = if (node.symbol.isStaticFieldInitializer) (irDeclaration as IrField).initializer?.expression else { val function = irDeclaration as IrFunction @@ -266,11 +263,11 @@ internal object StaticInitializersOptimization { } if (body == null) return - val filesWithInitializedGlobals = BitSet() - val filesWithInitializedThreadLocals = BitSet() - if (!node.symbol.isTopLevelFieldInitializer) { - initializedFiles.beforeCallGlobal[irDeclaration as IrFunction]?.let { filesWithInitializedGlobals.or(it) } - initializedFiles.beforeCallThreadLocal[irDeclaration]?.let { filesWithInitializedThreadLocals.or(it) } + val containersWithInitializedGlobals = BitSet() + val containersWithInitializedThreadLocals = BitSet() + if (!node.symbol.isStaticFieldInitializer) { + initializedContainers.beforeCallGlobal[irDeclaration as IrFunction]?.let { containersWithInitializedGlobals.or(it) } + initializedContainers.beforeCallThreadLocal[irDeclaration]?.let { containersWithInitializedThreadLocals.or(it) } } val producerInvocations = mutableMapOf() @@ -409,25 +406,24 @@ internal object StaticInitializersOptimization { if (this.get(bit)) this else copy().also { it.set(bit) } private fun getResultAfterCall(function: IrFunction, set: BitSet): BitSet { - val result = initializedFiles.afterCall[function] + val result = initializedContainers.afterCall[function] if (result == null) { - if (!function.callsFileInitializer()) return set - val file = function.fileOrNull ?: return set - return set.withSetBit(initializedFiles.fileIds[file]!!) + val file = function.calledInitializer ?: return set + return set.withSetBit(initializedContainers.containerIds[file]!!) } return result.copy().also { it.or(set) } } private fun updateResultForFunction(function: IrFunction, globalSet: BitSet, threadLocalSet: BitSet) { if (analysisGoal != AnalysisGoal.ComputeInitializedBeforeCall) return - intersectInitializedFiles(initializedFiles.beforeCallGlobal, function, globalSet) - intersectInitializedFiles(initializedFiles.beforeCallThreadLocal, function, threadLocalSet) + intersectInitializedFiles(initializedContainers.beforeCallGlobal, function, globalSet) + intersectInitializedFiles(initializedContainers.beforeCallThreadLocal, function, threadLocalSet) } private fun updateResultForFunction(function: IrFunction, set: BitSet) { if (analysisGoal != AnalysisGoal.ComputeInitializedBeforeCall) return - intersectInitializedFiles(initializedFiles.beforeCallGlobal, function, set.copy().also { it.or(filesWithInitializedGlobals) }) - intersectInitializedFiles(initializedFiles.beforeCallThreadLocal, function, set.copy().also { it.or(filesWithInitializedThreadLocals) }) + intersectInitializedFiles(initializedContainers.beforeCallGlobal, function, set.copy().also { it.or(containersWithInitializedGlobals) }) + intersectInitializedFiles(initializedContainers.beforeCallThreadLocal, function, set.copy().also { it.or(containersWithInitializedThreadLocals) }) } override fun visitGetObjectValue(expression: IrGetObjectValue, data: BitSet): BitSet { @@ -446,22 +442,22 @@ internal object StaticInitializersOptimization { val arguments = expression.getArgumentsWithIr() val argumentsResult = arguments.fold(data) { set, arg -> arg.second.accept(this, set) } updateResultForFunction(actualCallee, argumentsResult) - val file = actualCallee.fileOrNull - val fileId = file?.let { initializedFiles.fileIds[it]!! } ?: invalidFileId - if (analysisGoal == AnalysisGoal.CollectCallSites && file != null + val container = actualCallee.calledInitializer + val containerId = initializedContainers.containerIds[container] ?: invalidContainerId + if (analysisGoal == AnalysisGoal.CollectCallSites && // Only extract initializer calls from non-virtual functions. - && !actualCallee.isOverridable + !actualCallee.isOverridable ) { // The initializer won't be optimized away from the function. - if (!initializedFiles.beforeCallGlobal[actualCallee]!!.get(fileId)) { - if (argumentsResult.get(fileId) || filesWithInitializedGlobals.get(fileId)) + if (!initializedContainers.beforeCallGlobal[actualCallee]!!.get(containerId)) { + if (argumentsResult.get(containerId) || containersWithInitializedGlobals.get(containerId)) callSitesNotRequiringGlobalInitializerCall += expression else callSitesRequiringGlobalInitializerCall += expression } // The initializer won't be optimized away from the function. - if (!initializedFiles.beforeCallThreadLocal[actualCallee]!!.get(fileId)) { - if (argumentsResult.get(fileId) || filesWithInitializedThreadLocals.get(fileId)) + if (!initializedContainers.beforeCallThreadLocal[actualCallee]!!.get(containerId)) { + if (argumentsResult.get(containerId) || containersWithInitializedThreadLocals.get(containerId)) callSitesNotRequiringThreadLocalInitializerCall += expression else callSitesRequiringThreadLocalInitializerCall += expression @@ -480,7 +476,7 @@ internal object StaticInitializersOptimization { if (analysisGoal != AnalysisGoal.CollectCallSites) { require(!jobInvocation.isVirtualCall) { "Expected a static call but was: ${jobInvocation.render()}" } updateResultForFunction(jobInvocation.actualCallee, - curData.copy().also { it.or(filesWithInitializedGlobals) }, // Globals (= shared) visible to other threads as well. + curData.copy().also { it.or(containersWithInitializedGlobals) }, // Globals (= shared) visible to other threads as well. BitSet() // A new thread is about to be created - no thread locals initialized yet. ) } @@ -499,8 +495,8 @@ internal object StaticInitializersOptimization { processCall(expression, expression.actualCallee, data) override fun visitCall(expression: IrCall, data: BitSet): BitSet { - if (expression.symbol.owner.isFileInitializer) - return data.withSetBit(initializedFiles.fileIds[irDeclaration.file]!!) + if (expression.symbol.owner.isStaticInitializer) + return data.withSetBit(initializedContainers.containerIds[expression.symbol.owner.parent as IrDeclarationContainer]!!) if (expression.symbol == executeImplSymbol) return processExecuteImpl(expression, data) if (expression.symbol == getContinuationSymbol) @@ -528,8 +524,8 @@ internal object StaticInitializersOptimization { }, BitSet()) if (analysisGoal == AnalysisGoal.ComputeInitializedAfterCall) { - if (!node.symbol.isTopLevelFieldInitializer) - initializedFiles.afterCall[irDeclaration as IrFunction] = returnTargetsInitializedFiles[irDeclaration.symbol] ?: callerResult + if (!node.symbol.isStaticFieldInitializer) + initializedContainers.afterCall[irDeclaration as IrFunction] = returnTargetsInitializedFiles[irDeclaration.symbol] ?: callerResult } } } @@ -560,10 +556,10 @@ internal object StaticInitializersOptimization { .take(2) // The very first statements by construction. .filter { val calleeOrigin = (it as? IrCall)?.symbol?.owner?.origin - val isNotOptimizedAwayGlobalInitializerCall = calleeOrigin == DECLARATION_ORIGIN_FILE_GLOBAL_INITIALIZER + val isNotOptimizedAwayGlobalInitializerCall = calleeOrigin == DECLARATION_ORIGIN_STATIC_GLOBAL_INITIALIZER && callee !in analysisResult.functionsRequiringGlobalInitializerCall - val isNotOptimizedAwayThreadLocalInitializerCall = (calleeOrigin == DECLARATION_ORIGIN_FILE_THREAD_LOCAL_INITIALIZER - || calleeOrigin == DECLARATION_ORIGIN_FILE_STANDALONE_THREAD_LOCAL_INITIALIZER) + val isNotOptimizedAwayThreadLocalInitializerCall = (calleeOrigin == DECLARATION_ORIGIN_STATIC_THREAD_LOCAL_INITIALIZER + || calleeOrigin == DECLARATION_ORIGIN_STATIC_STANDALONE_THREAD_LOCAL_INITIALIZER) && callee !in analysisResult.functionsRequiringThreadLocalInitializerCall if (isNotOptimizedAwayGlobalInitializerCall) ++numberOfCallSitesToFunctionsWithGlobalInitializerCall @@ -596,7 +592,7 @@ internal object StaticInitializersOptimization { .take(2) // The very first statements by construction. .indexOfFirst { val calleeOrigin = (it as? IrCall)?.symbol?.owner?.origin - calleeOrigin == DECLARATION_ORIGIN_FILE_GLOBAL_INITIALIZER + calleeOrigin == DECLARATION_ORIGIN_STATIC_GLOBAL_INITIALIZER } if (globalInitializerCallIndex >= 0) { ++numberOfFunctionsWithGlobalInitializerCall @@ -609,8 +605,8 @@ internal object StaticInitializersOptimization { .take(2) .indexOfFirst { val calleeOrigin = (it as? IrCall)?.symbol?.owner?.origin - calleeOrigin == DECLARATION_ORIGIN_FILE_THREAD_LOCAL_INITIALIZER - || calleeOrigin == DECLARATION_ORIGIN_FILE_STANDALONE_THREAD_LOCAL_INITIALIZER + calleeOrigin == DECLARATION_ORIGIN_STATIC_THREAD_LOCAL_INITIALIZER + || calleeOrigin == DECLARATION_ORIGIN_STATIC_STANDALONE_THREAD_LOCAL_INITIALIZER } if (threadLocalInitializerCallIndex >= 0) { ++numberOfFunctionsWithThreadLocalInitializerCall