Skip to content
This repository was archived by the owner on Aug 10, 2021. It is now read-only.

Commit 032580a

Browse files
authored
Add a switch to destroy runtime only on shutdown (#4482)
1 parent 75f4f09 commit 032580a

File tree

18 files changed

+254
-37
lines changed

18 files changed

+254
-37
lines changed

backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,14 @@ class K2Native : CLICompiler<K2NativeCompilerArguments>() {
250250
put(DISABLE_FAKE_OVERRIDE_VALIDATOR, arguments.disableFakeOverrideValidator)
251251
putIfNotNull(PRE_LINK_CACHES, parsePreLinkCachesValue(configuration, arguments.preLinkCaches))
252252
putIfNotNull(OVERRIDE_KONAN_PROPERTIES, parseOverrideKonanProperties(arguments, configuration))
253+
put(DESTROY_RUNTIME_MODE, when (arguments.destroyRuntimeMode) {
254+
"legacy" -> DestroyRuntimeMode.LEGACY
255+
"on-shutdown" -> DestroyRuntimeMode.ON_SHUTDOWN
256+
else -> {
257+
configuration.report(ERROR, "Unsupported destroy runtime mode ${arguments.destroyRuntimeMode}")
258+
DestroyRuntimeMode.ON_SHUTDOWN
259+
}
260+
})
253261
}
254262
}
255263
}

backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,9 @@ class K2NativeCompilerArguments : CommonCompilerArguments() {
285285
)
286286
var overrideKonanProperties: Array<String>? = null
287287

288+
@Argument(value="-Xdestroy-runtime-mode", valueDescription = "<mode>", description = "When to destroy runtime. 'legacy' and 'on-shutdown' are currently supported. NOTE: 'legacy' mode is deprecated and will be removed.")
289+
var destroyRuntimeMode: String? = "on-shutdown"
290+
288291
override fun configureAnalysisFlags(collector: MessageCollector): MutableMap<AnalysisFlag<*>, Any> =
289292
super.configureAnalysisFlags(collector).also {
290293
val useExperimental = it[AnalysisFlags.useExperimental] as List<*>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*
2+
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
3+
* that can be found in the LICENSE file.
4+
*/
5+
package org.jetbrains.kotlin.backend.konan
6+
7+
// Must match `DestroyRuntimeMode` in Runtime.h
8+
enum class DestroyRuntimeMode(val value: Int) {
9+
LEGACY(0),
10+
ON_SHUTDOWN(1),
11+
}

backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
4545
?: target.family.isAppleFamily // Default is true for Apple targets.
4646

4747
val memoryModel: MemoryModel get() = configuration.get(KonanConfigKeys.MEMORY_MODEL)!!
48+
val destroyRuntimeMode: DestroyRuntimeMode get() = configuration.get(KonanConfigKeys.DESTROY_RUNTIME_MODE)!!
4849

4950
val needCompilerVerification: Boolean
5051
get() = configuration.get(KonanConfigKeys.VERIFY_COMPILER) ?:
@@ -128,6 +129,10 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
128129
configuration.report(CompilerMessageSeverity.STRONG_WARNING,
129130
"Experimental memory model requires threads, which are not supported on target ${target.name}. Used strict memory model.")
130131
MemoryModel.STRICT
132+
} else if (destroyRuntimeMode == DestroyRuntimeMode.LEGACY) {
133+
configuration.report(CompilerMessageSeverity.STRONG_WARNING,
134+
"Experimental memory model is incompatible with 'legacy' destroy runtime mode. Used strict memory model.")
135+
MemoryModel.STRICT
131136
} else {
132137
MemoryModel.EXPERIMENTAL
133138
}

backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ class KonanConfigKeys {
144144
= CompilerConfigurationKey.create("perform compiler caches pre-link")
145145
val OVERRIDE_KONAN_PROPERTIES: CompilerConfigurationKey<Map<String, String>>
146146
= CompilerConfigurationKey.create("override konan.properties values")
147+
val DESTROY_RUNTIME_MODE: CompilerConfigurationKey<DestroyRuntimeMode>
148+
= CompilerConfigurationKey.create("when to destroy runtime")
147149
}
148150
}
149151

backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
365365

366366
context.coverage.writeRegionInfo()
367367
appendDebugSelector()
368+
overrideRuntimeGlobals()
368369
appendLlvmUsed("llvm.used", context.llvm.usedFunctions + context.llvm.usedGlobals)
369370
appendLlvmUsed("llvm.compiler.used", context.llvm.compilerUsedGlobals)
370371
if (context.isNativeLibrary) {
@@ -2380,6 +2381,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
23802381
LLVMSetSection(llvmUsedGlobal.llvmGlobal, "llvm.metadata")
23812382
}
23822383

2384+
// TODO: Consider migrating `KonanNeedDebugInfo` to the `overrideRuntimeGlobal` mechanism from below.
23832385
private fun appendDebugSelector() {
23842386
if (!context.producedLlvmModuleContainsStdlib) return
23852387
val llvmDebugSelector =
@@ -2389,6 +2391,39 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
23892391
llvmDebugSelector.setLinkage(LLVMLinkage.LLVMExternalLinkage)
23902392
}
23912393

2394+
private fun overrideRuntimeGlobal(name: String, value: ConstValue) {
2395+
// TODO: A similar mechanism is used in `ObjCExportCodeGenerator`. Consider merging them.
2396+
if (context.llvmModuleSpecification.importsKotlinDeclarationsFromOtherSharedLibraries()) {
2397+
// When some dynamic caches are used, we consider that stdlib is in the dynamic cache as well.
2398+
// Runtime is linked into stdlib module only, so import runtime global from it.
2399+
val global = codegen.importGlobal(name, value.llvmType, context.standardLlvmSymbolsOrigin)
2400+
val initializer = generateFunction(codegen, functionType(voidType, false), "") {
2401+
store(value.llvm, global)
2402+
ret(null)
2403+
}
2404+
2405+
LLVMSetLinkage(initializer, LLVMLinkage.LLVMPrivateLinkage)
2406+
2407+
context.llvm.otherStaticInitializers += initializer
2408+
} else {
2409+
context.llvmImports.add(context.standardLlvmSymbolsOrigin)
2410+
// Define a strong runtime global. It'll overrule a weak global defined in a statically linked runtime.
2411+
val global = context.llvm.staticData.placeGlobal(name, value, true)
2412+
2413+
if (context.llvmModuleSpecification.importsKotlinDeclarationsFromOtherObjectFiles()) {
2414+
context.llvm.usedGlobals += global.llvmGlobal
2415+
LLVMSetVisibility(global.llvmGlobal, LLVMVisibility.LLVMHiddenVisibility)
2416+
}
2417+
}
2418+
}
2419+
2420+
private fun overrideRuntimeGlobals() {
2421+
if (!context.config.produce.isFinalBinary)
2422+
return
2423+
2424+
overrideRuntimeGlobal("Kotlin_destroyRuntimeMode", Int32(context.config.destroyRuntimeMode.value))
2425+
}
2426+
23922427
//-------------------------------------------------------------------------//
23932428
// Create type { i32, void ()*, i8* }
23942429

backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,7 @@ private fun ObjCExportCodeGenerator.replaceExternalWeakOrCommonGlobal(
474474
value: ConstValue,
475475
origin: CompiledKlibModuleOrigin
476476
) {
477+
// TODO: A similar mechanism is used in `IrToBitcode.overrideRuntimeGlobal`. Consider merging them.
477478
if (context.llvmModuleSpecification.importsKotlinDeclarationsFromOtherSharedLibraries()) {
478479
val global = codegen.importGlobal(name, value.llvmType, origin)
479480
externalGlobalInitializers[global] = value

backend.native/tests/build.gradle

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4361,9 +4361,19 @@ dynamicTest("interop_cleaners_leak") {
43614361
flags = ['-Xopt-in=kotlin.native.internal.InternalForKotlinNative']
43624362
}
43634363

4364+
dynamicTest("interop_migrating_main_thread_legacy") {
4365+
disabled = (project.target.name != project.hostName)
4366+
source = "interop/migrating_main_thread/lib.kt"
4367+
flags = ['-Xdestroy-runtime-mode=legacy']
4368+
clangFlags = ['-DIS_LEGACY']
4369+
cSource = "$projectDir/interop/migrating_main_thread/main.cpp"
4370+
clangTool = "clang++"
4371+
}
4372+
43644373
dynamicTest("interop_migrating_main_thread") {
43654374
disabled = (project.target.name != project.hostName)
43664375
source = "interop/migrating_main_thread/lib.kt"
4376+
flags = ['-Xdestroy-runtime-mode=on-shutdown']
43674377
cSource = "$projectDir/interop/migrating_main_thread/main.cpp"
43684378
clangTool = "clang++"
43694379
}
@@ -4374,7 +4384,7 @@ dynamicTest("interop_memory_leaks") {
43744384
source = "interop/memory_leaks/lib.kt"
43754385
cSource = "$projectDir/interop/memory_leaks/main.cpp"
43764386
clangTool = "clang++"
4377-
flags = ['-g']
4387+
flags = ['-g', '-Xdestroy-runtime-mode=legacy'] // Runtime cannot be destroyed with interop with on-shutdown.
43784388
expectedExitStatusChecker = { it != 0 }
43794389
outputChecker = { s -> s.contains("Memory leaks detected, 1 objects leaked!") }
43804390
}

backend.native/tests/interop/migrating_main_thread/lib.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,10 @@ fun writeToA(i: Int) {
1313
globalA.i = i
1414
}
1515

16-
fun readFromA() = globalA.i
16+
fun tryReadFromA(default: Int): Int {
17+
return try {
18+
globalA.i
19+
} catch (e: IncorrectDereferenceException) {
20+
default
21+
}
22+
}

backend.native/tests/interop/migrating_main_thread/main.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,26 @@
88
#include <cassert>
99
#include <thread>
1010

11+
constexpr int kInitialValue = 0;
12+
constexpr int kNewValue = 1;
13+
constexpr int kErrorValue = 2;
14+
1115
int main() {
1216
std::thread main1([]() {
13-
assert(testlib_symbols()->kotlin.root.readFromA() == 0);
14-
testlib_symbols()->kotlin.root.writeToA(1);
15-
assert(testlib_symbols()->kotlin.root.readFromA() == 1);
17+
assert(testlib_symbols()->kotlin.root.tryReadFromA(kErrorValue) == kInitialValue);
18+
testlib_symbols()->kotlin.root.writeToA(kNewValue);
19+
assert(testlib_symbols()->kotlin.root.tryReadFromA(kErrorValue) == kNewValue);
1620
});
1721
main1.join();
1822

1923
std::thread main2([]() {
24+
#if defined(IS_LEGACY)
2025
// Globals were reinitialized.
21-
assert(testlib_symbols()->kotlin.root.readFromA() == 0);
26+
assert(testlib_symbols()->kotlin.root.tryReadFromA(kErrorValue) == kInitialValue);
27+
#else
28+
// Globals are not accessible.
29+
assert(testlib_symbols()->kotlin.root.tryReadFromA(kErrorValue) == kErrorValue);
30+
#endif
2231
});
2332
main2.join();
2433

0 commit comments

Comments
 (0)