Extremely conservative escape analysis. (#212)

This commit is contained in:
Nikolay Igotti
2017-02-08 19:15:55 +03:00
committed by GitHub
parent a856e16d2f
commit 4b27074979
9 changed files with 571 additions and 68 deletions
@@ -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") }
}
}
@@ -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,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))
@@ -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))
}
@@ -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 {
@@ -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
@@ -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()
// }
}