Memory management design and implementation. (#148)

This commit is contained in:
Nikolay Igotti
2016-12-27 15:01:18 +02:00
committed by GitHub
parent c9fbd37f90
commit e65b86ab21
14 changed files with 329 additions and 61 deletions
@@ -103,20 +103,47 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
fun intToPtr(imm: LLVMValueRef?, DestTy: LLVMTypeRef, Name: String = "") = LLVMBuildIntToPtr(builder, imm, DestTy, Name)
fun alloca(type: KotlinType, name: String = ""): LLVMValueRef = alloca(getLLVMType(type), name)
fun alloca(type: LLVMTypeRef?, name: String = ""): LLVMValueRef {
appendingTo(prologueBb!!) {
return LLVMBuildAlloca(builder, type, name)!!
val result = LLVMBuildAlloca(builder, type, name)!!
if (isObjectType(type!!))
LLVMBuildStore(builder, kNullObjHeaderPtr, result)
return result
}
}
fun load(value: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildLoad(builder, value, name)!!
fun store(value: LLVMValueRef, ptr: LLVMValueRef): LLVMValueRef = LLVMBuildStore(builder, value, ptr)!!
fun store(value: LLVMValueRef, ptr: LLVMValueRef) {
// Use updateRef() or storeAny() API for that.
assert(!isObjectRef(value))
LLVMBuildStore(builder, value, ptr)
}
fun storeAnyLocal(value: LLVMValueRef, ptr: LLVMValueRef) {
if (isObjectRef(value)) {
updateLocalRef(value, ptr)
} else {
LLVMBuildStore(builder, value, ptr)
}
}
fun storeAnyGlobal(value: LLVMValueRef, ptr: LLVMValueRef) {
if (isObjectRef(value)) {
updateGlobalRef(value, ptr)
} else {
LLVMBuildStore(builder, value, ptr)
}
}
// Only use ignoreOld, when sure that memory is freshly inited and have no value.
fun updateLocalRef(value: LLVMValueRef, address: LLVMValueRef, ignoreOld: Boolean = false) {
call(if (ignoreOld) context.llvm.setLocalRefFunction else context.llvm.updateLocalRefFunction,
listOf(address, value))
}
fun updateGlobalRef(value: LLVMValueRef, address: LLVMValueRef, ignoreOld: Boolean = false) {
call(if (ignoreOld) context.llvm.setGlobalRefFunction else context.llvm.updateGlobalRefFunction,
listOf(address, value))
}
fun isConst(value: LLVMValueRef): Boolean = (LLVMIsConstant(value) == 1)
//-------------------------------------------------------------------------//
@@ -222,7 +222,9 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
val initInstanceFunction = importRtFunction("InitInstance")
val allocArrayFunction = importRtFunction("AllocArrayInstance")
val setLocalRefFunction = importRtFunction("SetLocalRef")
val setGlobalRefFunction = importRtFunction("SetGlobalRef")
val updateLocalRefFunction = importRtFunction("UpdateLocalRef")
val updateGlobalRefFunction = importRtFunction("UpdateGlobalRef")
val setArrayFunction = importRtFunction("Kotlin_Array_set")
val copyImplArrayFunction = importRtFunction("Kotlin_Array_copyImpl")
val lookupFieldOffset = importRtFunction("LookupFieldOffset")
@@ -204,7 +204,6 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
* Convenient [InnerScope] implementation that is bound to the [currentCodeContext].
*/
private abstract inner class InnerScopeImpl : InnerScope(currentCodeContext)
/**
* Executes [block] with [codeContext] substituted as [currentCodeContext].
*/
@@ -250,7 +249,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
val descriptor = irField.descriptor
val initialization = evaluateExpression(irField.initializer)
val globalPtr = LLVMGetNamedGlobal(context.llvmModule, descriptor.symbolName)
codegen.store(initialization!!, globalPtr!!)
codegen.storeAnyGlobal(initialization!!, globalPtr!!)
}
codegen.ret(null)
}
@@ -412,7 +411,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
fieldDeclaration.initializer?.let {
val value = evaluateExpression(it)!!
val fieldPtr = fieldPtrOfClass(thisPtr, fieldDescriptor)
codegen.store(value, fieldPtr)
codegen.storeAnyGlobal(value, fieldPtr)
}
}
@@ -1450,11 +1449,11 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
val valueToAssign = evaluateExpression(value.value)!!
if (value.descriptor.dispatchReceiverParameter != null) {
val thisPtr = instanceFieldAccessReceiver(value)
codegen.store(valueToAssign, fieldPtrOfClass(thisPtr, value.descriptor))
codegen.storeAnyGlobal(valueToAssign, fieldPtrOfClass(thisPtr, value.descriptor))
}
else {
val globalValue = LLVMGetNamedGlobal(context.llvmModule, value.descriptor.symbolName)
codegen.store(valueToAssign, globalValue!!)
codegen.storeAnyGlobal(valueToAssign, globalValue!!)
}
return null
@@ -1818,6 +1817,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
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
// TODO: use phis here!
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
@@ -142,10 +142,6 @@ internal fun structType(types: List<LLVMTypeRef>): LLVMTypeRef = memScoped {
LLVMStructType(allocArrayOf(types)[0].ptr, types.size, 0)!!
}
internal fun ContextUtils.isObjectType(type: LLVMTypeRef) : Boolean {
return type == kObjHeaderPtr || type == kArrayHeaderPtr
}
internal fun ContextUtils.getLlvmFunctionType(function: FunctionDescriptor): LLVMTypeRef {
val returnType = if (function is ConstructorDescriptor) voidType else getLLVMType(function.returnType!!)
val paramTypes = ArrayList(function.allValueParameters.map { getLLVMType(it.type) })
@@ -168,6 +164,14 @@ internal fun ContextUtils.isObjectReturn(functionType: LLVMTypeRef) : Boolean {
return isObjectType(returnType)
}
internal fun ContextUtils.isObjectRef(value: LLVMValueRef): Boolean {
return isObjectType(value.type)
}
internal fun ContextUtils.isObjectType(type: LLVMTypeRef): Boolean {
return type == kObjHeaderPtr || type == kArrayHeaderPtr
}
/**
* Reads [size] bytes contained in this array.
*/
@@ -208,6 +212,7 @@ internal fun functionType(returnType: LLVMTypeRef, isVarArg: Boolean = false, va
LLVMFunctionType(returnType, paramTypesPtr, paramTypes.size, if (isVarArg) 1 else 0)!!
}
fun llvm2string(value: LLVMValueRef?): String {
if (value == null) return "<null>"
return LLVMPrintValueToString(value)!!.asCString().toString()
@@ -160,7 +160,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
val refFieldIndices = classDesc.fields.mapIndexedNotNull { index, field ->
val type = field.returnType!!
if (!KotlinBuiltIns.isPrimitiveType(type)) {
if (isObjectType(getLLVMType(type))) {
index
} else {
null
@@ -18,7 +18,7 @@ internal class VariableManager(val codegen: CodeGenerator) {
return codegen.load(address)
}
override fun store(value: LLVMValueRef) {
codegen.store(value, address)
codegen.storeAnyLocal(value, address)
}
override fun address() : LLVMValueRef {
return this.address
@@ -54,10 +54,13 @@ internal class VariableManager(val codegen: CodeGenerator) {
fun releaseVars() {
// This function is called by codegen to cleanup local references when leaving frame.
for (variable in variables) {
if (variable.isRefSlot())
codegen.updateLocalRef(codegen.kNullObjHeaderPtr, variable.address())
}
}
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() || codegen.isObjectType(codegen.getLLVMType(descriptor.type)) || true) {
@@ -74,7 +77,7 @@ internal class VariableManager(val codegen: CodeGenerator) {
val type = codegen.getLLVMType(descriptor.type)
val slot = codegen.alloca(type, descriptor.name.asString())
if (value != null)
codegen.store(value, slot)
codegen.storeAnyLocal(value, slot)
variables.add(SlotRecord(slot, codegen.isObjectType(type)))
descriptors[scoped] = index
return index
@@ -95,7 +98,7 @@ internal class VariableManager(val codegen: CodeGenerator) {
val index = variables.size
val slot = codegen.alloca(type)
if (value != null)
codegen.store(value, slot)
codegen.storeAnyLocal(value, slot)
variables.add(SlotRecord(slot, codegen.isObjectType(type)))
return index
}
@@ -10,12 +10,12 @@ fun assertFalse(cond: Boolean) {
fun assertEquals(value1: Any?, value2: Any?) {
if (value1 != value2)
println("FAIL")
throw Error("FAIL " + value1 + " " + value2)
}
fun assertEquals(value1: Int, value2: Int) {
if (value1 != value2)
println("FAIL")
throw Error("FAIL " + value1 + " " + value2)
}
fun testBasic() {
@@ -0,0 +1,10 @@
class A {
var field: B? = null
}
class B(var field: Int)
fun main(args : Array<String>) {
val a = A()
a.field = B(2)
}