[K/N] Added isSubtype intrinsic

This commit is contained in:
Igor Chevdar
2023-05-10 10:03:42 +03:00
committed by Space Team
parent 67b65ec56a
commit 2f24c6df80
11 changed files with 56 additions and 43 deletions
@@ -354,6 +354,8 @@ internal class KonanSymbols(
val initInstance = internalFunction("initInstance") val initInstance = internalFunction("initInstance")
val isSubtype = internalFunction("isSubtype")
val println = irBuiltIns.findFunctions(Name.identifier("println"), "kotlin", "io") val println = irBuiltIns.findFunctions(Name.identifier("println"), "kotlin", "io")
.single { lookup.getValueParametersCount(it) == 1 && lookup.isValueParameterClass(it, 0, string) } .single { lookup.getValueParametersCount(it) == 1 && lookup.isValueParameterClass(it, 0, string) }
@@ -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.backend.konan.llvm.objc.*
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.konan.ForeignExceptionMode import org.jetbrains.kotlin.konan.ForeignExceptionMode
import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.CompilerOutputKind
@@ -213,7 +212,7 @@ private fun IrSimpleFunction.findOverriddenMethodOfAny(): IrSimpleFunction? {
} }
internal object VirtualTablesLookup { 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 interfaceTableSize = load(structGep(typeInfo, 9 /* interfaceTableSize_ */))
val interfaceTable = load(structGep(typeInfo, 10 /* interfaceTable_ */)) 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 { fun FunctionGenerationContext.getVirtualImpl(receiver: LLVMValueRef, irFunction: IrSimpleFunction): LlvmCallable {
assert(LLVMTypeOf(receiver) == codegen.kObjHeaderPtr) assert(LLVMTypeOf(receiver) == codegen.kObjHeaderPtr)
@@ -426,8 +426,8 @@ internal class CodegenLlvmHelpers(private val generationState: NativeGenerationS
val setCurrentFrameFunction = importRtFunction("SetCurrentFrame") val setCurrentFrameFunction = importRtFunction("SetCurrentFrame")
val checkCurrentFrameFunction = importRtFunction("CheckCurrentFrame") val checkCurrentFrameFunction = importRtFunction("CheckCurrentFrame")
val lookupInterfaceTableRecord = importRtFunction("LookupInterfaceTableRecord") val lookupInterfaceTableRecord = importRtFunction("LookupInterfaceTableRecord")
val isInstanceFunction = importRtFunction("IsInstance") val isSubtypeFunction = importRtFunction("IsSubtype")
val isInstanceOfClassFastFunction = importRtFunction("IsInstanceOfClassFast") val isSubclassFastFunction = importRtFunction("IsSubclassFast")
val throwExceptionFunction = importRtFunction("ThrowException") val throwExceptionFunction = importRtFunction("ThrowException")
val appendToInitalizersTail = importRtFunction("AppendToInitializersTail") val appendToInitalizersTail = importRtFunction("AppendToInitializersTail")
val callInitGlobalPossiblyLock = importRtFunction("CallInitGlobalPossiblyLock") val callInitGlobalPossiblyLock = importRtFunction("CallInitGlobalPossiblyLock")
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol 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.types.getClass
import org.jetbrains.kotlin.ir.util.findAnnotation import org.jetbrains.kotlin.ir.util.findAnnotation
@@ -65,6 +66,7 @@ internal enum class IntrinsicType {
IDENTITY, IDENTITY,
IMMUTABLE_BLOB, IMMUTABLE_BLOB,
INIT_INSTANCE, INIT_INSTANCE,
IS_SUBTYPE,
IS_EXPERIMENTAL_MM, IS_EXPERIMENTAL_MM,
THE_UNIT_INSTANCE, THE_UNIT_INSTANCE,
// Enums // Enums
@@ -247,6 +249,7 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv
IntrinsicType.INTEROP_WRITE_PRIMITIVE -> emitWritePrimitive(callSite, args) IntrinsicType.INTEROP_WRITE_PRIMITIVE -> emitWritePrimitive(callSite, args)
IntrinsicType.INTEROP_GET_POINTER_SIZE -> emitGetPointerSize() IntrinsicType.INTEROP_GET_POINTER_SIZE -> emitGetPointerSize()
IntrinsicType.CREATE_UNINITIALIZED_INSTANCE -> emitCreateUninitializedInstance(callSite, resultSlot) 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_TO_LONG -> emitNativePtrToLong(callSite, args)
IntrinsicType.INTEROP_NATIVE_PTR_PLUS_LONG -> emitNativePtrPlusLong(args) IntrinsicType.INTEROP_NATIVE_PTR_PLUS_LONG -> emitNativePtrPlusLong(args)
IntrinsicType.INTEROP_GET_NATIVE_NULL_PTR -> emitGetNativeNullPtr() 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) return allocInstance(enumIrClass, environment.calculateLifetime(callSite), resultSlot)
} }
private fun FunctionGenerationContext.emitIsSubtype(callSite: IrCall, args: List<LLVMValueRef>) =
with(VirtualTablesLookup) {
checkIsSubtype(
objTypeInfo = bitcast(pointerType(llvm.kTypeInfo), args.single()),
dstClass = callSite.getTypeArgument(0)!!.classOrNull!!.owner
)
}
private fun FunctionGenerationContext.emitGetPointerSize(): LLVMValueRef = private fun FunctionGenerationContext.emitGetPointerSize(): LLVMValueRef =
llvm.int32(LLVMPointerSize(codegen.llvmTargetData)) llvm.int32(LLVMPointerSize(codegen.llvmTargetData))
@@ -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()) { if (dstClass.defaultType.isObjCObjectType()) {
return genInstanceOfObjC(obj, dstClass) genInstanceOfObjC(obj, dstClass)
} } else with(VirtualTablesLookup) {
checkIsSubtype(
val srcObjInfoPtr = functionGenerationContext.bitcast(codegen.kObjHeaderPtr, obj) objTypeInfo = loadTypeInfo(bitcast(codegen.kObjHeaderPtr, obj)),
dstClass
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))
}
}
} }
} }
@@ -1361,8 +1361,8 @@ internal object DevirtualizationAnalysis {
fun devirtualize(irModule: IrModuleFragment, context: Context, externalModulesDFG: ExternalModulesDFG, fun devirtualize(irModule: IrModuleFragment, context: Context, externalModulesDFG: ExternalModulesDFG,
devirtualizedCallSites: Map<IrCall, DevirtualizedCallSite>) { devirtualizedCallSites: Map<IrCall, DevirtualizedCallSite>) {
val symbols = context.ir.symbols val symbols = context.ir.symbols
val irBuiltIns = context.irBuiltIns
val nativePtrEqualityOperatorSymbol = symbols.areEqualByValue[PrimitiveBinaryType.POINTER]!! val nativePtrEqualityOperatorSymbol = symbols.areEqualByValue[PrimitiveBinaryType.POINTER]!!
val isSubtype = symbols.isSubtype
val optimize = context.shouldOptimize() val optimize = context.shouldOptimize()
fun DataFlowIR.Type.resolved(): DataFlowIR.Type.Declared { fun DataFlowIR.Type.resolved(): DataFlowIR.Type.Declared {
@@ -1582,14 +1582,9 @@ internal object DevirtualizationAnalysis {
} }
} else { } else {
val receiverType = actualCallee.irFunction!!.parentAsClass val receiverType = actualCallee.irFunction!!.parentAsClass
IrTypeOperatorCallImpl( irCall(isSubtype, listOf(receiverType.defaultType)).apply {
startOffset = startOffset, putValueArgument(0, irGet(typeInfo))
endOffset = endOffset, }
type = irBuiltIns.booleanType,
operator = IrTypeOperator.INSTANCEOF,
typeOperand = receiverType.defaultType,
argument = irGet(receiver)
)
} }
} }
IrBranchImpl( IrBranchImpl(
@@ -53,8 +53,8 @@ touchFunction(FreezeSubgraph)
touchFunction(CheckGlobalsAccessible) touchFunction(CheckGlobalsAccessible)
touchFunction(LookupInterfaceTableRecord) touchFunction(LookupInterfaceTableRecord)
touchFunction(IsInstance) touchFunction(IsSubtype)
touchFunction(IsInstanceOfClassFast) touchFunction(IsSubclassFast)
touchFunction(ThrowException) touchFunction(ThrowException)
touchFunction(Kotlin_getExceptionObject) touchFunction(Kotlin_getExceptionObject)
+5 -4
View File
@@ -12,6 +12,10 @@ KBoolean IsInstance(const ObjHeader* obj, const TypeInfo* type_info) {
// We assume null check is handled by caller. // We assume null check is handled by caller.
RuntimeAssert(obj != nullptr, "must not be null"); RuntimeAssert(obj != nullptr, "must not be null");
const TypeInfo* obj_type_info = obj->type_info(); 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 it is an interface - check in list of implemented interfaces.
if ((type_info->flags_ & TF_INTERFACE) != 0) { if ((type_info->flags_ & TF_INTERFACE) != 0) {
for (int i = 0; i < obj_type_info->implementedInterfacesCount_; ++i) { 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; return obj_type_info != nullptr;
} }
KBoolean IsInstanceOfClassFast(const ObjHeader* obj, int32_t lo, int32_t hi) { KBoolean IsSubclassFast(const TypeInfo* obj_type_info, 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();
// Super type's interval should contain our interval. // Super type's interval should contain our interval.
return obj_type_info->classId_ >= lo && obj_type_info->classId_ <= hi; return obj_type_info->classId_ >= lo && obj_type_info->classId_ <= hi;
} }
+2 -1
View File
@@ -73,7 +73,8 @@ extern const TypeInfo* theCleanerImplTypeInfo;
extern const TypeInfo* theRegularWeakReferenceImplTypeInfo; extern const TypeInfo* theRegularWeakReferenceImplTypeInfo;
KBoolean IsInstance(const ObjHeader* obj, const TypeInfo* type_info) RUNTIME_PURE; 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); void CheckCast(const ObjHeader* obj, const TypeInfo* type_info);
KBoolean IsArray(KConstRef obj) RUNTIME_PURE; KBoolean IsArray(KConstRef obj) RUNTIME_PURE;
bool IsSubInterface(const TypeInfo* thiz, const TypeInfo* other) RUNTIME_PURE; bool IsSubInterface(const TypeInfo* thiz, const TypeInfo* other) RUNTIME_PURE;
@@ -52,6 +52,7 @@ internal class IntrinsicType {
const val IDENTITY = "IDENTITY" const val IDENTITY = "IDENTITY"
const val IMMUTABLE_BLOB = "IMMUTABLE_BLOB" const val IMMUTABLE_BLOB = "IMMUTABLE_BLOB"
const val INIT_INSTANCE = "INIT_INSTANCE" const val INIT_INSTANCE = "INIT_INSTANCE"
const val IS_SUBTYPE = "IS_SUBTYPE"
const val IS_EXPERIMENTAL_MM = "IS_EXPERIMENTAL_MM" const val IS_EXPERIMENTAL_MM = "IS_EXPERIMENTAL_MM"
const val THE_UNIT_INSTANCE = "THE_UNIT_INSTANCE" const val THE_UNIT_INSTANCE = "THE_UNIT_INSTANCE"
@@ -226,6 +226,10 @@ internal external fun <T> createUninitializedInstance(): T
@TypedIntrinsic(IntrinsicType.INIT_INSTANCE) @TypedIntrinsic(IntrinsicType.INIT_INSTANCE)
internal external fun initInstance(thiz: Any, constructorCall: Any): Unit internal external fun initInstance(thiz: Any, constructorCall: Any): Unit
@PublishedApi
@TypedIntrinsic(IntrinsicType.IS_SUBTYPE)
internal external fun <T> isSubtype(objTypeInfo: NativePtr): Boolean
@PublishedApi @PublishedApi
internal fun checkProgressionStep(step: Int) = internal fun checkProgressionStep(step: Int) =
if (step > 0) step else throw IllegalArgumentException("Step must be positive, was: $step.") if (step > 0) step else throw IllegalArgumentException("Step must be positive, was: $step.")