Data flow graph building
This commit is contained in:
+2
-1
@@ -59,7 +59,8 @@ enum class KonanPhase(val description: String,
|
||||
/* ... ... */ RETURNS_INSERTION("Returns insertion for Unit functions", AUTOBOX, LOWER_COROUTINES, LOWER_ENUMS),
|
||||
/* ... */ BITCODE("LLVM BitCode Generation"),
|
||||
/* ... ... */ RTTI("RTTI Generation"),
|
||||
/* ... ... */ ESCAPE_ANALYSIS("Escape analysis"),
|
||||
/* ... ... */ BUILD_DFG("Data flow graph building"),
|
||||
/* ... ... */ ESCAPE_ANALYSIS("Escape analysis", BUILD_DFG),
|
||||
/* ... ... */ CODEGEN("Code Generation"),
|
||||
/* ... ... */ BITCODE_LINKER("Bitcode linking"),
|
||||
/* */ LINK_STAGE("Link stage"),
|
||||
|
||||
+16
-2
@@ -25,6 +25,10 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
||||
|
||||
internal val FunctionDescriptor.canBeCalledVirtually: Boolean
|
||||
// We check that either method is open, or one of declarations it overrides is open.
|
||||
get() = isOverridable || DescriptorUtils.getAllOverriddenDeclarations(this).any { it.isOverridable }
|
||||
|
||||
internal class OverriddenFunctionDescriptor(val descriptor: FunctionDescriptor, overriddenDescriptor: FunctionDescriptor) {
|
||||
val overriddenDescriptor = overriddenDescriptor.original
|
||||
|
||||
@@ -41,8 +45,7 @@ internal class OverriddenFunctionDescriptor(val descriptor: FunctionDescriptor,
|
||||
}
|
||||
|
||||
// We check that either method is open, or one of declarations it overrides is open.
|
||||
return (overriddenDescriptor.isOverridable
|
||||
|| DescriptorUtils.getAllOverriddenDeclarations(overriddenDescriptor).any { it.isOverridable })
|
||||
return overriddenDescriptor.canBeCalledVirtually
|
||||
}
|
||||
|
||||
val inheritsBridge: Boolean
|
||||
@@ -50,6 +53,17 @@ internal class OverriddenFunctionDescriptor(val descriptor: FunctionDescriptor,
|
||||
&& OverridingUtil.overrides(descriptor.target, overriddenDescriptor)
|
||||
&& descriptor.bridgeDirectionsTo(overriddenDescriptor).allNotNeeded()
|
||||
|
||||
fun getImplementation(context: Context): FunctionDescriptor {
|
||||
val target = descriptor.target
|
||||
if (!needBridge) return target
|
||||
val bridgeOwner = if (inheritsBridge) {
|
||||
target // Bridge is inherited from superclass.
|
||||
} else {
|
||||
descriptor
|
||||
}
|
||||
return context.specialDeclarationsFactory.getBridgeDescriptor(OverriddenFunctionDescriptor(bridgeOwner, overriddenDescriptor))
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "(descriptor=$descriptor, overriddenDescriptor=$overriddenDescriptor)"
|
||||
}
|
||||
|
||||
+3
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.builtins.isSuspendFunctionType
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltinOperatorDescriptorBase
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||
@@ -80,6 +81,8 @@ private val intrinsicAnnotation = FqName("konan.internal.Intrinsic")
|
||||
internal val FunctionDescriptor.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
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.constants.StringValue
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.findOriginalTopMostOverriddenDescriptors
|
||||
@@ -64,6 +65,8 @@ internal tailrec fun DeclarationDescriptor.isExported(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
if (DescriptorUtils.isAnonymousObject(this))
|
||||
return false
|
||||
|
||||
if (this is ConstructorDescriptor && constructedClass.kind.isSingleton) {
|
||||
// Currently code generator can access the constructor of the singleton,
|
||||
|
||||
+8
@@ -24,6 +24,9 @@ import org.jetbrains.kotlin.backend.common.ir.ir2string
|
||||
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.library.impl.buildLibrary
|
||||
import org.jetbrains.kotlin.backend.konan.optimizations.*
|
||||
import org.jetbrains.kotlin.backend.konan.util.getValueOrNull
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
@@ -73,6 +76,11 @@ internal fun emitLLVM(context: Context) {
|
||||
|
||||
generateDebugInfoHeader(context)
|
||||
|
||||
var moduleDFG: ModuleDFG? = null
|
||||
phaser.phase(KonanPhase.BUILD_DFG) {
|
||||
moduleDFG = ModuleDFGBuilder(context, irModule).build()
|
||||
}
|
||||
|
||||
val lifetimes = mutableMapOf<IrElement, Lifetime>()
|
||||
val codegenVisitor = CodeGeneratorVisitor(context, lifetimes)
|
||||
phaser.phase(KonanPhase.ESCAPE_ANALYSIS) {
|
||||
|
||||
+6
-4
@@ -99,7 +99,9 @@ internal class StaticFieldLlvmDeclarations(val storage: LLVMValueRef)
|
||||
* All fields of the class instance.
|
||||
* The order respects the class hierarchy, i.e. a class [fields] contains superclass [fields] as a prefix.
|
||||
*/
|
||||
internal fun ContextUtils.getFields(classDescriptor: ClassDescriptor): List<PropertyDescriptor> {
|
||||
internal fun ContextUtils.getFields(classDescriptor: ClassDescriptor) = context.getFields(classDescriptor)
|
||||
|
||||
internal fun Context.getFields(classDescriptor: ClassDescriptor): List<PropertyDescriptor> {
|
||||
val superClass = classDescriptor.getSuperClassNotAny() // TODO: what if Any has fields?
|
||||
val superFields = if (superClass != null) getFields(superClass) else emptyList()
|
||||
|
||||
@@ -109,7 +111,7 @@ internal fun ContextUtils.getFields(classDescriptor: ClassDescriptor): List<Prop
|
||||
/**
|
||||
* Fields declared in the class.
|
||||
*/
|
||||
private fun ContextUtils.getDeclaredFields(classDescriptor: ClassDescriptor): List<PropertyDescriptor> {
|
||||
private fun Context.getDeclaredFields(classDescriptor: ClassDescriptor): List<PropertyDescriptor> {
|
||||
// TODO: Here's what is going on here:
|
||||
// The existence of a backing field for a property is only described in the IR,
|
||||
// but not in the PropertyDescriptor.
|
||||
@@ -117,10 +119,10 @@ private fun ContextUtils.getDeclaredFields(classDescriptor: ClassDescriptor): Li
|
||||
// We mark serialized properties with a Konan protobuf extension bit,
|
||||
// so it is present in DeserializedPropertyDescriptor.
|
||||
//
|
||||
// In this function we check the presence of the backing filed
|
||||
// In this function we check the presence of the backing field
|
||||
// two ways: first we check IR, then we check the protobuf extension.
|
||||
|
||||
val irClass = context.ir.moduleIndexForCodegen.classes[classDescriptor]
|
||||
val irClass = ir.moduleIndexForCodegen.classes[classDescriptor]
|
||||
val fields = if (irClass != null) {
|
||||
val declarations = irClass.declarations
|
||||
|
||||
|
||||
+1
-11
@@ -208,17 +208,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
exportTypeInfoIfRequired(classDesc, classDesc.llvmTypeInfoPtr)
|
||||
}
|
||||
|
||||
internal val OverriddenFunctionDescriptor.implementation: FunctionDescriptor
|
||||
get() {
|
||||
val target = descriptor.target
|
||||
if (!needBridge) return target
|
||||
val bridgeOwner = if (inheritsBridge) {
|
||||
target // Bridge is inherited from superclass.
|
||||
} else {
|
||||
descriptor
|
||||
}
|
||||
return context.specialDeclarationsFactory.getBridgeDescriptor(OverriddenFunctionDescriptor(bridgeOwner, overriddenDescriptor))
|
||||
}
|
||||
private val OverriddenFunctionDescriptor.implementation get() = getImplementation(context)
|
||||
|
||||
data class ReflectionInfo(val packageName: String?, val relativeName: String?)
|
||||
|
||||
|
||||
+580
@@ -0,0 +1,580 @@
|
||||
package org.jetbrains.kotlin.backend.konan.optimizations
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.descriptors.allParameters
|
||||
import org.jetbrains.kotlin.backend.common.descriptors.isSuspend
|
||||
import org.jetbrains.kotlin.backend.common.ir.ir2stringWhole
|
||||
import org.jetbrains.kotlin.backend.common.peek
|
||||
import org.jetbrains.kotlin.backend.common.pop
|
||||
import org.jetbrains.kotlin.backend.common.push
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.target
|
||||
import org.jetbrains.kotlin.backend.konan.ir.IrSuspendableExpression
|
||||
import org.jetbrains.kotlin.backend.konan.ir.IrSuspensionPoint
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.functionName
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.localHash
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
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.declarations.IrVariable
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
|
||||
import org.jetbrains.kotlin.ir.util.getArguments
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
|
||||
private fun computeErasure(type: KotlinType, erasure: MutableList<KotlinType>) {
|
||||
val descriptor = type.constructor.declarationDescriptor
|
||||
when (descriptor) {
|
||||
is ClassDescriptor -> erasure += type.makeNotNullable()
|
||||
is TypeParameterDescriptor -> {
|
||||
descriptor.upperBounds.forEach {
|
||||
computeErasure(it, erasure)
|
||||
}
|
||||
}
|
||||
else -> TODO(descriptor.toString())
|
||||
}
|
||||
}
|
||||
|
||||
internal fun KotlinType.erasure(): List<KotlinType> {
|
||||
val result = mutableListOf<KotlinType>()
|
||||
computeErasure(this, result)
|
||||
return result
|
||||
}
|
||||
|
||||
private fun MemberScope.getOverridingOf(function: FunctionDescriptor) = when (function) {
|
||||
is PropertyGetterDescriptor ->
|
||||
this.getContributedVariables(function.correspondingProperty.name, NoLookupLocation.FROM_BACKEND)
|
||||
.firstOrNull { OverridingUtil.overrides(it, function.correspondingProperty) }?.getter
|
||||
|
||||
is PropertySetterDescriptor ->
|
||||
this.getContributedVariables(function.correspondingProperty.name, NoLookupLocation.FROM_BACKEND)
|
||||
.firstOrNull { OverridingUtil.overrides(it, function.correspondingProperty) }?.setter
|
||||
|
||||
else -> this.getContributedFunctions(function.name, NoLookupLocation.FROM_BACKEND)
|
||||
.firstOrNull { OverridingUtil.overrides(it, function) }
|
||||
}
|
||||
|
||||
private fun IrTypeOperator.isCast() =
|
||||
this == IrTypeOperator.CAST || this == IrTypeOperator.IMPLICIT_CAST || this == IrTypeOperator.SAFE_CAST
|
||||
|
||||
|
||||
private class VariableValues {
|
||||
val elementData = HashMap<VariableDescriptor, MutableSet<IrExpression>>()
|
||||
|
||||
fun addEmpty(variable: VariableDescriptor) =
|
||||
elementData.getOrPut(variable, { mutableSetOf() })
|
||||
|
||||
fun add(variable: VariableDescriptor, element: IrExpression) =
|
||||
elementData[variable]?.add(element)
|
||||
|
||||
fun add(variable: VariableDescriptor, elements: Set<IrExpression>) =
|
||||
elementData[variable]?.addAll(elements)
|
||||
|
||||
fun get(variable: VariableDescriptor): Set<IrExpression>? =
|
||||
elementData[variable]
|
||||
|
||||
fun computeClosure() {
|
||||
elementData.forEach { key, _ ->
|
||||
add(key, computeValueClosure(key))
|
||||
}
|
||||
}
|
||||
|
||||
// Computes closure of all possible values for given variable.
|
||||
private fun computeValueClosure(value: VariableDescriptor): Set<IrExpression> {
|
||||
val result = mutableSetOf<IrExpression>()
|
||||
val seen = mutableSetOf<VariableDescriptor>()
|
||||
dfs(value, seen, result)
|
||||
return result
|
||||
}
|
||||
|
||||
private fun dfs(value: VariableDescriptor, seen: MutableSet<VariableDescriptor>, result: MutableSet<IrExpression>) {
|
||||
seen += value
|
||||
val elements = elementData[value]
|
||||
?: return
|
||||
for (element in elements) {
|
||||
if (element !is IrGetValue)
|
||||
result += element
|
||||
else {
|
||||
val descriptor = element.descriptor
|
||||
if (descriptor is VariableDescriptor && !seen.contains(descriptor))
|
||||
dfs(descriptor, seen, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ExpressionValuesExtractor(val returnableBlockValues: Map<IrReturnableBlock, List<IrExpression>>,
|
||||
val suspendableExpressionValues: Map<IrSuspendableExpression, List<IrSuspensionPoint>>) {
|
||||
|
||||
fun forEachValue(expression: IrExpression, block: (IrExpression) -> Unit) {
|
||||
when (expression) {
|
||||
is IrReturnableBlock -> returnableBlockValues[expression]!!.forEach { forEachValue(it, block) }
|
||||
|
||||
is IrSuspendableExpression ->
|
||||
(suspendableExpressionValues[expression]!! + expression.result).forEach { forEachValue(it, block) }
|
||||
|
||||
is IrSuspensionPoint -> {
|
||||
forEachValue(expression.result, block)
|
||||
forEachValue(expression.resumeResult, block)
|
||||
}
|
||||
|
||||
is IrContainerExpression -> {
|
||||
if (expression.statements.isNotEmpty())
|
||||
forEachValue(expression.statements.last() as IrExpression, block)
|
||||
}
|
||||
|
||||
is IrWhen -> expression.branches.forEach { forEachValue(it.result, block) }
|
||||
|
||||
is IrMemberAccessExpression -> block(expression)
|
||||
|
||||
is IrGetValue -> block(expression)
|
||||
|
||||
is IrGetField -> block(expression)
|
||||
|
||||
is IrVararg -> /* Sometimes, we keep vararg till codegen phase (for constant arrays). */
|
||||
block(expression)
|
||||
|
||||
is IrConst<*> -> block(expression)
|
||||
|
||||
is IrTypeOperatorCall -> {
|
||||
if (!expression.operator.isCast())
|
||||
block(expression)
|
||||
else { // Propagate cast to sub-values.
|
||||
forEachValue(expression.argument) { value ->
|
||||
with(expression) {
|
||||
block(IrTypeOperatorCallImpl(startOffset, endOffset, type, operator, typeOperand, value))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is IrTry -> {
|
||||
forEachValue(expression.tryResult, block)
|
||||
expression.catches.forEach { forEachValue(it.result, block) }
|
||||
}
|
||||
|
||||
is IrGetObjectValue -> block(expression)
|
||||
|
||||
is IrFunctionReference -> block(expression)
|
||||
|
||||
is IrSetField -> block(expression)
|
||||
|
||||
else -> {
|
||||
if ((expression.type.isUnit() || expression.type.isNothing())) {
|
||||
block(IrGetObjectValueImpl(expression.startOffset, expression.endOffset,
|
||||
expression.type, (expression.type.constructor.declarationDescriptor as ClassDescriptor)))
|
||||
}
|
||||
else TODO(ir2stringWhole(expression))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class ModuleDFG(val functions: Map<DataFlowIR.FunctionSymbol, DataFlowIR.Function>,
|
||||
val symbolTable: DataFlowIR.SymbolTable)
|
||||
|
||||
internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFragment) {
|
||||
|
||||
private val DEBUG = 0
|
||||
|
||||
private inline fun DEBUG_OUTPUT(severity: Int, block: () -> Unit) {
|
||||
if (DEBUG > severity) block()
|
||||
}
|
||||
|
||||
private val module = DataFlowIR.Module(irModule.descriptor)
|
||||
private val symbolTable = DataFlowIR.SymbolTable(context, irModule, module)
|
||||
|
||||
// Possible values of a returnable block.
|
||||
private val returnableBlockValues = mutableMapOf<IrReturnableBlock, MutableList<IrExpression>>()
|
||||
|
||||
// All suspension points within specified suspendable expression.
|
||||
private val suspendableExpressionValues = mutableMapOf<IrSuspendableExpression, MutableList<IrSuspensionPoint>>()
|
||||
|
||||
private val expressionValuesExtractor = ExpressionValuesExtractor(returnableBlockValues, suspendableExpressionValues)
|
||||
|
||||
fun build(): ModuleDFG {
|
||||
val functions = mutableMapOf<DataFlowIR.FunctionSymbol, DataFlowIR.Function>()
|
||||
irModule.accept(object : IrElementVisitorVoid {
|
||||
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitFunction(declaration: IrFunction) {
|
||||
declaration.body?.let {
|
||||
DEBUG_OUTPUT(1) {
|
||||
println("Analysing function ${declaration.descriptor}")
|
||||
println("IR: ${ir2stringWhole(declaration)}")
|
||||
}
|
||||
analyze(declaration.descriptor, it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitField(declaration: IrField) {
|
||||
declaration.initializer?.let {
|
||||
DEBUG_OUTPUT(1) {
|
||||
println("Analysing global field ${declaration.descriptor}")
|
||||
println("IR: ${ir2stringWhole(declaration)}")
|
||||
}
|
||||
analyze(declaration.descriptor, it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun analyze(descriptor: CallableDescriptor, body: IrElement) {
|
||||
// Find all interesting expressions, variables and functions.
|
||||
val visitor = ElementFinderVisitor()
|
||||
body.acceptVoid(visitor)
|
||||
|
||||
DEBUG_OUTPUT(1) {
|
||||
println("FIRST PHASE")
|
||||
visitor.variableValues.elementData.forEach { t, u ->
|
||||
println("VAR $t:")
|
||||
u.forEach {
|
||||
println(" ${ir2stringWhole(it)}")
|
||||
}
|
||||
}
|
||||
visitor.expressions.forEach { t ->
|
||||
println("EXP ${ir2stringWhole(t)}")
|
||||
}
|
||||
}
|
||||
|
||||
// Compute transitive closure of possible values for variables.
|
||||
visitor.variableValues.computeClosure()
|
||||
|
||||
DEBUG_OUTPUT(1) {
|
||||
println("SECOND PHASE")
|
||||
visitor.variableValues.elementData.forEach { t, u ->
|
||||
println("VAR $t:")
|
||||
u.forEach {
|
||||
println(" ${ir2stringWhole(it)}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val function = FunctionDFGBuilder(expressionValuesExtractor, visitor.variableValues,
|
||||
descriptor, visitor.expressions, visitor.returnValues).build()
|
||||
|
||||
DEBUG_OUTPUT(1) {
|
||||
function.debugOutput()
|
||||
}
|
||||
|
||||
functions.put(function.symbol, function)
|
||||
}
|
||||
}, data = null)
|
||||
|
||||
DEBUG_OUTPUT(2) {
|
||||
println("SYMBOL TABLE:")
|
||||
symbolTable.classMap.forEach { descriptor, type ->
|
||||
println(" DESCRIPTOR: $descriptor")
|
||||
println(" TYPE: $type")
|
||||
if (type !is DataFlowIR.Type.Declared)
|
||||
return@forEach
|
||||
println(" SUPER TYPES:")
|
||||
type.superTypes.forEach { println(" $it") }
|
||||
println(" VTABLE:")
|
||||
type.vtable.forEach { println(" $it") }
|
||||
println(" ITABLE:")
|
||||
type.itable.forEach { println(" ${it.key} -> ${it.value}") }
|
||||
}
|
||||
}
|
||||
|
||||
return ModuleDFG(functions, symbolTable)
|
||||
}
|
||||
|
||||
private inner class ElementFinderVisitor : IrElementVisitorVoid {
|
||||
|
||||
val expressions = mutableListOf<IrExpression>()
|
||||
val variableValues = VariableValues()
|
||||
val returnValues = mutableListOf<IrExpression>()
|
||||
|
||||
private val returnableBlocks = mutableMapOf<FunctionDescriptor, IrReturnableBlock>()
|
||||
private val suspendableExpressionStack = mutableListOf<IrSuspendableExpression>()
|
||||
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
private fun assignVariable(variable: VariableDescriptor, value: IrExpression) {
|
||||
expressionValuesExtractor.forEachValue(value) {
|
||||
variableValues.add(variable, it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitExpression(expression: IrExpression) {
|
||||
when (expression) {
|
||||
is IrMemberAccessExpression,
|
||||
is IrGetField,
|
||||
is IrGetObjectValue,
|
||||
is IrVararg,
|
||||
is IrConst<*>,
|
||||
is IrTypeOperatorCall ->
|
||||
expressions += expression
|
||||
}
|
||||
|
||||
if (expression is IrReturnableBlock) {
|
||||
returnableBlocks.put(expression.descriptor, expression)
|
||||
returnableBlockValues.put(expression, mutableListOf())
|
||||
}
|
||||
if (expression is IrSuspendableExpression) {
|
||||
suspendableExpressionStack.push(expression)
|
||||
suspendableExpressionValues.put(expression, mutableListOf())
|
||||
}
|
||||
if (expression is IrSuspensionPoint)
|
||||
suspendableExpressionValues[suspendableExpressionStack.peek()!!]!!.add(expression)
|
||||
super.visitExpression(expression)
|
||||
if (expression is IrReturnableBlock)
|
||||
returnableBlocks.remove(expression.descriptor)
|
||||
if (expression is IrSuspendableExpression)
|
||||
suspendableExpressionStack.pop()
|
||||
}
|
||||
|
||||
override fun visitSetField(expression: IrSetField) {
|
||||
expressions += expression
|
||||
super.visitSetField(expression)
|
||||
}
|
||||
|
||||
// TODO: hack to overcome bad code in InlineConstructorsTransformation.
|
||||
private val FQ_NAME_INLINE_CONSTRUCTOR = FqName("konan.internal.InlineConstructor")
|
||||
|
||||
override fun visitReturn(expression: IrReturn) {
|
||||
val returnableBlock = returnableBlocks[expression.returnTarget]
|
||||
if (returnableBlock != null) {
|
||||
returnableBlockValues[returnableBlock]!!.add(expression.value)
|
||||
} else { // Non-local return.
|
||||
if (!expression.type.isUnit()) {
|
||||
if (!expression.returnTarget.annotations.hasAnnotation(FQ_NAME_INLINE_CONSTRUCTOR)) // Not inline constructor.
|
||||
returnValues += expression.value
|
||||
}
|
||||
}
|
||||
super.visitReturn(expression)
|
||||
}
|
||||
|
||||
override fun visitSetVariable(expression: IrSetVariable) {
|
||||
super.visitSetVariable(expression)
|
||||
assignVariable(expression.descriptor, expression.value)
|
||||
}
|
||||
|
||||
override fun visitVariable(declaration: IrVariable) {
|
||||
variableValues.addEmpty(declaration.descriptor)
|
||||
super.visitVariable(declaration)
|
||||
declaration.initializer?.let { assignVariable(declaration.descriptor, it) }
|
||||
}
|
||||
}
|
||||
|
||||
private val doResumeFunctionDescriptor = context.getInternalClass("CoroutineImpl").unsubstitutedMemberScope
|
||||
.getContributedFunctions(Name.identifier("doResume"), NoLookupLocation.FROM_BACKEND).single()
|
||||
private val getContinuationSymbol = context.ir.symbols.getContinuation
|
||||
|
||||
private inner class FunctionDFGBuilder(val expressionValuesExtractor: ExpressionValuesExtractor,
|
||||
val variableValues: VariableValues,
|
||||
val descriptor: CallableDescriptor,
|
||||
val expressions: List<IrExpression>,
|
||||
val returnValues: List<IrExpression>) {
|
||||
|
||||
private val allParameters = (descriptor as? FunctionDescriptor)?.allParameters ?: emptyList()
|
||||
private val templateParameters = allParameters.withIndex().associateBy({ it.value }, { DataFlowIR.Node.Parameter(it.index) })
|
||||
|
||||
private val continuationParameter =
|
||||
if (descriptor.isSuspend)
|
||||
DataFlowIR.Node.Parameter(allParameters.size)
|
||||
else {
|
||||
if (doResumeFunctionDescriptor in descriptor.overriddenDescriptors) // <this> is a CoroutineImpl inheritor.
|
||||
templateParameters[descriptor.dispatchReceiverParameter!!] // It is its own continuation.
|
||||
else null
|
||||
}
|
||||
|
||||
private fun getContinuation() = continuationParameter ?: error("Function $descriptor has no continuation parameter")
|
||||
|
||||
private val nodes = mutableMapOf<IrExpression, DataFlowIR.Node>()
|
||||
private val variables = variableValues.elementData.keys.associateBy(
|
||||
{ it },
|
||||
{ DataFlowIR.Node.Variable(mutableListOf(), false) }
|
||||
)
|
||||
|
||||
fun build(): DataFlowIR.Function {
|
||||
expressions.forEach { getNode(it) }
|
||||
val returnsNode = DataFlowIR.Node.Variable(returnValues.map { expressionToEdge(it) }, true)
|
||||
variables.forEach { descriptor, node ->
|
||||
variableValues.elementData[descriptor]!!.forEach {
|
||||
node.values += expressionToEdge(it)
|
||||
}
|
||||
}
|
||||
val allNodes = nodes.values + variables.values + templateParameters.values + returnsNode +
|
||||
(if (descriptor.isSuspend) listOf(continuationParameter!!) else emptyList())
|
||||
|
||||
return DataFlowIR.Function(
|
||||
symbol = symbolTable.mapFunction(descriptor),
|
||||
isGlobalInitializer = descriptor is PropertyDescriptor,
|
||||
numberOfParameters = templateParameters.size + if (descriptor.isSuspend) 1 else 0,
|
||||
body = DataFlowIR.FunctionBody(allNodes.distinct().toList(), returnsNode)
|
||||
)
|
||||
}
|
||||
|
||||
private fun expressionToEdge(expression: IrExpression) =
|
||||
if (expression is IrTypeOperatorCall && expression.operator.isCast())
|
||||
DataFlowIR.Edge(getNode(expression.argument), symbolTable.mapType(expression.typeOperand))
|
||||
else DataFlowIR.Edge(getNode(expression), null)
|
||||
|
||||
private fun getNode(expression: IrExpression): DataFlowIR.Node {
|
||||
if (expression is IrGetValue) {
|
||||
val descriptor = expression.descriptor
|
||||
if (descriptor is ParameterDescriptor)
|
||||
return templateParameters[descriptor]!!
|
||||
return variables[descriptor as VariableDescriptor]!!
|
||||
}
|
||||
return nodes.getOrPut(expression) {
|
||||
DEBUG_OUTPUT(1) {
|
||||
println("Converting expression")
|
||||
println(ir2stringWhole(expression))
|
||||
}
|
||||
val values = mutableListOf<IrExpression>()
|
||||
expressionValuesExtractor.forEachValue(expression) { values += it }
|
||||
if (values.size != 1) {
|
||||
DataFlowIR.Node.Variable(values.map { expressionToEdge(it) }, true)
|
||||
} else {
|
||||
val value = values[0]
|
||||
if (value != expression) {
|
||||
val edge = expressionToEdge(value)
|
||||
if (edge.castToType == null)
|
||||
edge.node
|
||||
else
|
||||
DataFlowIR.Node.Variable(listOf(edge), true)
|
||||
} else {
|
||||
when (value) {
|
||||
is IrGetValue -> getNode(value)
|
||||
|
||||
is IrVararg,
|
||||
is IrConst<*>,
|
||||
is IrFunctionReference -> DataFlowIR.Node.Const(symbolTable.mapType(value.type))
|
||||
|
||||
is IrGetObjectValue -> DataFlowIR.Node.Singleton(
|
||||
symbolTable.mapType(value.type),
|
||||
if (value.type.isNothing()) // <Nothing> is not a singleton though its instance is get with <IrGetObject> operation.
|
||||
null
|
||||
else symbolTable.mapFunction(value.descriptor.constructors.single())
|
||||
)
|
||||
|
||||
is IrCall -> {
|
||||
if (value.symbol == getContinuationSymbol) {
|
||||
getContinuation()
|
||||
} else {
|
||||
val callee = value.descriptor
|
||||
val arguments = value.getArguments()
|
||||
.map { expressionToEdge(it.second) }
|
||||
.let {
|
||||
if (callee.isSuspend)
|
||||
it + DataFlowIR.Edge(getContinuation(), null)
|
||||
else
|
||||
it
|
||||
}
|
||||
if (callee is ConstructorDescriptor) {
|
||||
DataFlowIR.Node.NewObject(
|
||||
symbolTable.mapFunction(callee),
|
||||
arguments,
|
||||
symbolTable.mapClass(callee.constructedClass)
|
||||
)
|
||||
} else {
|
||||
if (callee.isOverridable && value.superQualifier == null) {
|
||||
val owner = callee.containingDeclaration as ClassDescriptor
|
||||
val vTableBuilder = context.getVtableBuilder(owner)
|
||||
if (owner.isInterface) {
|
||||
DataFlowIR.Node.ItableCall(
|
||||
symbolTable.mapFunction(callee.target),
|
||||
symbolTable.mapClass(owner),
|
||||
callee.functionName.localHash.value,
|
||||
arguments,
|
||||
symbolTable.mapType(callee.returnType!!),
|
||||
value
|
||||
)
|
||||
} else {
|
||||
val vtableIndex = vTableBuilder.vtableIndex(callee)
|
||||
assert(vtableIndex >= 0, { "Unable to find function $callee in vtable of $owner" })
|
||||
DataFlowIR.Node.VtableCall(
|
||||
symbolTable.mapFunction(callee.target),
|
||||
symbolTable.mapClass(owner),
|
||||
vtableIndex,
|
||||
arguments,
|
||||
symbolTable.mapType(callee.returnType!!),
|
||||
value
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val actualCallee = (value.superQualifier?.unsubstitutedMemberScope?.getOverridingOf(callee) ?: callee).target
|
||||
DataFlowIR.Node.StaticCall(
|
||||
symbolTable.mapFunction(actualCallee),
|
||||
arguments,
|
||||
symbolTable.mapType(actualCallee.returnType!!),
|
||||
actualCallee.dispatchReceiverParameter?.let { symbolTable.mapType(it.type) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is IrDelegatingConstructorCall -> {
|
||||
val thiz = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||
(descriptor as ConstructorDescriptor).constructedClass.thisAsReceiverParameter)
|
||||
val arguments = listOf(thiz) + value.getArguments().map { it.second }
|
||||
DataFlowIR.Node.StaticCall(
|
||||
symbolTable.mapFunction(value.descriptor),
|
||||
arguments.map { expressionToEdge(it) },
|
||||
symbolTable.mapClass(context.builtIns.unit),
|
||||
symbolTable.mapType(thiz.type)
|
||||
)
|
||||
}
|
||||
|
||||
is IrGetField -> {
|
||||
val receiver = value.receiver?.let { expressionToEdge(it) }
|
||||
val receiverType = value.receiver?.let { symbolTable.mapType(it.type) }
|
||||
DataFlowIR.Node.FieldRead(
|
||||
receiver,
|
||||
DataFlowIR.Field(
|
||||
receiverType,
|
||||
value.descriptor.name.asString()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
is IrSetField -> {
|
||||
val receiver = value.receiver?.let { expressionToEdge(it) }
|
||||
val receiverType = value.receiver?.let { symbolTable.mapType(it.type) }
|
||||
DataFlowIR.Node.FieldWrite(
|
||||
receiver,
|
||||
DataFlowIR.Field(
|
||||
receiverType,
|
||||
value.descriptor.name.asString()
|
||||
),
|
||||
expressionToEdge(value.value)
|
||||
)
|
||||
}
|
||||
|
||||
is IrTypeOperatorCall -> {
|
||||
assert(!value.operator.isCast(), { "Casts should've been handled earlier" })
|
||||
expressionToEdge(value.argument) // Put argument as a separate vertex.
|
||||
DataFlowIR.Node.Const(symbolTable.mapType(value.type)) // All operators except casts are basically constants.
|
||||
}
|
||||
|
||||
else -> TODO("Unknown expression: ${ir2stringWhole(value)}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+434
@@ -0,0 +1,434 @@
|
||||
package org.jetbrains.kotlin.backend.konan.optimizations
|
||||
|
||||
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.llvm.functionName
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.isExported
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.localHash
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.symbolName
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
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.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes
|
||||
|
||||
internal object DataFlowIR {
|
||||
|
||||
abstract class Type {
|
||||
// Special marker type forbidding devirtualization on its instances.
|
||||
object Virtual : Declared(false, true)
|
||||
|
||||
class External(val name: String) : Type() {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is External) return false
|
||||
|
||||
return name == other.name
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return name.hashCode()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "ExternalType(name='$name')"
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Declared(val isFinal: Boolean, val isAbstract: Boolean) : Type() {
|
||||
val superTypes = mutableListOf<Type>()
|
||||
val vtable = mutableListOf<FunctionSymbol>()
|
||||
val itable = mutableMapOf<Long, FunctionSymbol>()
|
||||
}
|
||||
|
||||
class Public(val name: String, isFinal: Boolean, isAbstract: Boolean) : Declared(isFinal, isAbstract) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is Public) return false
|
||||
|
||||
return name == other.name
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return name.hashCode()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "PublicType(name='$name')"
|
||||
}
|
||||
}
|
||||
|
||||
class Private(val name: String, val index: Int, isFinal: Boolean, isAbstract: Boolean) : Declared(isFinal, isAbstract) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is Private) return false
|
||||
|
||||
return index == other.index
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return index
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "PrivateType(index=$index, name='$name')"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Module(val descriptor: ModuleDescriptor) {
|
||||
var numberOfFunctions = 0
|
||||
}
|
||||
|
||||
abstract class FunctionSymbol {
|
||||
class External(val name: String) : FunctionSymbol() {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is External) return false
|
||||
|
||||
return name == other.name
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return name.hashCode()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "ExternalFunction(name='$name')"
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Declared(val module: Module, val symbolTableIndex: Int) : FunctionSymbol()
|
||||
|
||||
class Public(val name: String, module: Module, symbolTableIndex: Int) : Declared(module, symbolTableIndex) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is Public) return false
|
||||
|
||||
return name == other.name
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return name.hashCode()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "PublicFunction(name='$name')"
|
||||
}
|
||||
}
|
||||
|
||||
class Private(val name: String, val index: Int, module: Module, symbolTableIndex: Int) : Declared(module, symbolTableIndex) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is Private) return false
|
||||
|
||||
return index == other.index
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return index
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "PrivateFunction(index=$index, name='$name')"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Field(val type: Type?, val name: String)
|
||||
|
||||
class Edge(val castToType: Type?) {
|
||||
|
||||
lateinit var node: Node
|
||||
|
||||
constructor(node: Node, castToType: Type?) : this(castToType) {
|
||||
this.node = node
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Node {
|
||||
class Parameter(val index: Int) : Node()
|
||||
|
||||
class Const(val type: Type) : Node()
|
||||
|
||||
open class Call(val callee: FunctionSymbol, val arguments: List<Edge>, val returnType: Type) : Node()
|
||||
|
||||
class StaticCall(callee: FunctionSymbol, arguments: List<Edge>, returnType: Type,
|
||||
val receiverType: Type?) : Call(callee, arguments, returnType)
|
||||
|
||||
class NewObject(constructor: FunctionSymbol, arguments: List<Edge>, type: Type) : Call(constructor, arguments, type)
|
||||
|
||||
open class VirtualCall(callee: FunctionSymbol, arguments: List<Edge>, returnType: Type,
|
||||
val receiverType: Type, val callSite: IrCall?) : Call(callee, arguments, returnType)
|
||||
|
||||
class VtableCall(callee: FunctionSymbol, receiverType: Type, val calleeVtableIndex: Int,
|
||||
arguments: List<Edge>, returnType: Type, callSite: IrCall?)
|
||||
: VirtualCall(callee, arguments, returnType, receiverType, callSite)
|
||||
|
||||
class ItableCall(callee: FunctionSymbol, receiverType: Type, val calleeHash: Long,
|
||||
arguments: List<Edge>, returnType: Type, callSite: IrCall?)
|
||||
: VirtualCall(callee, arguments, returnType, receiverType, callSite)
|
||||
|
||||
class Singleton(val type: Type, val constructor: FunctionSymbol?) : Node()
|
||||
|
||||
class FieldRead(val receiver: Edge?, val field: Field) : Node()
|
||||
|
||||
class FieldWrite(val receiver: Edge?, val field: Field, val value: Edge) : Node()
|
||||
|
||||
class Variable(values: List<Edge>, val temp: Boolean) : Node() {
|
||||
val values = mutableListOf<Edge>().also { it += values }
|
||||
}
|
||||
}
|
||||
|
||||
class FunctionBody(val nodes: List<Node>, val returns: Node.Variable)
|
||||
|
||||
class Function(val symbol: FunctionSymbol,
|
||||
val isGlobalInitializer: Boolean,
|
||||
val numberOfParameters: Int,
|
||||
val body: FunctionBody) {
|
||||
|
||||
|
||||
fun printNode(node: Node, ids: Map<Node, Int>) {
|
||||
when (node) {
|
||||
is Node.Const ->
|
||||
println(" CONST ${node.type}")
|
||||
|
||||
is Node.Parameter ->
|
||||
println(" PARAM ${node.index}")
|
||||
|
||||
is Node.Singleton ->
|
||||
println(" SINGLETON ${node.type}")
|
||||
|
||||
is Node.StaticCall -> {
|
||||
println(" STATIC CALL ${node.callee}")
|
||||
node.arguments.forEach {
|
||||
print(" ARG #${ids[it.node]!!}")
|
||||
if (it.castToType == null)
|
||||
println()
|
||||
else
|
||||
println(" CASTED TO ${it.castToType}")
|
||||
}
|
||||
}
|
||||
|
||||
is Node.VtableCall -> {
|
||||
println(" VIRTUAL CALL ${node.callee}")
|
||||
println(" RECEIVER: ${node.receiverType}")
|
||||
println(" VTABLE INDEX: ${node.calleeVtableIndex}")
|
||||
node.arguments.forEach {
|
||||
print(" ARG #${ids[it.node]!!}")
|
||||
if (it.castToType == null)
|
||||
println()
|
||||
else
|
||||
println(" CASTED TO ${it.castToType}")
|
||||
}
|
||||
}
|
||||
|
||||
is Node.ItableCall -> {
|
||||
println(" INTERFACE CALL ${node.callee}")
|
||||
println(" RECEIVER: ${node.receiverType}")
|
||||
println(" METHOD HASH: ${node.calleeHash}")
|
||||
node.arguments.forEach {
|
||||
print(" ARG #${ids[it.node]!!}")
|
||||
if (it.castToType == null)
|
||||
println()
|
||||
else
|
||||
println(" CASTED TO ${it.castToType}")
|
||||
}
|
||||
}
|
||||
|
||||
is Node.NewObject -> {
|
||||
println(" NEW OBJECT ${node.callee}")
|
||||
println(" TYPE ${node.returnType}")
|
||||
node.arguments.forEach {
|
||||
print(" ARG #${ids[it.node]!!}")
|
||||
if (it.castToType == null)
|
||||
println()
|
||||
else
|
||||
println(" CASTED TO ${it.castToType}")
|
||||
}
|
||||
}
|
||||
|
||||
is Node.FieldRead -> {
|
||||
println(" FIELD READ ${node.field}")
|
||||
print(" RECEIVER #${node.receiver?.node?.let { ids[it]!! } ?: "null"}")
|
||||
if (node.receiver?.castToType == null)
|
||||
println()
|
||||
else
|
||||
println(" CASTED TO ${node.receiver.castToType}")
|
||||
}
|
||||
|
||||
is Node.FieldWrite -> {
|
||||
println(" FIELD WRITE ${node.field}")
|
||||
print(" RECEIVER #${node.receiver?.node?.let { ids[it]!! } ?: "null"}")
|
||||
if (node.receiver?.castToType == null)
|
||||
println()
|
||||
else
|
||||
println(" CASTED TO ${node.receiver.castToType}")
|
||||
print(" VALUE #${ids[node.value.node]!!}")
|
||||
if (node.value.castToType == null)
|
||||
println()
|
||||
else
|
||||
println(" CASTED TO ${node.value.castToType}")
|
||||
}
|
||||
|
||||
is Node.Variable -> {
|
||||
println(" ${if (node.temp) "TEMP VAR" else "VARIABLE"} ")
|
||||
node.values.forEach {
|
||||
print(" VAL #${ids[it.node]!!}")
|
||||
if (it.castToType == null)
|
||||
println()
|
||||
else
|
||||
println(" CASTED TO ${it.castToType}")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else -> {
|
||||
println(" UNKNOWN: ${node::class.java}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun debugOutput() {
|
||||
println("FUNCTION TEMPLATE $symbol")
|
||||
println("Params: $numberOfParameters")
|
||||
val ids = body.nodes.withIndex().associateBy({ it.value }, { it.index })
|
||||
body.nodes.forEach {
|
||||
println(" NODE #${ids[it]!!}")
|
||||
printNode(it, ids)
|
||||
}
|
||||
println(" RETURNS")
|
||||
printNode(body.returns, ids)
|
||||
}
|
||||
}
|
||||
|
||||
class SymbolTable(val context: Context, val irModule: IrModuleFragment, val module: Module) {
|
||||
val classMap = mutableMapOf<ClassDescriptor, Type>()
|
||||
val functionMap = mutableMapOf<CallableDescriptor, FunctionSymbol>()
|
||||
|
||||
var privateTypeIndex = 0
|
||||
var privateFunIndex = 0
|
||||
var couldBeCalledVirtuallyIndex = 0
|
||||
|
||||
init {
|
||||
irModule.accept(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitFunction(declaration: IrFunction) {
|
||||
declaration.body?.let { mapFunction(declaration.descriptor) }
|
||||
}
|
||||
|
||||
override fun visitField(declaration: IrField) {
|
||||
declaration.initializer?.let { mapFunction(declaration.descriptor) }
|
||||
}
|
||||
|
||||
override fun visitClass(declaration: IrClass) {
|
||||
declaration.acceptChildrenVoid(this)
|
||||
|
||||
mapClass(declaration.descriptor)
|
||||
}
|
||||
}, data = null)
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.isFinal() = modality == Modality.FINAL && kind != ClassKind.ENUM_CLASS
|
||||
|
||||
fun mapClass(descriptor: ClassDescriptor): Type {
|
||||
// Do not try to devirtualize ObjC classes.
|
||||
if (descriptor.module.name == Name.special("<forward declarations>") || descriptor.isObjCClass())
|
||||
return Type.Virtual
|
||||
|
||||
val name = descriptor.fqNameSafe.asString()
|
||||
if (descriptor.module != irModule.descriptor)
|
||||
return classMap.getOrPut(descriptor) { Type.External(name) }
|
||||
|
||||
classMap[descriptor]?.let { return it }
|
||||
|
||||
val isFinal = descriptor.isFinal()
|
||||
val isAbstract = descriptor.isAbstract()
|
||||
val type = if (descriptor.isExported())
|
||||
Type.Public(name, isFinal, isAbstract)
|
||||
else
|
||||
Type.Private(name, privateTypeIndex++, isFinal, isAbstract)
|
||||
if (!descriptor.isInterface) {
|
||||
val vtableBuilder = context.getVtableBuilder(descriptor)
|
||||
type.vtable += vtableBuilder.vtableEntries.map { mapFunction(it.getImplementation(context)) }
|
||||
if (!isAbstract) {
|
||||
vtableBuilder.methodTableEntries.forEach {
|
||||
type.itable.put(
|
||||
it.overriddenDescriptor.functionName.localHash.value,
|
||||
mapFunction(it.getImplementation(context))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
classMap.put(descriptor, type)
|
||||
type.superTypes += descriptor.defaultType.immediateSupertypes().map { mapType(it) }
|
||||
return type
|
||||
}
|
||||
|
||||
fun mapType(type: KotlinType) =
|
||||
mapClass(type.erasure().single().constructor.declarationDescriptor as ClassDescriptor)
|
||||
|
||||
// TODO: use from LlvmDeclarations.
|
||||
private fun getFqName(descriptor: DeclarationDescriptor): FqName {
|
||||
if (descriptor is PackageFragmentDescriptor) {
|
||||
return descriptor.fqName
|
||||
}
|
||||
|
||||
val containingDeclaration = descriptor.containingDeclaration
|
||||
val parent = if (containingDeclaration != null) {
|
||||
getFqName(containingDeclaration)
|
||||
} else {
|
||||
FqName.ROOT
|
||||
}
|
||||
|
||||
val localName = descriptor.name
|
||||
return parent.child(localName)
|
||||
}
|
||||
|
||||
fun mapFunction(descriptor: CallableDescriptor) = descriptor.original.let {
|
||||
functionMap.getOrPut(it) {
|
||||
when (it) {
|
||||
is PropertyDescriptor ->
|
||||
FunctionSymbol.Private("${it.symbolName}_init", privateFunIndex++, module, -1)
|
||||
|
||||
is FunctionDescriptor -> {
|
||||
if (it.module != irModule.descriptor || it.externalOrIntrinsic())
|
||||
FunctionSymbol.External(it.symbolName)
|
||||
else {
|
||||
val isAbstract = it.modality == Modality.ABSTRACT
|
||||
val classDescriptor = it.containingDeclaration as? ClassDescriptor
|
||||
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++
|
||||
if (it.isExported())
|
||||
FunctionSymbol.Public(it.symbolName, module, symbolTableIndex)
|
||||
else
|
||||
FunctionSymbol.Private(getFqName(it).asString() + "#internal", privateFunIndex++, module, symbolTableIndex)
|
||||
}
|
||||
}
|
||||
|
||||
else -> error("Unknown descriptor: $it")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
@@ -41,3 +41,5 @@ fun String.suffixIfNot(suffix: String) =
|
||||
|
||||
fun String.removeSuffixIfPresent(suffix: String) =
|
||||
if (this.endsWith(suffix)) this.dropLast(suffix.length) else this
|
||||
|
||||
fun <T> Lazy<T>.getValueOrNull(): T? = if (isInitialized()) value else null
|
||||
Reference in New Issue
Block a user