Extremely conservative escape analysis. (#212)
This commit is contained in:
+481
@@ -0,0 +1,481 @@
|
||||
package org.jetbrains.kotlin.backend.konan
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.allValueParameters
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.Lifetime
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.RuntimeAware
|
||||
import org.jetbrains.kotlin.backend.konan.ir.getArguments
|
||||
import org.jetbrains.kotlin.backend.konan.ir.ir2string
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.isObjectType
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
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.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||
|
||||
val DEBUG = false
|
||||
|
||||
// Roles in which particular object reference is being used. Lifetime is computed from
|
||||
// all roles reference.
|
||||
internal enum class Role {
|
||||
// If reference is created as call result.
|
||||
CALL_RESULT,
|
||||
// If reference is created as allocation call result.
|
||||
ALLOC_RESULT,
|
||||
// If reference is being returned.
|
||||
RETURN_VALUE,
|
||||
// If reference is being thrown.
|
||||
THROW_VALUE,
|
||||
// If reference's field is being read.
|
||||
FIELD_READ,
|
||||
// If reference's field is being written to.
|
||||
FIELD_WRITTEN,
|
||||
// If reference is being read from the field.
|
||||
READ_FROM_FIELD,
|
||||
// If reference is being written to the field.
|
||||
WRITTEN_TO_FIELD,
|
||||
// If reference is being read from the gloabl.
|
||||
READ_FROM_GLOBAL,
|
||||
// If reference is being written to the global.
|
||||
WRITTEN_TO_GLOBAL,
|
||||
// Outgoing call argument.
|
||||
CALL_ARGUMENT
|
||||
}
|
||||
|
||||
internal class RoleInfoEntry(val data: Any? = null)
|
||||
|
||||
internal open class RoleInfo {
|
||||
val entries = mutableListOf<RoleInfoEntry>()
|
||||
open fun add(entry: RoleInfoEntry) = entries.add(entry)
|
||||
}
|
||||
|
||||
private fun RuntimeAware.isInteresting(type: KotlinType) : Boolean =
|
||||
!type.isUnit() && !type.isNothing() && isObjectType(type)
|
||||
|
||||
internal class Roles {
|
||||
val data = HashMap<Role, RoleInfo>()
|
||||
|
||||
fun add(role: Role, info: RoleInfoEntry?) {
|
||||
val entry = data.getOrPut(role, { RoleInfo() })
|
||||
if (info != null) entry.add(info)
|
||||
}
|
||||
|
||||
fun add(roles: Roles) {
|
||||
roles.data.forEach { role, info ->
|
||||
info.entries.forEach { entry ->
|
||||
add(role, entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun remove(role: Role) = data.remove(role)
|
||||
|
||||
fun has(role: Role) : Boolean = data[role] != null
|
||||
|
||||
fun escapes() : Boolean {
|
||||
return has(Role.WRITTEN_TO_FIELD) || has(Role.WRITTEN_TO_GLOBAL) ||
|
||||
has(Role.CALL_ARGUMENT) || has(Role.THROW_VALUE)
|
||||
}
|
||||
|
||||
fun pure() : Boolean = !escapes() && has(Role.RETURN_VALUE)
|
||||
|
||||
fun info(role: Role) : RoleInfo? = data[role]
|
||||
|
||||
override fun toString() : String {
|
||||
val builder = StringBuilder()
|
||||
data.forEach { t, u ->
|
||||
builder.append(t.name)
|
||||
builder.append(": ")
|
||||
builder.append(u.entries.joinToString(", "))
|
||||
builder.append("; ")
|
||||
}
|
||||
return builder.toString()
|
||||
}
|
||||
}
|
||||
|
||||
internal class VariableValues {
|
||||
val elementData = HashMap<ValueDescriptor, MutableSet<IrExpression>>()
|
||||
|
||||
fun addEmpty(variable: ValueDescriptor) =
|
||||
elementData.getOrPut(variable, { mutableSetOf<IrExpression>() })
|
||||
fun add(variable: ValueDescriptor, element: IrExpression) =
|
||||
elementData.get(variable)?.add(element)
|
||||
fun add(variable: ValueDescriptor, elements: Set<IrExpression>) =
|
||||
elementData.get(variable)?.addAll(elements)
|
||||
fun get(variable: ValueDescriptor) : Set<IrExpression>? =
|
||||
elementData[variable]
|
||||
|
||||
fun computeClosure() {
|
||||
elementData.forEach { key, _ ->
|
||||
add(key, computeValueClosure(key))
|
||||
}
|
||||
}
|
||||
|
||||
// Computes closure of all possible values for given variable.
|
||||
fun computeValueClosure(value: ValueDescriptor): Set<IrExpression> {
|
||||
val result = mutableSetOf<IrExpression>()
|
||||
val workset = mutableSetOf<ValueDescriptor>(value)
|
||||
val seen = mutableSetOf<IrGetValue>()
|
||||
while (!workset.isEmpty()) {
|
||||
val value = workset.first()
|
||||
workset -= value
|
||||
val elements = elementData[value] ?: continue
|
||||
for (element in elements) {
|
||||
if (element is IrGetValue) {
|
||||
if (!seen.contains(element)) {
|
||||
seen.add(element)
|
||||
workset.add(element.descriptor)
|
||||
}
|
||||
} else {
|
||||
result.add(element)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
internal class ParameterRoles {
|
||||
val elementData = HashMap<ParameterDescriptor, Roles>()
|
||||
|
||||
fun addParameter(parameter: ParameterDescriptor) {
|
||||
elementData.getOrPut(parameter) { Roles() }
|
||||
}
|
||||
|
||||
fun add(parameter: ParameterDescriptor, role: Role, roleInfoEntry: RoleInfoEntry?) {
|
||||
val roles = elementData.getOrPut(parameter, { Roles() });
|
||||
roles.add(role, roleInfoEntry)
|
||||
}
|
||||
|
||||
fun canEliminateCallArgument(info: RoleInfo): Boolean {
|
||||
for (call in info.entries) {
|
||||
val entry = call.data as Pair<ParameterDescriptor, CallableDescriptor>
|
||||
val argument = entry.first
|
||||
val callee = entry.second
|
||||
// Virtual dispatch.
|
||||
if (callee is FunctionDescriptor && callee.isOverridable) return false
|
||||
if (DEBUG) println("passed as ${argument}")
|
||||
val rolesInCallee = elementData[argument] ?: return false
|
||||
if (DEBUG) println("In callee is: $rolesInCallee")
|
||||
// TODO: make recursive computation here.
|
||||
if (rolesInCallee.escapes()) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun propagateCallArguments(roles: Roles): Boolean {
|
||||
val info = roles.info(Role.CALL_ARGUMENT) ?: return false
|
||||
if (canEliminateCallArgument(info)) {
|
||||
roles.remove(Role.CALL_ARGUMENT)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun propagateCallArguments(expressionToRoles: MutableMap<IrExpression, Roles>) {
|
||||
expressionToRoles.forEach {
|
||||
if (propagateCallArguments(it.value) && DEBUG)
|
||||
println("unescaped expression ${it.key}")
|
||||
}
|
||||
elementData.forEach {
|
||||
if (propagateCallArguments(it.value) && DEBUG)
|
||||
println("unescaped paramater ${it.key}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun rolesToLifetime(roles: Roles) : Lifetime {
|
||||
val roleSetSize = roles.data.keys.size
|
||||
return when {
|
||||
// If reference is stored to global or generic field - it must be global.
|
||||
roles.escapes() ->
|
||||
Lifetime.GLOBAL
|
||||
// If we pass it to unknown methods or throw - it must be global.
|
||||
roles.has(Role.CALL_ARGUMENT) || roles.has(Role.THROW_VALUE) ->
|
||||
Lifetime.GLOBAL
|
||||
// If reference is only obtained as call result and never used - it can be local.
|
||||
roleSetSize == 1 && roles.has(Role.CALL_RESULT) ->
|
||||
Lifetime.LOCAL // throw Error()
|
||||
// If reference is obtained as some call result and returned - we can use return.
|
||||
roleSetSize == 2 && roles.has(Role.CALL_RESULT) && roles.has(Role.RETURN_VALUE) ->
|
||||
Lifetime.RETURN_VALUE
|
||||
// Otherwise, say it is global.
|
||||
else ->
|
||||
Lifetime.GLOBAL
|
||||
}
|
||||
}
|
||||
|
||||
internal class ElementFinderVisitor(
|
||||
val context: RuntimeAware,
|
||||
val expressionToRoles: MutableMap<IrExpression, Roles>,
|
||||
val variableValues: VariableValues,
|
||||
val parameterRoles: ParameterRoles)
|
||||
: IrElementVisitorVoid {
|
||||
|
||||
fun isInteresting(element: IrExpression) : Boolean {
|
||||
return (element is IrMemberAccessExpression && context.isInteresting(element.type)) ||
|
||||
(element is IrGetValue && context.isInteresting(element.type))
|
||||
}
|
||||
|
||||
fun isInteresting(variable: ValueDescriptor) : Boolean {
|
||||
return context.isInteresting(variable.type)
|
||||
}
|
||||
|
||||
override fun visitElement(element: IrElement) {
|
||||
if (element is IrExpression && isInteresting(element)) {
|
||||
expressionToRoles[element] = Roles()
|
||||
}
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitTypeOperator(expression: IrTypeOperatorCall) {
|
||||
super.visitTypeOperator(expression)
|
||||
if (expression.operator == IrTypeOperator.IMPLICIT_COERCION_TO_UNIT) {
|
||||
expressionToRoles.remove(expression.argument)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitFunction(declaration: IrFunction) {
|
||||
declaration.descriptor.allValueParameters.forEach {
|
||||
if (isInteresting(it))
|
||||
parameterRoles.addParameter(it)
|
||||
}
|
||||
super.visitFunction(declaration)
|
||||
}
|
||||
|
||||
override fun visitVariable(expression: IrVariable) {
|
||||
if (isInteresting(expression.descriptor))
|
||||
variableValues.addEmpty(expression.descriptor)
|
||||
super.visitVariable(expression)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// elementToRoles is filled with all possible roles given element can play.
|
||||
// varValues is filled with all possible elements that could be stored in a variable.
|
||||
//
|
||||
internal class RoleAssignerVisitor(
|
||||
val expressionRoles: MutableMap<IrExpression, Roles>,
|
||||
val variableValues: VariableValues,
|
||||
val parameterRoles: ParameterRoles,
|
||||
val useVarValues: Boolean) : IrElementVisitorVoid {
|
||||
|
||||
fun assignExpressionRole(expression: IrExpression, role: Role, infoEntry: RoleInfoEntry?) {
|
||||
if (!useVarValues)
|
||||
expressionRoles[expression]?.add(role, infoEntry)
|
||||
}
|
||||
|
||||
fun assignValueRole(value: ValueDescriptor, role: Role, infoEntry: RoleInfoEntry?) {
|
||||
if (!useVarValues) return
|
||||
// Whenever we see variable use in certain role - we propagate this role
|
||||
// to all possible expression this variable can be assigned to.
|
||||
val possibleValues = variableValues.get(value)
|
||||
if (possibleValues != null) {
|
||||
for (possibleValue in possibleValues) {
|
||||
expressionRoles[possibleValue]?.add(role, infoEntry)
|
||||
}
|
||||
}
|
||||
if (value is ParameterDescriptor) {
|
||||
parameterRoles.add(value, role, infoEntry)
|
||||
}
|
||||
}
|
||||
|
||||
// Here we handle variable assignment.
|
||||
fun assignVariable(variable: VariableDescriptor, value: IrExpression) {
|
||||
if (useVarValues) return
|
||||
// Sometimes we can assign value to Unit variable (unit4.kt) - we don't care.
|
||||
if (value.type.isUnit()) return
|
||||
when (value) {
|
||||
is IrContainerExpression -> assignVariable(variable, value.statements.last() as IrExpression)
|
||||
// is IrVararg -> value.elements.forEach { assignVariable(variable, it as IrExpression) }
|
||||
is IrWhen -> value.branches.forEach { assignVariable(variable, it.result) }
|
||||
is IrMemberAccessExpression -> variableValues.add(variable, value)
|
||||
is IrGetValue -> variableValues.add(variable, value)
|
||||
is IrGetField -> variableValues.add(variable, value)
|
||||
is IrConst<*> -> { }
|
||||
is IrTypeOperatorCall -> {
|
||||
when (value.operator) {
|
||||
IrTypeOperator.IMPLICIT_CAST, IrTypeOperator.CAST ->
|
||||
assignVariable(variable, value.argument)
|
||||
else -> TODO(ir2string(value))
|
||||
}
|
||||
}
|
||||
is IrTry -> (value.catches + value.tryResult).forEach {
|
||||
if (it is IrExpression) assignVariable(variable, it)
|
||||
}
|
||||
is IrThrow -> { /* Do nothing, error path in an assignment. */ }
|
||||
is IrGetObjectValue -> { }
|
||||
// TODO: is it correct?
|
||||
is IrReturn -> { /* Do nothing, return path in an assignment. */ }
|
||||
else -> TODO(ir2string(value))
|
||||
}
|
||||
}
|
||||
|
||||
// Here we assign a role to expression's value.
|
||||
fun assignRole(expression: IrExpression, role: Role, infoEntry: RoleInfoEntry?) {
|
||||
if (expression.type.isUnit()) return
|
||||
when (expression) {
|
||||
is IrContainerExpression -> assignRole(expression.statements.last() as IrExpression, role, infoEntry)
|
||||
is IrVararg -> expression.elements.forEach { assignRole(it as IrExpression, role, infoEntry) }
|
||||
is IrWhen -> expression.branches.forEach { assignRole(it.result, role, infoEntry) }
|
||||
is IrMemberAccessExpression -> assignExpressionRole(expression, role, infoEntry)
|
||||
is IrGetValue -> assignValueRole(expression.descriptor, role, infoEntry)
|
||||
// If field plays certain role - we cannot use this info now.
|
||||
is IrGetField -> {}
|
||||
// If constant plays certain role - this information is useless.
|
||||
is IrConst<*> -> {}
|
||||
is IrTypeOperatorCall -> {
|
||||
when (expression.operator) {
|
||||
IrTypeOperator.IMPLICIT_CAST, IrTypeOperator.CAST ->
|
||||
assignRole(expression.argument, role, infoEntry)
|
||||
// No info from those ones.
|
||||
IrTypeOperator.INSTANCEOF, IrTypeOperator.NOT_INSTANCEOF,
|
||||
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> {}
|
||||
else -> TODO(ir2string(expression))
|
||||
}
|
||||
}
|
||||
is IrThrow -> { /* Do nothing, error path in an assignment. */ }
|
||||
is IrGetObjectValue -> { /* Shall we do anything here? */ }
|
||||
// TODO: is it correct?
|
||||
is IrReturn -> { /* Do nothing, return path in an assignment. */ }
|
||||
else -> TODO(ir2string(expression))
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitCall(expression: IrCall) {
|
||||
assignRole(expression, Role.CALL_RESULT, RoleInfoEntry(expression))
|
||||
for (argument in expression.getArguments()) {
|
||||
assignRole(argument.second, Role.CALL_ARGUMENT,
|
||||
RoleInfoEntry(Pair(argument.first, expression.descriptor)))
|
||||
}
|
||||
super.visitCall(expression)
|
||||
}
|
||||
|
||||
override fun visitSetField(expression: IrSetField) {
|
||||
assignRole(expression.value, Role.WRITTEN_TO_FIELD, RoleInfoEntry(expression))
|
||||
super.visitSetField(expression)
|
||||
}
|
||||
|
||||
override fun visitField(declaration: IrField) {
|
||||
val initializer = declaration.initializer
|
||||
if (initializer != null) {
|
||||
assignRole(initializer.expression,
|
||||
Role.WRITTEN_TO_FIELD, RoleInfoEntry(declaration))
|
||||
}
|
||||
super.visitField(declaration)
|
||||
}
|
||||
|
||||
override fun visitSetVariable(expression: IrSetVariable) {
|
||||
assignVariable(expression.descriptor, expression.value)
|
||||
super.visitSetVariable(expression)
|
||||
}
|
||||
|
||||
override fun visitVariable(expression: IrVariable) {
|
||||
val initializer = expression.initializer
|
||||
if (initializer != null) {
|
||||
assignVariable(expression.descriptor, initializer)
|
||||
}
|
||||
super.visitVariable(expression)
|
||||
}
|
||||
|
||||
override fun visitReturn(expression: IrReturn) {
|
||||
assignRole(expression.value, Role.RETURN_VALUE, RoleInfoEntry(expression))
|
||||
super.visitReturn(expression)
|
||||
}
|
||||
|
||||
override fun visitThrow(expression: IrThrow) {
|
||||
assignRole(expression.value, Role.THROW_VALUE, RoleInfoEntry(expression))
|
||||
super.visitThrow(expression)
|
||||
}
|
||||
}
|
||||
|
||||
internal class ContextFinderVisitor(val needle: IrElement) : IrElementVisitorVoid {
|
||||
val stack = mutableListOf<IrElement>()
|
||||
var found = false
|
||||
|
||||
override fun visitElement(element: IrElement) {
|
||||
if (found) return
|
||||
stack.add(element)
|
||||
if (element == needle) {
|
||||
found = true
|
||||
}
|
||||
element.acceptChildrenVoid(this)
|
||||
if (found) return
|
||||
stack.removeAt(stack.size - 1)
|
||||
}
|
||||
|
||||
override fun visitModuleFragment(module: IrModuleFragment) {
|
||||
module.acceptChildrenVoid(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun findContext(irModule: IrModuleFragment, element: IrElement) : String {
|
||||
val visitor = ContextFinderVisitor(element)
|
||||
irModule.acceptVoid(visitor)
|
||||
val result = StringBuilder()
|
||||
if (visitor.found) {
|
||||
visitor.stack.forEach {
|
||||
result.append(ir2string(it))
|
||||
result.append(" -> ")
|
||||
}
|
||||
} else {
|
||||
result.append("<not found>")
|
||||
}
|
||||
return result.toString()
|
||||
}
|
||||
|
||||
//
|
||||
// Analysis we're implementing here is as following.
|
||||
// * compute roles IR value nodes can play
|
||||
// * merge role information with all methods being called
|
||||
// * squash set of roles to conservatively estimated lifetime
|
||||
//
|
||||
fun computeLifetimes(irModule: IrModuleFragment, context: RuntimeAware,
|
||||
lifetimes: MutableMap<IrElement, Lifetime>) {
|
||||
assert(lifetimes.size == 0)
|
||||
|
||||
val expressionToRoles = mutableMapOf<IrExpression, Roles>()
|
||||
val variableValues = VariableValues()
|
||||
val parameterRoles = ParameterRoles()
|
||||
irModule.acceptVoid(ElementFinderVisitor(
|
||||
context, expressionToRoles, variableValues, parameterRoles))
|
||||
// On this pass, we collect all possible variable values and assign roles
|
||||
// to expressions.
|
||||
irModule.acceptVoid(RoleAssignerVisitor(
|
||||
expressionToRoles, variableValues, parameterRoles, false))
|
||||
// Compute transitive closure of possible values for variables.
|
||||
variableValues.computeClosure()
|
||||
// On this pass, we use possible variable values to assign roles to expression.
|
||||
irModule.acceptVoid(RoleAssignerVisitor(
|
||||
expressionToRoles, variableValues, parameterRoles, true))
|
||||
parameterRoles.propagateCallArguments(expressionToRoles)
|
||||
|
||||
expressionToRoles.forEach { expression, roles ->
|
||||
if (DEBUG)
|
||||
println("for ${expression} roles are ${roles.data.keys.joinToString(", ")}")
|
||||
var role = rolesToLifetime(roles)
|
||||
if (role == Lifetime.LOCAL) {
|
||||
println("arena alloc for ${ir2string(expression)} in ${findContext(irModule, expression)}")
|
||||
// Not yet enabled.
|
||||
role = Lifetime.GLOBAL
|
||||
}
|
||||
lifetimes[expression] = role
|
||||
}
|
||||
if (DEBUG) {
|
||||
variableValues.elementData.forEach { variable, expressions ->
|
||||
println("variable $variable could be ${expressions.joinToString(", ")}") }
|
||||
parameterRoles.elementData.forEach { parameter, roles ->
|
||||
println("parameter $parameter could be $roles") }
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -29,7 +29,7 @@ internal enum class SlotType {
|
||||
}
|
||||
|
||||
// Lifetimes class of reference, computed by escape analysis.
|
||||
internal enum class Lifetime(val slotType: SlotType) {
|
||||
enum class Lifetime(val slotType: SlotType) {
|
||||
// If reference is frame-local (only obtained from some call and never leaves).
|
||||
LOCAL(SlotType.ARENA),
|
||||
// If reference is only returned.
|
||||
|
||||
+4
-1
@@ -4,6 +4,7 @@ import llvm.*
|
||||
import org.jetbrains.kotlin.backend.konan.ValueType
|
||||
import org.jetbrains.kotlin.backend.konan.isRepresentedAs
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||
|
||||
private val valueTypes = ValueType.values().associate {
|
||||
@@ -38,4 +39,6 @@ internal fun RuntimeAware.getLLVMReturnType(type: KotlinType): LLVMTypeRef {
|
||||
// KotlinBuiltIns.isNothing(type) -> LLVMVoidType()
|
||||
else -> getLLVMType(type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun RuntimeAware.isObjectType(type: KotlinType) : Boolean = isObjectType(getLLVMType(type))
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
package org.jetbrains.kotlin.backend.konan.llvm
|
||||
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
|
||||
// Analysis we're implementing here is as following.
|
||||
// We build graph with the following nodes:
|
||||
// * allocation set, keeping tuple of [local, ctor call, owner function], AS
|
||||
// * local store set, keeping pair [local, stored], LSS
|
||||
// * field store set, keeping tuple [local, object to store, stored field id], FSS
|
||||
// * global store set, [local, stored global address], GSS
|
||||
// Function we're trying to compute is the following:
|
||||
// for each element of AS, could it be referred by someone, whose value is
|
||||
// alive on return from function, where element was allocated.
|
||||
// Each element in RS is associated with few elements in AS, which it could refer to.
|
||||
//
|
||||
internal class EscapeAnalyzerVisitor(
|
||||
val lifetimes: MutableMap<IrMemberAccessExpression, Lifetime>) : IrElementVisitorVoid {
|
||||
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitModuleFragment(module: IrModuleFragment) {
|
||||
module.acceptChildrenVoid(this)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun computeLifetimes(irModule: IrModuleFragment,
|
||||
lifetimes: MutableMap<IrMemberAccessExpression, Lifetime>) {
|
||||
assert(lifetimes.size == 0)
|
||||
|
||||
irModule.acceptVoid(EscapeAnalyzerVisitor(lifetimes))
|
||||
}
|
||||
+17
-17
@@ -168,7 +168,7 @@ internal interface CodeContext {
|
||||
internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid {
|
||||
|
||||
val codegen = CodeGenerator(context)
|
||||
val resultLifetimes = mutableMapOf<IrMemberAccessExpression, Lifetime>()
|
||||
val resultLifetimes = mutableMapOf<IrElement, Lifetime>()
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
@@ -237,7 +237,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
override fun visitModuleFragment(module: IrModuleFragment) {
|
||||
context.log("visitModule : ${ir2string(module)}")
|
||||
|
||||
computeLifetimes(module, resultLifetimes)
|
||||
computeLifetimes(module, this.codegen, resultLifetimes)
|
||||
|
||||
module.acceptChildrenVoid(this)
|
||||
appendLlvmUsed(context.llvm.usedFunctions)
|
||||
@@ -476,11 +476,11 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
*/
|
||||
private fun bindParameters(descriptor: FunctionDescriptor?): Map<ParameterDescriptor, LLVMValueRef> {
|
||||
if (descriptor == null) return emptyMap()
|
||||
val paramDescriptors = descriptor.allValueParameters
|
||||
return paramDescriptors.mapIndexed { i, paramDescriptor ->
|
||||
val param = codegen.param(descriptor, i)
|
||||
assert(codegen.getLLVMType(paramDescriptor.type) == param.type)
|
||||
paramDescriptor to param
|
||||
val parameterDescriptors = descriptor.allValueParameters
|
||||
return parameterDescriptors.mapIndexed { i, parameterDescriptor ->
|
||||
val parameter = codegen.param(descriptor, i)
|
||||
assert(codegen.getLLVMType(parameterDescriptor.type) == parameter.type)
|
||||
parameterDescriptor to parameter
|
||||
}.toMap()
|
||||
}
|
||||
|
||||
@@ -1081,7 +1081,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
}
|
||||
|
||||
|
||||
assert (loop.type.isUnit())
|
||||
assert(loop.type.isUnit())
|
||||
return codegen.theUnitInstanceRef.llvm
|
||||
}
|
||||
|
||||
@@ -1104,7 +1104,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
codegen.positionAtEnd(loopScope.loopExit)
|
||||
}
|
||||
|
||||
assert (loop.type.isUnit())
|
||||
assert(loop.type.isUnit())
|
||||
return codegen.theUnitInstanceRef.llvm
|
||||
}
|
||||
|
||||
@@ -1119,11 +1119,11 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
|
||||
private fun evaluateSetVariable(value: IrSetVariable): LLVMValueRef {
|
||||
context.log("evaluateSetVariable : ${ir2string(value)}")
|
||||
val ret = evaluateExpression(value.value)
|
||||
val result = evaluateExpression(value.value)
|
||||
val variable = currentCodeContext.getDeclaredVariable(value.descriptor)
|
||||
codegen.vars.store(ret, variable)
|
||||
codegen.vars.store(result, variable)
|
||||
|
||||
assert (value.type.isUnit())
|
||||
assert(value.type.isUnit())
|
||||
return codegen.theUnitInstanceRef.llvm
|
||||
}
|
||||
|
||||
@@ -1131,8 +1131,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
|
||||
private fun generateVariable(value: IrVariable) {
|
||||
context.log("generateVariable : ${ir2string(value)}")
|
||||
val ret = value.initializer?.let { evaluateExpression(it) }
|
||||
currentCodeContext.genDeclareVariable(value.descriptor, ret)
|
||||
val result = value.initializer?.let { evaluateExpression(it) }
|
||||
currentCodeContext.genDeclareVariable(value.descriptor, result)
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -1454,7 +1454,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
}
|
||||
}
|
||||
|
||||
assert (value.type.isUnit())
|
||||
assert(value.type.isUnit())
|
||||
return codegen.theUnitInstanceRef.llvm
|
||||
}
|
||||
}
|
||||
@@ -1621,8 +1621,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
private fun resultLifetime(callee: IrMemberAccessExpression): Lifetime {
|
||||
return resultLifetimes.getOrElse(callee) { Lifetime.GLOBAL }
|
||||
private fun resultLifetime(callee: IrElement): Lifetime {
|
||||
return resultLifetimes.getOrElse(callee) { /* TODO: make IRRELEVANT */ Lifetime.GLOBAL }
|
||||
}
|
||||
|
||||
private fun evaluateConstructorCall(callee: IrCall, args: List<LLVMValueRef>): LLVMValueRef {
|
||||
|
||||
+3
@@ -2,6 +2,9 @@ package org.jetbrains.kotlin.backend.konan.llvm
|
||||
|
||||
import kotlinx.cinterop.*
|
||||
import llvm.*
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||
|
||||
interface RuntimeAware {
|
||||
val runtime: Runtime
|
||||
|
||||
+2
-7
@@ -70,17 +70,13 @@ internal class VariableManager(val codegen: CodeGenerator) {
|
||||
}
|
||||
|
||||
// Creates anonymous mutable variable.
|
||||
fun createAnonymousMutable(type: KotlinType, value: LLVMValueRef? = null) : Int {
|
||||
return createAnonymousMutable(codegen.getLLVMType(type), value)
|
||||
}
|
||||
|
||||
// Think of slot reuse.
|
||||
fun createAnonymousSlot(value: LLVMValueRef? = null) : LLVMValueRef {
|
||||
val index = createAnonymousMutable(codegen.kObjHeaderPtr, value)
|
||||
return addressOf(index)
|
||||
}
|
||||
|
||||
fun createAnonymousMutable(type: LLVMTypeRef, value: LLVMValueRef? = null) : Int {
|
||||
private fun createAnonymousMutable(type: LLVMTypeRef, value: LLVMValueRef? = null) : Int {
|
||||
val index = variables.size
|
||||
val slot = codegen.alloca(type)
|
||||
if (value != null)
|
||||
@@ -91,9 +87,8 @@ internal class VariableManager(val codegen: CodeGenerator) {
|
||||
|
||||
fun createImmutable(scoped: Pair<ValueDescriptor, CodeContext>, value: LLVMValueRef) : Int {
|
||||
val scopedVariable = ScopedVariable(scoped)
|
||||
if(contextVariablesToIndex.containsKey(scopedVariable))
|
||||
if (contextVariablesToIndex.containsKey(scopedVariable))
|
||||
throw Error("${scopedVariable.descriptor} is already defined")
|
||||
assert(!contextVariablesToIndex.containsKey(scopedVariable))
|
||||
val index = variables.size
|
||||
variables.add(ValueRecord(value, scopedVariable.descriptor.name))
|
||||
contextVariablesToIndex[scopedVariable] = index
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
fun foo1(arg: String) : String = arg
|
||||
|
||||
/*
|
||||
fun foo2(arg: String) : String = arg + " foo2"
|
||||
|
||||
fun bar(arg1: String, arg2: String) : String = arg1 + " bar " + arg2
|
||||
|
||||
fun zoo1() : String {
|
||||
var x = foo1("")
|
||||
var y = 4
|
||||
return x
|
||||
}
|
||||
|
||||
fun zoo2() : String {
|
||||
val x = foo1("")
|
||||
var y = 5
|
||||
return x
|
||||
}
|
||||
|
||||
class Node(var previous: Node?)
|
||||
|
||||
fun zoo3() : Node {
|
||||
var current = Node(null)
|
||||
for (i in 1 .. 5) {
|
||||
current = Node(current)
|
||||
}
|
||||
return current
|
||||
}
|
||||
|
||||
fun zoo4(arg: Int) : Any {
|
||||
var a = Any()
|
||||
var b = Any()
|
||||
var c = Any()
|
||||
a = b
|
||||
val x = 3
|
||||
a = when {
|
||||
x < arg -> b
|
||||
else -> c
|
||||
}
|
||||
return a
|
||||
} */
|
||||
|
||||
fun zoo5(arg: String) : String {
|
||||
return arg + foo1(arg)
|
||||
}
|
||||
|
||||
fun zoo6(arg: Any) : Any {
|
||||
return zoo7(arg, "foo", 11)
|
||||
}
|
||||
|
||||
fun zoo7(arg1: Any, arg2: Any, selector: Int) : Any {
|
||||
return if (selector < 2) arg1 else arg2;
|
||||
}
|
||||
|
||||
fun main(args : Array<String>) {
|
||||
val z = zoo7(Any(), Any(), 1)
|
||||
//println(bar(foo1(foo2("")), foo2(foo1(""))))
|
||||
}
|
||||
@@ -11,8 +11,9 @@ public abstract class AbstractCollection<out E> protected constructor() : Collec
|
||||
|
||||
override fun isEmpty(): Boolean = size == 0
|
||||
|
||||
/*
|
||||
override fun toString(): String = joinToString(", ", "[", "]") {
|
||||
if (it === this) "(this Collection)" else it.toString()
|
||||
} */
|
||||
|
||||
@Fixme
|
||||
override fun toString(): String = TODO() // joinToString(", ", "[", "]") {
|
||||
// if (it === this) "(this Collection)" else it.toString()
|
||||
// }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user