Dispatch Any methods called on interface via vtable instead of itable (#1753)

This commit is contained in:
Vsevolod Tolstopyatov
2018-07-04 10:10:58 +03:00
committed by SvyatoslavScherbina
parent 627d28db8a
commit a8d6113308
3 changed files with 56 additions and 14 deletions
@@ -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,
+5
View File
@@ -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()
}