Rework variable management for memory management's sake. (#156)

This commit is contained in:
Nikolay Igotti
2016-12-22 18:30:58 +03:00
committed by GitHub
parent 4acb0774cf
commit 25db86df3e
3 changed files with 218 additions and 105 deletions
@@ -8,7 +8,6 @@ import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.types.KotlinType
internal class CodeGenerator(override val context: Context) : ContextUtils {
@@ -17,6 +16,7 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
val returns: MutableMap<LLVMBasicBlockRef, LLVMValueRef> = mutableMapOf()
// TODO: remove, to make CodeGenerator descriptor-agnostic.
var constructedClass: ClassDescriptor? = null
val vars = VariableManager(this)
fun prologue(descriptor: FunctionDescriptor) {
prologue(llvmFunction(descriptor),
@@ -46,10 +46,14 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
appendingTo(epilogueBb!!) {
when {
returnType == voidType -> LLVMBuildRetVoid(builder)
returnType == voidType -> {
vars.releaseVars()
LLVMBuildRetVoid(builder)
}
returns.size > 0 -> {
val returnPhi = phi(returnType!!)
addPhiIncoming(returnPhi, *returns.toList().toTypedArray())
vars.releaseVars()
LLVMBuildRet(builder, returnPhi)
}
// Do nothing, all paths throw.
@@ -58,6 +62,7 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
}
returns.clear()
vars.clear()
}
private var prologueBb: LLVMBasicBlockRef? = null
@@ -12,14 +12,12 @@ import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltinOperatorDescriptorBase
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrBinaryPrimitiveImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrSetterCallImpl
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.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
@@ -116,6 +114,47 @@ internal class MetadatorVisitor(val context: Context) : IrElementVisitorVoid {
}
}
/**
* Defines how to generate context-dependent operations.
*/
interface CodeContext {
fun genReturn(target: CallableDescriptor, value: LLVMValueRef?)
fun genBreak(destination: IrBreak)
fun genContinue(destination: IrContinue)
fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>): LLVMValueRef
fun genThrow(exception: LLVMValueRef)
/**
* Declares the variable.
* @return index of declared variable.
*/
fun genDeclareVariable(descriptor: VariableDescriptor, value: LLVMValueRef?): Int
/**
* @return index of variable declared before, or -1 if no such variable has been declared yet.
*/
fun getDeclaredVariable(descriptor: VariableDescriptor): Int
/**
* Generates the code to obtain a value available in this context.
*
* @return the requested value
*/
fun genGetValue(descriptor: ValueDescriptor): LLVMValueRef
/**
* Returns owning function scope.
*
* @return the requested value
*/
fun functionScope(): CodeContext
}
//-------------------------------------------------------------------------//
@@ -128,47 +167,6 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
// TODO: consider eliminating mutable state
private var currentCodeContext: CodeContext = TopLevelCodeContext
/**
* Defines how to generate context-dependent operations.
*/
private interface CodeContext {
fun genReturn(target: CallableDescriptor, value: LLVMValueRef?)
fun genBreak(destination: IrBreak)
fun genContinue(destination: IrContinue)
fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>): LLVMValueRef
fun genThrow(exception: LLVMValueRef)
/**
* Declares the variable.
* @return pointer to the declared variable.
*/
fun genDeclareVariable(descriptor: VariableDescriptor): LLVMValueRef
/**
* @return pointer to the variable declared before, or `null` if no such variable has been declared yet.
*/
fun getDeclaredVariable(descriptor: VariableDescriptor): LLVMValueRef?
/**
* Generates the code to obtain a value available in this context.
*
* @return the requested value
*/
fun genGetValue(descriptor: ValueDescriptor, result: String = ""): LLVMValueRef
/**
* Returns owning function scope.
*
* @return the requested value
*/
fun functionScope(): CodeContext
}
/**
* Fake [CodeContext] that doesn't support any operation.
@@ -188,11 +186,11 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
override fun genThrow(exception: LLVMValueRef) = unsupported()
override fun genDeclareVariable(descriptor: VariableDescriptor) = unsupported(descriptor)
override fun genDeclareVariable(descriptor: VariableDescriptor, value: LLVMValueRef?) = unsupported(descriptor)
override fun getDeclaredVariable(descriptor: VariableDescriptor) = null
override fun getDeclaredVariable(descriptor: VariableDescriptor) = -1
override fun genGetValue(descriptor: ValueDescriptor, result: String) = unsupported(descriptor)
override fun genGetValue(descriptor: ValueDescriptor) = unsupported(descriptor)
override fun functionScope(): CodeContext = unsupported()
}
@@ -294,6 +292,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
if (context.llvm.fileInitializers.isEmpty())
return
// Create global initialization records.
val fileName = declaration.name.takeLastWhile { it != '/' }.dropLastWhile { it != '.' }.dropLast(1)
val initName = "${fileName}_init_${context.llvm.globalInitIndex}"
val nodeName = "${fileName}_node_${context.llvm.globalInitIndex}"
@@ -324,14 +324,14 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
val loopExit = codegen.basicBlock()
override fun genBreak(destination: IrBreak) {
if (destination.label == loop.label)
if (destination.label == null || destination.label == loop.label)
codegen.br(loopExit)
else
super.genBreak(destination)
}
override fun genContinue(destination: IrContinue) {
if (destination.label == loop.label)
if (destination.label == null || destination.label == loop.label)
codegen.br(loopCheck)
else
super.genContinue(destination)
@@ -380,7 +380,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
using(FunctionScope(constructorDeclaration)) {
val thisPtr = currentCodeContext.genGetValue(classDescriptor.thisAsReceiverParameter, "this")
val thisPtr = currentCodeContext.genGetValue(classDescriptor.thisAsReceiverParameter)
/**
* IR for kotlin.Any is:
@@ -494,30 +494,21 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
*/
private inner class VariableScope : InnerScopeImpl() {
private val variables = mutableMapOf<ValueDescriptor, LLVMValueRef>()
override fun genDeclareVariable(descriptor: VariableDescriptor): LLVMValueRef {
// Check that the variable is not declared yet:
assert (this.getDeclaredVariable(descriptor) == null)
val res = codegen.alloca(descriptor.type, descriptor.name.asString())
val previous = variables.put(descriptor, res)
assert (previous == null)
return res
override fun genDeclareVariable(descriptor: VariableDescriptor, value: LLVMValueRef?): Int {
return codegen.vars.createVariable(descriptor to this, value)
}
override fun getDeclaredVariable(descriptor: VariableDescriptor): LLVMValueRef? {
return variables[descriptor] ?: super.getDeclaredVariable(descriptor)
override fun getDeclaredVariable(descriptor: VariableDescriptor): Int {
val index = codegen.vars.indexOf(descriptor to this)
return if (index < 0) super.getDeclaredVariable(descriptor) else return index
}
override fun genGetValue(descriptor: ValueDescriptor, result: String): LLVMValueRef {
val variable = variables[descriptor]
if (variable != null) {
return codegen.load(variable, result)
override fun genGetValue(descriptor: ValueDescriptor): LLVMValueRef {
val index = codegen.vars.indexOf(descriptor to this)
if (index < 0) {
return super.genGetValue(descriptor)
} else {
return super.genGetValue(descriptor, result)
return codegen.vars.load(index)
}
}
}
@@ -526,23 +517,24 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
* The scope of parameter visibility.
*/
private open inner class ParameterScope(
private val parameters: Map<ParameterDescriptor, LLVMValueRef>): InnerScopeImpl() {
parameters: Map<ParameterDescriptor, LLVMValueRef>): InnerScopeImpl() {
// Note: it is possible to generate access to a parameter without any variables,
// however variables are named and thus the resulting bitcode can be more readable.
private val parameterVars = parameters.map { (descriptor, value) ->
val variable = codegen.alloca(descriptor.type, descriptor.name.asString())
codegen.store(value, variable)
descriptor to variable
}.toMap()
init {
parameters.map { (descriptor, value) ->
codegen.vars.createImmutable(descriptor to this, value)
}
override fun genGetValue(descriptor: ValueDescriptor, result: String): LLVMValueRef {
val variable = parameterVars[descriptor]
if (variable != null) {
return codegen.load(variable, result)
}
override fun genGetValue(descriptor: ValueDescriptor): LLVMValueRef {
val index = codegen.vars.indexOf(descriptor to this)
if (index < 0) {
return super.genGetValue(descriptor)
} else {
return super.genGetValue(descriptor, result)
return codegen.vars.load(index)
}
}
}
@@ -1082,7 +1074,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
codegen.appendingTo(body) {
using(VariableScope()) {
createVariable(it.parameter, exception)
currentCodeContext.genDeclareVariable(it.parameter, exception)
evaluateExpressionAndJump(it.result, success)
}
}
@@ -1250,7 +1242,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
val isUnit = KotlinBuiltIns.isUnit(expression.type)
val isNothing = KotlinBuiltIns.isNothing(expression.type)
val neitherUnitNorNothing = !isNothing && !isUnit
val tmpLlvmVariablePtr = if (!isUnit) codegen.alloca(expression.type) else null
val tmpLlvmVariable = if (!isUnit) codegen.vars.createAnonymousMutable(expression.type) else -1
expression.branches.forEach { // Iterate through "when" branches (clauses).
var bbNext = bbExit // For last clause bbNext coincides with bbExit.
@@ -1258,10 +1250,10 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
bbNext = codegen.basicBlock() // Create new basic block for next clause.
val isUnitBranch = KotlinBuiltIns.isUnit(it.result.type)
val isNothingBranch = KotlinBuiltIns.isNothing(it.result.type)
generateWhenCase(isUnitBranch, isNothingBranch, tmpLlvmVariablePtr, it, bbNext, bbExit) // Generate code for current clause.
generateWhenCase(isUnitBranch, isNothingBranch, tmpLlvmVariable, it, bbNext, bbExit) // Generate code for current clause.
}
if (neitherUnitNorNothing) // If result hasn't Unit type and block doesn't end with return
return codegen.load(tmpLlvmVariablePtr!!) // load value from variable.
return codegen.vars.load(tmpLlvmVariable) // load value from variable.
return null
}
@@ -1319,8 +1311,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
private fun evaluateSetVariable(value: IrSetVariable): LLVMValueRef? {
context.log("evaluateSetVariable : ${ir2string(value)}")
val ret = evaluateExpression(value.value)!!
val variable = currentCodeContext.getDeclaredVariable(value.descriptor)!!
codegen.store(ret, variable)
val variable = currentCodeContext.getDeclaredVariable(value.descriptor)
codegen.vars.store(ret, variable)
return null
}
@@ -1329,20 +1321,10 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
private fun evaluateVariable(value: IrVariable): LLVMValueRef? {
context.log("evaluateVariable : ${ir2string(value)}")
val ret = value.initializer?.let { evaluateExpression(it)!! }
createVariable(value.descriptor, ret)
currentCodeContext.genDeclareVariable(value.descriptor, ret)
return null
}
private fun createVariable(descriptor: VariableDescriptor, value: LLVMValueRef? = null): LLVMValueRef {
val newVariable = currentCodeContext.genDeclareVariable(descriptor)
if (value != null) {
codegen.store(value, newVariable)
}
return newVariable
}
//-------------------------------------------------------------------------//
private fun evaluateTypeOperator(value: IrTypeOperatorCall): LLVMValueRef? {
@@ -1366,23 +1348,23 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
val bbExit = codegen.basicBlock()
val resultLlvmType = codegen.getLLVMType(value.type)
val result = codegen.alloca(resultLlvmType)
val result = codegen.vars.createAnonymousMutable(resultLlvmType)
val condition = evaluateInstanceOf(value)
codegen.condBr(condition, bbTrue, bbFalse)
codegen.positionAtEnd(bbTrue)
val castResult = evaluateExpression(value.argument)
codegen.store(castResult!!, result)
codegen.vars.store(castResult!!, result)
codegen.br(bbExit)
codegen.positionAtEnd(bbFalse)
val nullResult = LLVMConstNull(resultLlvmType)!!
codegen.store(nullResult, result)
codegen.vars.store(nullResult, result)
codegen.br(bbExit)
codegen.positionAtEnd(bbExit)
return codegen.load(result)
return codegen.vars.load(result)
}
//-------------------------------------------------------------------------//
@@ -1870,13 +1852,14 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
//-------------------------------------------------------------------------//
private fun generateWhenCase(isUnit:Boolean, isNothing:Boolean, resultPtr: LLVMValueRef?, branch: IrBranch, bbNext: LLVMBasicBlockRef?, bbExit: LLVMBasicBlockRef?) {
private fun generateWhenCase(isUnit:Boolean, isNothing:Boolean, resultPtr: Int,
branch: IrBranch, bbNext: LLVMBasicBlockRef?, bbExit: LLVMBasicBlockRef?) {
val neitherUnitNorNothing = !isNothing && !isUnit // If branches doesn't end with 'return' either result hasn't got 'unit' type.
val branchResult = branch.result
if (isUnconditional(branch)) { // It is the "else" clause.
val brResult = evaluateExpression(branchResult) // Generate clause body.
if (neitherUnitNorNothing) // If nor unit neither result ends with return
codegen.store(brResult!!, resultPtr!!) // we store result to temporal variable.
codegen.vars.store(brResult!!, resultPtr) // we store result to temporal variable.
if (bbExit == null) return // If 'bbExit' isn't defined just return
codegen.br(bbExit) // Generate branch to bbExit.
codegen.positionAtEnd(bbExit) // Switch generation to bbExit.
@@ -1887,7 +1870,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
codegen.positionAtEnd(bbCurr) // Switch generation to block for clause body.
val brResult = evaluateExpression(branch.result) // Generate clause body.
if (neitherUnitNorNothing) // If nor unit neither result ends with return
codegen.store(brResult!!, resultPtr!!) // we store result to temporal variable.
codegen.vars.store(brResult!!, resultPtr) // we store result to temporal variable.
if (!isNothing) // If basic block doesn't end with 'return'
codegen.br(bbExit!!) // generate branch to bbExit.
codegen.positionAtEnd(bbNext!!) // Switch generation to bbNextClause.
@@ -0,0 +1,125 @@
package org.jetbrains.kotlin.backend.konan.llvm
import llvm.*
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.types.*
internal class VariableManager(val codegen: CodeGenerator) {
internal interface Record {
fun load() : LLVMValueRef
fun store(value: LLVMValueRef)
fun address() : LLVMValueRef
fun isRefSlot() : Boolean
}
inner class SlotRecord(val address: LLVMValueRef, val refSlot: Boolean) : Record {
override fun load() : LLVMValueRef {
return codegen.load(address)
}
override fun store(value: LLVMValueRef) {
codegen.store(value, address)
}
override fun address() : LLVMValueRef {
return this.address
}
override fun isRefSlot() = this.refSlot
override fun toString() = (if (refSlot) "refslot" else "slot") + " for ${address}"
}
class ValueRecord(val value: LLVMValueRef, val descriptor: ValueDescriptor) : Record {
override fun load() : LLVMValueRef {
return value
}
override fun store(value: LLVMValueRef) {
throw Error("writing to immutable: ${descriptor}")
}
override fun address() : LLVMValueRef {
throw Error("no address for: ${descriptor}")
}
override fun isRefSlot() : Boolean = false
override fun toString() = "value of ${value} from ${descriptor}"
}
val variables: ArrayList<Record> = arrayListOf()
val descriptors: HashMap<Pair<ValueDescriptor, CodeContext>, Int> = hashMapOf()
// Clears inner state of variable manager.
fun clear() {
variables.clear()
descriptors.clear()
}
fun releaseVars() {
// This function is called by codegen to cleanup local references when leaving frame.
}
fun createVariable(scoped: Pair<VariableDescriptor, CodeContext>, value: LLVMValueRef? = null) : Int {
// TODO: fix, due to the bug in frontend, we shall always create stack slot for variable now.
// Note that we always create slot for object references for memory management.
val descriptor = scoped.first
if (descriptor.isVar() || isObjectType(codegen.getLLVMType(descriptor.type)) || true) {
return createMutable(scoped, value)
} else {
return createImmutable(scoped, value!!)
}
}
fun createMutable(scoped: Pair<VariableDescriptor, CodeContext>, value: LLVMValueRef? = null) : Int {
val descriptor = scoped.first
assert(descriptors.get(scoped) == null)
val index = variables.size
val type = codegen.getLLVMType(descriptor.type)
val slot = codegen.alloca(type, descriptor.name.asString())
if (value != null)
codegen.store(value, slot)
variables.add(SlotRecord(slot, isObjectType(type)))
descriptors[scoped] = index
return index
}
// Creates anonymous mutable variable.
fun createAnonymousMutable(type: KotlinType, value: LLVMValueRef? = null) : Int {
return createAnonymousMutable(codegen.getLLVMType(type), value)
}
fun createAnonymousMutable(type: LLVMTypeRef, value: LLVMValueRef? = null) : Int {
val index = variables.size
val slot = codegen.alloca(type)
if (value != null)
codegen.store(value, slot)
variables.add(SlotRecord(slot, isObjectType(type)))
return index
}
fun createImmutable(scoped: Pair<ValueDescriptor, CodeContext>, value: LLVMValueRef) : Int {
val descriptor = scoped.first
assert(descriptors.get(scoped) == null)
val index = variables.size
variables.add(ValueRecord(value, descriptor))
descriptors[scoped] = index
return index
}
fun indexOf(scoped: Pair<ValueDescriptor, CodeContext>) : Int {
return descriptors.getOrElse(scoped) { -1 }
}
fun addressOf(index: Int): LLVMValueRef {
return variables[index].address()
}
fun load(index: Int): LLVMValueRef {
return variables[index].load()
}
fun store(value: LLVMValueRef, index: Int) {
variables[index].store(value)
}
private fun isObjectType(type: LLVMTypeRef) : Boolean {
return type == codegen.kObjHeaderPtr || type == codegen.kArrayHeaderPtr
}
}