Reincarnated devirtualization

This commit is contained in:
Igor Chevdar
2019-03-20 12:17:44 +03:00
parent 553e2fdda7
commit 7c0225bad9
13 changed files with 175 additions and 335 deletions
@@ -18,7 +18,6 @@ import org.jetbrains.kotlin.backend.konan.library.KonanLibraryWriter
import org.jetbrains.kotlin.backend.konan.library.LinkData
import org.jetbrains.kotlin.backend.konan.llvm.*
import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_BRIDGE_METHOD
import org.jetbrains.kotlin.backend.konan.optimizations.DataFlowIR
import org.jetbrains.kotlin.backend.konan.optimizations.Devirtualization
import org.jetbrains.kotlin.backend.konan.optimizations.ExternalModulesDFG
import org.jetbrains.kotlin.backend.konan.optimizations.ModuleDFG
@@ -321,9 +320,6 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
val coverage = CoverageManager(this)
lateinit var privateFunctions: List<Pair<IrFunction, DataFlowIR.FunctionSymbol.Declared>>
lateinit var privateClasses: List<Pair<IrClass, DataFlowIR.Type.Declared>>
// Cache used for source offset->(line,column) mapping.
val fileEntryCache = mutableMapOf<String, SourceManager.FileEntry>()
@@ -268,6 +268,8 @@ internal val bitcodePhase = namedIrModulePhase(
lower = contextLLVMSetupPhase then
RTTIPhase then
generateDebugInfoHeaderPhase then
buildDFGPhase then
serializeDFGPhase then
deserializeDFGPhase then
devirtualizationPhase then
escapeAnalysisPhase then
@@ -295,8 +297,6 @@ internal val toplevelPhase = namedUnitPhase(
dependenciesLowerPhase then // Then lower all libraries in topological order.
// With that we guarantee that inline functions are unlowered while being inlined.
moduleIndexForCodegenPhase then
buildDFGPhase then
serializeDFGPhase then
bitcodePhase then
produceOutputPhase then
verifyBitcodePhase then
@@ -309,9 +309,7 @@ internal val toplevelPhase = namedUnitPhase(
internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) {
with(config.configuration) {
disable(compileTimeEvaluatePhase)
disable(buildDFGPhase)
disable(deserializeDFGPhase)
disable(devirtualizationPhase)
disable(escapeAnalysisPhase)
disable(serializeDFGPhase)
@@ -384,6 +384,8 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val
val returnIfSuspended = internalFunction("returnIfSuspended")
val coroutineLaunchpad = internalFunction("coroutineLaunchpad")
val konanSuspendCoroutineUninterceptedOrReturn = internalFunction("suspendCoroutineUninterceptedOrReturn")
val konanCoroutineContextGetter = internalFunction("getCoroutineContext")
@@ -6,10 +6,8 @@
package org.jetbrains.kotlin.backend.konan.ir
import org.jetbrains.kotlin.backend.konan.SYNTHETIC_OFFSET
import org.jetbrains.kotlin.backend.konan.optimizations.DataFlowIR
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.SourceManager
import org.jetbrains.kotlin.ir.SourceManager.FileEntry
import org.jetbrains.kotlin.ir.SourceRangeInfo
@@ -18,14 +16,7 @@ import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.MetadataSource
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrContainerExpressionBase
import org.jetbrains.kotlin.ir.expressions.impl.IrExpressionBase
import org.jetbrains.kotlin.ir.expressions.impl.IrTerminalDeclarationReferenceBase
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrFileSymbol
import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrReturnableBlockSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.konan.file.File
@@ -131,91 +122,4 @@ class IrFileImpl(entry: SourceManager.FileEntry) : IrFile {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R {
TODO("not implemented")
}
}
//-----------------------------------------------------------------------------//
internal interface IrPrivateFunctionCall : IrExpression {
val valueArgumentsCount: Int
fun getValueArgument(index: Int): IrExpression?
fun putValueArgument(index: Int, valueArgument: IrExpression?)
fun removeValueArgument(index: Int)
val virtualCallee: IrCall?
val dfgSymbol: DataFlowIR.FunctionSymbol.Declared
val moduleDescriptor: ModuleDescriptor
val totalFunctions: Int
val functionIndex: Int
}
internal class IrPrivateFunctionCallImpl(startOffset: Int,
endOffset: Int,
type: IrType,
override val valueArgumentsCount: Int,
override val virtualCallee: IrCall?,
override val dfgSymbol: DataFlowIR.FunctionSymbol.Declared,
override val moduleDescriptor: ModuleDescriptor,
override val totalFunctions: Int,
override val functionIndex: Int
) : IrPrivateFunctionCall, IrExpressionBase(startOffset, endOffset, type) {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R {
return visitor.visitExpression(this, data)
}
private val argumentsByParameterIndex: Array<IrExpression?> = arrayOfNulls(valueArgumentsCount)
override fun getValueArgument(index: Int): IrExpression? {
if (index >= valueArgumentsCount) {
throw AssertionError("$this: No such value argument slot: $index")
}
return argumentsByParameterIndex[index]
}
override fun putValueArgument(index: Int, valueArgument: IrExpression?) {
if (index >= valueArgumentsCount) {
throw AssertionError("$this: No such value argument slot: $index")
}
argumentsByParameterIndex[index] = valueArgument
}
override fun removeValueArgument(index: Int) {
argumentsByParameterIndex[index] = null
}
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
argumentsByParameterIndex.forEach { it?.accept(visitor, data) }
}
override fun <D> transformChildren(transformer: IrElementTransformer<D>, data: D) {
argumentsByParameterIndex.forEachIndexed { i, irExpression ->
argumentsByParameterIndex[i] = irExpression?.transform(transformer, data)
}
}
}
internal interface IrPrivateClassReference : IrClassReference {
val moduleDescriptor: ModuleDescriptor
val totalClasses: Int
val classIndex: Int
val dfgSymbol: DataFlowIR.Type.Declared
}
internal class IrPrivateClassReferenceImpl(startOffset: Int,
endOffset: Int,
type: IrType,
symbol: IrClassifierSymbol,
override val classType: IrType,
override val moduleDescriptor: ModuleDescriptor,
override val totalClasses: Int,
override val classIndex: Int,
override val dfgSymbol: DataFlowIR.Type.Declared
) : 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)
}
@@ -63,38 +63,11 @@ internal val deserializeDFGPhase = makeKonanModuleOpPhase(
internal val devirtualizationPhase = makeKonanModuleOpPhase(
name = "Devirtualization",
description = "Devirtualization",
prerequisite = setOf(buildDFGPhase, deserializeDFGPhase),
prerequisite = setOf(buildDFGPhase),
op = { context, irModule ->
context.externalModulesDFG?.let { externalModulesDFG ->
context.devirtualizationAnalysisResult = Devirtualization.run(
irModule, context, context.moduleDFG!!, externalModulesDFG
)
val privateFunctions = context.moduleDFG!!.symbolTable.getPrivateFunctionsTableForExport()
privateFunctions.forEachIndexed { index, it ->
val function = context.codegenVisitor.codegen.llvmFunction(it.first)
LLVMAddAlias(
context.llvmModule,
function.type,
function,
irModule.descriptor.privateFunctionSymbolName(index, it.second.name)
)!!
}
context.privateFunctions = privateFunctions
val privateClasses = context.moduleDFG!!.symbolTable.getPrivateClassesTableForExport()
privateClasses.forEachIndexed { index, it ->
val typeInfoPtr = context.codegenVisitor.codegen.typeInfoValue(it.first)
LLVMAddAlias(
context.llvmModule,
typeInfoPtr.type,
typeInfoPtr,
irModule.descriptor.privateClassSymbolName(index, it.second.name)
)!!
}
context.privateClasses = privateClasses
}
context.devirtualizationAnalysisResult = Devirtualization.run(
irModule, context, context.moduleDFG!!, ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap())
)
}
)
@@ -5,12 +5,15 @@ import llvm.*
import org.jetbrains.kotlin.backend.konan.descriptors.TypedIntrinsic
import org.jetbrains.kotlin.backend.konan.descriptors.isTypedIntrinsic
import org.jetbrains.kotlin.backend.konan.llvm.objc.genObjCSelector
import org.jetbrains.kotlin.backend.konan.ir.isSuspend
import org.jetbrains.kotlin.backend.konan.reportCompilationError
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.name.Name
internal enum class IntrinsicType {
@@ -64,6 +67,7 @@ internal enum class IntrinsicType {
// Coroutines
GET_CONTINUATION,
RETURN_IF_SUSPEND,
COROUTINE_LAUNCHPAD,
// Interop
INTEROP_READ_BITS,
INTEROP_WRITE_BITS,
@@ -98,7 +102,7 @@ internal interface IntrinsicGeneratorEnvironment {
fun calculateLifetime(element: IrElement): Lifetime
fun evaluateCall(function: IrFunction, args: List<LLVMValueRef>, resultLifetime: Lifetime): LLVMValueRef
fun evaluateCall(function: IrFunction, args: List<LLVMValueRef>, resultLifetime: Lifetime, superClass: IrClass? = null): LLVMValueRef
fun evaluateExplicitArgs(expression: IrMemberAccessExpression): List<LLVMValueRef>
@@ -155,9 +159,8 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv
environment.functionGenerationContext.genObjCSelector(selector)
}
IntrinsicType.INIT_INSTANCE -> {
val callee = callSite as IrCall
val initializer = callee.getValueArgument(1) as IrCall
val thiz = environment.evaluateExpression(callee.getValueArgument(0)!!)
val initializer = callSite.getValueArgument(1) as IrCall
val thiz = environment.evaluateExpression(callSite.getValueArgument(0)!!)
environment.evaluateCall(
initializer.symbol.owner,
listOf(thiz) + environment.evaluateExplicitArgs(initializer),
@@ -165,6 +168,17 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv
)
codegen.theUnitInstanceRef.llvm
}
IntrinsicType.COROUTINE_LAUNCHPAD -> {
val suspendFunctionCall = callSite.getValueArgument(0) as IrCall
val continuation = environment.evaluateExpression(callSite.getValueArgument(1)!!)
val suspendFunction = suspendFunctionCall.symbol.owner
assert(suspendFunction.isSuspend) { "Call to a suspend function expected but was ${suspendFunction.dump()}" }
environment.evaluateCall(suspendFunction,
environment.evaluateExplicitArgs(suspendFunctionCall) + listOf(continuation),
environment.calculateLifetime(suspendFunctionCall),
suspendFunction.parent as? IrClass // Call non-virtually.
)
}
else -> null
}
}
@@ -238,6 +252,7 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv
reportNonLoweredIntrinsic(intrinsicType)
IntrinsicType.INIT_INSTANCE,
IntrinsicType.OBJC_INIT_BY,
IntrinsicType.COROUTINE_LAUNCHPAD,
IntrinsicType.OBJC_GET_SELECTOR,
IntrinsicType.IMMUTABLE_BLOB ->
reportSpecialIntrinsic(intrinsicType)
@@ -13,7 +13,6 @@ import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.*
import org.jetbrains.kotlin.backend.konan.ir.*
import org.jetbrains.kotlin.backend.konan.llvm.coverage.*
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport
import org.jetbrains.kotlin.backend.konan.optimizations.*
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.UnsignedType
@@ -220,8 +219,8 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
override val exceptionHandler: ExceptionHandler
get() = currentCodeContext.exceptionHandler
override fun evaluateCall(function: IrFunction, args: List<LLVMValueRef>, resultLifetime: Lifetime): LLVMValueRef =
evaluateSimpleFunctionCall(function, args, resultLifetime)
override fun evaluateCall(function: IrFunction, args: List<LLVMValueRef>, resultLifetime: Lifetime, superClass: IrClass?) =
evaluateSimpleFunctionCall(function, args, resultLifetime, superClass)
override fun evaluateExplicitArgs(expression: IrMemberAccessExpression): List<LLVMValueRef> =
this@CodeGeneratorVisitor.evaluateExplicitArgs(expression)
@@ -794,9 +793,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
is IrSuspendableExpression ->
return evaluateSuspendableExpression (value)
is IrSuspensionPoint -> return evaluateSuspensionPoint (value)
is IrPrivateFunctionCall -> return evaluatePrivateFunctionCall (value)
is IrPrivateClassReference ->
return evaluatePrivateClassReference (value)
is IrClassReference -> return evaluateClassReference (value)
else -> {
TODO(ir2string(value))
}
@@ -2017,42 +2014,8 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
//-------------------------------------------------------------------------//
private fun evaluatePrivateFunctionCall(callee: IrPrivateFunctionCall): LLVMValueRef {
val args = (0 until callee.valueArgumentsCount).map { index ->
callee.getValueArgument(index)?.let { evaluateExpression(it) }
?: run {
assert(index == callee.valueArgumentsCount - 1) { "Only last argument may be null - for suspend functions" }
getContinuation()
}
}
val dfgSymbol = callee.dfgSymbol
val functionIndex = callee.functionIndex
val function = if (callee.moduleDescriptor == context.irModule!!.descriptor) {
codegen.llvmFunction(context.privateFunctions[functionIndex].first)
} else {
context.llvm.externalFunction(
callee.moduleDescriptor.privateFunctionSymbolName(functionIndex, callee.dfgSymbol.name),
codegen.getLlvmFunctionType(dfgSymbol),
callee.moduleDescriptor.llvmSymbolOrigin
)
}
return call(dfgSymbol, function, args, resultLifetime = Lifetime.GLOBAL)
}
//-------------------------------------------------------------------------//
private fun evaluatePrivateClassReference(classReference: IrPrivateClassReference): LLVMValueRef {
val classIndex = classReference.classIndex
val typeInfoPtr = if (classReference.moduleDescriptor == context.irModule!!.descriptor) {
codegen.typeInfoValue(context.privateClasses[classIndex].first)
} else {
codegen.importGlobal(
classReference.moduleDescriptor.privateClassSymbolName(classIndex, classReference.dfgSymbol.name),
codegen.kTypeInfo,
classReference.moduleDescriptor.llvmSymbolOrigin
)
}
private fun evaluateClassReference(classReference: IrClassReference): LLVMValueRef {
val typeInfoPtr = codegen.typeInfoValue(classReference.symbol.owner as IrClass)
return functionGenerationContext.bitcast(int8TypePtr, typeInfoPtr)
}
@@ -479,11 +479,32 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
)
}
private fun IrType.erasure(): IrType {
if (this !is IrSimpleType) return this
val classifier = this.classifier
return when (classifier) {
is IrClassSymbol -> this
is IrTypeParameterSymbol -> {
val upperBound = classifier.owner.superTypes.singleOrNull() ?:
TODO("${classifier.descriptor} : ${classifier.descriptor.upperBounds}")
if (this.hasQuestionMark) {
// `T?`
upperBound.erasure().makeNullable()
} else {
upperBound.erasure()
}
}
else -> TODO(classifier.toString())
}
}
private fun expressionToEdge(expression: IrExpression) =
if (expression is IrTypeOperatorCall && expression.operator.isCast())
DataFlowIR.Edge(
getNode(expression.argument),
symbolTable.mapClassReferenceType(expression.typeOperand.getClass()!!)
symbolTable.mapClassReferenceType(expression.typeOperand.erasure().getClass()!!)
)
else DataFlowIR.Edge(getNode(expression), null)
@@ -986,7 +986,7 @@ internal object DFGSerializer {
++module.numberOfClasses
DataFlowIR.Type.Public(public.hash, public.intestines.base.isFinal,
public.intestines.base.isAbstract, public.intestines.base.primitiveBinaryType,
module, symbolTableIndex, public.intestines.base.name).also {
module, symbolTableIndex, null, public.intestines.base.name).also {
publicTypesMap.put(it.hash, it)
allTypes += it
}
@@ -998,7 +998,7 @@ internal object DFGSerializer {
++module.numberOfClasses
DataFlowIR.Type.Private(privateTypeIndex++, private.intestines.base.isFinal,
private.intestines.base.isAbstract, private.intestines.base.primitiveBinaryType,
module, symbolTableIndex, private.intestines.base.name).also {
module, symbolTableIndex, null, private.intestines.base.name).also {
allTypes += it
}
}
@@ -1013,14 +1013,14 @@ internal object DFGSerializer {
val private = it.private
when {
external != null ->
DataFlowIR.FunctionSymbol.External(external.hash, attributes, external.name)
DataFlowIR.FunctionSymbol.External(external.hash, attributes, null, external.name)
public != null -> {
val symbolTableIndex = public.index
if (symbolTableIndex >= 0)
++module.numberOfFunctions
DataFlowIR.FunctionSymbol.Public(public.hash,
module, symbolTableIndex, attributes, null, public.name).also {
module, symbolTableIndex, attributes, null, null, public.name).also {
publicFunctionsMap.put(it.hash, it)
}
}
@@ -1030,7 +1030,7 @@ internal object DFGSerializer {
if (symbolTableIndex >= 0)
++module.numberOfFunctions
DataFlowIR.FunctionSymbol.Private(privateFunIndex++,
module, symbolTableIndex, attributes, null, private.name)
module, symbolTableIndex, attributes, null, null, private.name)
}
}.apply {
escapes = it.base.escapes
@@ -41,7 +41,7 @@ internal object DataFlowIR {
val primitiveBinaryType: PrimitiveBinaryType?,
val name: String?) {
// Special marker type forbidding devirtualization on its instances.
object Virtual : Declared(false, true, null, null, -1, "\$VIRTUAL")
object Virtual : Declared(false, true, null, null, -1, null, "\$VIRTUAL")
class External(val hash: Long, isFinal: Boolean, isAbstract: Boolean,
primitiveBinaryType: PrimitiveBinaryType?, name: String? = null)
@@ -63,7 +63,7 @@ internal object DataFlowIR {
}
abstract class Declared(isFinal: Boolean, isAbstract: Boolean, primitiveBinaryType: PrimitiveBinaryType?,
val module: Module?, val symbolTableIndex: Int, name: String?)
val module: Module?, val symbolTableIndex: Int, val irClass: IrClass?, name: String?)
: Type(isFinal, isAbstract, primitiveBinaryType, name) {
val superTypes = mutableListOf<Type>()
val vtable = mutableListOf<FunctionSymbol>()
@@ -71,8 +71,8 @@ internal object DataFlowIR {
}
class Public(val hash: Long, isFinal: Boolean, isAbstract: Boolean, primitiveBinaryType: PrimitiveBinaryType?,
module: Module, symbolTableIndex: Int, name: String? = null)
: Declared(isFinal, isAbstract, primitiveBinaryType, module, symbolTableIndex, name) {
module: Module, symbolTableIndex: Int, irClass: IrClass?, name: String? = null)
: Declared(isFinal, isAbstract, primitiveBinaryType, module, symbolTableIndex, irClass, name) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Public) return false
@@ -90,8 +90,8 @@ internal object DataFlowIR {
}
class Private(val index: Int, isFinal: Boolean, isAbstract: Boolean, primitiveBinaryType: PrimitiveBinaryType?,
module: Module, symbolTableIndex: Int, name: String? = null)
: Declared(isFinal, isAbstract, primitiveBinaryType, module, symbolTableIndex, name) {
module: Module, symbolTableIndex: Int, irClass: IrClass?, name: String? = null)
: Declared(isFinal, isAbstract, primitiveBinaryType, module, symbolTableIndex, irClass, name) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Private) return false
@@ -122,7 +122,7 @@ internal object DataFlowIR {
class FunctionParameter(val type: Type, val boxFunction: FunctionSymbol?, val unboxFunction: FunctionSymbol?)
abstract class FunctionSymbol(val attributes: Int, val name: String?) {
abstract class FunctionSymbol(val attributes: Int, val irFunction: IrFunction?, val name: String?) {
lateinit var parameters: Array<FunctionParameter>
lateinit var returnParameter: FunctionParameter
@@ -133,8 +133,8 @@ internal object DataFlowIR {
var escapes: Int? = null
var pointsTo: IntArray? = null
class External(val hash: Long, attributes: Int, name: String? = null)
: FunctionSymbol(attributes, name) {
class External(val hash: Long, attributes: Int, irFunction: IrFunction?, name: String? = null)
: FunctionSymbol(attributes, irFunction, name) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
@@ -153,14 +153,14 @@ internal object DataFlowIR {
}
abstract class Declared(val module: Module, val symbolTableIndex: Int,
attributes: Int, var bridgeTarget: FunctionSymbol?, name: String?)
: FunctionSymbol(attributes, name) {
attributes: Int, irFunction: IrFunction?, var bridgeTarget: FunctionSymbol?, name: String?)
: FunctionSymbol(attributes, irFunction, name) {
}
class Public(val hash: Long, module: Module, symbolTableIndex: Int,
attributes: Int, bridgeTarget: FunctionSymbol?, name: String? = null)
: Declared(module, symbolTableIndex, attributes, bridgeTarget, name) {
attributes: Int, irFunction: IrFunction?, bridgeTarget: FunctionSymbol?, name: String? = null)
: Declared(module, symbolTableIndex, attributes, irFunction, bridgeTarget, name) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
@@ -179,8 +179,8 @@ internal object DataFlowIR {
}
class Private(val index: Int, module: Module, symbolTableIndex: Int,
attributes: Int, bridgeTarget: FunctionSymbol?, name: String? = null)
: Declared(module, symbolTableIndex, attributes, bridgeTarget, name) {
attributes: Int, irFunction: IrFunction?, bridgeTarget: FunctionSymbol?, name: String? = null)
: Declared(module, symbolTableIndex, attributes, irFunction, bridgeTarget, name) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
@@ -500,10 +500,6 @@ internal object DataFlowIR {
val isFinal = irClass.isFinal()
val isAbstract = irClass.isAbstract()
val name = irClass.fqNameSafe.asString()
if (irClass.module != irModule.descriptor)
return classMap.getOrPut(irClass) {
Type.External(name.localHash.value, isFinal, isAbstract, null, takeName { name })
}
classMap[irClass]?.let { return it }
@@ -511,10 +507,10 @@ internal object DataFlowIR {
val symbolTableIndex = if (placeToClassTable) module.numberOfClasses++ else -1
val type = if (irClass.isExported())
Type.Public(name.localHash.value, isFinal, isAbstract, null,
module, symbolTableIndex, takeName { name })
module, symbolTableIndex, irClass, takeName { name })
else
Type.Private(privateTypeIndex++, isFinal, isAbstract, null,
module, symbolTableIndex, takeName { name })
module, symbolTableIndex, irClass, takeName { name })
classMap[irClass] = type
@@ -586,14 +582,14 @@ internal object DataFlowIR {
if (returnsNothing)
attributes = attributes or FunctionAttributes.RETURNS_NOTHING
val symbol = when {
it.module != irModule.descriptor || it.isExternal || (it.origin == IrDeclarationOrigin.IR_BUILTINS_STUB) -> {
it.isExternal || (it.origin == IrDeclarationOrigin.IR_BUILTINS_STUB) -> {
val escapesAnnotation = it.descriptor.annotations.findAnnotation(FQ_NAME_ESCAPES)
val pointsToAnnotation = it.descriptor.annotations.findAnnotation(FQ_NAME_POINTS_TO)
@Suppress("UNCHECKED_CAST")
val escapesBitMask = (escapesAnnotation?.allValueArguments?.get(escapesWhoDescriptor.name) as? ConstantValue<Int>)?.value
@Suppress("UNCHECKED_CAST")
val pointsToBitMask = (pointsToAnnotation?.allValueArguments?.get(pointsToOnWhomDescriptor.name) as? ConstantValue<List<IntValue>>)?.value
FunctionSymbol.External(name.localHash.value, attributes, takeName { name }).apply {
FunctionSymbol.External(name.localHash.value, attributes, it, takeName { name }).apply {
escapes = escapesBitMask
pointsTo = pointsToBitMask?.let { it.map { it.value }.toIntArray() }
}
@@ -612,9 +608,9 @@ internal object DataFlowIR {
&& (it.isOverridableOrOverrides || bridgeTarget != null || function.isSpecial || !irClass.isFinal())
val symbolTableIndex = if (placeToFunctionsTable) module.numberOfFunctions++ else -1
if (it.isExported())
FunctionSymbol.Public(name.localHash.value, module, symbolTableIndex, attributes, bridgeTargetSymbol, takeName { name })
FunctionSymbol.Public(name.localHash.value, module, symbolTableIndex, attributes, it, bridgeTargetSymbol, takeName { name })
else
FunctionSymbol.Private(privateFunIndex++, module, symbolTableIndex, attributes, bridgeTargetSymbol, takeName { name })
FunctionSymbol.Private(privateFunIndex++, module, symbolTableIndex, attributes, it, bridgeTargetSymbol, takeName { name })
}
}
functionMap[it] = symbol
@@ -639,7 +635,7 @@ internal object DataFlowIR {
assert(irField.parent !is IrClass) { "All local properties initializers should've been lowered" }
val attributes = FunctionAttributes.IS_GLOBAL_INITIALIZER or FunctionAttributes.RETURNS_UNIT
val symbol = FunctionSymbol.Private(privateFunIndex++, module, -1, attributes, null, takeName { "${irField.symbolName}_init" })
val symbol = FunctionSymbol.Private(privateFunIndex++, module, -1, attributes, null, null, takeName { "${irField.symbolName}_init" })
functionMap[irField] = symbol
@@ -647,32 +643,5 @@ internal object DataFlowIR {
symbol.returnParameter = mapTypeToFunctionParameter(context.irBuiltIns.unitType)
return symbol
}
fun getPrivateFunctionsTableForExport() =
functionMap
.asSequence()
.filter { it.key is IrFunction }
.filter { it.value.let { it is DataFlowIR.FunctionSymbol.Declared && it.symbolTableIndex >= 0 } }
.sortedBy { (it.value as DataFlowIR.FunctionSymbol.Declared).symbolTableIndex }
.apply {
forEachIndexed { index, entry ->
assert((entry.value as DataFlowIR.FunctionSymbol.Declared).symbolTableIndex == index) { "Inconsistent function table" }
}
}
.map { (it.key as IrFunction) to (it.value as DataFlowIR.FunctionSymbol.Declared) }
.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 to (it.value as DataFlowIR.Type.Declared) }
.toList()
}
}
@@ -15,16 +15,11 @@ import org.jetbrains.kotlin.backend.common.lower.irBlock
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.getInlinedClass
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
import org.jetbrains.kotlin.backend.konan.ir.IrPrivateClassReferenceImpl
import org.jetbrains.kotlin.backend.konan.ir.IrPrivateFunctionCall
import org.jetbrains.kotlin.backend.konan.ir.IrPrivateFunctionCallImpl
import org.jetbrains.kotlin.backend.konan.ir.getErasedTypeClass
import org.jetbrains.kotlin.backend.konan.ir.isSuspend
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptorImpl
import org.jetbrains.kotlin.ir.expressions.*
@@ -1016,7 +1011,7 @@ internal object Devirtualization {
.filter { it.key.irCallSite != null }
.associate { it.key.irCallSite!! to it.value }
devirtualize(irModule, context, externalModulesDFG, devirtualizedCallSites)
removeRedundantCoercions(irModule, context, moduleDFG, externalModulesDFG)
removeRedundantCoercions(irModule, context)
return AnalysisResult(devirtualizationAnalysisResult)
}
@@ -1034,12 +1029,9 @@ internal object Devirtualization {
private val specialNames = listOf("<box>", "<unbox>")
// TODO: do it more reliably.
private fun IrExpression.isBoxOrUnboxCall() =
(this is IrCall && symbol.owner.name.asString().let { specialNames.contains(it) })
|| (this is IrPrivateFunctionCall && specialNames.any { dfgSymbol.name?.contains(it) == true })
private fun IrExpression.isBoxOrUnboxCall() = (this is IrCall && symbol.owner.name.asString().let { specialNames.contains(it) })
private fun devirtualize(irModule: IrModuleFragment, context: Context,
externalModulesDFG: ExternalModulesDFG,
private fun devirtualize(irModule: IrModuleFragment, context: Context, externalModulesDFG: ExternalModulesDFG,
devirtualizedCallSites: Map<IrCall, DevirtualizedCallSite>) {
val symbols = context.ir.symbols
val nativePtrEqualityOperatorSymbol = symbols.areEqualByValue[PrimitiveBinaryType.POINTER]!!
@@ -1067,17 +1059,7 @@ internal object Devirtualization {
fun IrBuilderWithScope.irCoerce(value: IrExpression, coercion: DataFlowIR.FunctionSymbol.Declared?) =
if (coercion == null)
value
else IrPrivateFunctionCallImpl(
startOffset = startOffset,
endOffset = endOffset,
type = value.type, // TODO: What type is actually must be here?
valueArgumentsCount = 1,
virtualCallee = null,
dfgSymbol = coercion,
moduleDescriptor = coercion.module.descriptor,
totalFunctions = coercion.module.numberOfFunctions,
functionIndex = coercion.symbolTableIndex
).apply {
else irCall(coercion.irFunction!!).apply {
putValueArgument(0, value)
}
@@ -1124,45 +1106,80 @@ internal object Devirtualization {
}
if (actualType.boxFunction == null)
return targetType.unboxFunction!!.resolved() as DataFlowIR.FunctionSymbol.Declared
return actualType.boxFunction!!.resolved() as DataFlowIR.FunctionSymbol.Declared
return actualType.boxFunction.resolved() as DataFlowIR.FunctionSymbol.Declared
}
fun irDevirtualizedCall(callee: IrCall, actualType: IrType, devirtualizedCallee: DataFlowIR.FunctionSymbol.Declared) =
IrPrivateFunctionCallImpl(
startOffset = callee.startOffset,
endOffset = callee.endOffset,
type = actualType,
valueArgumentsCount = devirtualizedCallee.parameters.size,
virtualCallee = callee,
dfgSymbol = devirtualizedCallee,
moduleDescriptor = devirtualizedCallee.module.descriptor,
totalFunctions = devirtualizedCallee.module.numberOfFunctions,
functionIndex = devirtualizedCallee.symbolTableIndex
)
fun IrCallImpl.putArgument(index: Int, value: IrExpression) {
var receiversCount = 0
val callee = symbol.owner
if (callee.dispatchReceiverParameter != null)
++receiversCount
if (callee.extensionReceiverParameter != null)
++receiversCount
if (index >= receiversCount)
putValueArgument(index - receiversCount, value)
else {
if (callee.dispatchReceiverParameter != null && index == 0)
dispatchReceiver = value
else
extensionReceiver = value
}
}
fun IrBuilderWithScope.irDevirtualizedCall(callSite: IrCall,
actualType: IrType,
devirtualizedCallee: DevirtualizedCallee,
arguments: List<IrExpression>): IrCall {
val actualCallee = devirtualizedCallee.callee.irFunction!!
val call = IrCallImpl(
callSite.startOffset, callSite.endOffset,
actualType,
actualCallee.symbol,
actualCallee.descriptor,
actualCallee.typeParameters.size,
actualCallee.valueParameters.size,
callSite.origin,
actualCallee.parentAsClass.symbol
)
if (actualCallee.explicitParameters.size == arguments.size) {
arguments.forEachIndexed { index, argument -> call.putArgument(index, argument) }
return call
}
assert(actualCallee.isSuspend && actualCallee.explicitParameters.size == arguments.size - 1) {
"Incorrect number of arguments: expected [${actualCallee.explicitParameters.size}] but was [${arguments.size - 1}]\n" +
actualCallee.dump()
}
val continuation = arguments.last()
for (index in 0..arguments.size - 2)
call.putArgument(index, arguments[index])
return irCall(context.ir.symbols.coroutineLaunchpad, actualType).apply {
putValueArgument(0, call)
putValueArgument(1, continuation)
}
}
fun IrBuilderWithScope.irDevirtualizedCall(callee: IrCall, actualType: IrType,
actualCallee: DataFlowIR.FunctionSymbol.Declared,
parameters: List<PossiblyCoercedValue>) =
actualCallee.bridgeTarget.let {
if (it == null)
irDevirtualizedCall(callee, actualType, actualCallee).apply {
parameters.forEachIndexed { index, value ->
putValueArgument(index, value.getFullValue(this@irDevirtualizedCall))
}
}
else {
val bridgeTarget = it.resolved() as DataFlowIR.FunctionSymbol.Declared
val callResult = irDevirtualizedCall(callee, actualType, bridgeTarget).apply {
parameters.forEachIndexed { index, value ->
devirtualizedCallee: DevirtualizedCallee,
arguments: List<PossiblyCoercedValue>): IrExpression {
val actualCallee = devirtualizedCallee.callee as DataFlowIR.FunctionSymbol.Declared
return actualCallee.bridgeTarget.let { bridgeTarget ->
if (bridgeTarget == null)
irDevirtualizedCall(callee, actualType,
devirtualizedCallee,
arguments.map { it.getFullValue(this@irDevirtualizedCall) })
else {
val callResult = irDevirtualizedCall(callee, actualType,
DevirtualizedCallee(devirtualizedCallee.receiverType, bridgeTarget),
arguments.mapIndexed { index, value ->
val coercion = getTypeConversion(actualCallee.parameters[index], bridgeTarget.parameters[index])
val fullValue = value.getFullValue(this@irDevirtualizedCall)
putValueArgument(index, coercion?.let { irCoerce(fullValue, coercion) } ?: fullValue)
}
}
val returnCoercion = getTypeConversion(bridgeTarget.returnParameter, actualCallee.returnParameter)
irCoerce(callResult, returnCoercion)
}
coercion?.let { irCoerce(fullValue, coercion) } ?: fullValue
})
val returnCoercion = getTypeConversion(bridgeTarget.returnParameter, actualCallee.returnParameter)
irCoerce(callResult, returnCoercion)
}
}
}
irModule.transformChildrenVoid(object: IrElementTransformerVoidWithContext() {
override fun visitCall(expression: IrCall): IrExpression {
@@ -1208,37 +1225,31 @@ internal object Devirtualization {
}
optimize && possibleCallees.size == 1 -> { // Monomorphic callsite.
val actualCallee = possibleCallees[0].callee as DataFlowIR.FunctionSymbol.Declared
irBlock(expression) {
val parameters = expression.getArgumentsWithSymbols().mapIndexed { index, arg ->
irSplitCoercion(arg.second, "arg$index", arg.first.owner.type)
}
+irDevirtualizedCall(expression, type, actualCallee, parameters)
+irDevirtualizedCall(expression, type, possibleCallees[0], parameters)
}
}
else -> irBlock(expression) {
val parameters = expression.getArgumentsWithSymbols().mapIndexed { index, arg ->
val arguments = expression.getArgumentsWithSymbols().mapIndexed { index, arg ->
irSplitCoercion(arg.second, "arg$index", arg.first.owner.type)
}
val typeInfo = irTemporary(irCall(symbols.getObjectTypeInfo).apply {
putValueArgument(0, parameters[0].getFullValue(this@irBlock))
putValueArgument(0, arguments[0].getFullValue(this@irBlock))
})
val branches = mutableListOf<IrBranchImpl>()
possibleCallees.mapIndexedTo(branches) { index, devirtualizedCallee ->
val actualCallee = devirtualizedCallee.callee as DataFlowIR.FunctionSymbol.Declared
val actualReceiverType = devirtualizedCallee.receiverType as DataFlowIR.Type.Declared
val expectedTypeInfo = IrPrivateClassReferenceImpl(
startOffset = startOffset,
endOffset = endOffset,
type = symbols.nativePtrType,
symbol = dispatchReceiver.type.getErasedTypeClass(),
classType = dispatchReceiver.type,
moduleDescriptor = actualReceiverType.module!!.descriptor,
totalClasses = actualReceiverType.module.numberOfClasses,
classIndex = actualReceiverType.symbolTableIndex,
dfgSymbol = actualReceiverType)
val expectedTypeInfo = IrClassReferenceImpl(
startOffset, endOffset,
symbols.nativePtrType,
actualReceiverType.irClass!!.symbol,
actualReceiverType.irClass.defaultType
)
val condition =
if (optimize && index == possibleCallees.size - 1)
irTrue() // Don't check last type in optimize mode.
@@ -1251,7 +1262,7 @@ internal object Devirtualization {
startOffset = startOffset,
endOffset = endOffset,
condition = condition,
result = irDevirtualizedCall(expression, type, actualCallee, parameters)
result = irDevirtualizedCall(expression, type, devirtualizedCallee, arguments)
)
}
if (!optimize) { // Add else branch throwing exception for debug purposes.
@@ -1285,14 +1296,7 @@ internal object Devirtualization {
})
}
private fun removeRedundantCoercions(irModule: IrModuleFragment, context: Context,
moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG) {
fun DataFlowIR.FunctionSymbol.resolved(): DataFlowIR.FunctionSymbol {
if (this is DataFlowIR.FunctionSymbol.External)
return externalModulesDFG.publicFunctions[this.hash] ?: this
return this
}
private fun removeRedundantCoercions(irModule: IrModuleFragment, context: Context) {
class PossiblyFoldedExpression(val expression: IrExpression, val folded: Boolean) {
fun getFullExpression(coercion: IrCall, cast: IrTypeOperatorCall?): IrExpression {
@@ -1355,21 +1359,12 @@ internal object Devirtualization {
val coercionDeclaringClass = coercion.symbol.owner.parentAsClass
if (expression.isBoxOrUnboxCall()) {
expression as IrCall
val result =
(expression as? IrCall)?.let {
if (coercionDeclaringClass == it.symbol.owner.parentAsClass)
it.getArguments().single().second
else expression
} ?: (expression as IrPrivateFunctionCall).let {
val argarg = it.getValueArgument(0)!!
val boxFunction = context.getBoxFunction(coercionDeclaringClass)
val unboxFunction = context.getUnboxFunction(coercionDeclaringClass)
val boxFunctionSymbol = moduleDFG.symbolTable.mapFunction(boxFunction).resolved()
val unboxFunctionSymbol = moduleDFG.symbolTable.mapFunction(unboxFunction).resolved()
if (it.dfgSymbol == boxFunctionSymbol || it.dfgSymbol == unboxFunctionSymbol)
argarg
else it
}
if (coercionDeclaringClass == expression.symbol.owner.parentAsClass)
expression.getArguments().single().second
else expression
return PossiblyFoldedExpression(result.transformIfAsked(), result != expression)
}
return when (expression) {
@@ -25,3 +25,6 @@ internal inline suspend fun getCoroutineContext(): CoroutineContext =
@TypedIntrinsic(IntrinsicType.RETURN_IF_SUSPEND)
@PublishedApi
internal external suspend fun <T> returnIfSuspended(@Suppress("UNUSED_PARAMETER") argument: Any?): T
@TypedIntrinsic(IntrinsicType.COROUTINE_LAUNCHPAD)
internal external fun coroutineLaunchpad(suspendFunctionCall: Any?, continuation: Continuation<*>): Any?
@@ -57,6 +57,7 @@ class IntrinsicType {
const val GET_CONTINUATION = "GET_CONTINUATION"
const val RETURN_IF_SUSPEND = "RETURN_IF_SUSPEND"
const val COROUTINE_LAUNCHPAD = "COROUTINE_LAUNCHPAD"
// Interop
const val INTEROP_READ_PRIMITIVE = "INTEROP_READ_PRIMITIVE"