From 8a6146ac088153de5a3c1671b0bdca2a30783136 Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Tue, 27 Aug 2019 16:02:10 +0300 Subject: [PATCH] Implemented fast type check for classes --- .../konan/descriptors/ClassLayoutBuilder.kt | 63 +++++++++++++++++++ .../jetbrains/kotlin/backend/konan/ir/Ir.kt | 2 + .../backend/konan/llvm/BitcodePhases.kt | 7 ++- .../kotlin/backend/konan/llvm/ContextUtils.kt | 2 +- .../kotlin/backend/konan/llvm/IrToBitcode.kt | 39 ++++++------ .../backend/konan/llvm/RTTIGenerator.kt | 13 ++++ runtime/src/main/cpp/TypeInfo.h | 3 + runtime/src/main/cpp/Types.cpp | 15 ++--- 8 files changed, 117 insertions(+), 27 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt index 19b0383ab67..8febefa3080 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt @@ -12,9 +12,13 @@ import org.jetbrains.kotlin.backend.konan.llvm.functionName import org.jetbrains.kotlin.backend.konan.llvm.localHash import org.jetbrains.kotlin.backend.konan.lower.bridgeTarget import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrClassReference import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid +import org.jetbrains.kotlin.ir.visitors.acceptVoid import org.jetbrains.kotlin.name.FqName internal class OverriddenFunctionInfo( @@ -77,6 +81,63 @@ internal class OverriddenFunctionInfo( } } +internal class ClassGlobalHierarchyInfo(val classIdLo: Int, val classIdHi: Int) { + companion object { + val DUMMY = ClassGlobalHierarchyInfo(0, 0) + } +} + +internal class GlobalHierarchyAnalysis(val context: Context) { + fun run() { + /* + * Here's the explanation of what's happening here: + * Given a tree we can traverse it with the DFS and save for each vertex two times: + * the enter time (the first time we saw this vertex) and the exit time (the last time we saw it). + * It turns out that if we assign then for each vertex the interval (enterTime, exitTime), + * then the following claim holds for any two vertices v and w: + * ----- v is ancestor of w iff interval(v) contains interval(w) ------ + * Now apply this idea to the classes hierarchy tree and we'll get a fast type check. + * + * And one more observation: for each pair of intervals they either don't intersect or + * one contains the other. With that in mind, we can save in a type info only one end of an interval. + */ + val root = context.irBuiltIns.anyClass.owner + val immediateInheritors = mutableMapOf>() + val allClasses = mutableListOf() + context.irModule!!.acceptVoid(object: IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitClass(declaration: IrClass) { + if (declaration.isInterface) + context.getLayoutBuilder(declaration).hierarchyInfo = ClassGlobalHierarchyInfo(0, 0) + else { + allClasses += declaration + if (declaration != root) { + val superClass = declaration.getSuperClassNotAny() ?: root + val inheritors = immediateInheritors.getOrPut(superClass) { mutableListOf() } + inheritors.add(declaration) + } + } + super.visitClass(declaration) + } + }) + var time = 0 + + fun dfs(irClass: IrClass) { + ++time + // Make the Any's interval's left border -1 in order to correctly generate classes for ObjC blocks. + val enterTime = if (irClass == root) -1 else time + immediateInheritors[irClass]?.forEach { dfs(it) } + val exitTime = time + context.getLayoutBuilder(irClass).hierarchyInfo = ClassGlobalHierarchyInfo(enterTime, exitTime) + } + + dfs(root) + } +} + internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) { private val DEBUG = 0 @@ -218,6 +279,8 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) { result } + lateinit var hierarchyInfo: ClassGlobalHierarchyInfo + /** * Fields declared in the class. */ diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt index e3e748bfd2f..a4879cebbde 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt @@ -225,6 +225,8 @@ internal class KonanSymbols( override val ThrowTypeCastException = internalFunction("ThrowTypeCastException") + val throwClassCastException = internalFunction("ThrowClassCastException") + val throwInvalidReceiverTypeException = internalFunction("ThrowInvalidReceiverTypeException") val throwIllegalStateException = internalFunction("ThrowIllegalStateException") diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt index 7c1b12627e2..bb906ae36cf 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.konan.llvm import llvm.* import org.jetbrains.kotlin.backend.konan.* +import org.jetbrains.kotlin.backend.konan.descriptors.GlobalHierarchyAnalysis import org.jetbrains.kotlin.backend.konan.optimizations.* import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.ir.IrElement @@ -37,7 +38,11 @@ internal val contextLLVMSetupPhase = makeKonanModuleOpPhase( internal val RTTIPhase = makeKonanModuleOpPhase( name = "RTTI", description = "RTTI generation", - op = { context, irModule -> irModule.acceptVoid(RTTIGeneratorVisitor(context)) } + op = { context, irModule -> + if (context.shouldOptimize()) + GlobalHierarchyAnalysis(context).run() + irModule.acceptVoid(RTTIGeneratorVisitor(context)) + } ) internal val generateDebugInfoHeaderPhase = makeKonanModuleOpPhase( diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index 65a2fb7450b..02fa09b8309 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -434,7 +434,7 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) { val leaveFrameFunction = importModelSpecificRtFunction("LeaveFrame") val lookupOpenMethodFunction = importRtFunction("LookupOpenMethod") val isInstanceFunction = importRtFunction("IsInstance") - val checkInstanceFunction = importRtFunction("CheckInstance") + val isInstanceOfClassFastFunction = importRtFunction("IsInstanceOfClassFast") val throwExceptionFunction = importRtFunction("ThrowException") val appendToInitalizersTail = importRtFunction("AppendToInitializersTail") val initRuntimeIfNeeded = importRtFunction("Kotlin_initRuntimeIfNeeded") diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index 2cd22eee524..a60e8e68eaa 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -1271,28 +1271,27 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Maptype_info(); + // Super type's interval should contain our interval. + return obj_type_info->classId_ >= lo && obj_type_info->classId_ <= hi; +} + KBoolean IsArray(KConstRef obj) { RuntimeAssert(obj != nullptr, "Object must not be null"); return obj->type_info()->instanceSize_ < 0; } -void CheckInstance(const ObjHeader* obj, const TypeInfo* type_info) { - if (IsInstance(obj, type_info)) { - return; - } - ThrowClassCastException(obj, type_info); -} - KBoolean Kotlin_TypeInfo_isInstance(KConstRef obj, KNativePtr typeInfo) { return IsInstance(obj, reinterpret_cast(typeInfo)); }