Implemented DCE before code generation
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
package kotlinx.cinterop
|
||||
import kotlin.native.*
|
||||
import kotlin.native.internal.ExportTypeInfo
|
||||
import kotlin.native.internal.ExportForCppRuntime
|
||||
import kotlin.native.internal.TypedIntrinsic
|
||||
import kotlin.native.internal.IntrinsicType
|
||||
|
||||
@@ -63,6 +64,7 @@ internal fun <T : Any?> Any?.uncheckedCast(): T = @Suppress("UNCHECKED_CAST") (t
|
||||
@SymbolName("Kotlin_Interop_refFromObjC")
|
||||
external fun <T> interpretObjCPointerOrNull(objcPtr: NativePtr): T?
|
||||
|
||||
@ExportForCppRuntime
|
||||
inline fun <T : Any> interpretObjCPointer(objcPtr: NativePtr): T = interpretObjCPointerOrNull<T>(objcPtr)!!
|
||||
|
||||
@SymbolName("Kotlin_Interop_refToObjC")
|
||||
|
||||
+3
@@ -215,6 +215,7 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
|
||||
private val packageScope by lazy { builtIns.builtInsModule.getPackage(KonanFqNames.internalPackageName).memberScope }
|
||||
|
||||
val nativePtr by lazy { packageScope.getContributedClassifier(NATIVE_PTR_NAME) as ClassDescriptor }
|
||||
val nonNullNativePtr by lazy { packageScope.getContributedClassifier(NON_NULL_NATIVE_PTR_NAME) as ClassDescriptor }
|
||||
val getNativeNullPtr by lazy { packageScope.getContributedFunctions("getNativeNullPtr").single() }
|
||||
val immutableBlobOf by lazy {
|
||||
builtIns.builtInsModule.getPackage(KonanFqNames.packageName).memberScope.getContributedFunctions("immutableBlobOf").single()
|
||||
@@ -459,6 +460,8 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
|
||||
lateinit var codegenVisitor: CodeGeneratorVisitor
|
||||
var devirtualizationAnalysisResult: Devirtualization.AnalysisResult? = null
|
||||
|
||||
var referencedFunctions: Set<IrFunction>? = null
|
||||
|
||||
val isNativeLibrary: Boolean by lazy {
|
||||
val kind = config.configuration.get(KonanConfigKeys.PRODUCE)
|
||||
kind == CompilerOutputKind.DYNAMIC || kind == CompilerOutputKind.STATIC
|
||||
|
||||
+3
-2
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.konan.ir.containsNull
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
@@ -275,6 +276,6 @@ private object IrTypeInlineClassesSupport : InlineClassesSupport<IrClass, IrType
|
||||
.firstOrNull { it.descriptor.fqNameUnsafe == InteropFqNames.nativePointed }
|
||||
|
||||
override fun getInlinedClassUnderlyingType(clazz: IrClass): IrType =
|
||||
clazz.constructors.first { it.isPrimary }.valueParameters.single().type
|
||||
|
||||
clazz.constructors.firstOrNull { it.isPrimary }?.valueParameters?.single()?.type
|
||||
?: clazz.declarations.filterIsInstance<IrProperty>().single { it.backingField != null }.backingField!!.type
|
||||
}
|
||||
|
||||
+2
-1
@@ -10,13 +10,14 @@ import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
internal const val NATIVE_PTR_NAME = "NativePtr"
|
||||
internal const val NON_NULL_NATIVE_PTR_NAME = "NonNullNativePtr"
|
||||
|
||||
object KonanFqNames {
|
||||
|
||||
val packageName = FqName("kotlin.native")
|
||||
val internalPackageName = FqName("kotlin.native.internal")
|
||||
val nativePtr = internalPackageName.child(Name.identifier(NATIVE_PTR_NAME)).toUnsafe()
|
||||
val nonNullNativePtr = FqNameUnsafe("kotlin.native.internal.NonNullNativePtr")
|
||||
val nonNullNativePtr = internalPackageName.child(Name.identifier(NON_NULL_NATIVE_PTR_NAME)).toUnsafe()
|
||||
val throws = FqName("kotlin.native.Throws")
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.ir.types.classifierOrFail
|
||||
import org.jetbrains.kotlin.ir.util.constructedClass
|
||||
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
|
||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
||||
import org.jetbrains.kotlin.ir.util.isReal
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition
|
||||
@@ -223,6 +224,7 @@ fun ConstructorDescriptor.objCConstructorIsDesignated(): Boolean {
|
||||
|
||||
val IrConstructor.isObjCConstructor get() = this.descriptor.annotations.hasAnnotation(objCConstructorFqName)
|
||||
|
||||
// TODO-DCE-OBJC-INIT: Selector should be preserved by DCE.
|
||||
fun IrConstructor.getObjCInitMethod(): IrSimpleFunction? {
|
||||
return this.descriptor.annotations.findAnnotation(objCConstructorFqName)?.let {
|
||||
val initSelector = it.getStringValue("initSelector")
|
||||
|
||||
+1
@@ -8,6 +8,7 @@ object RuntimeNames {
|
||||
val exportForCompilerAnnotation = FqName("kotlin.native.internal.ExportForCompiler")
|
||||
val exportTypeInfoAnnotation = FqName("kotlin.native.internal.ExportTypeInfo")
|
||||
val cCall = FqName("kotlinx.cinterop.internal.CCall")
|
||||
val objCMethodImp = FqName("kotlinx.cinterop.ObjCMethodImp")
|
||||
val independent = FqName("kotlin.native.internal.Independent")
|
||||
val filterExceptions = FqName("kotlin.native.internal.FilterExceptions")
|
||||
val kotlinNativeInternalPackageName = FqName.fromSegments(listOf("kotlin", "native", "internal"))
|
||||
|
||||
+5
-4
@@ -291,13 +291,14 @@ internal val dependenciesLowerPhase = SameTypeNamedPhaseWrapper(
|
||||
internal val bitcodePhase = namedIrModulePhase(
|
||||
name = "Bitcode",
|
||||
description = "LLVM Bitcode generation",
|
||||
lower = contextLLVMSetupPhase then
|
||||
RTTIPhase then
|
||||
generateDebugInfoHeaderPhase then
|
||||
buildDFGPhase then
|
||||
lower = buildDFGPhase then
|
||||
serializeDFGPhase then
|
||||
deserializeDFGPhase then
|
||||
devirtualizationPhase then
|
||||
dcePhase then
|
||||
contextLLVMSetupPhase then
|
||||
RTTIPhase then
|
||||
generateDebugInfoHeaderPhase then
|
||||
escapeAnalysisPhase then
|
||||
codegenPhase then
|
||||
finalizeDebugInfoPhase then
|
||||
|
||||
+1
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.types.isUnit
|
||||
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
|
||||
import org.jetbrains.kotlin.ir.util.isReal
|
||||
import org.jetbrains.kotlin.ir.util.isSuspend
|
||||
import org.jetbrains.kotlin.ir.util.overrides
|
||||
import org.jetbrains.kotlin.types.SimpleType
|
||||
|
||||
-5
@@ -17,11 +17,6 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
// This file contains some IR utilities which actually use descriptors.
|
||||
// TODO: port this code to IR.
|
||||
|
||||
internal val IrDeclaration.isAnonymousObject get() = DescriptorUtils.isAnonymousObject(this.descriptor)
|
||||
internal val IrDeclaration.isLocal get() = DescriptorUtils.isLocal(this.descriptor)
|
||||
|
||||
internal val IrDeclaration.module get() = this.descriptor.module
|
||||
|
||||
internal fun IrFunction.getObjCMethodInfo() = this.descriptor.getObjCMethodInfo()
|
||||
internal fun IrFunction.getExternalObjCMethodInfo() = this.descriptor.getExternalObjCMethodInfo()
|
||||
internal fun IrFunction.isObjCClassMethod() = this.descriptor.isObjCClassMethod()
|
||||
|
||||
+10
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
@@ -53,7 +54,9 @@ internal class KonanSymbols(context: Context, private val symbolTable: SymbolTab
|
||||
val string = symbolTable.referenceClass(builtIns.string)
|
||||
val enum = symbolTable.referenceClass(builtIns.enum)
|
||||
val nativePtr = symbolTable.referenceClass(context.nativePtr)
|
||||
val nativePointed = symbolTable.referenceClass(context.interopBuiltIns.nativePointed)
|
||||
val nativePtrType = nativePtr.typeWith(arguments = emptyList())
|
||||
val nonNullNativePtr = symbolTable.referenceClass(context.nonNullNativePtr)
|
||||
|
||||
private fun unsignedClass(unsignedType: UnsignedType): IrClassSymbol = classById(unsignedType.classId)
|
||||
|
||||
@@ -351,6 +354,13 @@ internal class KonanSymbols(context: Context, private val symbolTable: SymbolTab
|
||||
|
||||
val continuationImpl = topLevelClass("kotlin.coroutines.native.internal.ContinuationImpl")
|
||||
|
||||
val invokeSuspendFunction =
|
||||
symbolTable.referenceSimpleFunction(
|
||||
baseContinuationImpl.descriptor.unsubstitutedMemberScope
|
||||
.getContributedFunctions(Name.identifier("invokeSuspend"), NoLookupLocation.FROM_BACKEND)
|
||||
.single()
|
||||
)
|
||||
|
||||
override val coroutineSuspendedGetter = symbolTable.referenceSimpleFunction(
|
||||
coroutinesIntrinsicsPackage
|
||||
.getContributedVariables(COROUTINE_SUSPENDED_NAME, NoLookupLocation.FROM_BACKEND)
|
||||
|
||||
-25
@@ -23,23 +23,6 @@ import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
|
||||
val IrDeclaration.name: Name
|
||||
get() = when (this) {
|
||||
is IrSimpleFunction -> this.name
|
||||
is IrClass -> this.name
|
||||
is IrEnumEntry -> this.name
|
||||
is IrProperty -> this.name
|
||||
is IrLocalDelegatedProperty -> this.name
|
||||
is IrField -> this.name
|
||||
is IrVariable -> this.name
|
||||
is IrConstructor -> SPECIAL_INIT_NAME
|
||||
is IrValueParameter -> this.name
|
||||
else -> error(this)
|
||||
}
|
||||
|
||||
private val SPECIAL_INIT_NAME = Name.special("<init>")
|
||||
|
||||
val IrField.fqNameForIrSerialization: FqName get() = this.parent.fqNameForIrSerialization.child(this.name)
|
||||
|
||||
/**
|
||||
@@ -66,14 +49,6 @@ fun IrClass.isNothing() = this.fqNameForIrSerialization == KotlinBuiltIns.FQ_NAM
|
||||
|
||||
fun IrClass.getSuperInterfaces() = this.superClasses.map { it.owner }.filter { it.isInterface }
|
||||
|
||||
val IrProperty.konanBackingField: IrField?
|
||||
get() {
|
||||
assert(this.isReal)
|
||||
return this.backingField
|
||||
}
|
||||
|
||||
val IrFunction.isReal get() = this.origin != IrDeclarationOrigin.FAKE_OVERRIDE
|
||||
|
||||
// Note: psi2ir doesn't set `origin = FAKE_OVERRIDE` for fields and properties yet.
|
||||
val IrProperty.isReal: Boolean get() = this.descriptor.kind.isReal
|
||||
val IrField.isReal: Boolean get() = this.descriptor.kind.isReal
|
||||
|
||||
+100
-2
@@ -8,7 +8,13 @@ package org.jetbrains.kotlin.backend.konan.llvm
|
||||
import llvm.*
|
||||
import org.jetbrains.kotlin.backend.konan.*
|
||||
import org.jetbrains.kotlin.backend.konan.optimizations.*
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.*
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
internal val contextLLVMSetupPhase = makeKonanModuleOpPhase(
|
||||
name = "ContextLLVMSetup",
|
||||
@@ -71,6 +77,98 @@ internal val devirtualizationPhase = makeKonanModuleOpPhase(
|
||||
}
|
||||
)
|
||||
|
||||
internal val IrFunction.longName: String
|
||||
get() = "${(parent as? IrClass)?.name?.asString() ?: "<root>"}.${(this as? IrSimpleFunction)?.name ?: "<init>"}"
|
||||
|
||||
internal val dcePhase = makeKonanModuleOpPhase(
|
||||
name = "DCEPhase",
|
||||
description = "Dead code elimination",
|
||||
prerequisite = setOf(devirtualizationPhase),
|
||||
op = { context, _ ->
|
||||
val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap())
|
||||
|
||||
val callGraph = CallGraphBuilder(
|
||||
context, context.moduleDFG!!,
|
||||
externalModulesDFG,
|
||||
context.devirtualizationAnalysisResult!!,
|
||||
true
|
||||
).build()
|
||||
|
||||
val referencedFunctions = mutableSetOf<IrFunction>()
|
||||
for (node in callGraph.directEdges.values) {
|
||||
if (!node.symbol.isGlobalInitializer)
|
||||
referencedFunctions.add(node.symbol.irFunction ?: error("No IR for: ${node.symbol}"))
|
||||
node.callSites.forEach {
|
||||
assert (!it.isVirtual) { "There should be no virtual calls in the call graph, but was: ${it.actualCallee}" }
|
||||
referencedFunctions.add(it.actualCallee.irFunction ?: error("No IR for: ${it.actualCallee}"))
|
||||
}
|
||||
}
|
||||
|
||||
context.irModule!!.acceptChildrenVoid(object: IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitFunction(declaration: IrFunction) {
|
||||
// TODO: Generalize somehow, not that graceful.
|
||||
if (declaration.name == OperatorNameConventions.INVOKE
|
||||
&& declaration.parent.let { it is IrClass && it.defaultType.isFunction() }) {
|
||||
referencedFunctions.add(declaration)
|
||||
}
|
||||
super.visitFunction(declaration)
|
||||
}
|
||||
|
||||
override fun visitConstructor(declaration: IrConstructor) {
|
||||
// TODO: NativePointed is the only inline class for which the field's type and
|
||||
// the constructor parameter's type are different.
|
||||
// Thus we need to conserve the constructor no matter if it was actually referenced somehow or not.
|
||||
// See [IrTypeInlineClassesSupport.getInlinedClassUnderlyingType] why.
|
||||
if (declaration.parentAsClass.name.asString() == InteropFqNames.nativePointedName && declaration.isPrimary)
|
||||
referencedFunctions.add(declaration)
|
||||
super.visitConstructor(declaration)
|
||||
}
|
||||
|
||||
override fun visitClass(declaration: IrClass) {
|
||||
context.getLayoutBuilder(declaration).associatedObjects.values.forEach {
|
||||
assert (it.kind == ClassKind.OBJECT) { "An object expected but was ${it.dump()}" }
|
||||
referencedFunctions.add(it.constructors.single())
|
||||
}
|
||||
super.visitClass(declaration)
|
||||
}
|
||||
})
|
||||
|
||||
context.irModule!!.transformChildrenVoid(object: IrElementTransformerVoid() {
|
||||
override fun visitFile(declaration: IrFile): IrFile {
|
||||
declaration.declarations.removeAll {
|
||||
(it is IrFunction && !referencedFunctions.contains(it))
|
||||
}
|
||||
return super.visitFile(declaration)
|
||||
}
|
||||
|
||||
override fun visitClass(declaration: IrClass): IrStatement {
|
||||
if (declaration == context.ir.symbols.nativePointed)
|
||||
return super.visitClass(declaration)
|
||||
declaration.declarations.removeAll {
|
||||
(it is IrFunction && it.isReal && !referencedFunctions.contains(it))
|
||||
}
|
||||
return super.visitClass(declaration)
|
||||
}
|
||||
|
||||
override fun visitProperty(declaration: IrProperty): IrStatement {
|
||||
if (declaration.getter.let { it != null && it.isReal && !referencedFunctions.contains(it) }) {
|
||||
declaration.getter = null
|
||||
}
|
||||
if (declaration.setter.let { it != null && it.isReal && !referencedFunctions.contains(it) }) {
|
||||
declaration.setter = null
|
||||
}
|
||||
return super.visitProperty(declaration)
|
||||
}
|
||||
})
|
||||
|
||||
context.referencedFunctions = referencedFunctions
|
||||
}
|
||||
)
|
||||
|
||||
internal val escapeAnalysisPhase = makeKonanModuleOpPhase(
|
||||
// Disabled by default !!!!
|
||||
name = "EscapeAnalysis",
|
||||
@@ -81,7 +179,7 @@ internal val escapeAnalysisPhase = makeKonanModuleOpPhase(
|
||||
val callGraph = CallGraphBuilder(
|
||||
context, context.moduleDFG!!,
|
||||
externalModulesDFG,
|
||||
context.devirtualizationAnalysisResult,
|
||||
context.devirtualizationAnalysisResult!!,
|
||||
false
|
||||
).build()
|
||||
EscapeAnalysis.computeLifetimes(
|
||||
|
||||
+1
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.util.file
|
||||
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
|
||||
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
||||
import org.jetbrains.kotlin.ir.util.isReal
|
||||
import org.jetbrains.kotlin.konan.library.KonanLibrary
|
||||
import org.jetbrains.kotlin.konan.library.resolver.TopologicalLibraryOrder
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
|
||||
+2
-5
@@ -1893,16 +1893,13 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private val invokeSuspendFunction = context.ir.symbols.baseContinuationImpl.owner.declarations
|
||||
.filterIsInstance<IrSimpleFunction>().single { it.name.asString() == "invokeSuspend" }
|
||||
|
||||
private fun getContinuation(): LLVMValueRef {
|
||||
val caller = functionGenerationContext.irFunction!!
|
||||
return if (caller.isSuspend)
|
||||
codegen.param(caller, caller.allParameters.size) // The last argument.
|
||||
else {
|
||||
// Suspend call from non-suspend function - must be [BaseContinuationImpl].
|
||||
assert ((caller as IrSimpleFunction).overrides(invokeSuspendFunction),
|
||||
// Suspend call from non-suspend function - must be [invokeSuspend].
|
||||
assert ((caller as IrSimpleFunction).overrides(context.ir.symbols.invokeSuspendFunction.owner),
|
||||
{ "Expected 'BaseContinuationImpl.invokeSuspend' but was '$caller'" })
|
||||
currentCodeContext.genGetValue(caller.dispatchReceiverParameter!!)
|
||||
}
|
||||
|
||||
+2
-5
@@ -13,10 +13,7 @@ import org.jetbrains.kotlin.backend.konan.ir.*
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.types.isNothing
|
||||
import org.jetbrains.kotlin.ir.util.defaultType
|
||||
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
|
||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
||||
import org.jetbrains.kotlin.ir.util.file
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
@@ -127,7 +124,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
|
||||
return objectNamer.getName(parent, declaration)
|
||||
}
|
||||
|
||||
return declaration.name
|
||||
return declaration.nameForIrSerialization
|
||||
}
|
||||
|
||||
private fun getFqName(declaration: IrDeclaration): FqName {
|
||||
|
||||
+5
-4
@@ -10,8 +10,6 @@ import org.jetbrains.kotlin.backend.konan.*
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.*
|
||||
import org.jetbrains.kotlin.backend.konan.ir.*
|
||||
import org.jetbrains.kotlin.backend.konan.ir.isAnonymousObject
|
||||
import org.jetbrains.kotlin.backend.konan.ir.isLocal
|
||||
import org.jetbrains.kotlin.backend.konan.isExternalObjCClassMethod
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
@@ -249,7 +247,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
// TODO: compile-time resolution limits binary compatibility.
|
||||
val vtableEntries = context.getLayoutBuilder(irClass).vtableEntries.map {
|
||||
val implementation = it.implementation
|
||||
if (implementation == null || implementation.isExternalObjCClassMethod()) {
|
||||
if (implementation == null || implementation.isExternalObjCClassMethod() || context.referencedFunctions?.contains(implementation) == false) {
|
||||
NullPointer(int8Type)
|
||||
} else {
|
||||
implementation.entryPointAddress
|
||||
@@ -269,7 +267,10 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
|
||||
// TODO: compile-time resolution limits binary compatibility.
|
||||
val implementation = it.implementation
|
||||
val methodEntryPoint = implementation?.entryPointAddress
|
||||
val methodEntryPoint =
|
||||
if (implementation == null || context.referencedFunctions?.contains(implementation) == false)
|
||||
null
|
||||
else implementation.entryPointAddress
|
||||
MethodTableRecord(nameSignature, methodEntryPoint)
|
||||
}.sortedBy { it.nameSignature.value }
|
||||
}
|
||||
|
||||
+90
-58
@@ -47,7 +47,7 @@ internal class CallGraph(val directEdges: Map<DataFlowIR.FunctionSymbol, CallGra
|
||||
internal class CallGraphBuilder(val context: Context,
|
||||
val moduleDFG: ModuleDFG,
|
||||
val externalModulesDFG: ExternalModulesDFG,
|
||||
devirtualizationAnalysisResult: Devirtualization.AnalysisResult?,
|
||||
val devirtualizationAnalysisResult: Devirtualization.AnalysisResult,
|
||||
val gotoExternal: Boolean) {
|
||||
|
||||
private val DEBUG = 0
|
||||
@@ -56,7 +56,7 @@ internal class CallGraphBuilder(val context: Context,
|
||||
if (DEBUG > severity) block()
|
||||
}
|
||||
|
||||
private val devirtualizedCallSites = devirtualizationAnalysisResult?.devirtualizedCallSites
|
||||
private val devirtualizedCallSites = devirtualizationAnalysisResult.devirtualizedCallSites
|
||||
|
||||
private fun DataFlowIR.FunctionSymbol.resolved(): DataFlowIR.FunctionSymbol {
|
||||
if (this is DataFlowIR.FunctionSymbol.External)
|
||||
@@ -88,6 +88,10 @@ internal class CallGraphBuilder(val context: Context,
|
||||
reversedEdges.put(symbol, list)
|
||||
}
|
||||
|
||||
private val symbols = context.ir.symbols
|
||||
private val arrayGet = symbols.arrayGet[symbols.array]!!.owner
|
||||
private val arraySet = symbols.arraySet[symbols.array]!!.owner
|
||||
|
||||
private inline fun DataFlowIR.FunctionBody.forEachCallSite(block: (DataFlowIR.Node.Call) -> Unit) =
|
||||
nodes.forEach { node ->
|
||||
when (node) {
|
||||
@@ -95,42 +99,81 @@ internal class CallGraphBuilder(val context: Context,
|
||||
|
||||
is DataFlowIR.Node.Singleton ->
|
||||
node.constructor?.let { block(DataFlowIR.Node.Call(it, emptyList(), null)) }
|
||||
|
||||
is DataFlowIR.Node.ArrayRead ->
|
||||
block(DataFlowIR.Node.Call(
|
||||
callee = moduleDFG.symbolTable.mapFunction(arrayGet),
|
||||
arguments = listOf(node.array, node.index),
|
||||
irCallSite = null)
|
||||
)
|
||||
|
||||
is DataFlowIR.Node.ArrayWrite ->
|
||||
block(DataFlowIR.Node.Call(
|
||||
callee = moduleDFG.symbolTable.mapFunction(arraySet),
|
||||
arguments = listOf(node.array, node.index, node.value),
|
||||
irCallSite = null)
|
||||
)
|
||||
|
||||
is DataFlowIR.Node.FunctionReference ->
|
||||
block(DataFlowIR.Node.Call(
|
||||
callee = node.symbol,
|
||||
arguments = emptyList(),
|
||||
irCallSite = null
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
private fun staticCall(caller: DataFlowIR.FunctionSymbol, call: DataFlowIR.Node.Call, callee: DataFlowIR.FunctionSymbol) {
|
||||
callGraph.addEdge(caller, CallGraphNode.CallSite(call, false, callee))
|
||||
if (callee is DataFlowIR.FunctionSymbol.Declared
|
||||
&& !directEdges.containsKey(callee))
|
||||
dfs(callee)
|
||||
}
|
||||
|
||||
private fun dfs(symbol: DataFlowIR.FunctionSymbol) {
|
||||
visitedFunctions += symbol
|
||||
if (gotoExternal) {
|
||||
addNode(symbol)
|
||||
val function = moduleDFG.functions[symbol] ?: externalModulesDFG.functionDFGs[symbol]
|
||||
val body = function!!.body
|
||||
body
|
||||
.forEachCallSite { call ->
|
||||
val devirtualizedCallSite = (call as? DataFlowIR.Node.VirtualCall)?.let { devirtualizedCallSites?.get(it) }
|
||||
if (devirtualizedCallSite == null) {
|
||||
val callee = call.callee.resolved()
|
||||
callGraph.addEdge(symbol, CallGraphNode.CallSite(call, call is DataFlowIR.Node.VirtualCall, callee))
|
||||
if (callee is DataFlowIR.FunctionSymbol.Declared
|
||||
&& call !is DataFlowIR.Node.VirtualCall
|
||||
&& !directEdges.containsKey(callee))
|
||||
dfs(callee)
|
||||
} else {
|
||||
devirtualizedCallSite.possibleCallees.forEach {
|
||||
val callee = it.callee.resolved()
|
||||
callGraph.addEdge(symbol, CallGraphNode.CallSite(call, false, callee))
|
||||
if (callee is DataFlowIR.FunctionSymbol.Declared
|
||||
&& !directEdges.containsKey(callee))
|
||||
dfs(callee)
|
||||
}
|
||||
val function = moduleDFG.functions[symbol] ?: externalModulesDFG.functionDFGs[symbol] ?: return
|
||||
val body = function.body
|
||||
body.forEachCallSite { call ->
|
||||
val devirtualizedCallSite = (call as? DataFlowIR.Node.VirtualCall)?.let { devirtualizedCallSites[it] }
|
||||
when {
|
||||
call !is DataFlowIR.Node.VirtualCall -> staticCall(symbol, call, call.callee.resolved())
|
||||
|
||||
devirtualizedCallSite != null -> {
|
||||
devirtualizedCallSite.possibleCallees.forEach {
|
||||
staticCall(symbol, call, it.callee.resolved())
|
||||
}
|
||||
}
|
||||
body.nodes.filterIsInstance<DataFlowIR.Node.FunctionReference>()
|
||||
.forEach {
|
||||
val callee = it.symbol.resolved()
|
||||
if (callee is DataFlowIR.FunctionSymbol.Declared
|
||||
&& !directEdges.containsKey(callee))
|
||||
dfs(callee)
|
||||
|
||||
call.receiverType == DataFlowIR.Type.Virtual -> {
|
||||
// Skip callsite. This can only be for invocations Any's methods on instances of ObjC classes.
|
||||
}
|
||||
|
||||
else -> {
|
||||
// Callsite has not been devirtualized - conservatively assume the worst:
|
||||
// any inheritor of the receiver type is possible here.
|
||||
val typeHierarcy = devirtualizationAnalysisResult.typeHierarchy
|
||||
typeHierarcy.inheritorsOf(call.receiverType as DataFlowIR.Type.Declared)
|
||||
.filterNot { it.isAbstract }
|
||||
// TODO: Unconservative way - when we can use it?
|
||||
//.filter { devirtualizationAnalysisResult.instantiatingClasses.contains(it) }
|
||||
.forEach { receiverType ->
|
||||
val actualCallee = when (call) {
|
||||
is DataFlowIR.Node.VtableCall ->
|
||||
receiverType.vtable[call.calleeVtableIndex]
|
||||
|
||||
is DataFlowIR.Node.ItableCall ->
|
||||
receiverType.itable[call.calleeHash]!!
|
||||
|
||||
else -> error("Unreachable")
|
||||
}
|
||||
staticCall(symbol, call, actualCallee.resolved())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var function = moduleDFG.functions[symbol]
|
||||
var local = true
|
||||
@@ -141,42 +184,31 @@ internal class CallGraphBuilder(val context: Context,
|
||||
local = false
|
||||
}
|
||||
val body = function.body
|
||||
body
|
||||
.forEachCallSite { call ->
|
||||
val devirtualizedCallSite = (call as? DataFlowIR.Node.VirtualCall)?.let { devirtualizedCallSites?.get(it) }
|
||||
if (devirtualizedCallSite == null) {
|
||||
val callee = call.callee.resolved()
|
||||
if (moduleDFG.functions.containsKey(callee))
|
||||
addNode(callee)
|
||||
if (local)
|
||||
callGraph.addEdge(symbol, CallGraphNode.CallSite(call, call is DataFlowIR.Node.VirtualCall, callee))
|
||||
if (callee is DataFlowIR.FunctionSymbol.Declared
|
||||
&& call !is DataFlowIR.Node.VirtualCall
|
||||
&& !visitedFunctions.contains(callee))
|
||||
dfs(callee)
|
||||
} else {
|
||||
devirtualizedCallSite.possibleCallees.forEach {
|
||||
val callee = it.callee.resolved()
|
||||
if (moduleDFG.functions.containsKey(callee))
|
||||
addNode(callee)
|
||||
if (local)
|
||||
callGraph.addEdge(symbol, CallGraphNode.CallSite(call, false, callee))
|
||||
if (callee is DataFlowIR.FunctionSymbol.Declared
|
||||
&& !visitedFunctions.contains(callee))
|
||||
dfs(callee)
|
||||
}
|
||||
}
|
||||
}
|
||||
body.nodes.filterIsInstance<DataFlowIR.Node.FunctionReference>()
|
||||
.forEach {
|
||||
val callee = it.symbol.resolved()
|
||||
body.forEachCallSite { call ->
|
||||
val devirtualizedCallSite = (call as? DataFlowIR.Node.VirtualCall)?.let { devirtualizedCallSites?.get(it) }
|
||||
if (devirtualizedCallSite == null) {
|
||||
val callee = call.callee.resolved()
|
||||
if (moduleDFG.functions.containsKey(callee))
|
||||
addNode(callee)
|
||||
if (local)
|
||||
callGraph.addEdge(symbol, CallGraphNode.CallSite(call, call is DataFlowIR.Node.VirtualCall, callee))
|
||||
if (callee is DataFlowIR.FunctionSymbol.Declared
|
||||
&& call !is DataFlowIR.Node.VirtualCall
|
||||
&& !visitedFunctions.contains(callee))
|
||||
dfs(callee)
|
||||
} else {
|
||||
devirtualizedCallSite.possibleCallees.forEach {
|
||||
val callee = it.callee.resolved()
|
||||
if (moduleDFG.functions.containsKey(callee))
|
||||
addNode(callee)
|
||||
if (local)
|
||||
callGraph.addEdge(symbol, CallGraphNode.CallSite(call, false, callee))
|
||||
if (callee is DataFlowIR.FunctionSymbol.Declared
|
||||
&& !visitedFunctions.contains(callee))
|
||||
dfs(callee)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-3
@@ -13,11 +13,11 @@ import org.jetbrains.kotlin.backend.common.pop
|
||||
import org.jetbrains.kotlin.backend.common.push
|
||||
import org.jetbrains.kotlin.backend.konan.*
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenFunctions
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.target
|
||||
import org.jetbrains.kotlin.backend.konan.ir.*
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.functionName
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.localHash
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.longName
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
@@ -212,12 +212,16 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
|
||||
}
|
||||
|
||||
override fun visitFunction(declaration: IrFunction) {
|
||||
declaration.body?.let {
|
||||
val body = declaration.body
|
||||
if (body == null) {
|
||||
// External function or intrinsic.
|
||||
symbolTable.mapFunction(declaration)
|
||||
} else {
|
||||
DEBUG_OUTPUT(0) {
|
||||
println("Analysing function ${declaration.descriptor}")
|
||||
println("IR: ${ir2stringWhole(declaration)}")
|
||||
}
|
||||
analyze(declaration, it)
|
||||
analyze(declaration, body)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+18
-14
@@ -9,10 +9,9 @@ import org.jetbrains.kotlin.backend.konan.*
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.target
|
||||
import org.jetbrains.kotlin.backend.konan.ir.*
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.functionName
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.isExported
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.localHash
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.symbolName
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.*
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.KonanMangler.functionName
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.KonanMangler.symbolName
|
||||
import org.jetbrains.kotlin.backend.konan.lower.bridgeTarget
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
@@ -27,9 +26,7 @@ import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.getClass
|
||||
import org.jetbrains.kotlin.ir.types.isNothing
|
||||
import org.jetbrains.kotlin.ir.types.isUnit
|
||||
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
|
||||
import org.jetbrains.kotlin.ir.util.isInterface
|
||||
import org.jetbrains.kotlin.ir.util.isSuspend
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature
|
||||
@@ -121,6 +118,7 @@ internal object DataFlowIR {
|
||||
val IS_GLOBAL_INITIALIZER = 1
|
||||
val RETURNS_UNIT = 2
|
||||
val RETURNS_NOTHING = 4
|
||||
val EXPLICITLY_EXPORTED = 8
|
||||
}
|
||||
|
||||
class FunctionParameter(val type: Type, val boxFunction: FunctionSymbol?, val unboxFunction: FunctionSymbol?)
|
||||
@@ -132,6 +130,7 @@ internal object DataFlowIR {
|
||||
val isGlobalInitializer = attributes.and(FunctionAttributes.IS_GLOBAL_INITIALIZER) != 0
|
||||
val returnsUnit = attributes.and(FunctionAttributes.RETURNS_UNIT) != 0
|
||||
val returnsNothing = attributes.and(FunctionAttributes.RETURNS_NOTHING) != 0
|
||||
val explicitlyExported = attributes.and(FunctionAttributes.EXPLICITLY_EXPORTED) != 0
|
||||
|
||||
var escapes: Int? = null
|
||||
var pointsTo: IntArray? = null
|
||||
@@ -572,12 +571,6 @@ internal object DataFlowIR {
|
||||
inlinedClass?.let { mapFunction(context.getUnboxFunction(it)) })
|
||||
}
|
||||
|
||||
// TODO: use from LlvmDeclarations.
|
||||
private fun getFqName(declaration: IrDeclaration): FqName =
|
||||
declaration.parent.fqNameForIrSerialization.child(declaration.name)
|
||||
|
||||
private val IrFunction.internalName get() = getFqName(this).asString() + "#internal"
|
||||
|
||||
fun mapFunction(declaration: IrDeclaration): FunctionSymbol = when (declaration) {
|
||||
is IrFunction -> mapFunction(declaration)
|
||||
is IrField -> mapPropertyInitializer(declaration)
|
||||
@@ -587,7 +580,13 @@ internal object DataFlowIR {
|
||||
private fun mapFunction(function: IrFunction): FunctionSymbol = function.target.let {
|
||||
functionMap[it]?.let { return it }
|
||||
|
||||
val name = if (it.isExported()) it.symbolName else it.internalName
|
||||
val parent = it.parent
|
||||
|
||||
val containingDeclarationPart = parent.fqNameForIrSerialization.let {
|
||||
if (it.isRoot) "" else "$it."
|
||||
}
|
||||
val name = "kfun:$containingDeclarationPart${it.functionName}"
|
||||
|
||||
val returnsUnit = it is IrConstructor || (!it.isSuspend && it.returnType.isUnit())
|
||||
val returnsNothing = !it.isSuspend && it.returnType.isNothing()
|
||||
var attributes = 0
|
||||
@@ -595,6 +594,11 @@ internal object DataFlowIR {
|
||||
attributes = attributes or FunctionAttributes.RETURNS_UNIT
|
||||
if (returnsNothing)
|
||||
attributes = attributes or FunctionAttributes.RETURNS_NOTHING
|
||||
if (it.hasAnnotation(RuntimeNames.exportForCppRuntime)
|
||||
|| it.getExternalObjCMethodInfo() != null // TODO-DCE-OBJC-INIT
|
||||
|| it.hasAnnotation(RuntimeNames.objCMethodImp)) {
|
||||
attributes = attributes or FunctionAttributes.EXPLICITLY_EXPORTED
|
||||
}
|
||||
val symbol = when {
|
||||
it.isExternal || (it.symbol in context.irBuiltIns.irBuiltInsSymbols) -> {
|
||||
val escapesAnnotation = it.descriptor.annotations.findAnnotation(FQ_NAME_ESCAPES)
|
||||
|
||||
+49
-35
@@ -22,9 +22,11 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptorImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrReturnableBlockSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrFail
|
||||
import org.jetbrains.kotlin.ir.types.impl.originalKotlinType
|
||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
||||
import org.jetbrains.kotlin.ir.util.irCall
|
||||
@@ -67,19 +69,22 @@ internal object Devirtualization {
|
||||
listOf(moduleDFG.symbolTable.mapFunction(entryPoint).resolved())
|
||||
else
|
||||
// In a library every public function and every function accessible via virtual call belongs to the rootset.
|
||||
moduleDFG.functions.keys.filterIsInstance<DataFlowIR.FunctionSymbol.Public>() +
|
||||
moduleDFG.symbolTable.functionMap.values.filterIsInstance<DataFlowIR.FunctionSymbol.Public>() +
|
||||
moduleDFG.symbolTable.classMap.values
|
||||
.filterIsInstance<DataFlowIR.Type.Declared>()
|
||||
.flatMap { it.vtable + it.itable.values }
|
||||
.filterIsInstance<DataFlowIR.FunctionSymbol.Declared>()
|
||||
.filter { moduleDFG.functions.containsKey(it) }
|
||||
// TODO: Are globals inititalizers 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?
|
||||
val globalInitializers =
|
||||
moduleDFG.functions.keys.filter { it.isGlobalInitializer } +
|
||||
moduleDFG.symbolTable.functionMap.values.filter { it.isGlobalInitializer } +
|
||||
externalModulesDFG.functionDFGs.keys.filter { it.isGlobalInitializer }
|
||||
|
||||
return (exportedFunctions + globalInitializers).distinct()
|
||||
val explicitlyExportedFunctions =
|
||||
moduleDFG.symbolTable.functionMap.values.filter { it.explicitlyExported } +
|
||||
externalModulesDFG.functionDFGs.keys.filter { it.explicitlyExported }
|
||||
|
||||
return (exportedFunctions + globalInitializers + explicitlyExportedFunctions).distinct()
|
||||
}
|
||||
|
||||
fun BitSet.format(allTypes: List<DataFlowIR.Type.Declared>): String {
|
||||
@@ -88,9 +93,9 @@ internal object Devirtualization {
|
||||
|
||||
private val VIRTUAL_TYPE_ID = 0 // Id of [DataFlowIR.Type.Virtual].
|
||||
|
||||
private class DevirtualizationAnalysis(val context: Context,
|
||||
val moduleDFG: ModuleDFG,
|
||||
val externalModulesDFG: ExternalModulesDFG) {
|
||||
internal class DevirtualizationAnalysis(val context: Context,
|
||||
val moduleDFG: ModuleDFG,
|
||||
val externalModulesDFG: ExternalModulesDFG) {
|
||||
|
||||
private val entryPoint = context.ir.symbols.entryPoint?.owner
|
||||
|
||||
@@ -188,7 +193,7 @@ internal object Devirtualization {
|
||||
return this
|
||||
}
|
||||
|
||||
private inner class TypeHierarchy(types: List<DataFlowIR.Type.Declared>) {
|
||||
inner class TypeHierarchy(types: List<DataFlowIR.Type.Declared>) {
|
||||
private val typesSubTypes = mutableMapOf<DataFlowIR.Type.Declared, MutableList<DataFlowIR.Type.Declared>>()
|
||||
|
||||
init {
|
||||
@@ -229,6 +234,7 @@ internal object Devirtualization {
|
||||
|
||||
fun search(): Set<DataFlowIR.Type.Declared> {
|
||||
// Rapid Type Analysis: find all instantiations and conservatively estimate call graph.
|
||||
|
||||
// Add all final parameters of the roots.
|
||||
rootSet.forEach {
|
||||
it.parameters
|
||||
@@ -238,8 +244,7 @@ internal object Devirtualization {
|
||||
}
|
||||
if (entryPoint == null) {
|
||||
// For library assume all public non-abstract classes could be instantiated.
|
||||
moduleDFG.symbolTable.classMap.values
|
||||
.asSequence()
|
||||
symbolTable.classMap.values
|
||||
.filterIsInstance<DataFlowIR.Type.Public>()
|
||||
.filter { !it.isAbstract }
|
||||
.forEach { addInstantiatingClass(it) }
|
||||
@@ -333,7 +338,7 @@ internal object Devirtualization {
|
||||
|
||||
val function = (moduleDFG.functions[resolvedFunctionSymbol]
|
||||
?: externalModulesDFG.functionDFGs[resolvedFunctionSymbol])
|
||||
?: error("Unknown function $resolvedFunctionSymbol")
|
||||
?: return
|
||||
|
||||
DEBUG_OUTPUT(1) { function.debugOutput() }
|
||||
|
||||
@@ -452,7 +457,7 @@ internal object Devirtualization {
|
||||
} while (cur != node)
|
||||
}
|
||||
|
||||
fun analyze(): Map<DataFlowIR.Node.VirtualCall, DevirtualizedCallSite> {
|
||||
fun analyze(): AnalysisResult {
|
||||
val functions = moduleDFG.functions + externalModulesDFG.functionDFGs
|
||||
val typeHierarchy = TypeHierarchy(symbolTable.classMap.values.filterIsInstance<DataFlowIR.Type.Declared>() +
|
||||
externalModulesDFG.allTypes)
|
||||
@@ -665,7 +670,7 @@ internal object Devirtualization {
|
||||
}
|
||||
}
|
||||
|
||||
return result.asSequence().associateBy({ it.key }, { it.value.first })
|
||||
return AnalysisResult(result.asSequence().associateBy({ it.key }, { it.value.first }), typeHierarchy, instantiatingClasses.keys)
|
||||
}
|
||||
|
||||
private inner class ConstraintGraphBuilder(val functionNodesMap: MutableMap<DataFlowIR.Node, Node>,
|
||||
@@ -719,10 +724,10 @@ internal object Devirtualization {
|
||||
fieldNode(constraintGraph.arrayItemField)
|
||||
)
|
||||
}
|
||||
rootSet.forEach { createFunctionConstraintGraph(it, true)!! }
|
||||
rootSet.forEach { createFunctionConstraintGraph(it, true) }
|
||||
while (stack.isNotEmpty()) {
|
||||
val symbol = stack.pop()
|
||||
val function = functions[symbol] ?: error("Unknown function: $symbol")
|
||||
val function = functions[symbol] ?: continue
|
||||
val body = function.body
|
||||
val functionConstraintGraph = constraintGraph.functions[symbol]!!
|
||||
|
||||
@@ -757,7 +762,7 @@ internal object Devirtualization {
|
||||
symbol.parameters.forEachIndexed { index, type ->
|
||||
val resolvedType = type.type.resolved()
|
||||
val node = if (!resolvedType.isFinal)
|
||||
constraintGraph.virtualNode
|
||||
constraintGraph.virtualNode // TODO: May be do this only for a library?
|
||||
else
|
||||
concreteClass(resolvedType)
|
||||
node.addEdge(parameters[index])
|
||||
@@ -783,14 +788,18 @@ internal object Devirtualization {
|
||||
return Node.CastEdge(node, suitableTypes)
|
||||
}
|
||||
|
||||
private fun doCast(function: Function, node: Node, type: DataFlowIR.Type.Declared): Node {
|
||||
val castNode = ordinaryNode { "Cast\$${function.symbol}" }
|
||||
val castEdge = createCastEdge(castNode, type)
|
||||
node.addCastEdge(castEdge)
|
||||
return castNode
|
||||
}
|
||||
|
||||
private fun edgeToConstraintNode(function: Function,
|
||||
edge: DataFlowIR.Edge): Node {
|
||||
val result = dfgNodeToConstraintNode(function, edge.node)
|
||||
val castToType = edge.castToType?.resolved() ?: return result
|
||||
val castNode = ordinaryNode { "Cast\$${function.symbol}" }
|
||||
val castEdge = createCastEdge(castNode, castToType)
|
||||
result.addCastEdge(castEdge)
|
||||
return castNode
|
||||
return doCast(function, result, castToType)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -809,7 +818,7 @@ internal object Devirtualization {
|
||||
else -> error("Unexpected argument: $argument")
|
||||
}
|
||||
|
||||
fun doCall(callee: Function, arguments: List<Any>): Node {
|
||||
fun doCall(callee: Function, arguments: List<Any>, returnType: DataFlowIR.Type.Declared): Node {
|
||||
assert(callee.parameters.size == arguments.size) {
|
||||
"Function ${callee.symbol} takes ${callee.parameters.size} but caller ${function.symbol}" +
|
||||
" provided ${arguments.size}"
|
||||
@@ -818,7 +827,7 @@ internal object Devirtualization {
|
||||
val argument = argumentToConstraintNode(arguments[index])
|
||||
argument.addEdge(parameter)
|
||||
}
|
||||
return callee.returns
|
||||
return doCast(function, callee.returns, returnType)
|
||||
}
|
||||
|
||||
fun doCall(callee: DataFlowIR.FunctionSymbol,
|
||||
@@ -832,20 +841,25 @@ internal object Devirtualization {
|
||||
val fictitiousReturnNode = ordinaryNode { "External$resolvedCallee" }
|
||||
if (returnType.isFinal)
|
||||
concreteClass(returnType).addEdge(fictitiousReturnNode)
|
||||
else
|
||||
else {
|
||||
constraintGraph.virtualNode.addEdge(fictitiousReturnNode)
|
||||
// TODO: Unconservative way - when we can use it?
|
||||
// typeHierarchy.inheritorsOf(returnType)
|
||||
// .filterNot { it.isAbstract }
|
||||
// .filter { instantiatingClasses.containsKey(it) }
|
||||
// .forEach { concreteClass(it).addEdge(fictitiousReturnNode) }
|
||||
}
|
||||
fictitiousReturnNode
|
||||
}
|
||||
} else {
|
||||
calleeConstraintGraph.throws.addEdge(function.throws)
|
||||
if (receiverType == null)
|
||||
doCall(calleeConstraintGraph, arguments)
|
||||
doCall(calleeConstraintGraph, arguments, returnType)
|
||||
else {
|
||||
val receiverNode = argumentToConstraintNode(arguments[0])
|
||||
val castedReceiver = ordinaryNode { "CastedReceiver\$${function.symbol}" }
|
||||
val castedEdge = createCastEdge(castedReceiver, receiverType)
|
||||
receiverNode.addCastEdge(castedEdge)
|
||||
doCall(calleeConstraintGraph, listOf(castedReceiver) + arguments.drop(1))
|
||||
doCall(calleeConstraintGraph,
|
||||
listOf(doCast(function, receiverNode, receiverType)) + arguments.drop(1),
|
||||
returnType)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -923,9 +937,7 @@ internal object Devirtualization {
|
||||
val receiverNode = edgeToConstraintNode(node.arguments[0])
|
||||
if (receiverType == DataFlowIR.Type.Virtual)
|
||||
constraintGraph.virtualNode.addEdge(receiverNode)
|
||||
val castedReceiver = ordinaryNode { "CastedReceiver\$${function.symbol}" }
|
||||
val castedEdge = createCastEdge(castedReceiver, receiverType)
|
||||
receiverNode.addCastEdge(castedEdge)
|
||||
val castedReceiver = doCast(function, receiverNode, receiverType)
|
||||
val arguments = listOf(castedReceiver) + node.arguments.drop(1)
|
||||
|
||||
val returnsNode = ordinaryNode { "VirtualCallReturns\$${function.symbol}" }
|
||||
@@ -935,7 +947,7 @@ internal object Devirtualization {
|
||||
// Add cast to [Virtual] edge from receiver to returns, if return type is not final.
|
||||
// With this we're reflecting the fact that unknown function can return anything.
|
||||
val virtualTypeFilter = BitSet().apply { set(VIRTUAL_TYPE_ID) }
|
||||
if (!returnType.isFinal) {
|
||||
if (!returnType.isFinal && entryPoint == null) {
|
||||
receiverNode.addCastEdge(Node.CastEdge(returnsNode, virtualTypeFilter))
|
||||
}
|
||||
// And throw anything.
|
||||
@@ -1007,19 +1019,21 @@ internal object Devirtualization {
|
||||
|
||||
class DevirtualizedCallSite(val callee: DataFlowIR.FunctionSymbol, val possibleCallees: List<DevirtualizedCallee>)
|
||||
|
||||
class AnalysisResult(val devirtualizedCallSites: Map<DataFlowIR.Node.VirtualCall, DevirtualizedCallSite>)
|
||||
class AnalysisResult(val devirtualizedCallSites: Map<DataFlowIR.Node.VirtualCall, DevirtualizedCallSite>,
|
||||
val typeHierarchy: DevirtualizationAnalysis.TypeHierarchy,
|
||||
val instantiatingClasses: Set<DataFlowIR.Type.Declared>)
|
||||
|
||||
fun run(irModule: IrModuleFragment, context: Context, moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG)
|
||||
: AnalysisResult {
|
||||
val devirtualizationAnalysisResult = DevirtualizationAnalysis(context, moduleDFG, externalModulesDFG).analyze()
|
||||
val devirtualizedCallSites =
|
||||
devirtualizationAnalysisResult
|
||||
devirtualizationAnalysisResult.devirtualizedCallSites
|
||||
.asSequence()
|
||||
.filter { it.key.irCallSite != null }
|
||||
.associate { it.key.irCallSite!! to it.value }
|
||||
devirtualize(irModule, context, externalModulesDFG, devirtualizedCallSites)
|
||||
removeRedundantCoercions(irModule, context)
|
||||
return AnalysisResult(devirtualizationAnalysisResult)
|
||||
return devirtualizationAnalysisResult
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -52,8 +52,8 @@ public final class String : Comparable<String>, CharSequence {
|
||||
external public override fun equals(other: Any?): Boolean
|
||||
}
|
||||
|
||||
public operator fun kotlin.String?.plus(other: kotlin.Any?): kotlin.String =
|
||||
public inline operator fun kotlin.String?.plus(other: kotlin.Any?): kotlin.String =
|
||||
(this?.toString() ?: "null").plus(other?.toString() ?: "null")
|
||||
|
||||
|
||||
public fun Any?.toString() = this?.toString() ?: "null"
|
||||
public inline fun Any?.toString() = this?.toString() ?: "null"
|
||||
@@ -286,6 +286,7 @@ private fun Kotlin_ObjCExport_isUnchecked(exception: Throwable): Boolean =
|
||||
|
||||
@PublishedApi
|
||||
@SymbolName("Kotlin_ObjCExport_trapOnUndeclaredException")
|
||||
@ExportForCppRuntime
|
||||
internal external fun trapOnUndeclaredException(exception: Throwable)
|
||||
|
||||
@ExportForCppRuntime
|
||||
|
||||
@@ -28,10 +28,12 @@ fun ThrowTypeCastException(): Nothing {
|
||||
throw TypeCastException()
|
||||
}
|
||||
|
||||
@ExportForCppRuntime
|
||||
fun ThrowInvalidReceiverTypeException(klass: KClass<*>): Nothing {
|
||||
throw RuntimeException("Unexpected receiver type: " + (klass.qualifiedName ?: "noname"))
|
||||
}
|
||||
|
||||
@ExportForCppRuntime
|
||||
internal fun ThrowArithmeticException() : Nothing {
|
||||
throw ArithmeticException()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user