JVM IR: clear BindingContext after psi2ir
This helps to reduce peak memory in lowerings/codegen by a lot. A more robust approach would be to have a separate BindingContext for each file, and clear each of them after running psi2ir on it. This would also lower peak memory usage in psi2ir. Provide a fallback workaround compiler argument -Xir-do-not-clear-binding-context just in case BindingContext is in fact used somewhere and it's not caught by tests.
This commit is contained in:
@@ -53,7 +53,7 @@ class GenerationState private constructor(
|
|||||||
val project: Project,
|
val project: Project,
|
||||||
builderFactory: ClassBuilderFactory,
|
builderFactory: ClassBuilderFactory,
|
||||||
val module: ModuleDescriptor,
|
val module: ModuleDescriptor,
|
||||||
bindingContext: BindingContext,
|
val originalFrontendBindingContext: BindingContext,
|
||||||
val files: List<KtFile>,
|
val files: List<KtFile>,
|
||||||
val configuration: CompilerConfiguration,
|
val configuration: CompilerConfiguration,
|
||||||
val generateDeclaredClassFilter: GenerateClassFilter,
|
val generateDeclaredClassFilter: GenerateClassFilter,
|
||||||
@@ -178,7 +178,7 @@ class GenerationState private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val extraJvmDiagnosticsTrace: BindingTrace =
|
val extraJvmDiagnosticsTrace: BindingTrace =
|
||||||
DelegatingBindingTrace(bindingContext, "For extra diagnostics in ${this::class.java}", false)
|
DelegatingBindingTrace(originalFrontendBindingContext, "For extra diagnostics in ${this::class.java}", false)
|
||||||
private val interceptedBuilderFactory: ClassBuilderFactory
|
private val interceptedBuilderFactory: ClassBuilderFactory
|
||||||
private var used = false
|
private var used = false
|
||||||
|
|
||||||
@@ -199,13 +199,13 @@ class GenerationState private constructor(
|
|||||||
val moduleName: String = moduleName ?: JvmCodegenUtil.getModuleName(module)
|
val moduleName: String = moduleName ?: JvmCodegenUtil.getModuleName(module)
|
||||||
val classBuilderMode: ClassBuilderMode = builderFactory.classBuilderMode
|
val classBuilderMode: ClassBuilderMode = builderFactory.classBuilderMode
|
||||||
val bindingTrace: BindingTrace = DelegatingBindingTrace(
|
val bindingTrace: BindingTrace = DelegatingBindingTrace(
|
||||||
bindingContext, "trace in GenerationState",
|
originalFrontendBindingContext, "trace in GenerationState",
|
||||||
filter = if (wantsDiagnostics) BindingTraceFilter.ACCEPT_ALL else BindingTraceFilter.NO_DIAGNOSTICS
|
filter = if (wantsDiagnostics) BindingTraceFilter.ACCEPT_ALL else BindingTraceFilter.NO_DIAGNOSTICS
|
||||||
)
|
)
|
||||||
val bindingContext: BindingContext = bindingTrace.bindingContext
|
val bindingContext: BindingContext = bindingTrace.bindingContext
|
||||||
val mainFunctionDetector = MainFunctionDetector(bindingContext, languageVersionSettings)
|
val mainFunctionDetector = MainFunctionDetector(originalFrontendBindingContext, languageVersionSettings)
|
||||||
val typeMapper: KotlinTypeMapper = KotlinTypeMapper(
|
val typeMapper: KotlinTypeMapper = KotlinTypeMapper(
|
||||||
this.bindingContext,
|
bindingContext,
|
||||||
classBuilderMode,
|
classBuilderMode,
|
||||||
this.moduleName,
|
this.moduleName,
|
||||||
languageVersionSettings,
|
languageVersionSettings,
|
||||||
@@ -311,7 +311,7 @@ class GenerationState private constructor(
|
|||||||
it
|
it
|
||||||
else
|
else
|
||||||
BuilderFactoryForDuplicateSignatureDiagnostics(
|
BuilderFactoryForDuplicateSignatureDiagnostics(
|
||||||
it, this.bindingContext, diagnostics, this.moduleName, this.languageVersionSettings,
|
it, bindingContext, diagnostics, this.moduleName, languageVersionSettings,
|
||||||
shouldGenerate = { origin -> !shouldOnlyCollectSignatures(origin) },
|
shouldGenerate = { origin -> !shouldOnlyCollectSignatures(origin) },
|
||||||
).apply { duplicateSignatureFactory = this }
|
).apply { duplicateSignatureFactory = this }
|
||||||
},
|
},
|
||||||
@@ -322,7 +322,7 @@ class GenerationState private constructor(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
.wrapWith(ClassBuilderInterceptorExtension.getInstances(project)) { classBuilderFactory, extension ->
|
.wrapWith(ClassBuilderInterceptorExtension.getInstances(project)) { classBuilderFactory, extension ->
|
||||||
extension.interceptClassBuilderFactory(classBuilderFactory, bindingContext, diagnostics)
|
extension.interceptClassBuilderFactory(classBuilderFactory, originalFrontendBindingContext, diagnostics)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.factory = ClassFileFactory(this, interceptedBuilderFactory)
|
this.factory = ClassFileFactory(this, interceptedBuilderFactory)
|
||||||
|
|||||||
+6
@@ -108,6 +108,12 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
|
|||||||
)
|
)
|
||||||
var isIrWithStableAbi: Boolean by FreezableVar(false)
|
var isIrWithStableAbi: Boolean by FreezableVar(false)
|
||||||
|
|
||||||
|
@Argument(
|
||||||
|
value = "-Xir-do-not-clear-binding-context",
|
||||||
|
description = "When using the IR backend, do not clear BindingContext between psi2ir and lowerings"
|
||||||
|
)
|
||||||
|
var doNotClearBindingContext: Boolean by FreezableVar(false)
|
||||||
|
|
||||||
@Argument(value = "-Xmodule-path", valueDescription = "<path>", description = "Paths where to find Java 9+ modules")
|
@Argument(value = "-Xmodule-path", valueDescription = "<path>", description = "Paths where to find Java 9+ modules")
|
||||||
var javaModulePath: String? by NullableStringFreezableVar(null)
|
var javaModulePath: String? by NullableStringFreezableVar(null)
|
||||||
|
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ fun CompilerConfiguration.configureAdvancedJvmOptions(arguments: K2JVMCompilerAr
|
|||||||
|
|
||||||
put(JVMConfigurationKeys.IR, arguments.useIR && !arguments.noUseIR)
|
put(JVMConfigurationKeys.IR, arguments.useIR && !arguments.noUseIR)
|
||||||
put(JVMConfigurationKeys.IS_IR_WITH_STABLE_ABI, arguments.isIrWithStableAbi)
|
put(JVMConfigurationKeys.IS_IR_WITH_STABLE_ABI, arguments.isIrWithStableAbi)
|
||||||
|
put(JVMConfigurationKeys.DO_NOT_CLEAR_BINDING_CONTEXT, arguments.doNotClearBindingContext)
|
||||||
put(JVMConfigurationKeys.DISABLE_CALL_ASSERTIONS, arguments.noCallAssertions)
|
put(JVMConfigurationKeys.DISABLE_CALL_ASSERTIONS, arguments.noCallAssertions)
|
||||||
put(JVMConfigurationKeys.DISABLE_RECEIVER_ASSERTIONS, arguments.noReceiverAssertions)
|
put(JVMConfigurationKeys.DISABLE_RECEIVER_ASSERTIONS, arguments.noReceiverAssertions)
|
||||||
put(JVMConfigurationKeys.DISABLE_PARAM_ASSERTIONS, arguments.noParamAssertions)
|
put(JVMConfigurationKeys.DISABLE_PARAM_ASSERTIONS, arguments.noParamAssertions)
|
||||||
|
|||||||
@@ -120,6 +120,9 @@ public class JVMConfigurationKeys {
|
|||||||
public static final CompilerConfigurationKey<Boolean> IS_IR_WITH_STABLE_ABI =
|
public static final CompilerConfigurationKey<Boolean> IS_IR_WITH_STABLE_ABI =
|
||||||
CompilerConfigurationKey.create("Is IR with stable ABI");
|
CompilerConfigurationKey.create("Is IR with stable ABI");
|
||||||
|
|
||||||
|
public static final CompilerConfigurationKey<Boolean> DO_NOT_CLEAR_BINDING_CONTEXT =
|
||||||
|
CompilerConfigurationKey.create("When using the IR backend, do not clear BindingContext between psi2ir and lowerings");
|
||||||
|
|
||||||
public static final CompilerConfigurationKey<Boolean> NO_OPTIMIZED_CALLABLE_REFERENCES =
|
public static final CompilerConfigurationKey<Boolean> NO_OPTIMIZED_CALLABLE_REFERENCES =
|
||||||
CompilerConfigurationKey.create("Do not use optimized callable reference superclasses available from 1.4");
|
CompilerConfigurationKey.create("Do not use optimized callable reference superclasses available from 1.4");
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,8 @@ import java.util.regex.Pattern
|
|||||||
data class DiagnosticsRenderingConfiguration(
|
data class DiagnosticsRenderingConfiguration(
|
||||||
val platform: String?,
|
val platform: String?,
|
||||||
val withNewInference: Boolean,
|
val withNewInference: Boolean,
|
||||||
val languageVersionSettings: LanguageVersionSettings?
|
val languageVersionSettings: LanguageVersionSettings?,
|
||||||
|
val skipDebugInfoDiagnostics: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
object CheckerTestUtil {
|
object CheckerTestUtil {
|
||||||
@@ -130,18 +131,20 @@ object CheckerTestUtil {
|
|||||||
diagnostics.add(ActualDiagnostic(SyntaxErrorDiagnostic(errorElement), configuration.platform, configuration.withNewInference))
|
diagnostics.add(ActualDiagnostic(SyntaxErrorDiagnostic(errorElement), configuration.platform, configuration.withNewInference))
|
||||||
}
|
}
|
||||||
|
|
||||||
diagnostics.addAll(
|
if (!configuration.skipDebugInfoDiagnostics) {
|
||||||
getDebugInfoDiagnostics(
|
diagnostics.addAll(
|
||||||
root,
|
getDebugInfoDiagnostics(
|
||||||
bindingContext,
|
root,
|
||||||
markDynamicCalls,
|
bindingContext,
|
||||||
dynamicCallDescriptors,
|
markDynamicCalls,
|
||||||
configuration,
|
dynamicCallDescriptors,
|
||||||
dataFlowValueFactory,
|
configuration,
|
||||||
moduleDescriptor,
|
dataFlowValueFactory,
|
||||||
diagnosedRanges
|
moduleDescriptor,
|
||||||
|
diagnosedRanges
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
|
|
||||||
return diagnostics
|
return diagnostics
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,11 +37,9 @@ public class BindingTraceContext implements BindingTrace {
|
|||||||
/* package */ final static boolean TRACK_WITH_STACK_TRACES = true;
|
/* package */ final static boolean TRACK_WITH_STACK_TRACES = true;
|
||||||
|
|
||||||
private final MutableSlicedMap map;
|
private final MutableSlicedMap map;
|
||||||
@Nullable private final MutableDiagnosticsWithSuppression mutableDiagnostics;
|
private final MutableDiagnosticsWithSuppression mutableDiagnostics;
|
||||||
@NotNull private final BindingTraceFilter filter;
|
|
||||||
|
|
||||||
private final BindingContext bindingContext = new BindingContext() {
|
|
||||||
|
|
||||||
|
private final BindingContext bindingContext = new CleanableBindingContext() {
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Diagnostics getDiagnostics() {
|
public Diagnostics getDiagnostics() {
|
||||||
@@ -76,6 +74,11 @@ public class BindingTraceContext implements BindingTrace {
|
|||||||
public void addOwnDataTo(@NotNull BindingTrace trace, boolean commitDiagnostics) {
|
public void addOwnDataTo(@NotNull BindingTrace trace, boolean commitDiagnostics) {
|
||||||
BindingContextUtils.addOwnDataTo(trace, null, commitDiagnostics, map, mutableDiagnostics);
|
BindingContextUtils.addOwnDataTo(trace, null, commitDiagnostics, map, mutableDiagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
map.clear();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public BindingTraceContext() {
|
public BindingTraceContext() {
|
||||||
@@ -87,7 +90,6 @@ public class BindingTraceContext implements BindingTrace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public BindingTraceContext(BindingTraceFilter filter, boolean allowSliceRewrite) {
|
public BindingTraceContext(BindingTraceFilter filter, boolean allowSliceRewrite) {
|
||||||
//noinspection ConstantConditions
|
|
||||||
this(TRACK_REWRITES && !allowSliceRewrite ? new TrackingSlicedMap(TRACK_WITH_STACK_TRACES) : new SlicedMapImpl(allowSliceRewrite), filter);
|
this(TRACK_REWRITES && !allowSliceRewrite ? new TrackingSlicedMap(TRACK_WITH_STACK_TRACES) : new SlicedMapImpl(allowSliceRewrite), filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +99,6 @@ public class BindingTraceContext implements BindingTrace {
|
|||||||
this.mutableDiagnostics = !filter.getIgnoreDiagnostics()
|
this.mutableDiagnostics = !filter.getIgnoreDiagnostics()
|
||||||
? new MutableDiagnosticsWithSuppression(bindingContext, Diagnostics.Companion.getEMPTY())
|
? new MutableDiagnosticsWithSuppression(bindingContext, Diagnostics.Companion.getEMPTY())
|
||||||
: null;
|
: null;
|
||||||
this.filter = filter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestOnly
|
@TestOnly
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 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.resolve
|
||||||
|
|
||||||
|
interface CleanableBindingContext : BindingContext {
|
||||||
|
/**
|
||||||
|
* Removes all recorded data except diagnostics.
|
||||||
|
*/
|
||||||
|
fun clear()
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.backend.jvm.codegen.DescriptorMetadataSerializer
|
|||||||
import org.jetbrains.kotlin.backend.jvm.lower.MultifileFacadeFileEntry
|
import org.jetbrains.kotlin.backend.jvm.lower.MultifileFacadeFileEntry
|
||||||
import org.jetbrains.kotlin.backend.jvm.serialization.JvmIdSignatureDescriptor
|
import org.jetbrains.kotlin.backend.jvm.serialization.JvmIdSignatureDescriptor
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
|
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.konan.DeserializedKlibModuleOrigin
|
import org.jetbrains.kotlin.descriptors.konan.DeserializedKlibModuleOrigin
|
||||||
import org.jetbrains.kotlin.descriptors.konan.KlibModuleOrigin
|
import org.jetbrains.kotlin.descriptors.konan.KlibModuleOrigin
|
||||||
@@ -36,6 +37,7 @@ import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration
|
|||||||
import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator
|
import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator
|
||||||
import org.jetbrains.kotlin.psi2ir.PsiSourceManager
|
import org.jetbrains.kotlin.psi2ir.PsiSourceManager
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.CleanableBindingContext
|
||||||
|
|
||||||
object JvmBackendFacade {
|
object JvmBackendFacade {
|
||||||
fun doGenerateFiles(files: Collection<KtFile>, state: GenerationState, phaseConfig: PhaseConfig) {
|
fun doGenerateFiles(files: Collection<KtFile>, state: GenerationState, phaseConfig: PhaseConfig) {
|
||||||
@@ -109,6 +111,12 @@ object JvmBackendFacade {
|
|||||||
// We need to compile all files we reference in Klibs
|
// We need to compile all files we reference in Klibs
|
||||||
irModuleFragment.files.addAll(dependencies.flatMap { it.files })
|
irModuleFragment.files.addAll(dependencies.flatMap { it.files })
|
||||||
|
|
||||||
|
if (!state.configuration.getBoolean(JVMConfigurationKeys.DO_NOT_CLEAR_BINDING_CONTEXT)) {
|
||||||
|
val originalBindingContext = state.originalFrontendBindingContext as? CleanableBindingContext
|
||||||
|
?: error("BindingContext should be cleanable in JVM IR to avoid leaking memory: ${state.originalFrontendBindingContext}")
|
||||||
|
originalBindingContext.clear()
|
||||||
|
}
|
||||||
|
|
||||||
doGenerateFilesInternal(
|
doGenerateFilesInternal(
|
||||||
state, irModuleFragment, psi2irContext.symbolTable, psi2irContext.sourceManager, phaseConfig,
|
state, irModuleFragment, psi2irContext.symbolTable, psi2irContext.sourceManager, phaseConfig,
|
||||||
irProviders, extensions, ::DescriptorMetadataSerializer
|
irProviders, extensions, ::DescriptorMetadataSerializer
|
||||||
|
|||||||
+2
@@ -19,6 +19,8 @@ where advanced options include:
|
|||||||
'enable' since language version 1.3
|
'enable' since language version 1.3
|
||||||
-Xdump-declarations-to=<path> Path to JSON file to dump Java to Kotlin declaration mappings
|
-Xdump-declarations-to=<path> Path to JSON file to dump Java to Kotlin declaration mappings
|
||||||
-Xdisable-standard-script Disable standard kotlin script support
|
-Xdisable-standard-script Disable standard kotlin script support
|
||||||
|
-Xir-do-not-clear-binding-context
|
||||||
|
When using the IR backend, do not clear BindingContext between psi2ir and lowerings
|
||||||
-Xemit-jvm-type-annotations Emit JVM type annotations in bytecode
|
-Xemit-jvm-type-annotations Emit JVM type annotations in bytecode
|
||||||
-Xfriend-paths=<path> Paths to output directories for friend modules (whose internals should be visible)
|
-Xfriend-paths=<path> Paths to output directories for friend modules (whose internals should be visible)
|
||||||
-Xmultifile-parts-inherit Compile multifile classes as a hierarchy of parts and facade
|
-Xmultifile-parts-inherit Compile multifile classes as a hierarchy of parts and facade
|
||||||
|
|||||||
@@ -34,10 +34,7 @@ import org.jetbrains.kotlin.checkers.diagnostics.factories.SyntaxErrorDiagnostic
|
|||||||
import org.jetbrains.kotlin.checkers.utils.CheckerTestUtil
|
import org.jetbrains.kotlin.checkers.utils.CheckerTestUtil
|
||||||
import org.jetbrains.kotlin.checkers.utils.DiagnosticsRenderingConfiguration
|
import org.jetbrains.kotlin.checkers.utils.DiagnosticsRenderingConfiguration
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
import org.jetbrains.kotlin.config.JvmTarget
|
import org.jetbrains.kotlin.config.*
|
||||||
import org.jetbrains.kotlin.config.LanguageFeature
|
|
||||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
|
||||||
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
|
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||||
import org.jetbrains.kotlin.diagnostics.*
|
import org.jetbrains.kotlin.diagnostics.*
|
||||||
@@ -269,6 +266,8 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<TestModule, Tes
|
|||||||
platform = null,
|
platform = null,
|
||||||
withNewInference,
|
withNewInference,
|
||||||
languageVersionSettings,
|
languageVersionSettings,
|
||||||
|
// When using JVM IR, binding context is empty at the end of compilation, so debug info markers can't be computed.
|
||||||
|
environment.configuration.getBoolean(JVMConfigurationKeys.IR),
|
||||||
),
|
),
|
||||||
DataFlowValueFactoryImpl(languageVersionSettings),
|
DataFlowValueFactoryImpl(languageVersionSettings),
|
||||||
moduleDescriptor,
|
moduleDescriptor,
|
||||||
|
|||||||
+1
@@ -169,6 +169,7 @@ class DebuggerTestCompilerFacility(
|
|||||||
val configuration = CompilerConfiguration()
|
val configuration = CompilerConfiguration()
|
||||||
configuration.put(JVMConfigurationKeys.JVM_TARGET, jvmTarget)
|
configuration.put(JVMConfigurationKeys.JVM_TARGET, jvmTarget)
|
||||||
configuration.put(JVMConfigurationKeys.IR, useIrBackend)
|
configuration.put(JVMConfigurationKeys.IR, useIrBackend)
|
||||||
|
configuration.put(JVMConfigurationKeys.DO_NOT_CLEAR_BINDING_CONTEXT, true)
|
||||||
|
|
||||||
val state = GenerationUtils.generateFiles(project, files, configuration, ClassBuilderFactories.BINARIES, analysisResult) {
|
val state = GenerationUtils.generateFiles(project, files, configuration, ClassBuilderFactories.BINARIES, analysisResult) {
|
||||||
generateDeclaredClassFilter(GenerationState.GenerateClassFilter.GENERATE_ALL)
|
generateDeclaredClassFilter(GenerationState.GenerateClassFilter.GENERATE_ALL)
|
||||||
|
|||||||
Reference in New Issue
Block a user