[K/N] Added isSubtype intrinsic
This commit is contained in:
+2
@@ -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) }
|
||||
|
||||
|
||||
+16
-2
@@ -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)
|
||||
|
||||
|
||||
+2
-2
@@ -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")
|
||||
|
||||
+11
@@ -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))
|
||||
|
||||
|
||||
+7
-23
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-9
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.")
|
||||
|
||||
Reference in New Issue
Block a user