[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 isSubtype = internalFunction("isSubtype")
val println = irBuiltIns.findFunctions(Name.identifier("println"), "kotlin", "io")
.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.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)
@@ -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")
@@ -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<LLVMValueRef>) =
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))
@@ -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
)
}
}
@@ -1361,8 +1361,8 @@ internal object DevirtualizationAnalysis {
fun devirtualize(irModule: IrModuleFragment, context: Context, externalModulesDFG: ExternalModulesDFG,
devirtualizedCallSites: Map<IrCall, DevirtualizedCallSite>) {
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(
@@ -53,8 +53,8 @@ touchFunction(FreezeSubgraph)
touchFunction(CheckGlobalsAccessible)
touchFunction(LookupInterfaceTableRecord)
touchFunction(IsInstance)
touchFunction(IsInstanceOfClassFast)
touchFunction(IsSubtype)
touchFunction(IsSubclassFast)
touchFunction(ThrowException)
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.
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;
}
+2 -1
View File
@@ -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;
@@ -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"
@@ -226,6 +226,10 @@ internal external fun <T> createUninitializedInstance(): T
@TypedIntrinsic(IntrinsicType.INIT_INSTANCE)
internal external fun initInstance(thiz: Any, constructorCall: Any): Unit
@PublishedApi
@TypedIntrinsic(IntrinsicType.IS_SUBTYPE)
internal external fun <T> isSubtype(objTypeInfo: NativePtr): Boolean
@PublishedApi
internal fun checkProgressionStep(step: Int) =
if (step > 0) step else throw IllegalArgumentException("Step must be positive, was: $step.")