[K/N] Apply initializers lowering to any declaration container
This commit is contained in:
committed by
Space Team
parent
9516094755
commit
7006eb938d
+1
-1
@@ -163,7 +163,7 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config), Confi
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun needGlobalInit(field: IrField): Boolean {
|
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
|
// TODO: add some smartness here. Maybe if package of the field is in never accessed
|
||||||
// assume its global init can be actually omitted.
|
// assume its global init can be actually omitted.
|
||||||
return true
|
return true
|
||||||
|
|||||||
+1
-1
@@ -184,7 +184,7 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
|
|||||||
configuration.get(BinaryOptions.mimallocUseDefaultOptions) ?: false
|
configuration.get(BinaryOptions.mimallocUseDefaultOptions) ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
val mimallocUseCompaction by lazy {
|
val mimallocUseCompaction: Boolean by lazy {
|
||||||
// Turned off by default, because it slows down allocation.
|
// Turned off by default, because it slows down allocation.
|
||||||
configuration.get(BinaryOptions.mimallocUseCompaction) ?: false
|
configuration.get(BinaryOptions.mimallocUseCompaction) ?: false
|
||||||
}
|
}
|
||||||
|
|||||||
-2
@@ -573,8 +573,6 @@ internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) {
|
|||||||
}
|
}
|
||||||
disableUnless(verifyBitcodePhase, config.needCompilerVerification || getBoolean(KonanConfigKeys.VERIFY_BITCODE))
|
disableUnless(verifyBitcodePhase, config.needCompilerVerification || getBoolean(KonanConfigKeys.VERIFY_BITCODE))
|
||||||
|
|
||||||
disableUnless(staticInitializersPhase, config.propertyLazyInitialization)
|
|
||||||
disableUnless(removeRedundantCallsToStaticInitializersPhase, config.propertyLazyInitialization)
|
|
||||||
|
|
||||||
disableUnless(removeRedundantSafepointsPhase, config.memoryModel == MemoryModel.EXPERIMENTAL)
|
disableUnless(removeRedundantSafepointsPhase, config.memoryModel == MemoryModel.EXPERIMENTAL)
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -101,11 +101,11 @@ internal val dcePhase = makeKonanModuleOpPhase(
|
|||||||
|
|
||||||
val referencedFunctions = mutableSetOf<IrFunction>()
|
val referencedFunctions = mutableSetOf<IrFunction>()
|
||||||
callGraph.rootExternalFunctions.forEach {
|
callGraph.rootExternalFunctions.forEach {
|
||||||
if (!it.isTopLevelFieldInitializer)
|
if (!it.isStaticFieldInitializer)
|
||||||
referencedFunctions.add(it.irFunction ?: error("No IR for: $it"))
|
referencedFunctions.add(it.irFunction ?: error("No IR for: $it"))
|
||||||
}
|
}
|
||||||
for (node in callGraph.directEdges.values) {
|
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}"))
|
referencedFunctions.add(node.symbol.irFunction ?: error("No IR for: ${node.symbol}"))
|
||||||
node.callSites.forEach {
|
node.callSites.forEach {
|
||||||
assert (!it.isVirtual) { "There should be no virtual calls in the call graph, but was: ${it.actualCallee}" }
|
assert (!it.isVirtual) { "There should be no virtual calls in the call graph, but was: ${it.actualCallee}" }
|
||||||
|
|||||||
+17
-12
@@ -235,25 +235,30 @@ internal interface ContextUtils : RuntimeAware {
|
|||||||
*/
|
*/
|
||||||
internal fun stringAsBytes(str: String) = str.toByteArray(Charsets.UTF_8)
|
internal fun stringAsBytes(str: String) = str.toByteArray(Charsets.UTF_8)
|
||||||
|
|
||||||
internal class InitializersGenerationState {
|
internal class ScopeInitializersGenerationState {
|
||||||
val fileGlobalInitStates = mutableMapOf<IrFile, LLVMValueRef>()
|
|
||||||
val fileThreadLocalInitStates = mutableMapOf<IrFile, AddressAccess>()
|
|
||||||
|
|
||||||
val topLevelFields = mutableListOf<IrField>()
|
val topLevelFields = mutableListOf<IrField>()
|
||||||
var globalInitFunction: IrFunction? = null
|
var globalInitFunction: IrFunction? = null
|
||||||
var globalInitState: LLVMValueRef? = null
|
var globalInitState: LLVMValueRef? = null
|
||||||
var threadLocalInitFunction: IrFunction? = null
|
var threadLocalInitFunction: IrFunction? = null
|
||||||
var threadLocalInitState: AddressAccess? = null
|
var threadLocalInitState: AddressAccess? = null
|
||||||
|
val globalSharedObjects = mutableSetOf<LLVMValueRef>()
|
||||||
|
fun isEmpty() = topLevelFields.isEmpty() &&
|
||||||
|
globalInitState == null &&
|
||||||
|
threadLocalInitState == null &&
|
||||||
|
globalSharedObjects.isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
fun reset() {
|
internal class InitializersGenerationState {
|
||||||
topLevelFields.clear()
|
val fileGlobalInitStates = mutableMapOf<IrDeclarationContainer, LLVMValueRef>()
|
||||||
globalInitFunction = null
|
val fileThreadLocalInitStates = mutableMapOf<IrDeclarationContainer, AddressAccess>()
|
||||||
globalInitState = null
|
|
||||||
threadLocalInitFunction = null
|
var scopeState = ScopeInitializersGenerationState()
|
||||||
threadLocalInitState = null
|
|
||||||
|
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 {
|
internal class ConstInt1(llvm: Llvm, val value: Boolean) : ConstValue {
|
||||||
|
|||||||
+63
-63
@@ -15,9 +15,9 @@ import org.jetbrains.kotlin.backend.konan.descriptors.*
|
|||||||
import org.jetbrains.kotlin.backend.konan.ir.*
|
import org.jetbrains.kotlin.backend.konan.ir.*
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.coverage.LLVMCoverageInstrumentation
|
import org.jetbrains.kotlin.backend.konan.llvm.coverage.LLVMCoverageInstrumentation
|
||||||
import org.jetbrains.kotlin.backend.konan.lower.*
|
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_STATIC_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_STATIC_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_THREAD_LOCAL_INITIALIZER
|
||||||
import org.jetbrains.kotlin.builtins.UnsignedType
|
import org.jetbrains.kotlin.builtins.UnsignedType
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
@@ -364,24 +364,16 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun runAndProcessInitializers(konanLibrary: KotlinLibrary?, f: () -> Unit) {
|
private fun buildInitializerFunctions(scopeState: ScopeInitializersGenerationState) {
|
||||||
// TODO: collect those two in one place.
|
scopeState.globalInitFunction?.let { fileInitFunction ->
|
||||||
llvm.fileUsesThreadLocalObjects = false
|
|
||||||
llvm.globalSharedObjects.clear()
|
|
||||||
|
|
||||||
llvm.initializersGenerationState.reset()
|
|
||||||
|
|
||||||
f()
|
|
||||||
|
|
||||||
llvm.initializersGenerationState.globalInitFunction?.let { fileInitFunction ->
|
|
||||||
generateFunction(codegen, fileInitFunction, fileInitFunction.location(start = true), fileInitFunction.location(start = false)) {
|
generateFunction(codegen, fileInitFunction, fileInitFunction.location(start = true), fileInitFunction.location(start = false)) {
|
||||||
using(FunctionScope(fileInitFunction, this)) {
|
using(FunctionScope(fileInitFunction, this)) {
|
||||||
val parameterScope = ParameterScope(fileInitFunction, functionGenerationContext)
|
val parameterScope = ParameterScope(fileInitFunction, functionGenerationContext)
|
||||||
using(parameterScope) usingParameterScope@{
|
using(parameterScope) usingParameterScope@{
|
||||||
using(VariableScope()) usingVariableScope@{
|
using(VariableScope()) usingVariableScope@{
|
||||||
llvm.initializersGenerationState.topLevelFields
|
scopeState.topLevelFields
|
||||||
.filter { it.storageKind(context) != FieldStorageKind.THREAD_LOCAL }
|
.filter { it.storageKind(context) != FieldStorageKind.THREAD_LOCAL }
|
||||||
.filterNot { it.shouldBeInitializedEagerly }
|
.filterNot { context.shouldBeInitializedEagerly(it) }
|
||||||
.forEach { initGlobalField(it) }
|
.forEach { initGlobalField(it) }
|
||||||
ret(null)
|
ret(null)
|
||||||
}
|
}
|
||||||
@@ -390,15 +382,15 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm.initializersGenerationState.threadLocalInitFunction?.let { fileInitFunction ->
|
scopeState.threadLocalInitFunction?.let { fileInitFunction ->
|
||||||
generateFunction(codegen, fileInitFunction, fileInitFunction.location(start = true), fileInitFunction.location(start = false)) {
|
generateFunction(codegen, fileInitFunction, fileInitFunction.location(start = true), fileInitFunction.location(start = false)) {
|
||||||
using(FunctionScope(fileInitFunction, this)) {
|
using(FunctionScope(fileInitFunction, this)) {
|
||||||
val parameterScope = ParameterScope(fileInitFunction, functionGenerationContext)
|
val parameterScope = ParameterScope(fileInitFunction, functionGenerationContext)
|
||||||
using(parameterScope) usingParameterScope@{
|
using(parameterScope) usingParameterScope@{
|
||||||
using(VariableScope()) usingVariableScope@{
|
using(VariableScope()) usingVariableScope@{
|
||||||
llvm.initializersGenerationState.topLevelFields
|
scopeState.topLevelFields
|
||||||
.filter { it.storageKind(context) == FieldStorageKind.THREAD_LOCAL }
|
.filter { it.storageKind(context) == FieldStorageKind.THREAD_LOCAL }
|
||||||
.filterNot { it.shouldBeInitializedEagerly }
|
.filterNot { context.shouldBeInitializedEagerly(it) }
|
||||||
.forEach { initThreadLocalField(it) }
|
.forEach { initThreadLocalField(it) }
|
||||||
ret(null)
|
ret(null)
|
||||||
}
|
}
|
||||||
@@ -406,15 +398,17 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!llvm.fileUsesThreadLocalObjects && llvm.globalSharedObjects.isEmpty()
|
private fun runAndProcessInitializers(konanLibrary: KotlinLibrary?, f: () -> Unit) {
|
||||||
&& llvm.initializersGenerationState.isEmpty()) {
|
val oldScopeState = llvm.initializersGenerationState.reset(ScopeInitializersGenerationState())
|
||||||
return
|
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<IrE
|
|||||||
val FILE_NOT_INITIALIZED = 0
|
val FILE_NOT_INITIALIZED = 0
|
||||||
val FILE_INITIALIZED = 2
|
val FILE_INITIALIZED = 2
|
||||||
|
|
||||||
private fun createInitBody(): LLVMValueRef {
|
private fun createInitBody(state: ScopeInitializersGenerationState): LLVMValueRef {
|
||||||
val initFunction = addLlvmFunctionWithDefaultAttributes(
|
val initFunction = addLlvmFunctionWithDefaultAttributes(
|
||||||
context,
|
context,
|
||||||
llvm.module,
|
llvm.module,
|
||||||
@@ -495,21 +489,16 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
|
|
||||||
// Globals initializers may contain accesses to objects, so visit them first.
|
// Globals initializers may contain accesses to objects, so visit them first.
|
||||||
appendingTo(bbInit) {
|
appendingTo(bbInit) {
|
||||||
llvm.initializersGenerationState.topLevelFields
|
state.topLevelFields
|
||||||
.filter { !context.useLazyFileInitializers() || it.shouldBeInitializedEagerly }
|
.filter { context.shouldBeInitializedEagerly(it) }
|
||||||
.filterNot { it.storageKind(context) == FieldStorageKind.THREAD_LOCAL }
|
.filterNot { it.storageKind(context) == FieldStorageKind.THREAD_LOCAL }
|
||||||
.forEach { initGlobalField(it) }
|
.forEach { initGlobalField(it) }
|
||||||
ret(null)
|
ret(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
appendingTo(bbLocalInit) {
|
appendingTo(bbLocalInit) {
|
||||||
llvm.initializersGenerationState.threadLocalInitState?.let {
|
state.topLevelFields
|
||||||
val address = it.getAddress(functionGenerationContext)
|
.filter { context.shouldBeInitializedEagerly(it) }
|
||||||
store(llvm.int32(FILE_NOT_INITIALIZED), address)
|
|
||||||
LLVMSetInitializer(address, llvm.int32(FILE_NOT_INITIALIZED))
|
|
||||||
}
|
|
||||||
llvm.initializersGenerationState.topLevelFields
|
|
||||||
.filter { !context.useLazyFileInitializers() || it.shouldBeInitializedEagerly }
|
|
||||||
.filter { it.storageKind(context) == FieldStorageKind.THREAD_LOCAL }
|
.filter { it.storageKind(context) == FieldStorageKind.THREAD_LOCAL }
|
||||||
.forEach { initThreadLocalField(it) }
|
.forEach { initThreadLocalField(it) }
|
||||||
ret(null)
|
ret(null)
|
||||||
@@ -524,7 +513,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
}
|
}
|
||||||
|
|
||||||
appendingTo(bbGlobalDeinit) {
|
appendingTo(bbGlobalDeinit) {
|
||||||
llvm.initializersGenerationState.topLevelFields
|
state.topLevelFields
|
||||||
// Only if a subject for memory management.
|
// Only if a subject for memory management.
|
||||||
.forEach { irField ->
|
.forEach { irField ->
|
||||||
if (irField.type.binaryTypeIsReference() && irField.storageKind(context) != FieldStorageKind.THREAD_LOCAL) {
|
if (irField.type.binaryTypeIsReference() && irField.storageKind(context) != FieldStorageKind.THREAD_LOCAL) {
|
||||||
@@ -534,10 +523,10 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
storeHeapRef(codegen.kNullObjHeaderPtr, address)
|
storeHeapRef(codegen.kNullObjHeaderPtr, address)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
llvm.globalSharedObjects.forEach { address ->
|
state.globalSharedObjects.forEach { address ->
|
||||||
storeHeapRef(codegen.kNullObjHeaderPtr, address)
|
storeHeapRef(codegen.kNullObjHeaderPtr, address)
|
||||||
}
|
}
|
||||||
llvm.initializersGenerationState.globalInitState?.let {
|
state.globalInitState?.let {
|
||||||
store(llvm.int32(FILE_NOT_INITIALIZED), it)
|
store(llvm.int32(FILE_NOT_INITIALIZED), it)
|
||||||
}
|
}
|
||||||
ret(null)
|
ret(null)
|
||||||
@@ -774,16 +763,24 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
}.toMap()
|
}.toMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getGlobalInitStateFor(file: IrFile): LLVMValueRef =
|
private val IrDeclarationContainer.initVariableSuffix get() = when (this) {
|
||||||
llvm.initializersGenerationState.fileGlobalInitStates.getOrPut(file) {
|
is IrFile -> "${fqName}\$${fileEntry.name}"
|
||||||
codegen.addGlobal("state_global$${file.fileEntry.name}", llvm.int32Type, false).also {
|
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))
|
LLVMSetInitializer(it, llvm.int32(FILE_NOT_INITIALIZED))
|
||||||
|
LLVMSetLinkage(it, LLVMLinkage.LLVMInternalLinkage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getThreadLocalInitStateFor(file: IrFile): AddressAccess =
|
private fun getThreadLocalInitStateFor(container: IrDeclarationContainer): AddressAccess =
|
||||||
llvm.initializersGenerationState.fileThreadLocalInitStates.getOrPut(file) {
|
llvm.initializersGenerationState.fileThreadLocalInitStates.getOrPut(container) {
|
||||||
codegen.addKotlinThreadLocal("state_thread_local$${file.fileEntry.name}", llvm.int32Type)
|
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) {
|
override fun visitFunction(declaration: IrFunction) {
|
||||||
@@ -791,22 +788,23 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
|
|
||||||
val body = declaration.body
|
val body = declaration.body
|
||||||
|
|
||||||
if (declaration.origin == DECLARATION_ORIGIN_FILE_GLOBAL_INITIALIZER) {
|
val scopeState = llvm.initializersGenerationState.scopeState
|
||||||
require(llvm.initializersGenerationState.globalInitFunction == null) { "There can only be at most one global file initializer" }
|
if (declaration.origin == DECLARATION_ORIGIN_STATIC_GLOBAL_INITIALIZER) {
|
||||||
|
require(scopeState.globalInitFunction == null) { "There can only be at most one global file initializer" }
|
||||||
require(body == null) { "The body of file initializer should be null" }
|
require(body == null) { "The body of file initializer should be null" }
|
||||||
require(declaration.valueParameters.isEmpty()) { "File initializer must be parameterless" }
|
require(declaration.valueParameters.isEmpty()) { "File initializer must be parameterless" }
|
||||||
require(declaration.returnsUnit()) { "File initializer must return Unit" }
|
require(declaration.returnsUnit()) { "File initializer must return Unit" }
|
||||||
llvm.initializersGenerationState.globalInitFunction = declaration
|
scopeState.globalInitFunction = declaration
|
||||||
llvm.initializersGenerationState.globalInitState = getGlobalInitStateFor(declaration.parent as IrFile)
|
scopeState.globalInitState = getGlobalInitStateFor(declaration.parent as IrDeclarationContainer)
|
||||||
}
|
}
|
||||||
if (declaration.origin == DECLARATION_ORIGIN_FILE_THREAD_LOCAL_INITIALIZER
|
if (declaration.origin == DECLARATION_ORIGIN_STATIC_THREAD_LOCAL_INITIALIZER
|
||||||
|| declaration.origin == DECLARATION_ORIGIN_FILE_STANDALONE_THREAD_LOCAL_INITIALIZER) {
|
|| declaration.origin == DECLARATION_ORIGIN_STATIC_STANDALONE_THREAD_LOCAL_INITIALIZER) {
|
||||||
require(llvm.initializersGenerationState.threadLocalInitFunction == null) { "There can only be at most one thread local file initializer" }
|
require(scopeState.threadLocalInitFunction == null) { "There can only be at most one thread local file initializer" }
|
||||||
require(body == null) { "The body of file initializer should be null" }
|
require(body == null) { "The body of file initializer should be null" }
|
||||||
require(declaration.valueParameters.isEmpty()) { "File initializer must be parameterless" }
|
require(declaration.valueParameters.isEmpty()) { "File initializer must be parameterless" }
|
||||||
require(declaration.returnsUnit()) { "File initializer must return Unit" }
|
require(declaration.returnsUnit()) { "File initializer must return Unit" }
|
||||||
llvm.initializersGenerationState.threadLocalInitFunction = declaration
|
scopeState.threadLocalInitFunction = declaration
|
||||||
llvm.initializersGenerationState.threadLocalInitState = getThreadLocalInitStateFor(declaration.parent as IrFile)
|
scopeState.threadLocalInitState = getThreadLocalInitStateFor(declaration.parent as IrDeclarationContainer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -879,8 +877,10 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
using(ClassScope(declaration)) {
|
using(ClassScope(declaration)) {
|
||||||
declaration.declarations.forEach {
|
runAndProcessInitializers(declaration.konanLibrary) {
|
||||||
it.acceptVoid(this)
|
declaration.declarations.forEach {
|
||||||
|
it.acceptVoid(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -947,7 +947,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
// (Cannot do this before the global is initialized).
|
// (Cannot do this before the global is initialized).
|
||||||
LLVMSetLinkage(globalProperty, LLVMLinkage.LLVMInternalLinkage)
|
LLVMSetLinkage(globalProperty, LLVMLinkage.LLVMInternalLinkage)
|
||||||
}
|
}
|
||||||
llvm.initializersGenerationState.topLevelFields.add(declaration)
|
llvm.initializersGenerationState.scopeState.topLevelFields.add(declaration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2399,15 +2399,15 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
return when {
|
return when {
|
||||||
function.isTypedIntrinsic -> intrinsicGenerator.evaluateCall(callee, args, resultSlot)
|
function.isTypedIntrinsic -> intrinsicGenerator.evaluateCall(callee, args, resultSlot)
|
||||||
function.isBuiltInOperator -> evaluateOperatorCall(callee, args)
|
function.isBuiltInOperator -> evaluateOperatorCall(callee, args)
|
||||||
function.origin == DECLARATION_ORIGIN_FILE_GLOBAL_INITIALIZER -> evaluateFileGlobalInitializerCall(function)
|
function.origin == DECLARATION_ORIGIN_STATIC_GLOBAL_INITIALIZER -> evaluateFileGlobalInitializerCall(function)
|
||||||
function.origin == DECLARATION_ORIGIN_FILE_THREAD_LOCAL_INITIALIZER -> evaluateFileThreadLocalInitializerCall(function)
|
function.origin == DECLARATION_ORIGIN_STATIC_THREAD_LOCAL_INITIALIZER -> evaluateFileThreadLocalInitializerCall(function)
|
||||||
function.origin == DECLARATION_ORIGIN_FILE_STANDALONE_THREAD_LOCAL_INITIALIZER -> evaluateFileStandaloneThreadLocalInitializerCall(function)
|
function.origin == DECLARATION_ORIGIN_STATIC_STANDALONE_THREAD_LOCAL_INITIALIZER -> evaluateFileStandaloneThreadLocalInitializerCall(function)
|
||||||
else -> evaluateSimpleFunctionCall(function, args, resultLifetime, callee.superQualifierSymbol?.owner, resultSlot)
|
else -> evaluateSimpleFunctionCall(function, args, resultLifetime, callee.superQualifierSymbol?.owner, resultSlot)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun evaluateFileGlobalInitializerCall(fileInitializer: IrFunction) = with(functionGenerationContext) {
|
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 initializerPtr = with(codegen) { fileInitializer.llvmFunction.llvmValue }
|
||||||
|
|
||||||
val bbInit = basicBlock("label_init", null)
|
val bbInit = basicBlock("label_init", null)
|
||||||
@@ -2426,8 +2426,8 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun evaluateFileThreadLocalInitializerCall(fileInitializer: IrFunction) = with(functionGenerationContext) {
|
private fun evaluateFileThreadLocalInitializerCall(fileInitializer: IrFunction) = with(functionGenerationContext) {
|
||||||
val globalStatePtr = getGlobalInitStateFor(fileInitializer.parent as IrFile)
|
val globalStatePtr = getGlobalInitStateFor(fileInitializer.parent as IrDeclarationContainer)
|
||||||
val localState = getThreadLocalInitStateFor(fileInitializer.parent as IrFile)
|
val localState = getThreadLocalInitStateFor(fileInitializer.parent as IrDeclarationContainer)
|
||||||
val localStatePtr = localState.getAddress(functionGenerationContext)
|
val localStatePtr = localState.getAddress(functionGenerationContext)
|
||||||
val initializerPtr = with(codegen) { fileInitializer.llvmFunction.llvmValue }
|
val initializerPtr = with(codegen) { fileInitializer.llvmFunction.llvmValue }
|
||||||
|
|
||||||
@@ -2453,7 +2453,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun evaluateFileStandaloneThreadLocalInitializerCall(fileInitializer: IrFunction) = with(functionGenerationContext) {
|
private fun evaluateFileStandaloneThreadLocalInitializerCall(fileInitializer: IrFunction) = with(functionGenerationContext) {
|
||||||
val state = getThreadLocalInitStateFor(fileInitializer.parent as IrFile)
|
val state = getThreadLocalInitStateFor(fileInitializer.parent as IrDeclarationContainer)
|
||||||
val statePtr = state.getAddress(functionGenerationContext)
|
val statePtr = state.getAddress(functionGenerationContext)
|
||||||
val initializerPtr = with(codegen) { fileInitializer.llvmFunction.llvmValue }
|
val initializerPtr = with(codegen) { fileInitializer.llvmFunction.llvmValue }
|
||||||
|
|
||||||
|
|||||||
+44
-21
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.konan.lower
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||||
|
import org.jetbrains.kotlin.backend.konan.ConfigChecks
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
import org.jetbrains.kotlin.backend.konan.DECLARATION_ORIGIN_ENTRY_POINT
|
import org.jetbrains.kotlin.backend.konan.DECLARATION_ORIGIN_ENTRY_POINT
|
||||||
import org.jetbrains.kotlin.backend.konan.KonanFqNames
|
import org.jetbrains.kotlin.backend.konan.KonanFqNames
|
||||||
@@ -14,6 +15,7 @@ import org.jetbrains.kotlin.backend.konan.llvm.FieldStorageKind
|
|||||||
import org.jetbrains.kotlin.backend.konan.llvm.needsGCRegistration
|
import org.jetbrains.kotlin.backend.konan.llvm.needsGCRegistration
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.storageKind
|
import org.jetbrains.kotlin.backend.konan.llvm.storageKind
|
||||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||||
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.builders.*
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
@@ -23,34 +25,55 @@ import org.jetbrains.kotlin.ir.util.SYNTHETIC_OFFSET
|
|||||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
||||||
import org.jetbrains.kotlin.ir.util.hasNonConstInitializer
|
import org.jetbrains.kotlin.ir.util.hasNonConstInitializer
|
||||||
import org.jetbrains.kotlin.ir.util.simpleFunctions
|
import org.jetbrains.kotlin.ir.util.simpleFunctions
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
|
||||||
internal object DECLARATION_ORIGIN_FILE_GLOBAL_INITIALIZER : IrDeclarationOriginImpl("FILE_GLOBAL_INITIALIZER")
|
internal object DECLARATION_ORIGIN_STATIC_GLOBAL_INITIALIZER : IrDeclarationOriginImpl("STATIC_GLOBAL_INITIALIZER")
|
||||||
internal object DECLARATION_ORIGIN_FILE_THREAD_LOCAL_INITIALIZER : IrDeclarationOriginImpl("FILE_THREAD_LOCAL_INITIALIZER")
|
internal object DECLARATION_ORIGIN_STATIC_THREAD_LOCAL_INITIALIZER : IrDeclarationOriginImpl("STATIC_THREAD_LOCAL_INITIALIZER")
|
||||||
internal object DECLARATION_ORIGIN_FILE_STANDALONE_THREAD_LOCAL_INITIALIZER : IrDeclarationOriginImpl("FILE_STANDALONE_THREAD_LOCAL_INITIALIZER")
|
internal object DECLARATION_ORIGIN_STATIC_STANDALONE_THREAD_LOCAL_INITIALIZER : IrDeclarationOriginImpl("STATIC_STANDALONE_THREAD_LOCAL_INITIALIZER")
|
||||||
|
|
||||||
internal val IrFunction.isFileInitializer: Boolean
|
internal val IrFunction.isStaticInitializer: Boolean
|
||||||
get() = origin == DECLARATION_ORIGIN_FILE_GLOBAL_INITIALIZER
|
get() = origin == DECLARATION_ORIGIN_STATIC_GLOBAL_INITIALIZER
|
||||||
|| origin == DECLARATION_ORIGIN_FILE_THREAD_LOCAL_INITIALIZER
|
|| origin == DECLARATION_ORIGIN_STATIC_THREAD_LOCAL_INITIALIZER
|
||||||
|| origin == DECLARATION_ORIGIN_FILE_STANDALONE_THREAD_LOCAL_INITIALIZER
|
|| origin == DECLARATION_ORIGIN_STATIC_STANDALONE_THREAD_LOCAL_INITIALIZER
|
||||||
|
|
||||||
internal fun IrBuilderWithScope.irCallFileInitializer(initializer: IrFunctionSymbol) =
|
internal fun IrBuilderWithScope.irCallFileInitializer(initializer: IrFunctionSymbol) =
|
||||||
irCall(initializer)
|
irCall(initializer)
|
||||||
|
|
||||||
internal val IrField.shouldBeInitializedEagerly: Boolean
|
internal fun ConfigChecks.shouldBeInitializedEagerly(irField: IrField): Boolean {
|
||||||
get() {
|
if (irField.parent is IrFile || irField.correspondingPropertySymbol?.owner?.parent is IrFile) {
|
||||||
val annotations = correspondingPropertySymbol?.owner?.annotations ?: annotations
|
if (!useLazyFileInitializers()) return true
|
||||||
return annotations.hasAnnotation(KonanFqNames.eagerInitialization)
|
|
||||||
}
|
}
|
||||||
|
val annotations = irField.correspondingPropertySymbol?.owner?.annotations ?: irField.annotations
|
||||||
|
return annotations.hasAnnotation(KonanFqNames.eagerInitialization)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: ExplicitlyExported for IR proto are not longer needed.
|
// TODO: ExplicitlyExported for IR proto are not longer needed.
|
||||||
internal class StaticInitializersLowering(val context: Context) : FileLoweringPass {
|
internal class StaticInitializersLowering(val context: Context) : FileLoweringPass {
|
||||||
override fun lower(irFile: IrFile) {
|
override fun lower(irFile: IrFile) {
|
||||||
|
irFile.acceptVoid(object : IrElementVisitorVoid {
|
||||||
|
override fun visitElement(element: IrElement) {
|
||||||
|
element.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
override fun visitFile(declaration: IrFile) {
|
||||||
|
processDeclarationContainter(declaration)
|
||||||
|
declaration.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
override fun visitClass(declaration: IrClass) {
|
||||||
|
processDeclarationContainter(declaration)
|
||||||
|
declaration.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun processDeclarationContainter(container: IrDeclarationContainer) {
|
||||||
var requireGlobalInitializer = false
|
var requireGlobalInitializer = false
|
||||||
var requireThreadLocalInitializer = false
|
var requireThreadLocalInitializer = false
|
||||||
for (declaration in irFile.declarations) {
|
for (declaration in container.declarations) {
|
||||||
val irField = (declaration as? IrField) ?: (declaration as? IrProperty)?.backingField
|
val irField = (declaration as? IrField) ?: (declaration as? IrProperty)?.backingField
|
||||||
if (irField == null || !irField.needsInitializationAtRuntime || irField.shouldBeInitializedEagerly) continue
|
if (irField == null || !irField.isStatic || !irField.needsInitializationAtRuntime || context.shouldBeInitializedEagerly(irField)) continue
|
||||||
if (irField.storageKind(context) != FieldStorageKind.THREAD_LOCAL) {
|
if (irField.storageKind(context) != FieldStorageKind.THREAD_LOCAL) {
|
||||||
requireGlobalInitializer = true
|
requireGlobalInitializer = true
|
||||||
} else {
|
} else {
|
||||||
@@ -64,18 +87,18 @@ internal class StaticInitializersLowering(val context: Context) : FileLoweringPa
|
|||||||
|
|
||||||
val globalInitFunction =
|
val globalInitFunction =
|
||||||
if (requireGlobalInitializer)
|
if (requireGlobalInitializer)
|
||||||
buildInitFileFunction(irFile, "\$init_global", DECLARATION_ORIGIN_FILE_GLOBAL_INITIALIZER)
|
buildInitFileFunction(container, "\$init_global", DECLARATION_ORIGIN_STATIC_GLOBAL_INITIALIZER)
|
||||||
else null
|
else null
|
||||||
val threadLocalInitFunction =
|
val threadLocalInitFunction =
|
||||||
if (requireThreadLocalInitializer)
|
if (requireThreadLocalInitializer)
|
||||||
buildInitFileFunction(irFile, "\$init_thread_local",
|
buildInitFileFunction(container, "\$init_thread_local",
|
||||||
if (requireGlobalInitializer)
|
if (requireGlobalInitializer)
|
||||||
DECLARATION_ORIGIN_FILE_THREAD_LOCAL_INITIALIZER
|
DECLARATION_ORIGIN_STATIC_THREAD_LOCAL_INITIALIZER
|
||||||
else DECLARATION_ORIGIN_FILE_STANDALONE_THREAD_LOCAL_INITIALIZER
|
else DECLARATION_ORIGIN_STATIC_STANDALONE_THREAD_LOCAL_INITIALIZER
|
||||||
)
|
)
|
||||||
else null
|
else null
|
||||||
|
|
||||||
irFile.simpleFunctions()
|
container.simpleFunctions()
|
||||||
.filterNot { it.origin == DECLARATION_ORIGIN_ENTRY_POINT }
|
.filterNot { it.origin == DECLARATION_ORIGIN_ENTRY_POINT }
|
||||||
.forEach {
|
.forEach {
|
||||||
val body = it.body ?: return@forEach
|
val body = it.body ?: return@forEach
|
||||||
@@ -89,7 +112,7 @@ internal class StaticInitializersLowering(val context: Context) : FileLoweringPa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildInitFileFunction(irFile: IrFile, name: String, origin: IrDeclarationOrigin) = context.irFactory.buildFun {
|
private fun buildInitFileFunction(container: IrDeclarationContainer, name: String, origin: IrDeclarationOrigin) = context.irFactory.buildFun {
|
||||||
startOffset = SYNTHETIC_OFFSET
|
startOffset = SYNTHETIC_OFFSET
|
||||||
endOffset = SYNTHETIC_OFFSET
|
endOffset = SYNTHETIC_OFFSET
|
||||||
this.origin = origin
|
this.origin = origin
|
||||||
@@ -97,8 +120,8 @@ internal class StaticInitializersLowering(val context: Context) : FileLoweringPa
|
|||||||
visibility = DescriptorVisibilities.PRIVATE
|
visibility = DescriptorVisibilities.PRIVATE
|
||||||
returnType = context.irBuiltIns.unitType
|
returnType = context.irBuiltIns.unitType
|
||||||
}.apply {
|
}.apply {
|
||||||
parent = irFile
|
parent = container
|
||||||
irFile.declarations.add(0, this)
|
container.declarations.add(0, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val IrField.needsInitializationAtRuntime: Boolean
|
private val IrField.needsInitializationAtRuntime: Boolean
|
||||||
|
|||||||
+1
-1
@@ -205,7 +205,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitField(declaration: IrField) {
|
override fun visitField(declaration: IrField) {
|
||||||
if (declaration.parent is IrFile)
|
if (declaration.isStatic)
|
||||||
declaration.initializer?.let {
|
declaration.initializer?.let {
|
||||||
context.logMultiple {
|
context.logMultiple {
|
||||||
+"Analysing global field ${declaration.descriptor}"
|
+"Analysing global field ${declaration.descriptor}"
|
||||||
|
|||||||
+4
-4
@@ -110,7 +110,7 @@ internal object DataFlowIR {
|
|||||||
}
|
}
|
||||||
|
|
||||||
object FunctionAttributes {
|
object FunctionAttributes {
|
||||||
val IS_TOP_LEVEL_FIELD_INITIALIZER = 1
|
val IS_STATIC_FIELD_INITIALIZER = 1
|
||||||
val RETURNS_UNIT = 4
|
val RETURNS_UNIT = 4
|
||||||
val RETURNS_NOTHING = 8
|
val RETURNS_NOTHING = 8
|
||||||
val EXPLICITLY_EXPORTED = 16
|
val EXPLICITLY_EXPORTED = 16
|
||||||
@@ -122,7 +122,7 @@ internal object DataFlowIR {
|
|||||||
lateinit var parameters: Array<FunctionParameter>
|
lateinit var parameters: Array<FunctionParameter>
|
||||||
lateinit var returnParameter: FunctionParameter
|
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 returnsUnit = attributes.and(FunctionAttributes.RETURNS_UNIT) != 0
|
||||||
val returnsNothing = attributes.and(FunctionAttributes.RETURNS_NOTHING) != 0
|
val returnsNothing = attributes.and(FunctionAttributes.RETURNS_NOTHING) != 0
|
||||||
val explicitlyExported = attributes.and(FunctionAttributes.EXPLICITLY_EXPORTED) != 0
|
val explicitlyExported = attributes.and(FunctionAttributes.EXPLICITLY_EXPORTED) != 0
|
||||||
@@ -656,8 +656,8 @@ internal object DataFlowIR {
|
|||||||
private fun mapPropertyInitializer(irField: IrField): FunctionSymbol {
|
private fun mapPropertyInitializer(irField: IrField): FunctionSymbol {
|
||||||
functionMap[irField]?.let { return it }
|
functionMap[irField]?.let { return it }
|
||||||
|
|
||||||
assert(irField.parent !is IrClass) { "All local properties initializers should've been lowered" }
|
assert(irField.isStatic) { "All local properties initializers should've been lowered" }
|
||||||
val attributes = FunctionAttributes.IS_TOP_LEVEL_FIELD_INITIALIZER or FunctionAttributes.RETURNS_UNIT
|
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" })
|
val symbol = FunctionSymbol.Private(privateFunIndex++, module, -1, attributes, irField, null, takeName { "${irField.computeSymbolName()}_init" })
|
||||||
|
|
||||||
functionMap[irField] = symbol
|
functionMap[irField] = symbol
|
||||||
|
|||||||
+2
-3
@@ -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.common.lower.irBlock
|
||||||
import org.jetbrains.kotlin.backend.konan.*
|
import org.jetbrains.kotlin.backend.konan.*
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.isBoxOrUnboxCall
|
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.IntArrayList
|
||||||
import org.jetbrains.kotlin.backend.konan.util.LongArrayList
|
import org.jetbrains.kotlin.backend.konan.util.LongArrayList
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
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: 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.
|
// TODO: With the changed semantics of global initializers this is no longer the case - rework.
|
||||||
val globalInitializers =
|
val globalInitializers =
|
||||||
moduleDFG.symbolTable.functionMap.values.filter { it.isTopLevelFieldInitializer } +
|
moduleDFG.symbolTable.functionMap.values.filter { it.isStaticFieldInitializer } +
|
||||||
externalModulesDFG.functionDFGs.keys.filter { it.isTopLevelFieldInitializer }
|
externalModulesDFG.functionDFGs.keys.filter { it.isStaticFieldInitializer }
|
||||||
|
|
||||||
val explicitlyExported =
|
val explicitlyExported =
|
||||||
moduleDFG.symbolTable.functionMap.values.filter { it.explicitlyExported } +
|
moduleDFG.symbolTable.functionMap.values.filter { it.explicitlyExported } +
|
||||||
|
|||||||
+49
-53
@@ -58,13 +58,13 @@ internal object StaticInitializersOptimization {
|
|||||||
val callSitesRequiringGlobalInitializerCall: Set<IrFunctionAccessExpression>,
|
val callSitesRequiringGlobalInitializerCall: Set<IrFunctionAccessExpression>,
|
||||||
val callSitesRequiringThreadLocalInitializerCall: Set<IrFunctionAccessExpression>)
|
val callSitesRequiringThreadLocalInitializerCall: Set<IrFunctionAccessExpression>)
|
||||||
|
|
||||||
private class InitializedFiles(val fileIds: Map<IrFile, Int>) {
|
private class InitializedContainers(val containerIds: Map<IrDeclarationContainer, Int>) {
|
||||||
val afterCall = mutableMapOf<IrFunction, BitSet>()
|
val afterCall = mutableMapOf<IrFunction, BitSet>()
|
||||||
val beforeCallGlobal = mutableMapOf<IrFunction, BitSet>()
|
val beforeCallGlobal = mutableMapOf<IrFunction, BitSet>()
|
||||||
val beforeCallThreadLocal = mutableMapOf<IrFunction, BitSet>()
|
val beforeCallThreadLocal = mutableMapOf<IrFunction, BitSet>()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val invalidFileId = 0
|
private val invalidContainerId = 0
|
||||||
|
|
||||||
private class InterproceduralAnalysis(val context: Context, val callGraph: CallGraph,
|
private class InterproceduralAnalysis(val context: Context, val callGraph: CallGraph,
|
||||||
val rootSet: Set<IrFunction>) {
|
val rootSet: Set<IrFunction>) {
|
||||||
@@ -109,20 +109,18 @@ internal object StaticInitializersOptimization {
|
|||||||
+""
|
+""
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileId = invalidFileId
|
val functions = buildSet {
|
||||||
val fileIds = mutableMapOf<IrFile, Int>()
|
callGraph.directEdges.values.forEach {
|
||||||
for (node in callGraph.directEdges.values) {
|
add(it.symbol.irFunction)
|
||||||
val callerFile = node.symbol.irFile
|
it.callSites.forEach { callSite -> add(callSite.actualCallee.irFunction) }
|
||||||
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 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" }
|
context.log { "FIRST PHASE: compute initialized after call" }
|
||||||
|
|
||||||
@@ -163,11 +161,10 @@ internal object StaticInitializersOptimization {
|
|||||||
): Set<IrFunction> {
|
): Set<IrFunction> {
|
||||||
val result = mutableSetOf<IrFunction>()
|
val result = mutableSetOf<IrFunction>()
|
||||||
initializedFiles.forEach { (function, functionInitializedFiles) ->
|
initializedFiles.forEach { (function, functionInitializedFiles) ->
|
||||||
val irFile = function.fileOrNull
|
val containter = function.calledInitializer ?: return@forEach
|
||||||
val backingField = (function as? IrSimpleFunction)?.correspondingPropertySymbol?.owner?.backingField
|
val backingField = (function as? IrSimpleFunction)?.correspondingPropertySymbol?.owner?.backingField
|
||||||
val isDefaultAccessor = backingField != null && function.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR
|
val isDefaultAccessor = backingField != null && function.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR
|
||||||
if (irFile == null ||
|
if ((!functionInitializedFiles.get(containerIds[containter]!!)
|
||||||
(!functionInitializedFiles.get(fileIds[irFile]!!)
|
|
||||||
&& function !in functionsWhoseInitializerCallCanBeExtractedToCallSites
|
&& function !in functionsWhoseInitializerCallCanBeExtractedToCallSites
|
||||||
// Extract calls to file initializers off of default accessors to simplify their inlining.
|
// Extract calls to file initializers off of default accessors to simplify their inlining.
|
||||||
&& (!isDefaultAccessor || function in rootSet))
|
&& (!isDefaultAccessor || function in rootSet))
|
||||||
@@ -194,7 +191,7 @@ internal object StaticInitializersOptimization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun analyze(multiNode: DirectedGraphMultiNode<DataFlowIR.FunctionSymbol.Declared>,
|
private fun analyze(multiNode: DirectedGraphMultiNode<DataFlowIR.FunctionSymbol.Declared>,
|
||||||
initializedFiles: InitializedFiles,
|
initializedFiles: InitializedContainers,
|
||||||
analysisGoal: AnalysisGoal) {
|
analysisGoal: AnalysisGoal) {
|
||||||
val nodes = multiNode.nodes.toList()
|
val nodes = multiNode.nodes.toList()
|
||||||
|
|
||||||
@@ -244,12 +241,12 @@ internal object StaticInitializersOptimization {
|
|||||||
CollectCallSites
|
CollectCallSites
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IrFunction.callsFileInitializer() =
|
private val IrFunction.calledInitializer get() =
|
||||||
(body?.statements?.get(0) as? IrCall)?.symbol?.owner?.isFileInitializer == true
|
(body?.statements?.get(0) as? IrCall)?.symbol?.owner?.takeIf { it.isStaticInitializer }?.parent as IrDeclarationContainer?
|
||||||
|
|
||||||
private fun intraproceduralAnalysis(
|
private fun intraproceduralAnalysis(
|
||||||
node: CallGraphNode,
|
node: CallGraphNode,
|
||||||
initializedFiles: InitializedFiles,
|
initializedContainers: InitializedContainers,
|
||||||
analysisGoal: AnalysisGoal,
|
analysisGoal: AnalysisGoal,
|
||||||
callSitesRequiringGlobalInitializerCall: MutableSet<IrFunctionAccessExpression> = dummySet,
|
callSitesRequiringGlobalInitializerCall: MutableSet<IrFunctionAccessExpression> = dummySet,
|
||||||
callSitesRequiringThreadLocalInitializerCall: MutableSet<IrFunctionAccessExpression> = dummySet,
|
callSitesRequiringThreadLocalInitializerCall: MutableSet<IrFunctionAccessExpression> = dummySet,
|
||||||
@@ -257,7 +254,7 @@ internal object StaticInitializersOptimization {
|
|||||||
callSitesNotRequiringThreadLocalInitializerCall: MutableSet<IrFunctionAccessExpression> = dummySet
|
callSitesNotRequiringThreadLocalInitializerCall: MutableSet<IrFunctionAccessExpression> = dummySet
|
||||||
) {
|
) {
|
||||||
val irDeclaration = node.symbol.irDeclaration ?: return
|
val irDeclaration = node.symbol.irDeclaration ?: return
|
||||||
val body = if (node.symbol.isTopLevelFieldInitializer)
|
val body = if (node.symbol.isStaticFieldInitializer)
|
||||||
(irDeclaration as IrField).initializer?.expression
|
(irDeclaration as IrField).initializer?.expression
|
||||||
else {
|
else {
|
||||||
val function = irDeclaration as IrFunction
|
val function = irDeclaration as IrFunction
|
||||||
@@ -266,11 +263,11 @@ internal object StaticInitializersOptimization {
|
|||||||
}
|
}
|
||||||
if (body == null) return
|
if (body == null) return
|
||||||
|
|
||||||
val filesWithInitializedGlobals = BitSet()
|
val containersWithInitializedGlobals = BitSet()
|
||||||
val filesWithInitializedThreadLocals = BitSet()
|
val containersWithInitializedThreadLocals = BitSet()
|
||||||
if (!node.symbol.isTopLevelFieldInitializer) {
|
if (!node.symbol.isStaticFieldInitializer) {
|
||||||
initializedFiles.beforeCallGlobal[irDeclaration as IrFunction]?.let { filesWithInitializedGlobals.or(it) }
|
initializedContainers.beforeCallGlobal[irDeclaration as IrFunction]?.let { containersWithInitializedGlobals.or(it) }
|
||||||
initializedFiles.beforeCallThreadLocal[irDeclaration]?.let { filesWithInitializedThreadLocals.or(it) }
|
initializedContainers.beforeCallThreadLocal[irDeclaration]?.let { containersWithInitializedThreadLocals.or(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
val producerInvocations = mutableMapOf<IrExpression, IrCall>()
|
val producerInvocations = mutableMapOf<IrExpression, IrCall>()
|
||||||
@@ -409,25 +406,24 @@ internal object StaticInitializersOptimization {
|
|||||||
if (this.get(bit)) this else copy().also { it.set(bit) }
|
if (this.get(bit)) this else copy().also { it.set(bit) }
|
||||||
|
|
||||||
private fun getResultAfterCall(function: IrFunction, set: BitSet): BitSet {
|
private fun getResultAfterCall(function: IrFunction, set: BitSet): BitSet {
|
||||||
val result = initializedFiles.afterCall[function]
|
val result = initializedContainers.afterCall[function]
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
if (!function.callsFileInitializer()) return set
|
val file = function.calledInitializer ?: return set
|
||||||
val file = function.fileOrNull ?: return set
|
return set.withSetBit(initializedContainers.containerIds[file]!!)
|
||||||
return set.withSetBit(initializedFiles.fileIds[file]!!)
|
|
||||||
}
|
}
|
||||||
return result.copy().also { it.or(set) }
|
return result.copy().also { it.or(set) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateResultForFunction(function: IrFunction, globalSet: BitSet, threadLocalSet: BitSet) {
|
private fun updateResultForFunction(function: IrFunction, globalSet: BitSet, threadLocalSet: BitSet) {
|
||||||
if (analysisGoal != AnalysisGoal.ComputeInitializedBeforeCall) return
|
if (analysisGoal != AnalysisGoal.ComputeInitializedBeforeCall) return
|
||||||
intersectInitializedFiles(initializedFiles.beforeCallGlobal, function, globalSet)
|
intersectInitializedFiles(initializedContainers.beforeCallGlobal, function, globalSet)
|
||||||
intersectInitializedFiles(initializedFiles.beforeCallThreadLocal, function, threadLocalSet)
|
intersectInitializedFiles(initializedContainers.beforeCallThreadLocal, function, threadLocalSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateResultForFunction(function: IrFunction, set: BitSet) {
|
private fun updateResultForFunction(function: IrFunction, set: BitSet) {
|
||||||
if (analysisGoal != AnalysisGoal.ComputeInitializedBeforeCall) return
|
if (analysisGoal != AnalysisGoal.ComputeInitializedBeforeCall) return
|
||||||
intersectInitializedFiles(initializedFiles.beforeCallGlobal, function, set.copy().also { it.or(filesWithInitializedGlobals) })
|
intersectInitializedFiles(initializedContainers.beforeCallGlobal, function, set.copy().also { it.or(containersWithInitializedGlobals) })
|
||||||
intersectInitializedFiles(initializedFiles.beforeCallThreadLocal, function, set.copy().also { it.or(filesWithInitializedThreadLocals) })
|
intersectInitializedFiles(initializedContainers.beforeCallThreadLocal, function, set.copy().also { it.or(containersWithInitializedThreadLocals) })
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitGetObjectValue(expression: IrGetObjectValue, data: BitSet): BitSet {
|
override fun visitGetObjectValue(expression: IrGetObjectValue, data: BitSet): BitSet {
|
||||||
@@ -446,22 +442,22 @@ internal object StaticInitializersOptimization {
|
|||||||
val arguments = expression.getArgumentsWithIr()
|
val arguments = expression.getArgumentsWithIr()
|
||||||
val argumentsResult = arguments.fold(data) { set, arg -> arg.second.accept(this, set) }
|
val argumentsResult = arguments.fold(data) { set, arg -> arg.second.accept(this, set) }
|
||||||
updateResultForFunction(actualCallee, argumentsResult)
|
updateResultForFunction(actualCallee, argumentsResult)
|
||||||
val file = actualCallee.fileOrNull
|
val container = actualCallee.calledInitializer
|
||||||
val fileId = file?.let { initializedFiles.fileIds[it]!! } ?: invalidFileId
|
val containerId = initializedContainers.containerIds[container] ?: invalidContainerId
|
||||||
if (analysisGoal == AnalysisGoal.CollectCallSites && file != null
|
if (analysisGoal == AnalysisGoal.CollectCallSites &&
|
||||||
// Only extract initializer calls from non-virtual functions.
|
// Only extract initializer calls from non-virtual functions.
|
||||||
&& !actualCallee.isOverridable
|
!actualCallee.isOverridable
|
||||||
) {
|
) {
|
||||||
// The initializer won't be optimized away from the function.
|
// The initializer won't be optimized away from the function.
|
||||||
if (!initializedFiles.beforeCallGlobal[actualCallee]!!.get(fileId)) {
|
if (!initializedContainers.beforeCallGlobal[actualCallee]!!.get(containerId)) {
|
||||||
if (argumentsResult.get(fileId) || filesWithInitializedGlobals.get(fileId))
|
if (argumentsResult.get(containerId) || containersWithInitializedGlobals.get(containerId))
|
||||||
callSitesNotRequiringGlobalInitializerCall += expression
|
callSitesNotRequiringGlobalInitializerCall += expression
|
||||||
else
|
else
|
||||||
callSitesRequiringGlobalInitializerCall += expression
|
callSitesRequiringGlobalInitializerCall += expression
|
||||||
}
|
}
|
||||||
// The initializer won't be optimized away from the function.
|
// The initializer won't be optimized away from the function.
|
||||||
if (!initializedFiles.beforeCallThreadLocal[actualCallee]!!.get(fileId)) {
|
if (!initializedContainers.beforeCallThreadLocal[actualCallee]!!.get(containerId)) {
|
||||||
if (argumentsResult.get(fileId) || filesWithInitializedThreadLocals.get(fileId))
|
if (argumentsResult.get(containerId) || containersWithInitializedThreadLocals.get(containerId))
|
||||||
callSitesNotRequiringThreadLocalInitializerCall += expression
|
callSitesNotRequiringThreadLocalInitializerCall += expression
|
||||||
else
|
else
|
||||||
callSitesRequiringThreadLocalInitializerCall += expression
|
callSitesRequiringThreadLocalInitializerCall += expression
|
||||||
@@ -480,7 +476,7 @@ internal object StaticInitializersOptimization {
|
|||||||
if (analysisGoal != AnalysisGoal.CollectCallSites) {
|
if (analysisGoal != AnalysisGoal.CollectCallSites) {
|
||||||
require(!jobInvocation.isVirtualCall) { "Expected a static call but was: ${jobInvocation.render()}" }
|
require(!jobInvocation.isVirtualCall) { "Expected a static call but was: ${jobInvocation.render()}" }
|
||||||
updateResultForFunction(jobInvocation.actualCallee,
|
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.
|
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)
|
processCall(expression, expression.actualCallee, data)
|
||||||
|
|
||||||
override fun visitCall(expression: IrCall, data: BitSet): BitSet {
|
override fun visitCall(expression: IrCall, data: BitSet): BitSet {
|
||||||
if (expression.symbol.owner.isFileInitializer)
|
if (expression.symbol.owner.isStaticInitializer)
|
||||||
return data.withSetBit(initializedFiles.fileIds[irDeclaration.file]!!)
|
return data.withSetBit(initializedContainers.containerIds[expression.symbol.owner.parent as IrDeclarationContainer]!!)
|
||||||
if (expression.symbol == executeImplSymbol)
|
if (expression.symbol == executeImplSymbol)
|
||||||
return processExecuteImpl(expression, data)
|
return processExecuteImpl(expression, data)
|
||||||
if (expression.symbol == getContinuationSymbol)
|
if (expression.symbol == getContinuationSymbol)
|
||||||
@@ -528,8 +524,8 @@ internal object StaticInitializersOptimization {
|
|||||||
}, BitSet())
|
}, BitSet())
|
||||||
|
|
||||||
if (analysisGoal == AnalysisGoal.ComputeInitializedAfterCall) {
|
if (analysisGoal == AnalysisGoal.ComputeInitializedAfterCall) {
|
||||||
if (!node.symbol.isTopLevelFieldInitializer)
|
if (!node.symbol.isStaticFieldInitializer)
|
||||||
initializedFiles.afterCall[irDeclaration as IrFunction] = returnTargetsInitializedFiles[irDeclaration.symbol] ?: callerResult
|
initializedContainers.afterCall[irDeclaration as IrFunction] = returnTargetsInitializedFiles[irDeclaration.symbol] ?: callerResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -560,10 +556,10 @@ internal object StaticInitializersOptimization {
|
|||||||
.take(2) // The very first statements by construction.
|
.take(2) // The very first statements by construction.
|
||||||
.filter {
|
.filter {
|
||||||
val calleeOrigin = (it as? IrCall)?.symbol?.owner?.origin
|
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
|
&& callee !in analysisResult.functionsRequiringGlobalInitializerCall
|
||||||
val isNotOptimizedAwayThreadLocalInitializerCall = (calleeOrigin == DECLARATION_ORIGIN_FILE_THREAD_LOCAL_INITIALIZER
|
val isNotOptimizedAwayThreadLocalInitializerCall = (calleeOrigin == DECLARATION_ORIGIN_STATIC_THREAD_LOCAL_INITIALIZER
|
||||||
|| calleeOrigin == DECLARATION_ORIGIN_FILE_STANDALONE_THREAD_LOCAL_INITIALIZER)
|
|| calleeOrigin == DECLARATION_ORIGIN_STATIC_STANDALONE_THREAD_LOCAL_INITIALIZER)
|
||||||
&& callee !in analysisResult.functionsRequiringThreadLocalInitializerCall
|
&& callee !in analysisResult.functionsRequiringThreadLocalInitializerCall
|
||||||
if (isNotOptimizedAwayGlobalInitializerCall)
|
if (isNotOptimizedAwayGlobalInitializerCall)
|
||||||
++numberOfCallSitesToFunctionsWithGlobalInitializerCall
|
++numberOfCallSitesToFunctionsWithGlobalInitializerCall
|
||||||
@@ -596,7 +592,7 @@ internal object StaticInitializersOptimization {
|
|||||||
.take(2) // The very first statements by construction.
|
.take(2) // The very first statements by construction.
|
||||||
.indexOfFirst {
|
.indexOfFirst {
|
||||||
val calleeOrigin = (it as? IrCall)?.symbol?.owner?.origin
|
val calleeOrigin = (it as? IrCall)?.symbol?.owner?.origin
|
||||||
calleeOrigin == DECLARATION_ORIGIN_FILE_GLOBAL_INITIALIZER
|
calleeOrigin == DECLARATION_ORIGIN_STATIC_GLOBAL_INITIALIZER
|
||||||
}
|
}
|
||||||
if (globalInitializerCallIndex >= 0) {
|
if (globalInitializerCallIndex >= 0) {
|
||||||
++numberOfFunctionsWithGlobalInitializerCall
|
++numberOfFunctionsWithGlobalInitializerCall
|
||||||
@@ -609,8 +605,8 @@ internal object StaticInitializersOptimization {
|
|||||||
.take(2)
|
.take(2)
|
||||||
.indexOfFirst {
|
.indexOfFirst {
|
||||||
val calleeOrigin = (it as? IrCall)?.symbol?.owner?.origin
|
val calleeOrigin = (it as? IrCall)?.symbol?.owner?.origin
|
||||||
calleeOrigin == DECLARATION_ORIGIN_FILE_THREAD_LOCAL_INITIALIZER
|
calleeOrigin == DECLARATION_ORIGIN_STATIC_THREAD_LOCAL_INITIALIZER
|
||||||
|| calleeOrigin == DECLARATION_ORIGIN_FILE_STANDALONE_THREAD_LOCAL_INITIALIZER
|
|| calleeOrigin == DECLARATION_ORIGIN_STATIC_STANDALONE_THREAD_LOCAL_INITIALIZER
|
||||||
}
|
}
|
||||||
if (threadLocalInitializerCallIndex >= 0) {
|
if (threadLocalInitializerCallIndex >= 0) {
|
||||||
++numberOfFunctionsWithThreadLocalInitializerCall
|
++numberOfFunctionsWithThreadLocalInitializerCall
|
||||||
|
|||||||
Reference in New Issue
Block a user