Dispatch Any methods called on interface via vtable instead of itable (#1753)
This commit is contained in:
committed by
SvyatoslavScherbina
parent
627d28db8a
commit
a8d6113308
+33
-14
@@ -19,16 +19,20 @@ package org.jetbrains.kotlin.backend.konan.llvm
|
||||
|
||||
import kotlinx.cinterop.*
|
||||
import llvm.*
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.*
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.*
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.stdlibModule
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.backend.konan.irasdescriptors.*
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.objc.ObjCDataGenerator
|
||||
import org.jetbrains.kotlin.ir.util.constructors
|
||||
import org.jetbrains.kotlin.ir.util.defaultType
|
||||
import org.jetbrains.kotlin.konan.target.Family
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.jetbrains.kotlin.backend.konan.irasdescriptors.ClassConstructorDescriptor
|
||||
import org.jetbrains.kotlin.backend.konan.irasdescriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.backend.konan.irasdescriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.backend.konan.irasdescriptors.SimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.objc.*
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.konan.target.*
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.resolveFakeOverride
|
||||
|
||||
internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||
|
||||
@@ -618,8 +622,6 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
||||
fun lookupVirtualImpl(receiver: LLVMValueRef, descriptor: FunctionDescriptor): LLVMValueRef {
|
||||
assert(LLVMTypeOf(receiver) == codegen.kObjHeaderPtr)
|
||||
|
||||
val owner = descriptor.containingDeclaration as ClassDescriptor
|
||||
|
||||
val typeInfoPtr: LLVMValueRef = if (descriptor.getObjCMethodInfo() != null) {
|
||||
call(context.llvm.getObjCKotlinTypeInfo, listOf(receiver))
|
||||
} else {
|
||||
@@ -629,14 +631,21 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
||||
load(typeInfoPtrPtr)
|
||||
}
|
||||
|
||||
assert (typeInfoPtr.type == codegen.kTypeInfoPtr) { LLVMPrintTypeToString(typeInfoPtr.type)!!.toKString() }
|
||||
assert(typeInfoPtr.type == codegen.kTypeInfoPtr) { LLVMPrintTypeToString(typeInfoPtr.type)!!.toKString() }
|
||||
|
||||
/*
|
||||
* Resolve owner of the call with special handling of Any methods:
|
||||
* if toString/eq/hc is invoked on an interface instance, we resolve
|
||||
* owner as Any and dispatch it via vtable
|
||||
*/
|
||||
val anyMethod = (descriptor as SimpleFunctionDescriptor).findOverriddenMethodOfAny()
|
||||
val owner = (anyMethod ?: descriptor).containingDeclaration as ClassDescriptor
|
||||
|
||||
val llvmMethod = if (!owner.isInterface) {
|
||||
// If this is a virtual method of the class - we can call via vtable.
|
||||
val index = context.getVtableBuilder(owner).vtableIndex(descriptor as SimpleFunctionDescriptor)
|
||||
|
||||
val index = context.getVtableBuilder(owner).vtableIndex(anyMethod ?: descriptor)
|
||||
val vtablePlace = gep(typeInfoPtr, Int32(1).llvm) // typeInfoPtr + 1
|
||||
val vtable = bitcast(kInt8PtrPtr, vtablePlace)
|
||||
|
||||
val slot = gep(vtable, Int32(index).llvm)
|
||||
load(slot)
|
||||
} else {
|
||||
@@ -652,6 +661,16 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
||||
return bitcast(functionPtrType, llvmMethod) // Cast method address to the type
|
||||
}
|
||||
|
||||
private fun IrSimpleFunction.findOverriddenMethodOfAny(): IrSimpleFunction? {
|
||||
if (modality == Modality.ABSTRACT) return null
|
||||
val resolved = resolveFakeOverride()
|
||||
if ((resolved.parent as ClassDescriptor).isAny()) {
|
||||
return resolved
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun getObjectValue(
|
||||
descriptor: ClassDescriptor,
|
||||
shared: Boolean,
|
||||
|
||||
@@ -1478,6 +1478,11 @@ task vtable1(type: RunKonanTest) {
|
||||
source = "datagen/rtti/vtable1.kt"
|
||||
}
|
||||
|
||||
task vtable_any(type: RunKonanTest) {
|
||||
source = "datagen/rtti/vtable_any.kt"
|
||||
goldValue = "[1]\n[2]\n[3]\n[4]\n"
|
||||
}
|
||||
|
||||
task strdedup1(type: RunKonanTest) {
|
||||
goldValue = "true\ntrue\n"
|
||||
source = "datagen/literals/strdedup1.kt"
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package datagen.rtti.vtable_any
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
@Test
|
||||
fun runTest() {
|
||||
// Try to trick devirtualizer
|
||||
println(anyMethods(arrayListOf("1")))
|
||||
println(anyMethods(mapOf("2" to 1).keys))
|
||||
println(anyMethods(mapOf("1" to 3).values))
|
||||
println(anyMethods(setOf("4")))
|
||||
}
|
||||
|
||||
fun anyMethods(iterable: Iterable<*>): String {
|
||||
assert(iterable.equals(iterable))
|
||||
assert(iterable.hashCode() != 0)
|
||||
return iterable.toString()
|
||||
}
|
||||
Reference in New Issue
Block a user