Implemented devirtualization of multimorphic callsites
This commit is contained in:
-2
@@ -78,8 +78,6 @@ private val intrinsicAnnotation = FqName("konan.internal.Intrinsic")
|
||||
internal val CallableDescriptor.isIntrinsic: Boolean
|
||||
get() = this.annotations.findAnnotation(intrinsicAnnotation) != null
|
||||
|
||||
internal fun FunctionDescriptor.externalOrIntrinsic() = isExternal || isIntrinsic || (this is IrBuiltinOperatorDescriptorBase)
|
||||
|
||||
private val intrinsicTypes = setOf(
|
||||
"kotlin.Boolean", "kotlin.Char",
|
||||
"kotlin.Byte", "kotlin.Short",
|
||||
|
||||
+3
@@ -132,6 +132,9 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable): Sym
|
||||
override val ThrowTypeCastException = symbolTable.referenceSimpleFunction(
|
||||
context.getInternalFunctions("ThrowTypeCastException").single())
|
||||
|
||||
val throwInvalidReceiverTypeException = symbolTable.referenceSimpleFunction(
|
||||
context.getInternalFunctions("ThrowInvalidReceiverTypeException").single())
|
||||
|
||||
override val ThrowUninitializedPropertyAccessException = symbolTable.referenceSimpleFunction(
|
||||
context.getInternalFunctions("ThrowUninitializedPropertyAccessException").single()
|
||||
)
|
||||
|
||||
+27
@@ -24,10 +24,13 @@ import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrClassReference
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallWithIndexedArgumentsBase
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrExpressionBase
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrTerminalDeclarationReferenceBase
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFileSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
|
||||
@@ -209,4 +212,28 @@ internal class IrPrivateFunctionCallImpl(startOffset: Int,
|
||||
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
|
||||
visitor.visitCall(this, data)
|
||||
|
||||
}
|
||||
|
||||
internal interface IrPrivateClassReference : IrClassReference {
|
||||
val moduleDescriptor: ModuleDescriptor
|
||||
val totalClasses: Int
|
||||
val classIndex: Int
|
||||
}
|
||||
|
||||
internal class IrPrivateClassReferenceImpl(startOffset: Int,
|
||||
endOffset: Int,
|
||||
type: KotlinType,
|
||||
symbol: IrClassifierSymbol,
|
||||
override val classType: KotlinType,
|
||||
override val moduleDescriptor: ModuleDescriptor,
|
||||
override val totalClasses: Int,
|
||||
override val classIndex: Int
|
||||
) : IrPrivateClassReference,
|
||||
IrTerminalDeclarationReferenceBase<IrClassifierSymbol, ClassifierDescriptor>(
|
||||
startOffset, endOffset, type,
|
||||
symbol, symbol.descriptor
|
||||
)
|
||||
{
|
||||
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
|
||||
visitor.visitClassReference(this, data)
|
||||
}
|
||||
+2
@@ -274,3 +274,5 @@ internal val ClassDescriptor.typeInfoHasVtableAttached: Boolean
|
||||
get() = !this.isAbstract() && !this.isExternalObjCClass()
|
||||
|
||||
internal val ModuleDescriptor.privateFunctionsTableSymbolName get() = "private_functions_${name.asString()}"
|
||||
|
||||
internal val ModuleDescriptor.privateClassesTableSymbolName get() = "private_classes_${name.asString()}"
|
||||
|
||||
-1
@@ -48,7 +48,6 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||
|
||||
private fun countParams(fn: FunctionDescriptor) = LLVMCountParams(fn.llvmFunction)
|
||||
|
||||
fun functionLlvmValue(descriptor: FunctionDescriptor) = descriptor.llvmFunction
|
||||
fun functionEntryPointAddress(descriptor: FunctionDescriptor) = descriptor.entryPointAddress.llvm
|
||||
fun functionHash(descriptor: FunctionDescriptor): LLVMValueRef = descriptor.functionName.localHash.llvm
|
||||
|
||||
|
||||
+26
-2
@@ -96,7 +96,14 @@ internal fun emitLLVM(context: Context) {
|
||||
val privateFunctions = moduleDFG!!.symbolTable.getPrivateFunctionsTableForExport()
|
||||
|
||||
codegenVisitor.codegen.staticData.placeGlobalConstArray(irModule.descriptor.privateFunctionsTableSymbolName, int8TypePtr,
|
||||
privateFunctions.map { constPointer(codegenVisitor.codegen.functionLlvmValue(it)).bitcast(int8TypePtr) },
|
||||
privateFunctions.map { constPointer(codegenVisitor.codegen.llvmFunction(it)).bitcast(int8TypePtr) },
|
||||
isExported = true
|
||||
)
|
||||
|
||||
val privateClasses = moduleDFG!!.symbolTable.getPrivateClassesTableForExport()
|
||||
|
||||
codegenVisitor.codegen.staticData.placeGlobalConstArray(irModule.descriptor.privateClassesTableSymbolName, int8TypePtr,
|
||||
privateClasses.map { constPointer(codegenVisitor.codegen.typeInfoValue(it)).bitcast(int8TypePtr) },
|
||||
isExported = true
|
||||
)
|
||||
}
|
||||
@@ -766,6 +773,8 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
is IrSuspendableExpression ->
|
||||
return evaluateSuspendableExpression (value)
|
||||
is IrSuspensionPoint -> return evaluateSuspensionPoint (value)
|
||||
is IrPrivateClassReference ->
|
||||
return evaluatePrivateClassReference (value)
|
||||
else -> {
|
||||
TODO(ir2string(value))
|
||||
}
|
||||
@@ -1942,6 +1951,21 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private fun evaluatePrivateClassReference(classReference: IrPrivateClassReference): LLVMValueRef {
|
||||
val classesListName = classReference.moduleDescriptor.privateClassesTableSymbolName
|
||||
// LLVM inlines access to this global array (with -opt option on).
|
||||
val functionsList =
|
||||
if (classReference.moduleDescriptor == context.irModule!!.descriptor)
|
||||
LLVMGetNamedGlobal(context.llvmModule, classesListName)
|
||||
else
|
||||
codegen.importGlobal(classesListName, LLVMArrayType(int8TypePtr, classReference.totalClasses)!!, classReference.moduleDescriptor.llvmSymbolOrigin)
|
||||
val classIndex = Int32(classReference.classIndex).llvm
|
||||
val classPlacePtr = LLVMBuildGEP(functionGenerationContext.builder, functionsList, cValuesOf(kImmZero, classIndex), 2, "")!!
|
||||
return functionGenerationContext.load(classPlacePtr)
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private fun evaluateSimpleFunctionCall(
|
||||
descriptor: FunctionDescriptor, args: List<LLVMValueRef>,
|
||||
resultLifetime: Lifetime, superClass: ClassDescriptor? = null): LLVMValueRef {
|
||||
@@ -2362,7 +2386,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
fun callDirect(descriptor: FunctionDescriptor, args: List<LLVMValueRef>,
|
||||
resultLifetime: Lifetime): LLVMValueRef {
|
||||
val realDescriptor = descriptor.target
|
||||
val llvmFunction = codegen.functionLlvmValue(realDescriptor)
|
||||
val llvmFunction = codegen.llvmFunction(realDescriptor)
|
||||
return call(descriptor, llvmFunction, args, resultLifetime)
|
||||
}
|
||||
|
||||
|
||||
+10
-12
@@ -207,18 +207,16 @@ private fun maskParameterSymbol(function: IrFunction, number: Int) =
|
||||
|
||||
private fun markerParameterDescriptor(descriptor: FunctionDescriptor) = descriptor.valueParameters.single { it.name == kConstructorMarkerName }
|
||||
|
||||
private fun nullConst(expression: IrElement, type: KotlinType): IrExpression? {
|
||||
when {
|
||||
KotlinBuiltIns.isFloat(type) -> return IrConstImpl.float (expression.startOffset, expression.endOffset, type, 0.0F)
|
||||
KotlinBuiltIns.isDouble(type) -> return IrConstImpl.double (expression.startOffset, expression.endOffset, type, 0.0)
|
||||
KotlinBuiltIns.isBoolean(type) -> return IrConstImpl.boolean (expression.startOffset, expression.endOffset, type, false)
|
||||
KotlinBuiltIns.isByte(type) -> return IrConstImpl.byte (expression.startOffset, expression.endOffset, type, 0)
|
||||
KotlinBuiltIns.isChar(type) -> return IrConstImpl.char (expression.startOffset, expression.endOffset, type, 0.toChar())
|
||||
KotlinBuiltIns.isShort(type) -> return IrConstImpl.short (expression.startOffset, expression.endOffset, type, 0)
|
||||
KotlinBuiltIns.isInt(type) -> return IrConstImpl.int (expression.startOffset, expression.endOffset, type, 0)
|
||||
KotlinBuiltIns.isLong(type) -> return IrConstImpl.long (expression.startOffset, expression.endOffset, type, 0)
|
||||
else -> return IrConstImpl.constNull (expression.startOffset, expression.endOffset, type.builtIns.nullableNothingType)
|
||||
}
|
||||
fun nullConst(expression: IrElement, type: KotlinType) = when {
|
||||
KotlinBuiltIns.isFloat(type) -> IrConstImpl.float (expression.startOffset, expression.endOffset, type, 0.0F)
|
||||
KotlinBuiltIns.isDouble(type) -> IrConstImpl.double (expression.startOffset, expression.endOffset, type, 0.0)
|
||||
KotlinBuiltIns.isBoolean(type) -> IrConstImpl.boolean (expression.startOffset, expression.endOffset, type, false)
|
||||
KotlinBuiltIns.isByte(type) -> IrConstImpl.byte (expression.startOffset, expression.endOffset, type, 0)
|
||||
KotlinBuiltIns.isChar(type) -> IrConstImpl.char (expression.startOffset, expression.endOffset, type, 0.toChar())
|
||||
KotlinBuiltIns.isShort(type) -> IrConstImpl.short (expression.startOffset, expression.endOffset, type, 0)
|
||||
KotlinBuiltIns.isInt(type) -> IrConstImpl.int (expression.startOffset, expression.endOffset, type, 0)
|
||||
KotlinBuiltIns.isLong(type) -> IrConstImpl.long (expression.startOffset, expression.endOffset, type, 0)
|
||||
else -> IrConstImpl.constNull (expression.startOffset, expression.endOffset, type.builtIns.nullableNothingType)
|
||||
}
|
||||
|
||||
class DefaultParameterInjector constructor(val context: CommonBackendContext): BodyLoweringPass {
|
||||
|
||||
+22
-8
@@ -204,15 +204,16 @@ internal object DFGSerializer {
|
||||
}
|
||||
}
|
||||
|
||||
class DeclaredType(val isFinal: Boolean, val isAbstract: Boolean, val superTypes: IntArray,
|
||||
class DeclaredType(val isFinal: Boolean, val isAbstract: Boolean, val index: Int, val superTypes: IntArray,
|
||||
val vtable: IntArray, val itable: Array<ItableSlot>) {
|
||||
|
||||
constructor(data: ArraySlice) : this(data.readBoolean(), data.readBoolean(), data.readIntArray(),
|
||||
constructor(data: ArraySlice) : this(data.readBoolean(), data.readBoolean(), data.readInt(), data.readIntArray(),
|
||||
data.readIntArray(), data.readArray { ItableSlot(this) })
|
||||
|
||||
fun write(result: ArraySlice) {
|
||||
result.writeBoolean(isFinal)
|
||||
result.writeBoolean(isAbstract)
|
||||
result.writeInt(index)
|
||||
result.writeIntArray(superTypes)
|
||||
result.writeIntArray(vtable)
|
||||
result.writeArray(itable) { it.write(this) }
|
||||
@@ -699,7 +700,7 @@ internal object DFGSerializer {
|
||||
println("TYPES: ${typeMap.size}, " +
|
||||
"FUNCTIONS: ${functionSymbolMap.size}, " +
|
||||
"PRIVATE FUNCTIONS: ${functionSymbolMap.keys.count { it is DataFlowIR.FunctionSymbol.Private }}, " +
|
||||
"FUNCTION TABLE SIZE: ${symbolTable.couldBeCalledVirtuallyIndex}"
|
||||
"FUNCTION TABLE SIZE: ${symbolTable.privateFunIndex}"
|
||||
)
|
||||
}
|
||||
val types = typeMap.entries
|
||||
@@ -709,6 +710,7 @@ internal object DFGSerializer {
|
||||
DeclaredType(
|
||||
type.isFinal,
|
||||
type.isAbstract,
|
||||
type.symbolTableIndex,
|
||||
type.superTypes.map { typeMap[it]!! }.toIntArray(),
|
||||
type.vtable.map { functionSymbolMap[it]!! }.toIntArray(),
|
||||
type.itable.map { (hash, symbol) -> ItableSlot(hash, functionSymbolMap[symbol]!!) }.toTypedArray()
|
||||
@@ -865,18 +867,26 @@ internal object DFGSerializer {
|
||||
when {
|
||||
external != null -> DataFlowIR.Type.External(external.hash, external.name)
|
||||
|
||||
public != null ->
|
||||
public != null -> {
|
||||
val symbolTableIndex = public.intestines.index
|
||||
if (symbolTableIndex >= 0)
|
||||
++module.numberOfClasses
|
||||
DataFlowIR.Type.Public(public.hash, public.intestines.isFinal,
|
||||
public.intestines.isAbstract, public.name).also {
|
||||
public.intestines.isAbstract, module, symbolTableIndex, public.name).also {
|
||||
publicTypesMap.put(it.hash, it)
|
||||
allTypes += it
|
||||
}
|
||||
}
|
||||
|
||||
else ->
|
||||
DataFlowIR.Type.Private(privateTypeIndex++, private!!.intestines.isFinal,
|
||||
private.intestines.isAbstract, private.name).also {
|
||||
else -> {
|
||||
val symbolTableIndex = private!!.intestines.index
|
||||
if (symbolTableIndex >= 0)
|
||||
++module.numberOfClasses
|
||||
DataFlowIR.Type.Private(privateTypeIndex++, private.intestines.isFinal,
|
||||
private.intestines.isAbstract, module, symbolTableIndex, private.name).also {
|
||||
allTypes += it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1120,6 +1130,10 @@ internal object DFGSerializer {
|
||||
println(" $it")
|
||||
}
|
||||
}
|
||||
|
||||
functions.forEach {
|
||||
println(it.key)
|
||||
}
|
||||
}
|
||||
|
||||
return ExternalModulesDFG(allTypes, publicTypesMap, publicFunctionsMap, functions)
|
||||
|
||||
+33
-17
@@ -18,10 +18,10 @@ package org.jetbrains.kotlin.backend.konan.optimizations
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.descriptors.allParameters
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.externalOrIntrinsic
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
||||
import org.jetbrains.kotlin.backend.konan.isObjCClass
|
||||
import org.jetbrains.kotlin.backend.konan.isValueType
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.functionName
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.isExported
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.localHash
|
||||
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrField
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltinOperatorDescriptorBase
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
@@ -50,7 +51,7 @@ internal object DataFlowIR {
|
||||
|
||||
abstract class Type {
|
||||
// Special marker type forbidding devirtualization on its instances.
|
||||
object Virtual : Declared(false, true)
|
||||
object Virtual : Declared(false, true, null, -1)
|
||||
|
||||
class External(val hash: Long, val name: String? = null) : Type() {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
@@ -69,13 +70,14 @@ internal object DataFlowIR {
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Declared(val isFinal: Boolean, val isAbstract: Boolean) : Type() {
|
||||
abstract class Declared(val isFinal: Boolean, val isAbstract: Boolean, val module: Module?, val symbolTableIndex: Int) : Type() {
|
||||
val superTypes = mutableListOf<Type>()
|
||||
val vtable = mutableListOf<FunctionSymbol>()
|
||||
val itable = mutableMapOf<Long, FunctionSymbol>()
|
||||
}
|
||||
|
||||
class Public(val hash: Long, isFinal: Boolean, isAbstract: Boolean, val name: String? = null) : Declared(isFinal, isAbstract) {
|
||||
class Public(val hash: Long, isFinal: Boolean, isAbstract: Boolean, module: Module, symbolTableIndex: Int, val name: String? = null)
|
||||
: Declared(isFinal, isAbstract, module, symbolTableIndex) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is Public) return false
|
||||
@@ -88,11 +90,12 @@ internal object DataFlowIR {
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "PublicType(hash='$hash', name='$name')"
|
||||
return "PublicType(hash='$hash', symbolTableIndex='$symbolTableIndex', name='$name')"
|
||||
}
|
||||
}
|
||||
|
||||
class Private(val index: Int, isFinal: Boolean, isAbstract: Boolean, val name: String? = null) : Declared(isFinal, isAbstract) {
|
||||
class Private(val index: Int, isFinal: Boolean, isAbstract: Boolean, module: Module, symbolTableIndex: Int, val name: String? = null)
|
||||
: Declared(isFinal, isAbstract, module, symbolTableIndex) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is Private) return false
|
||||
@@ -105,13 +108,14 @@ internal object DataFlowIR {
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "PrivateType(index=$index, name='$name')"
|
||||
return "PrivateType(index=$index, symbolTableIndex='$symbolTableIndex', name='$name')"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Module(val descriptor: ModuleDescriptor) {
|
||||
var numberOfFunctions = 0
|
||||
var numberOfClasses = 0
|
||||
}
|
||||
|
||||
abstract class FunctionSymbol(val numberOfParameters: Int, val isGlobalInitializer: Boolean, val name: String?) {
|
||||
@@ -154,7 +158,7 @@ internal object DataFlowIR {
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "PublicFunction(hash='$hash', name='$name')"
|
||||
return "PublicFunction(hash='$hash', symbolTableIndex='$symbolTableIndex', name='$name')"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +177,7 @@ internal object DataFlowIR {
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "PrivateFunction(index=$index, name='$name')"
|
||||
return "PrivateFunction(index=$index, symbolTableIndex='$symbolTableIndex', name='$name')"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -435,7 +439,6 @@ internal object DataFlowIR {
|
||||
|
||||
var privateTypeIndex = 0
|
||||
var privateFunIndex = 0
|
||||
var couldBeCalledVirtuallyIndex = 0
|
||||
|
||||
init {
|
||||
irModule.accept(object : IrElementVisitorVoid {
|
||||
@@ -474,10 +477,12 @@ internal object DataFlowIR {
|
||||
|
||||
val isFinal = descriptor.isFinal()
|
||||
val isAbstract = descriptor.isAbstract()
|
||||
val placeToClassTable = !descriptor.defaultType.isValueType()
|
||||
val symbolTableIndex = if (placeToClassTable) module.numberOfClasses++ else -1
|
||||
val type = if (descriptor.isExported())
|
||||
Type.Public(name.localHash.value, isFinal, isAbstract, takeName { name })
|
||||
else
|
||||
Type.Private(privateTypeIndex++, isFinal, isAbstract, takeName { name })
|
||||
Type.Public(name.localHash.value, isFinal, isAbstract, module, symbolTableIndex, takeName { name })
|
||||
else
|
||||
Type.Private(privateTypeIndex++, isFinal, isAbstract, module, symbolTableIndex, takeName { name })
|
||||
if (!descriptor.isInterface) {
|
||||
val vtableBuilder = context.getVtableBuilder(descriptor)
|
||||
type.vtable += vtableBuilder.vtableEntries.map { mapFunction(it.getImplementation(context)) }
|
||||
@@ -528,7 +533,7 @@ internal object DataFlowIR {
|
||||
is FunctionDescriptor -> {
|
||||
val name = if (it.isExported()) it.symbolName else it.internalName
|
||||
val numberOfParameters = it.allParameters.size + if (it.isSuspend) 1 else 0
|
||||
if (it.module != irModule.descriptor || it.externalOrIntrinsic()) {
|
||||
if (it.module != irModule.descriptor || it.isExternal || (it is IrBuiltinOperatorDescriptorBase)) {
|
||||
val escapesAnnotation = it.annotations.findAnnotation(FQ_NAME_ESCAPES)
|
||||
val pointsToAnnotation = it.annotations.findAnnotation(FQ_NAME_POINTS_TO)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@@ -543,9 +548,7 @@ internal object DataFlowIR {
|
||||
val placeToFunctionsTable = !isAbstract && it !is ConstructorDescriptor && classDescriptor != null
|
||||
&& classDescriptor.kind != ClassKind.ANNOTATION_CLASS
|
||||
&& (it.isOverridableOrOverrides || it.name.asString().contains("<bridge-") || !classDescriptor.isFinal())
|
||||
if (placeToFunctionsTable)
|
||||
++module.numberOfFunctions
|
||||
val symbolTableIndex = if (!placeToFunctionsTable) -1 else couldBeCalledVirtuallyIndex++
|
||||
val symbolTableIndex = if (placeToFunctionsTable) module.numberOfFunctions++ else -1
|
||||
if (it.isExported())
|
||||
FunctionSymbol.Public(name.localHash.value, numberOfParameters, module, symbolTableIndex, false, takeName { name })
|
||||
else
|
||||
@@ -571,5 +574,18 @@ internal object DataFlowIR {
|
||||
}
|
||||
.map { it.key as FunctionDescriptor }
|
||||
.toList()
|
||||
|
||||
fun getPrivateClassesTableForExport() =
|
||||
classMap
|
||||
.asSequence()
|
||||
.filter { it.value.let { it is DataFlowIR.Type.Declared && it.symbolTableIndex >= 0 } }
|
||||
.sortedBy { (it.value as DataFlowIR.Type.Declared).symbolTableIndex }
|
||||
.apply {
|
||||
forEachIndexed { index, entry ->
|
||||
assert((entry.value as DataFlowIR.Type.Declared).symbolTableIndex == index) { "Inconsistent class table" }
|
||||
}
|
||||
}
|
||||
.map { it.key }
|
||||
.toList()
|
||||
}
|
||||
}
|
||||
+118
-17
@@ -21,15 +21,26 @@ import org.jetbrains.kotlin.backend.common.ir.ir2stringWhole
|
||||
import org.jetbrains.kotlin.backend.common.pop
|
||||
import org.jetbrains.kotlin.backend.common.push
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.irBlock
|
||||
import org.jetbrains.kotlin.backend.konan.*
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
||||
import org.jetbrains.kotlin.backend.konan.ir.IrPrivateClassReferenceImpl
|
||||
import org.jetbrains.kotlin.backend.konan.ir.IrPrivateFunctionCallImpl
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.*
|
||||
import org.jetbrains.kotlin.backend.konan.lower.nullConst
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.getErasedTypeClass
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.getValueArgument
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrBranchImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrWhenImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrClassSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import java.util.*
|
||||
|
||||
// Devirtualization analysis is performed using Variable Type Analysis algorithm.
|
||||
@@ -873,37 +884,127 @@ internal object Devirtualization {
|
||||
|
||||
private fun devirtualize(irModule: IrModuleFragment, context: Context,
|
||||
devirtualizedCallSites: Map<IrCall, DevirtualizedCallSite>) {
|
||||
val nativePtrType = context.builtIns.nativePtr.defaultType
|
||||
val nativePtrEqualityOperatorSymbol = context.ir.symbols.areEqualByValue.single { it.descriptor.valueParameters[0].type == nativePtrType }
|
||||
|
||||
irModule.transformChildrenVoid(object: IrElementTransformerVoidWithContext() {
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
val devirtualizedCallSite = devirtualizedCallSites[expression]
|
||||
val actualCallee = devirtualizedCallSite?.possibleCallees?.singleOrNull()?.callee as? DataFlowIR.FunctionSymbol.Declared
|
||||
?: return expression
|
||||
val possibleCallees = devirtualizedCallSite?.possibleCallees
|
||||
if (possibleCallees == null
|
||||
|| possibleCallees.any { it.callee is DataFlowIR.FunctionSymbol.External }
|
||||
|| possibleCallees.any { it.receiverType is DataFlowIR.Type.External })
|
||||
return expression
|
||||
|
||||
val descriptor = expression.descriptor
|
||||
val owner = (descriptor.containingDeclaration as ClassDescriptor)
|
||||
val maxUnfoldFactor = if (owner.isInterface) 3 else 1
|
||||
if (possibleCallees.size > maxUnfoldFactor) {
|
||||
// Callsite too complicated to devirtualize.
|
||||
return expression
|
||||
}
|
||||
|
||||
val startOffset = expression.startOffset
|
||||
val endOffset = expression.endOffset
|
||||
val type = if (descriptor.isSuspend)
|
||||
context.builtIns.nullableAnyType
|
||||
else descriptor.original.returnType!!
|
||||
val irBuilder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, startOffset, endOffset)
|
||||
irBuilder.run {
|
||||
val dispatchReceiver = expression.dispatchReceiver!!
|
||||
return IrPrivateFunctionCallImpl(
|
||||
startOffset,
|
||||
endOffset,
|
||||
expression.type,
|
||||
expression.symbol,
|
||||
expression.descriptor,
|
||||
(expression as? IrCallImpl)?.typeArguments,
|
||||
actualCallee.module.descriptor,
|
||||
actualCallee.module.numberOfFunctions,
|
||||
actualCallee.symbolTableIndex
|
||||
).apply {
|
||||
this.dispatchReceiver = dispatchReceiver
|
||||
this.extensionReceiver = expression.extensionReceiver
|
||||
expression.descriptor.valueParameters.forEach {
|
||||
this.putValueArgument(it.index, expression.getValueArgument(it))
|
||||
return when {
|
||||
possibleCallees.isEmpty() -> irBlock(expression) {
|
||||
+irCall(context.ir.symbols.throwInvalidReceiverTypeException).apply {
|
||||
putValueArgument(0, irCall(context.ir.symbols.kClassImplConstructor, listOf(dispatchReceiver.type)).apply {
|
||||
putValueArgument(0, irCall(context.ir.symbols.getObjectTypeInfo).apply {
|
||||
putValueArgument(0, dispatchReceiver)
|
||||
})
|
||||
})
|
||||
}
|
||||
+nullConst(expression, type)
|
||||
}
|
||||
|
||||
possibleCallees.size == 1 -> { // Monomorphic callsite.
|
||||
val actualCallee = possibleCallees[0].callee as DataFlowIR.FunctionSymbol.Declared
|
||||
irDevirtualizedCall(expression, type, actualCallee).apply {
|
||||
this.dispatchReceiver = dispatchReceiver
|
||||
this.extensionReceiver = expression.extensionReceiver
|
||||
expression.descriptor.valueParameters.forEach {
|
||||
this.putValueArgument(it.index, expression.getValueArgument(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> irBlock(expression) {
|
||||
val receiver = irTemporary(dispatchReceiver, "receiver")
|
||||
val extensionReceiver = expression.extensionReceiver?.let { irTemporary(it, "extensionReceiver") }
|
||||
val parameters = expression.descriptor.valueParameters.associate { it to irTemporary(expression.getValueArgument(it)!!) }
|
||||
val typeInfo = irTemporary(irCall(context.ir.symbols.getObjectTypeInfo).apply {
|
||||
putValueArgument(0, irGet(receiver.symbol))
|
||||
})
|
||||
|
||||
val branches = possibleCallees.mapIndexed { index, devirtualizedCallee ->
|
||||
val actualCallee = devirtualizedCallee.callee as DataFlowIR.FunctionSymbol.Declared
|
||||
val actualReceiverType = devirtualizedCallee.receiverType as DataFlowIR.Type.Declared
|
||||
val expectedTypeInfo = IrPrivateClassReferenceImpl(
|
||||
startOffset,
|
||||
endOffset,
|
||||
nativePtrType,
|
||||
IrClassSymbolImpl(dispatchReceiver.type.getErasedTypeClass()),
|
||||
receiver.type,
|
||||
actualReceiverType.module!!.descriptor,
|
||||
actualReceiverType.module.numberOfClasses,
|
||||
actualReceiverType.symbolTableIndex)
|
||||
val condition =
|
||||
if (index == possibleCallees.size - 1)
|
||||
irTrue()
|
||||
else
|
||||
irCall(nativePtrEqualityOperatorSymbol).apply {
|
||||
putValueArgument(0, irGet(typeInfo.symbol))
|
||||
putValueArgument(1, expectedTypeInfo)
|
||||
}
|
||||
IrBranchImpl(
|
||||
startOffset,
|
||||
endOffset,
|
||||
condition,
|
||||
irDevirtualizedCall(expression, type, actualCallee).apply {
|
||||
this.dispatchReceiver = irGet(receiver.symbol)
|
||||
this.extensionReceiver = extensionReceiver?.let { irGet(it.symbol) }
|
||||
expression.descriptor.valueParameters.forEach {
|
||||
this.putValueArgument(it.index, irGet(parameters[it]!!.symbol))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
+IrWhenImpl(
|
||||
startOffset,
|
||||
endOffset,
|
||||
type,
|
||||
expression.origin,
|
||||
branches
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun IrBuilderWithScope.irDevirtualizedCall(callee: IrCall, actualType: KotlinType,
|
||||
devirtualizedCallee: DataFlowIR.FunctionSymbol.Declared) =
|
||||
IrPrivateFunctionCallImpl(
|
||||
startOffset,
|
||||
endOffset,
|
||||
actualType,
|
||||
callee.symbol,
|
||||
callee.descriptor,
|
||||
(callee as? IrCallImpl)?.typeArguments,
|
||||
devirtualizedCallee.module.descriptor,
|
||||
devirtualizedCallee.module.numberOfFunctions,
|
||||
devirtualizedCallee.symbolTableIndex
|
||||
)
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package konan.internal
|
||||
|
||||
import kotlin.internal.getProgressionLastElement
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@ExportForCppRuntime
|
||||
fun ThrowNullPointerException(): Nothing {
|
||||
@@ -37,6 +38,10 @@ fun ThrowTypeCastException(): Nothing {
|
||||
throw TypeCastException()
|
||||
}
|
||||
|
||||
fun ThrowInvalidReceiverTypeException(klass: KClass<*>): Nothing {
|
||||
throw RuntimeException("Unexpected receiver type: " + (klass.qualifiedName ?: "noname"))
|
||||
}
|
||||
|
||||
@ExportForCppRuntime
|
||||
internal fun ThrowArithmeticException() : Nothing {
|
||||
throw ArithmeticException()
|
||||
|
||||
Reference in New Issue
Block a user