Frame-local allocations. (#193)
This commit is contained in:
+44
-6
@@ -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.
|
||||
|
||||
+10
-2
@@ -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")
|
||||
|
||||
+36
@@ -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))
|
||||
}
|
||||
+27
-29
@@ -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 {
|
||||
|
||||
+3
-1
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user