Frame-local allocations. (#193)

This commit is contained in:
Nikolay Igotti
2017-01-24 17:16:54 +03:00
committed by GitHub
parent afeb1b2d55
commit 66293cb89f
11 changed files with 400 additions and 206 deletions
@@ -20,6 +20,7 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
var returnSlot: LLVMValueRef? = null
var slotsPhi: LLVMValueRef? = null
var slotCount = 0
var localAllocs = 0
fun prologue(descriptor: FunctionDescriptor) {
prologue(llvmFunction(descriptor),
@@ -45,16 +46,18 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
cleanupLandingpad = LLVMAppendBasicBlock(function, "cleanup_landingpad")!!
positionAtEnd(entryBb!!)
slotsPhi = phi(kObjHeaderPtrPtr)
slotCount = 0
// First slot can be assigned to keep pointer to frame local arena.
slotCount = 1
localAllocs = 0
}
fun epilogue() {
appendingTo(prologueBb!!) {
val slots = if (slotCount > 0)
val slots = if (needSlots)
LLVMBuildArrayAlloca(builder, kObjHeaderPtr, Int32(slotCount).llvm, "")!!
else
kNullObjHeaderPtrPtr
if (slotCount > 0) {
if (needSlots) {
// Zero-init slots.
val slotsMem = bitcast(kInt8Ptr, slots)
val pointerSize = LLVMABISizeOfType(llvmTargetData, kObjHeaderPtr).toInt()
@@ -102,9 +105,14 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
slotsPhi = null
}
fun releaseVars() {
if (slotCount > 0) {
call(context.llvm.releaseLocalRefsFunction,
private val needSlots: Boolean
get() {
return slotCount > 1 || localAllocs > 0
}
private fun releaseVars() {
if (needSlots) {
call(context.llvm.leaveFrameFunction,
listOf(slotsPhi!!, Int32(slotCount).llvm))
}
}
@@ -148,6 +156,36 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
return LLVMBuildAlloca(builder, type, name)!!
}
}
// Return object slot (ab)used for arena matching given allocation.
private fun arenaSlot() : LLVMValueRef {
return gep(slotsPhi!!, Int32(0).llvm)
}
fun allocInstance(typeInfo: LLVMValueRef, hint: Int) : LLVMValueRef {
if (hint == SCOPE_FRAME) {
val aux = arenaSlot()
localAllocs++
return call(context.llvm.arenaAllocInstanceFunction, listOf(typeInfo, aux))
} else {
val slot = vars.createAnonymousSlot()
return call(context.llvm.allocInstanceFunction, listOf(typeInfo, slot))
}
}
fun allocArray(
typeInfo: LLVMValueRef, hint: Int, count: LLVMValueRef) : LLVMValueRef {
if (hint == SCOPE_FRAME) {
val aux = arenaSlot()
localAllocs++
return call(context.llvm.arenaAllocArrayFunction, listOf(typeInfo, count, aux))
} else {
val slot = vars.createAnonymousSlot()
return call(context.llvm.allocArrayFunction, listOf(typeInfo, count, slot))
}
}
fun load(value: LLVMValueRef, name: String = ""): LLVMValueRef {
val result = LLVMBuildLoad(builder, value, name)!!
// Use loadSlot() API for that.
@@ -24,6 +24,12 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
// Different scopes/lifetimes of an object, computed by escape analysis.
const val SCOPE_FRAME = 0
const val SCOPE_GLOBAL = 1
const val SCOPE_ARENA = 2
const val SCOPE_PERMANENT = 3
/**
* Provides utility methods to the implementer.
*/
@@ -263,13 +269,15 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
var globalInitIndex:Int = 0
val allocInstanceFunction = importRtFunction("AllocInstance")
val initInstanceFunction = importRtFunction("InitInstance")
val arenaAllocInstanceFunction = importRtFunction("ArenaAllocInstance")
val allocArrayFunction = importRtFunction("AllocArrayInstance")
val arenaAllocArrayFunction = importRtFunction("ArenaAllocArrayInstance")
val initInstanceFunction = importRtFunction("InitInstance")
val setLocalRefFunction = importRtFunction("SetLocalRef")
val setGlobalRefFunction = importRtFunction("SetGlobalRef")
val updateLocalRefFunction = importRtFunction("UpdateLocalRef")
val updateGlobalRefFunction = importRtFunction("UpdateGlobalRef")
val releaseLocalRefsFunction = importRtFunction("ReleaseLocalRefs")
val leaveFrameFunction = importRtFunction("LeaveFrame")
val setArrayFunction = importRtFunction("Kotlin_Array_set")
val copyImplArrayFunction = importRtFunction("Kotlin_Array_copyImpl")
val lookupFieldOffset = importRtFunction("LookupFieldOffset")
@@ -0,0 +1,36 @@
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.IrCall
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, stored], FSS
// * global store set, [local, stored], 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.
// TODO: exact algorithm TBD.
internal class EscapeAnalyzerVisitor(val allocHints: MutableMap<IrCall, Int>) : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitModuleFragment(module: IrModuleFragment) {
module.acceptChildrenVoid(this)
}
}
fun prepareAllocHints(irModule: IrModuleFragment, allocHints: MutableMap<IrCall, Int>) {
assert(allocHints.size == 0)
irModule.acceptVoid(EscapeAnalyzerVisitor(allocHints))
}
@@ -172,6 +172,7 @@ interface CodeContext {
internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid {
val codegen = CodeGenerator(context)
val allocHints = mutableMapOf<IrCall, Int>()
//-------------------------------------------------------------------------//
@@ -240,6 +241,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
override fun visitModuleFragment(module: IrModuleFragment) {
context.log("visitModule : ${ir2string(module)}")
prepareAllocHints(module, allocHints)
module.acceptChildrenVoid(this)
appendLlvmUsed(context.llvm.usedFunctions)
appendStaticInitializers(context.llvm.staticInitializers)
@@ -691,10 +694,9 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
codegen.positionAtEnd(bbInit)
val typeInfo = codegen.typeInfoValue(value.descriptor)
val allocHint = Int32(1).llvm
val initFunction = value.descriptor.constructors.first { it.valueParameters.size == 0 }
val ctor = codegen.llvmFunction(initFunction)
val args = listOf(objectPtr, typeInfo, allocHint, ctor)
val args = listOf(objectPtr, typeInfo, ctor)
val newValue = call(context.llvm.initInstanceFunction, args)
val bbInitResult = codegen.currentBlock
codegen.br(bbExit)
@@ -802,9 +804,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
codegen.plus(sum, size!!)
}
val typeInfo = codegen.typeInfoValue(value.type)!!
val arrayCreationArgs = listOf(typeInfo, kImmInt32One, finalLength)
val array = call(context.llvm.allocArrayFunction, arrayCreationArgs)
val array = codegen.allocArray(codegen.typeInfoValue(value.type)!!, SCOPE_GLOBAL, finalLength)
elements.fold(kImmZero) { sum, (exp, size, isArray) ->
if (!isArray) {
call(context.llvm.setArrayFunction, listOf(array, sum, exp))
@@ -832,6 +832,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
val kStringLength = KonanPlatform.builtIns.string.getter2Descriptor(kNameLength)
val kStringBuilderToString = kStringBuilder.signature2Descriptor(kNameToString)
//TODO: make it lowering pass.
private fun evaluateStringConcatenation(value: IrStringConcatenation): LLVMValueRef {
data class Element(val string: LLVMValueRef, val llvmLenght: LLVMValueRef?, val length: Int)
@@ -857,8 +858,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
val constructor = kStringBuilder!!.constructors
.firstOrNull { it -> it.valueParameters.size == 1 && KotlinBuiltIns.isInt(it.valueParameters[0].type) }!!
val stringBuilderObj = call(context.llvm.allocInstanceFunction,
listOf(codegen.typeInfoValue(kStringBuilder.defaultType)!!, kImmOne))
val stringBuilderObj = codegen.allocInstance(codegen.typeInfoValue(kStringBuilder), SCOPE_FRAME)
call(codegen.llvmFunction(constructor), listOf(stringBuilderObj, totalLength))
stringsWithLengths.fold(stringBuilderObj) { sum, (string, _, _) ->
@@ -1434,11 +1435,10 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
*/
private fun fieldPtrOfClass(thisPtr: LLVMValueRef, value: PropertyDescriptor): LLVMValueRef {
val objHeaderPtr = codegen.bitcast(codegen.kObjHeaderPtr, thisPtr)
val typePtr = pointerType(codegen.classType(value.containingDeclaration as ClassDescriptor))
memScoped {
val args = allocArrayOf(kImmOne)
val objectPtr = LLVMBuildGEP(codegen.builder, objHeaderPtr, args[0].ptr, 1, "")
val objectPtr = LLVMBuildGEP(codegen.builder, thisPtr, args[0].ptr, 1, "")
val typedObjPtr = codegen.bitcast(typePtr, objectPtr!!)
val fieldPtr = LLVMBuildStructGEP(codegen.builder, typedObjPtr, codegen.indexInClass(value), "")
return fieldPtr!!
@@ -1742,24 +1742,22 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
}
//-------------------------------------------------------------------------//
private fun hintForCall(callee: IrCall): Int {
return allocHints.getOrElse(callee) { SCOPE_GLOBAL }
}
private fun evaluateConstructorCall(callee: IrCall, args: List<LLVMValueRef>): LLVMValueRef {
context.log("evaluateConstructorCall : ${ir2string(callee)}")
memScoped {
val constructedClass = (callee.descriptor as ConstructorDescriptor).constructedClass
val typeInfo = codegen.typeInfoValue(constructedClass)
val allocHint = Int32(1).llvm
val thisValue = if (constructedClass.isArray) {
assert(args.size >= 1 && args[0].type == int32Type)
val allocArrayInstanceArgs = listOf(typeInfo, allocHint, args[0])
call(context.llvm.allocArrayFunction, allocArrayInstanceArgs)
codegen.allocArray(codegen.typeInfoValue(constructedClass), hintForCall(callee), args[0])
} else {
call(context.llvm.allocInstanceFunction, listOf(typeInfo, allocHint))
codegen.allocInstance(codegen.typeInfoValue(constructedClass), hintForCall(callee))
}
val constructorParams: MutableList<LLVMValueRef> = mutableListOf()
constructorParams += thisValue
constructorParams += args
evaluateSimpleFunctionCall(callee.descriptor as FunctionDescriptor, constructorParams)
evaluateSimpleFunctionCall(callee.descriptor as FunctionDescriptor,
listOf(thisValue) + args)
return thisValue
}
}
@@ -1772,8 +1770,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
return when (name) {
"konan.internal.areEqualByValue" -> {
val arg0 = args[0]!!
val arg1 = args[1]!!
val arg0 = args[0]
val arg1 = args[1]
assert (arg0.type == arg1.type)
when (LLVMGetTypeKind(arg0.type)) {
@@ -1801,12 +1799,12 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
val descriptor = callee.descriptor
val ib = context.irModule!!.irBuiltins
when (descriptor) {
ib.eqeqeq -> return codegen.icmpEq(args[0]!!, args[1]!!)
ib.gt0 -> return codegen.icmpGt(args[0]!!, kImmZero)
ib.gteq0 -> return codegen.icmpGe(args[0]!!, kImmZero)
ib.lt0 -> return codegen.icmpLt(args[0]!!, kImmZero)
ib.lteq0 -> return codegen.icmpLe(args[0]!!, kImmZero)
ib.booleanNot -> return codegen.icmpNe(args[0]!!, kTrue)
ib.eqeqeq -> return codegen.icmpEq(args[0], args[1])
ib.gt0 -> return codegen.icmpGt(args[0], kImmZero)
ib.gteq0 -> return codegen.icmpGe(args[0], kImmZero)
ib.lt0 -> return codegen.icmpLt(args[0], kImmZero)
ib.lteq0 -> return codegen.icmpLe(args[0], kImmZero)
ib.booleanNot -> return codegen.icmpNe(args[0], kTrue)
else -> {
TODO(descriptor.name.toString())
}
@@ -1838,7 +1836,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
if (resultPhi != null && !isNothing)
codegen.assignPhis(resultPhi to brResult)
if (bbExit != null && !isNothing)
codegen.br(bbExit!!)
codegen.br(bbExit)
if (bbNext != null) // Switch generation to next or exit.
codegen.positionAtEnd(bbNext)
else if (bbExit != null)
@@ -1878,7 +1876,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
val slot = codegen.gep(vtable, Int32(index).llvm)
codegen.load(slot)
} else {
// Otherwise, call via hashtable.
// Otherwise, call by hash.
// TODO: optimize by storing interface number in lower bits of 'this' pointer
// when passing object as an interface. This way we can use those bits as index
// for an additional per-interface vtable.
@@ -1914,7 +1912,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
private fun call(function: LLVMValueRef, args: List<LLVMValueRef>): LLVMValueRef {
if (codegen.isObjectReturn(function.type)) {
// If function returns an object - create slot for the returned value.
// This allows appropriate rootset accounting by just looking on stack slots.
// This allows appropriate rootset accounting by just looking at the stack slots.
val resultSlot = codegen.vars.createAnonymousSlot()
return currentCodeContext.genCall(function, args + resultSlot)
} else {
@@ -115,6 +115,8 @@ internal val ContextUtils.kTypeInfo: LLVMTypeRef
get() = LLVMGetTypeByName(context.llvmModule, "struct.TypeInfo")!!
internal val ContextUtils.kObjHeader: LLVMTypeRef
get() = LLVMGetTypeByName(context.llvmModule, "struct.ObjHeader")!!
internal val ContextUtils.kContainerHeader: LLVMTypeRef
get() = LLVMGetTypeByName(context.llvmModule, "struct.ContainerHeader")!!
internal val ContextUtils.kObjHeaderPtr: LLVMTypeRef
get() = pointerType(kObjHeader)
internal val ContextUtils.kObjHeaderPtrPtr: LLVMTypeRef
@@ -129,7 +131,7 @@ internal val kInt1 = LLVMInt1Type()!!
internal val kBoolean = kInt1
internal val kInt8Ptr = pointerType(int8Type)
internal val kInt8PtrPtr = pointerType(kInt8Ptr)
internal val kNullInt8Ptr = LLVMConstNull(kInt8Ptr)
internal val kNullInt8Ptr = LLVMConstNull(kInt8Ptr)!!
internal val kImmInt32One = Int32(1).llvm
internal val kImmInt64One = Int64(1).llvm
internal val ContextUtils.kNullObjHeaderPtr: LLVMValueRef