Skip to content

Commit 62d5d40

Browse files
committed
Failed attempt JetBrains#1 at modifying EA and DFG
1 parent 93f58b9 commit 62d5d40

File tree

7 files changed

+68
-47
lines changed

7 files changed

+68
-47
lines changed

kotlin-native/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeMem.kt

+10-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1+
12
/*
23
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
34
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
45
*/
56
@file:OptIn(ExperimentalForeignApi::class)
67
package kotlinx.cinterop
78

8-
import kotlin.native.*
9+
import kotlin.internal.InlineOnly
910
import kotlin.native.internal.GCUnsafeCall
10-
import kotlin.native.internal.Intrinsic
1111
import kotlin.native.internal.TypedIntrinsic
1212
import kotlin.native.internal.IntrinsicType
1313

@@ -19,6 +19,14 @@ internal inline val pointerSize: Int
1919
@TypedIntrinsic(IntrinsicType.INTEROP_GET_POINTER_SIZE)
2020
internal external fun getPointerSize(): Int
2121

22+
@PublishedApi
23+
@TypedIntrinsic(IntrinsicType.INTEROP_ALLOCA)
24+
internal external fun alloca(size: Long, align: Int): NativePointed
25+
26+
@PublishedApi
27+
@InlineOnly
28+
internal inline fun <reified T : CVariable> alloca(): T = alloca(sizeOf<T>(), alignOf<T>()).reinterpret()
29+
2230
// TODO: do not use singleton because it leads to init-check on any access.
2331
@PublishedApi
2432
internal object nativeMemUtils {
@@ -46,8 +54,6 @@ internal object nativeMemUtils {
4654
@TypedIntrinsic(IntrinsicType.INTEROP_READ_PRIMITIVE) external fun getVector(mem: NativePointed): Vector128
4755
@TypedIntrinsic(IntrinsicType.INTEROP_WRITE_PRIMITIVE) external fun putVector(mem: NativePointed, value: Vector128)
4856

49-
@TypedIntrinsic(IntrinsicType.INTEROP_ALLOCA) external fun alloca(size: Long, align: Int): NativePointed
50-
5157
// TODO: optimize
5258
fun getByteArray(source: NativePointed, dest: ByteArray, length: Int) {
5359
val sourceArray = source.reinterpret<ByteVar>().ptr

kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/LTO.kt

+11-5
Original file line numberDiff line numberDiff line change
@@ -128,13 +128,19 @@ internal data class EscapeAnalysisInput(
128128
get() = irModule
129129
}
130130

131-
internal val EscapeAnalysisPhase = createSimpleNamedCompilerPhase<NativeGenerationState, EscapeAnalysisInput, Map<IrElement, Lifetime>>(
131+
internal data class EscapeAnalysisOutput(
132+
val lifetimes: Map<IrElement, Lifetime> = emptyMap(),
133+
val interopLifetimes: Map<IrElement, Lifetime> = emptyMap()
134+
)
135+
136+
internal val EscapeAnalysisPhase = createSimpleNamedCompilerPhase<NativeGenerationState, EscapeAnalysisInput, EscapeAnalysisOutput>(
132137
name = "EscapeAnalysis",
133-
outputIfNotEnabled = { _, _, _, _ -> emptyMap() },
138+
outputIfNotEnabled = { _, _, _, _ -> EscapeAnalysisOutput() },
134139
preactions = getDefaultIrActions(),
135140
postactions = getDefaultIrActions(),
136141
op = { generationState, input ->
137-
val lifetimes = mutableMapOf<IrElement, Lifetime>()
142+
val lifetimes = HashMap<IrElement, Lifetime>()
143+
val interopLifetimes = HashMap<IrElement, Lifetime>()
138144
val context = generationState.context
139145
val entryPoint = context.ir.symbols.entryPoint?.owner
140146
val nonDevirtualizedCallSitesUnfoldFactor =
@@ -157,8 +163,8 @@ internal val EscapeAnalysisPhase = createSimpleNamedCompilerPhase<NativeGenerati
157163
DevirtualizationUnfoldFactors.DFG_DEVIRTUALIZED_CALL,
158164
nonDevirtualizedCallSitesUnfoldFactor
159165
).build()
160-
EscapeAnalysis.computeLifetimes(context, generationState, input.moduleDFG, callGraph, lifetimes)
161-
lifetimes
166+
EscapeAnalysis.computeLifetimes(context, generationState, input.moduleDFG, callGraph, lifetimes, interopLifetimes)
167+
EscapeAnalysisOutput(lifetimes, interopLifetimes)
162168
}
163169
)
164170

kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/TopLevelPhases.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -500,9 +500,9 @@ private fun PhaseEngine<NativeGenerationState>.runCodegen(module: IrModuleFragme
500500
runPhase(CreateLLVMDeclarationsPhase, module)
501501
runPhase(GHAPhase, module, disable = !optimize)
502502
runPhase(RTTIPhase, RTTIInput(module, dceResult))
503-
val lifetimes = runPhase(EscapeAnalysisPhase, EscapeAnalysisInput(module, moduleDFG), disable = !optimize)
504-
runPhase(InteropAllocationsTransformPhase, InteropAllocationsTransformInput(module, lifetimes))
505-
runPhase(CodegenPhase, CodegenInput(module, irBuiltIns, lifetimes))
503+
val eaOutput = runPhase(EscapeAnalysisPhase, EscapeAnalysisInput(module, moduleDFG), disable = !optimize)
504+
runPhase(InteropAllocationsTransformPhase, InteropAllocationsTransformInput(module, eaOutput.interopLifetimes))
505+
runPhase(CodegenPhase, CodegenInput(module, irBuiltIns, eaOutput.lifetimes))
506506
}
507507

508508
private fun PhaseEngine<NativeGenerationState>.findDependenciesToCompile(): List<IrModuleFragment> {

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

+2
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ internal enum class IntrinsicType {
9191
INTEROP_NARROW,
9292
INTEROP_STATIC_C_FUNCTION,
9393
INTEROP_FUNPTR_INVOKE,
94+
INTEROP_ALLOCA,
9495
// Worker
9596
WORKER_EXECUTE,
9697
// Atomics
@@ -250,6 +251,7 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv
250251
IntrinsicType.INTEROP_NATIVE_PTR_TO_LONG -> emitNativePtrToLong(callSite, args)
251252
IntrinsicType.INTEROP_NATIVE_PTR_PLUS_LONG -> emitNativePtrPlusLong(args)
252253
IntrinsicType.INTEROP_GET_NATIVE_NULL_PTR -> emitGetNativeNullPtr()
254+
IntrinsicType.INTEROP_ALLOCA -> reportNonLoweredIntrinsic(intrinsicType) // TODO: ...
253255
IntrinsicType.IDENTITY -> emitIdentity(args)
254256
IntrinsicType.THE_UNIT_INSTANCE -> theUnitInstanceRef.llvm
255257
IntrinsicType.ATOMIC_GET_FIELD -> reportNonLoweredIntrinsic(intrinsicType)

kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,7 @@ internal class FunctionDFGBuilder(private val generationState: NativeGenerationS
408408
private val executeImplProducerInvoke = executeImplProducerClass.simpleFunctions()
409409
.single { it.name == OperatorNameConventions.INVOKE }
410410
private val reinterpret = symbols.reinterpret
411+
private val interopGetPtr = symbols.interopGetPtr
411412
private val saveCoroutineState = symbols.saveCoroutineState
412413
private val restoreCoroutineState = symbols.restoreCoroutineState
413414
private val objCObjectRawValueGetter = symbols.interopObjCObjectRawValueGetter
@@ -651,7 +652,7 @@ internal class FunctionDFGBuilder(private val generationState: NativeGenerationS
651652
// like a fixed-size object.
652653
DataFlowIR.Node.AllocInstance(symbolTable.mapType(createEmptyStringSymbol.owner.returnType), value)
653654

654-
reinterpret -> getNode(value.extensionReceiver!!).value
655+
reinterpret, interopGetPtr -> getNode(value.extensionReceiver!!).value
655656

656657
initInstanceSymbol -> error("Should've been lowered: ${value.render()}")
657658

kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/EscapeAnalysis.kt

+9-2
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ internal object EscapeAnalysis {
346346
val callGraph: CallGraph,
347347
val moduleDFG: ModuleDFG,
348348
val lifetimes: MutableMap<IrElement, Lifetime>,
349+
val interopLifetimes: MutableMap<IrElement, Lifetime>,
349350
val propagateExiledToHeapObjects: Boolean
350351
) {
351352

@@ -534,10 +535,15 @@ internal object EscapeAnalysis {
534535
++stats.totalStackAllocsCount
535536
if (!isFilteredOut)
536537
++stats.filteredStackAllocsCount
538+
537539
}
538540

539541
lifetimes[it] = lifetime
540542
}
543+
544+
if (node is DataFlowIR.Node.Call && isInteropAllocCallee(node.callee.irFunction)) {
545+
interopLifetimes[it] = lifetime
546+
}
541547
}
542548
}
543549
}
@@ -1768,13 +1774,14 @@ internal object EscapeAnalysis {
17681774
generationState: NativeGenerationState,
17691775
moduleDFG: ModuleDFG,
17701776
callGraph: CallGraph,
1771-
lifetimes: MutableMap<IrElement, Lifetime>
1777+
lifetimes: MutableMap<IrElement, Lifetime>,
1778+
interopLifetimes: MutableMap<IrElement, Lifetime>,
17721779
) {
17731780
assert(lifetimes.isEmpty())
17741781

17751782
try {
17761783
InterproceduralAnalysis(context, generationState, callGraph,
1777-
moduleDFG, lifetimes,
1784+
moduleDFG, lifetimes, interopLifetimes,
17781785
// The GC must be careful not to scan exiled objects, that have already became dead,
17791786
// as they may reference other already destroyed stack-allocated objects.
17801787
// TODO somehow tag these object, so that GC could handle them properly.

kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/TransformInteropAllocations.kt

+31-32
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,12 @@ import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
1515
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
1616
import org.jetbrains.kotlin.ir.expressions.IrCall
1717
import org.jetbrains.kotlin.ir.expressions.IrExpression
18-
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
19-
import org.jetbrains.kotlin.ir.util.copyValueArgumentsFrom
20-
import org.jetbrains.kotlin.ir.util.kotlinFqName
21-
import org.jetbrains.kotlin.ir.util.parentClassOrNull
18+
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
19+
import org.jetbrains.kotlin.ir.util.IdSignature
2220
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
2321
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
24-
import org.jetbrains.kotlin.name.ClassId
2522
import org.jetbrains.kotlin.name.FqName
2623
import org.jetbrains.kotlin.name.Name
27-
import org.jetbrains.kotlin.utils.findIsInstanceAnd
2824

2925
/**
3026
* A simple transformation pass after escape analysis which transforms
@@ -35,22 +31,38 @@ import org.jetbrains.kotlin.utils.findIsInstanceAnd
3531
*/
3632

3733
private val interopPackageName: FqName = FqName("kotlinx.cinterop")
38-
private val nativePlacementName: FqName = ClassId(interopPackageName, Name.identifier("NativePlacement")).asSingleFqName()
39-
private val arenaBaseName: FqName = ClassId(interopPackageName, Name.identifier("ArenaBase")).asSingleFqName()
4034
private val allocFunctionName: Name = Name.identifier("alloc")
41-
private val nativeMemUtilsName: FqName = ClassId(interopPackageName, Name.identifier("nativeMemUtils")).asSingleFqName()
42-
private val allocaFunctionName: Name = Name.identifier("alloca")
35+
36+
private val allocaFunctionSignature: IdSignature = IdSignature.CommonSignature(
37+
interopPackageName.asString(),
38+
"${interopPackageName.asString()}.alloca",
39+
null, 0L, null
40+
)
41+
42+
internal fun isInteropAllocCallee(callee: IrSimpleFunction?): Boolean {
43+
if (callee == null) return false
44+
val signature = callee.symbol.signature ?: return false
45+
return signature.packageFqName() == interopPackageName && allocFunctionName.asString() in callee.name.asString()
46+
}
4347

4448
private class InteropAllocationsTransformer(
4549
generationState: NativeGenerationState,
4650
private val lifetimes: Map<IrElement, Lifetime>
4751
) : IrElementTransformerVoid() {
48-
private val allocaFunction = generationState.context.irModules.values
49-
.flatMap { it.files }
50-
.flatMap { it.declarations }
51-
.findIsInstanceAnd<IrSimpleFunction> {
52-
it.parentClassOrNull?.kotlinFqName == nativeMemUtilsName && it.name == allocaFunctionName
53-
} ?: error("Could not find alloca intrinsic")
52+
private val allocaFunction: IrSimpleFunctionSymbol by lazy {
53+
generationState.context.symbolTable.referenceSimpleFunction(allocaFunctionSignature)
54+
}
55+
56+
private fun computeLifetime(expression: IrExpression): Lifetime {
57+
return lifetimes.getOrDefault(expression, Lifetime.GLOBAL) // TODO: move default to IRRELEVANT once globally changed
58+
}
59+
60+
private fun isApplicableCallSite(callSite: IrCall): Boolean {
61+
val lifetime = computeLifetime(callSite)
62+
return lifetime == Lifetime.LOCAL
63+
|| lifetime == Lifetime.STACK
64+
|| lifetime == Lifetime.ARGUMENT
65+
}
5466

5567
override fun visitElement(element: IrElement): IrElement {
5668
element.transformChildrenVoid(this)
@@ -60,21 +72,8 @@ private class InteropAllocationsTransformer(
6072
override fun visitCall(expression: IrCall): IrExpression {
6173
super.visitCall(expression)
6274
val callee = expression.symbol.owner
63-
val parentName = callee.parentClassOrNull?.kotlinFqName ?: return expression
64-
// We need to assume that calls may be devirtualized into direct calls to ArenaBase's alloc overload
65-
if ((parentName != nativePlacementName && parentName != arenaBaseName) || callee.name != allocFunctionName) return expression
66-
val lifetime = lifetimes[expression]
67-
// We only care about things that never leave the scope or are exclusively used to pass down
68-
if (lifetime != Lifetime.LOCAL && lifetime != Lifetime.STACK && lifetime != Lifetime.ARGUMENT) return expression
69-
// Transform alloc -> alloca call and copy over value arguments
70-
return IrCallImpl(
71-
expression.startOffset,
72-
expression.endOffset,
73-
expression.type,
74-
allocaFunction.symbol
75-
).apply {
76-
copyValueArgumentsFrom(expression, callee)
77-
}
75+
if (!isApplicableCallSite(expression)) return expression
76+
return expression
7877
}
7978
}
8079

@@ -91,6 +90,6 @@ internal val InteropAllocationsTransformPhase = createSimpleNamedCompilerPhase<N
9190
preactions = getDefaultIrActions(),
9291
postactions = getDefaultIrActions(),
9392
op = { generationState, input ->
94-
input.irModule.transformChildrenVoid(InteropAllocationsTransformer(generationState, input.lifetimes))
93+
input.irModule.transform(InteropAllocationsTransformer(generationState, input.lifetimes), null)
9594
}
9695
)

0 commit comments

Comments
 (0)