diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt index b5c310e0e40..67a00993db7 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt @@ -354,6 +354,8 @@ internal class KonanSymbols( val initInstance = internalFunction("initInstance") + val isSubtype = internalFunction("isSubtype") + val println = irBuiltIns.findFunctions(Name.identifier("println"), "kotlin", "io") .single { lookup.getValueParametersCount(it) == 1 && lookup.isValueParameterClass(it, 0, string) } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index cb0d6de394f..6d239bda7c5 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt @@ -18,7 +18,6 @@ import org.jetbrains.kotlin.backend.konan.llvm.ThreadState.Runnable import org.jetbrains.kotlin.backend.konan.llvm.objc.* import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.konan.ForeignExceptionMode import org.jetbrains.kotlin.konan.target.CompilerOutputKind @@ -213,7 +212,7 @@ private fun IrSimpleFunction.findOverriddenMethodOfAny(): IrSimpleFunction? { } internal object VirtualTablesLookup { - fun FunctionGenerationContext.getInterfaceTableRecord(typeInfo: LLVMValueRef, interfaceId: Int): LLVMValueRef { + private fun FunctionGenerationContext.getInterfaceTableRecord(typeInfo: LLVMValueRef, interfaceId: Int): LLVMValueRef { val interfaceTableSize = load(structGep(typeInfo, 9 /* interfaceTableSize_ */)) val interfaceTable = load(structGep(typeInfo, 10 /* interfaceTable_ */)) @@ -254,6 +253,21 @@ internal object VirtualTablesLookup { } } + fun FunctionGenerationContext.checkIsSubtype(objTypeInfo: LLVMValueRef, dstClass: IrClass) = if (!context.ghaEnabled()) { + call(llvm.isSubtypeFunction, listOf(objTypeInfo, codegen.typeInfoValue(dstClass))) + } else { + val dstHierarchyInfo = context.getLayoutBuilder(dstClass).hierarchyInfo + if (!dstClass.isInterface) { + call(llvm.isSubclassFastFunction, + listOf(objTypeInfo, llvm.int32(dstHierarchyInfo.classIdLo), llvm.int32(dstHierarchyInfo.classIdHi))) + } else { + // Essentially: typeInfo.itable[place(interfaceId)].id == interfaceId + val interfaceId = dstHierarchyInfo.interfaceId + val interfaceTableRecord = getInterfaceTableRecord(objTypeInfo, interfaceId) + icmpEq(load(structGep(interfaceTableRecord, 0 /* id */)), llvm.int32(interfaceId)) + } + } + fun FunctionGenerationContext.getVirtualImpl(receiver: LLVMValueRef, irFunction: IrSimpleFunction): LlvmCallable { assert(LLVMTypeOf(receiver) == codegen.kObjHeaderPtr) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index 354d9538aa5..1e6874406d3 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -426,8 +426,8 @@ internal class CodegenLlvmHelpers(private val generationState: NativeGenerationS val setCurrentFrameFunction = importRtFunction("SetCurrentFrame") val checkCurrentFrameFunction = importRtFunction("CheckCurrentFrame") val lookupInterfaceTableRecord = importRtFunction("LookupInterfaceTableRecord") - val isInstanceFunction = importRtFunction("IsInstance") - val isInstanceOfClassFastFunction = importRtFunction("IsInstanceOfClassFast") + val isSubtypeFunction = importRtFunction("IsSubtype") + val isSubclassFastFunction = importRtFunction("IsSubclassFast") val throwExceptionFunction = importRtFunction("ThrowException") val appendToInitalizersTail = importRtFunction("AppendToInitializersTail") val callInitGlobalPossiblyLock = importRtFunction("CallInitGlobalPossiblyLock") diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IntrinsicGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IntrinsicGenerator.kt index 0426fd79522..aa158ee0db1 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IntrinsicGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IntrinsicGenerator.kt @@ -16,6 +16,7 @@ import org.jetbrains.kotlin.ir.declarations.IrField import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol +import org.jetbrains.kotlin.ir.types.classOrNull import org.jetbrains.kotlin.ir.types.getClass import org.jetbrains.kotlin.ir.util.findAnnotation @@ -65,6 +66,7 @@ internal enum class IntrinsicType { IDENTITY, IMMUTABLE_BLOB, INIT_INSTANCE, + IS_SUBTYPE, IS_EXPERIMENTAL_MM, THE_UNIT_INSTANCE, // Enums @@ -247,6 +249,7 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv IntrinsicType.INTEROP_WRITE_PRIMITIVE -> emitWritePrimitive(callSite, args) IntrinsicType.INTEROP_GET_POINTER_SIZE -> emitGetPointerSize() IntrinsicType.CREATE_UNINITIALIZED_INSTANCE -> emitCreateUninitializedInstance(callSite, resultSlot) + IntrinsicType.IS_SUBTYPE -> emitIsSubtype(callSite, args) IntrinsicType.INTEROP_NATIVE_PTR_TO_LONG -> emitNativePtrToLong(callSite, args) IntrinsicType.INTEROP_NATIVE_PTR_PLUS_LONG -> emitNativePtrPlusLong(args) IntrinsicType.INTEROP_GET_NATIVE_NULL_PTR -> emitGetNativeNullPtr() @@ -419,6 +422,14 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv return allocInstance(enumIrClass, environment.calculateLifetime(callSite), resultSlot) } + private fun FunctionGenerationContext.emitIsSubtype(callSite: IrCall, args: List) = + with(VirtualTablesLookup) { + checkIsSubtype( + objTypeInfo = bitcast(pointerType(llvm.kTypeInfo), args.single()), + dstClass = callSite.getTypeArgument(0)!!.classOrNull!!.owner + ) + } + private fun FunctionGenerationContext.emitGetPointerSize(): LLVMValueRef = llvm.int32(LLVMPointerSize(codegen.llvmTargetData)) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index a4a6f2ba900..c91b7fc914b 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -1585,30 +1585,14 @@ internal class CodeGeneratorVisitor( //-------------------------------------------------------------------------// - private fun genInstanceOf(obj: LLVMValueRef, dstClass: IrClass): LLVMValueRef { + private fun genInstanceOf(obj: LLVMValueRef, dstClass: IrClass) = with(functionGenerationContext) { if (dstClass.defaultType.isObjCObjectType()) { - return genInstanceOfObjC(obj, dstClass) - } - - val srcObjInfoPtr = functionGenerationContext.bitcast(codegen.kObjHeaderPtr, obj) - - return if (!context.ghaEnabled()) { - call(llvm.isInstanceFunction, listOf(srcObjInfoPtr, codegen.typeInfoValue(dstClass))) - } else { - val dstHierarchyInfo = context.getLayoutBuilder(dstClass).hierarchyInfo - if (!dstClass.isInterface) { - call(llvm.isInstanceOfClassFastFunction, - listOf(srcObjInfoPtr, llvm.int32(dstHierarchyInfo.classIdLo), llvm.int32(dstHierarchyInfo.classIdHi))) - } else { - // Essentially: typeInfo.itable[place(interfaceId)].id == interfaceId - val interfaceId = dstHierarchyInfo.interfaceId - val typeInfo = functionGenerationContext.loadTypeInfo(srcObjInfoPtr) - with(functionGenerationContext) { - - val interfaceTableRecord = with(VirtualTablesLookup) { getInterfaceTableRecord(typeInfo, interfaceId) } - icmpEq(load(structGep(interfaceTableRecord, 0 /* id */)), llvm.int32(interfaceId)) - } - } + genInstanceOfObjC(obj, dstClass) + } else with(VirtualTablesLookup) { + checkIsSubtype( + objTypeInfo = loadTypeInfo(bitcast(codegen.kObjHeaderPtr, obj)), + dstClass + ) } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DevirtualizationAnalysis.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DevirtualizationAnalysis.kt index 7a127ecb93f..219922c548c 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DevirtualizationAnalysis.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DevirtualizationAnalysis.kt @@ -1361,8 +1361,8 @@ internal object DevirtualizationAnalysis { fun devirtualize(irModule: IrModuleFragment, context: Context, externalModulesDFG: ExternalModulesDFG, devirtualizedCallSites: Map) { val symbols = context.ir.symbols - val irBuiltIns = context.irBuiltIns val nativePtrEqualityOperatorSymbol = symbols.areEqualByValue[PrimitiveBinaryType.POINTER]!! + val isSubtype = symbols.isSubtype val optimize = context.shouldOptimize() fun DataFlowIR.Type.resolved(): DataFlowIR.Type.Declared { @@ -1582,14 +1582,9 @@ internal object DevirtualizationAnalysis { } } else { val receiverType = actualCallee.irFunction!!.parentAsClass - IrTypeOperatorCallImpl( - startOffset = startOffset, - endOffset = endOffset, - type = irBuiltIns.booleanType, - operator = IrTypeOperator.INSTANCEOF, - typeOperand = receiverType.defaultType, - argument = irGet(receiver) - ) + irCall(isSubtype, listOf(receiverType.defaultType)).apply { + putValueArgument(0, irGet(typeInfo)) + } } } IrBranchImpl( diff --git a/kotlin-native/runtime/src/compiler_interface/cpp/CompilerCInterface.cpp b/kotlin-native/runtime/src/compiler_interface/cpp/CompilerCInterface.cpp index bb8742fadb1..ef251fe172e 100644 --- a/kotlin-native/runtime/src/compiler_interface/cpp/CompilerCInterface.cpp +++ b/kotlin-native/runtime/src/compiler_interface/cpp/CompilerCInterface.cpp @@ -53,8 +53,8 @@ touchFunction(FreezeSubgraph) touchFunction(CheckGlobalsAccessible) touchFunction(LookupInterfaceTableRecord) -touchFunction(IsInstance) -touchFunction(IsInstanceOfClassFast) +touchFunction(IsSubtype) +touchFunction(IsSubclassFast) touchFunction(ThrowException) touchFunction(Kotlin_getExceptionObject) diff --git a/kotlin-native/runtime/src/main/cpp/Types.cpp b/kotlin-native/runtime/src/main/cpp/Types.cpp index e2c58146589..7de0518d914 100644 --- a/kotlin-native/runtime/src/main/cpp/Types.cpp +++ b/kotlin-native/runtime/src/main/cpp/Types.cpp @@ -12,6 +12,10 @@ KBoolean IsInstance(const ObjHeader* obj, const TypeInfo* type_info) { // We assume null check is handled by caller. RuntimeAssert(obj != nullptr, "must not be null"); const TypeInfo* obj_type_info = obj->type_info(); + return IsSubtype(obj_type_info, type_info); +} + +KBoolean IsSubtype(const TypeInfo* obj_type_info, const TypeInfo* type_info) { // If it is an interface - check in list of implemented interfaces. if ((type_info->flags_ & TF_INTERFACE) != 0) { for (int i = 0; i < obj_type_info->implementedInterfacesCount_; ++i) { @@ -27,10 +31,7 @@ KBoolean IsInstance(const ObjHeader* obj, const TypeInfo* type_info) { return obj_type_info != nullptr; } -KBoolean IsInstanceOfClassFast(const ObjHeader* obj, int32_t lo, int32_t hi) { - // We assume null check is handled by caller. - RuntimeAssert(obj != nullptr, "must not be null"); - const TypeInfo* obj_type_info = obj->type_info(); +KBoolean IsSubclassFast(const TypeInfo* obj_type_info, int32_t lo, int32_t hi) { // Super type's interval should contain our interval. return obj_type_info->classId_ >= lo && obj_type_info->classId_ <= hi; } diff --git a/kotlin-native/runtime/src/main/cpp/Types.h b/kotlin-native/runtime/src/main/cpp/Types.h index 28a9e78ea23..f5f982fc7b0 100644 --- a/kotlin-native/runtime/src/main/cpp/Types.h +++ b/kotlin-native/runtime/src/main/cpp/Types.h @@ -73,7 +73,8 @@ extern const TypeInfo* theCleanerImplTypeInfo; extern const TypeInfo* theRegularWeakReferenceImplTypeInfo; KBoolean IsInstance(const ObjHeader* obj, const TypeInfo* type_info) RUNTIME_PURE; -KBoolean IsInstanceOfClassFast(const ObjHeader* obj, int32_t lo, int32_t hi) RUNTIME_PURE; +KBoolean IsSubtype(const TypeInfo* obj_type_info, const TypeInfo* type_info) RUNTIME_PURE; +KBoolean IsSubclassFast(const TypeInfo* obj_type_info, int32_t lo, int32_t hi) RUNTIME_PURE; void CheckCast(const ObjHeader* obj, const TypeInfo* type_info); KBoolean IsArray(KConstRef obj) RUNTIME_PURE; bool IsSubInterface(const TypeInfo* thiz, const TypeInfo* other) RUNTIME_PURE; diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/IntrinsicType.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/IntrinsicType.kt index e0115795d49..bab9eda4e14 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/IntrinsicType.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/IntrinsicType.kt @@ -52,6 +52,7 @@ internal class IntrinsicType { const val IDENTITY = "IDENTITY" const val IMMUTABLE_BLOB = "IMMUTABLE_BLOB" const val INIT_INSTANCE = "INIT_INSTANCE" + const val IS_SUBTYPE = "IS_SUBTYPE" const val IS_EXPERIMENTAL_MM = "IS_EXPERIMENTAL_MM" const val THE_UNIT_INSTANCE = "THE_UNIT_INSTANCE" diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/RuntimeUtils.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/RuntimeUtils.kt index 338e3593494..d884cfe50d4 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/RuntimeUtils.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/RuntimeUtils.kt @@ -226,6 +226,10 @@ internal external fun createUninitializedInstance(): T @TypedIntrinsic(IntrinsicType.INIT_INSTANCE) internal external fun initInstance(thiz: Any, constructorCall: Any): Unit +@PublishedApi +@TypedIntrinsic(IntrinsicType.IS_SUBTYPE) +internal external fun isSubtype(objTypeInfo: NativePtr): Boolean + @PublishedApi internal fun checkProgressionStep(step: Int) = if (step > 0) step else throw IllegalArgumentException("Step must be positive, was: $step.")