Local escape analysis (#3625)
This commit is contained in:
+2
@@ -485,6 +485,8 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
|
|||||||
producingCache = config.produce.isCache,
|
producingCache = config.produce.isCache,
|
||||||
librariesToCache = config.librariesToCache
|
librariesToCache = config.librariesToCache
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val declaredLocalArrays: MutableMap<String, LLVMTypeRef> = HashMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MemberScope.getContributedClassifier(name: String) =
|
private fun MemberScope.getContributedClassifier(name: String) =
|
||||||
|
|||||||
+2
@@ -393,6 +393,7 @@ internal val bitcodePhase = namedIrModulePhase(
|
|||||||
RTTIPhase then
|
RTTIPhase then
|
||||||
generateDebugInfoHeaderPhase then
|
generateDebugInfoHeaderPhase then
|
||||||
escapeAnalysisPhase then
|
escapeAnalysisPhase then
|
||||||
|
localEscapeAnalysisPhase then
|
||||||
codegenPhase then
|
codegenPhase then
|
||||||
finalizeDebugInfoPhase then
|
finalizeDebugInfoPhase then
|
||||||
cStubsPhase
|
cStubsPhase
|
||||||
@@ -458,6 +459,7 @@ internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) {
|
|||||||
disableIf(testProcessorPhase, getNotNull(KonanConfigKeys.GENERATE_TEST_RUNNER) == TestRunnerKind.NONE)
|
disableIf(testProcessorPhase, getNotNull(KonanConfigKeys.GENERATE_TEST_RUNNER) == TestRunnerKind.NONE)
|
||||||
disableUnless(buildDFGPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
|
disableUnless(buildDFGPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
|
||||||
disableUnless(devirtualizationPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
|
disableUnless(devirtualizationPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
|
||||||
|
disableUnless(localEscapeAnalysisPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
|
||||||
disableUnless(dcePhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
|
disableUnless(dcePhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
|
||||||
disableUnless(ghaPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
|
disableUnless(ghaPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
|
||||||
disableUnless(verifyBitcodePhase, config.needCompilerVerification || getBoolean(KonanConfigKeys.VERIFY_BITCODE))
|
disableUnless(verifyBitcodePhase, config.needCompilerVerification || getBoolean(KonanConfigKeys.VERIFY_BITCODE))
|
||||||
|
|||||||
+12
@@ -95,10 +95,22 @@ internal val arrayTypes = setOf(
|
|||||||
"kotlin.native.internal.NativePtrArray"
|
"kotlin.native.internal.NativePtrArray"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
internal val arraysWithFixedSizeItems = setOf(
|
||||||
|
"kotlin.ByteArray",
|
||||||
|
"kotlin.CharArray",
|
||||||
|
"kotlin.ShortArray",
|
||||||
|
"kotlin.IntArray",
|
||||||
|
"kotlin.LongArray",
|
||||||
|
"kotlin.FloatArray",
|
||||||
|
"kotlin.DoubleArray",
|
||||||
|
"kotlin.BooleanArray"
|
||||||
|
)
|
||||||
|
|
||||||
internal val IrClass.isArray: Boolean
|
internal val IrClass.isArray: Boolean
|
||||||
get() = this.fqNameForIrSerialization.asString() in arrayTypes
|
get() = this.fqNameForIrSerialization.asString() in arrayTypes
|
||||||
|
|
||||||
|
internal val IrClass.isArrayWithFixedSizeItems: Boolean
|
||||||
|
get() = this.fqNameForIrSerialization.asString() in arraysWithFixedSizeItems
|
||||||
|
|
||||||
fun IrClass.isAbstract() = this.modality == Modality.SEALED || this.modality == Modality.ABSTRACT
|
fun IrClass.isAbstract() = this.modality == Modality.SEALED || this.modality == Modality.ABSTRACT
|
||||||
|
|
||||||
|
|||||||
+9
@@ -211,6 +211,15 @@ internal val escapeAnalysisPhase = makeKonanModuleOpPhase(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
internal val localEscapeAnalysisPhase = makeKonanModuleOpPhase(
|
||||||
|
name = "LocalEscapeAnalysis",
|
||||||
|
description = "Local escape analysis",
|
||||||
|
prerequisite = setOf(buildDFGPhase, devirtualizationPhase),
|
||||||
|
op = { context, _ ->
|
||||||
|
LocalEscapeAnalysis.computeLifetimes(context, context.moduleDFG!!, context.lifetimes)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
internal val serializeDFGPhase = makeKonanModuleOpPhase(
|
internal val serializeDFGPhase = makeKonanModuleOpPhase(
|
||||||
name = "SerializeDFG",
|
name = "SerializeDFG",
|
||||||
description = "Data flow graph serializing",
|
description = "Data flow graph serializing",
|
||||||
|
|||||||
+86
-22
@@ -190,7 +190,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
|||||||
val vars = VariableManager(this)
|
val vars = VariableManager(this)
|
||||||
private val basicBlockToLastLocation = mutableMapOf<LLVMBasicBlockRef, LocationInfoRange>()
|
private val basicBlockToLastLocation = mutableMapOf<LLVMBasicBlockRef, LocationInfoRange>()
|
||||||
|
|
||||||
private fun update(block:LLVMBasicBlockRef, startLocationInfo: LocationInfo?, endLocation: LocationInfo? = startLocationInfo) {
|
private fun update(block: LLVMBasicBlockRef, startLocationInfo: LocationInfo?, endLocation: LocationInfo? = startLocationInfo) {
|
||||||
startLocationInfo ?: return
|
startLocationInfo ?: return
|
||||||
basicBlockToLastLocation.put(block, LocationInfoRange(startLocationInfo, endLocation))
|
basicBlockToLastLocation.put(block, LocationInfoRange(startLocationInfo, endLocation))
|
||||||
}
|
}
|
||||||
@@ -205,13 +205,14 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
|||||||
(LLVMStoreSizeOfType(llvmTargetData, runtime.frameOverlayType) / runtime.pointerSize).toInt()
|
(LLVMStoreSizeOfType(llvmTargetData, runtime.frameOverlayType) / runtime.pointerSize).toInt()
|
||||||
private var slotCount = frameOverlaySlotCount
|
private var slotCount = frameOverlaySlotCount
|
||||||
private var localAllocs = 0
|
private var localAllocs = 0
|
||||||
private var arenaSlot: LLVMValueRef? = null
|
// TODO: remove if exactly unused.
|
||||||
private val slotToVariableLocation = mutableMapOf<Int,VariableDebugLocation>()
|
//private var arenaSlot: LLVMValueRef? = null
|
||||||
|
private val slotToVariableLocation = mutableMapOf<Int, VariableDebugLocation>()
|
||||||
|
|
||||||
private val prologueBb = basicBlockInFunction("prologue", startLocation)
|
private val prologueBb = basicBlockInFunction("prologue", startLocation)
|
||||||
private val localsInitBb = basicBlockInFunction("locals_init", startLocation)
|
private val localsInitBb = basicBlockInFunction("locals_init", startLocation)
|
||||||
private val entryBb = basicBlockInFunction("entry", startLocation)
|
private val entryBb = basicBlockInFunction("entry", startLocation)
|
||||||
private val epilogueBb = basicBlockInFunction("epilogue", endLocation)
|
private val epilogueBb = basicBlockInFunction("epilogue", endLocation)
|
||||||
private val cleanupLandingpad = basicBlockInFunction("cleanup_landingpad", endLocation)
|
private val cleanupLandingpad = basicBlockInFunction("cleanup_landingpad", endLocation)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -238,7 +239,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
|||||||
return bb
|
return bb
|
||||||
}
|
}
|
||||||
|
|
||||||
fun basicBlock(name:String = "label_", startLocationInfo:LocationInfo?, endLocationInfo: LocationInfo? = startLocationInfo): LLVMBasicBlockRef {
|
fun basicBlock(name: String = "label_", startLocationInfo: LocationInfo?, endLocationInfo: LocationInfo? = startLocationInfo): LLVMBasicBlockRef {
|
||||||
val result = LLVMInsertBasicBlockInContext(llvmContext, this.currentBlock, name)!!
|
val result = LLVMInsertBasicBlockInContext(llvmContext, this.currentBlock, name)!!
|
||||||
update(result, startLocationInfo, endLocationInfo)
|
update(result, startLocationInfo, endLocationInfo)
|
||||||
LLVMMoveBasicBlockAfter(result, this.currentBlock)
|
LLVMMoveBasicBlockAfter(result, this.currentBlock)
|
||||||
@@ -261,13 +262,13 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
|||||||
val slotAddress = LLVMBuildAlloca(builder, type, name)!!
|
val slotAddress = LLVMBuildAlloca(builder, type, name)!!
|
||||||
variableLocation?.let {
|
variableLocation?.let {
|
||||||
DIInsertDeclaration(
|
DIInsertDeclaration(
|
||||||
builder = codegen.context.debugInfo.builder,
|
builder = codegen.context.debugInfo.builder,
|
||||||
value = slotAddress,
|
value = slotAddress,
|
||||||
localVariable = it.localVariable,
|
localVariable = it.localVariable,
|
||||||
location = it.location,
|
location = it.location,
|
||||||
bb = prologueBb,
|
bb = prologueBb,
|
||||||
expr = null,
|
expr = null,
|
||||||
exprCount = 0)
|
exprCount = 0)
|
||||||
}
|
}
|
||||||
return slotAddress
|
return slotAddress
|
||||||
}
|
}
|
||||||
@@ -327,7 +328,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
|||||||
|
|
||||||
fun freeze(value: LLVMValueRef, exceptionHandler: ExceptionHandler) {
|
fun freeze(value: LLVMValueRef, exceptionHandler: ExceptionHandler) {
|
||||||
if (isObjectRef(value))
|
if (isObjectRef(value))
|
||||||
call(context.llvm.freezeSubgraph, listOf(value), Lifetime.IRRELEVANT, exceptionHandler)
|
call(context.llvm.freezeSubgraph, listOf(value), Lifetime.IRRELEVANT, exceptionHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkMainThread(exceptionHandler: ExceptionHandler) {
|
fun checkMainThread(exceptionHandler: ExceptionHandler) {
|
||||||
@@ -368,7 +369,13 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
|||||||
val resultSlot = when (resultLifetime.slotType) {
|
val resultSlot = when (resultLifetime.slotType) {
|
||||||
SlotType.ARENA -> {
|
SlotType.ARENA -> {
|
||||||
localAllocs++
|
localAllocs++
|
||||||
arenaSlot!!
|
// Case of local call. Use memory allocated on stack.
|
||||||
|
val type = LLVMGetReturnType(LLVMGetElementType(llvmFunction.type))!!
|
||||||
|
val stackPointer = alloca(type)
|
||||||
|
//val objectHeader = structGep(stackPointer, 0)
|
||||||
|
//setTypeInfoForLocalObject(objectHeader)
|
||||||
|
stackPointer
|
||||||
|
//arenaSlot!!
|
||||||
}
|
}
|
||||||
|
|
||||||
SlotType.RETURN -> returnSlot!!
|
SlotType.RETURN -> returnSlot!!
|
||||||
@@ -385,7 +392,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
|||||||
private fun callRaw(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>,
|
private fun callRaw(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>,
|
||||||
exceptionHandler: ExceptionHandler): LLVMValueRef {
|
exceptionHandler: ExceptionHandler): LLVMValueRef {
|
||||||
val rargs = args.toCValues()
|
val rargs = args.toCValues()
|
||||||
if (LLVMIsAFunction(llvmFunction) != null /* the function declaration */ &&
|
if (LLVMIsAFunction(llvmFunction) != null /* the function declaration */ &&
|
||||||
isFunctionNoUnwind(llvmFunction)) {
|
isFunctionNoUnwind(llvmFunction)) {
|
||||||
return LLVMBuildCall(builder, llvmFunction, rargs, args.size, "")!!
|
return LLVMBuildCall(builder, llvmFunction, rargs, args.size, "")!!
|
||||||
} else {
|
} else {
|
||||||
@@ -439,13 +446,70 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
|||||||
call(context.llvm.allocInstanceFunction, listOf(typeInfo), lifetime)
|
call(context.llvm.allocInstanceFunction, listOf(typeInfo), lifetime)
|
||||||
|
|
||||||
fun allocInstance(irClass: IrClass, lifetime: Lifetime): LLVMValueRef =
|
fun allocInstance(irClass: IrClass, lifetime: Lifetime): LLVMValueRef =
|
||||||
allocInstance(codegen.typeInfoForAllocation(irClass), lifetime)
|
if (lifetime == Lifetime.LOCAL) {
|
||||||
|
val stackSlot = alloca(context.llvmDeclarations.forClass(irClass).bodyType)
|
||||||
|
val objectHeader = structGep(stackSlot, 0, "objHeader")
|
||||||
|
setTypeInfoForLocalObject(objectHeader)
|
||||||
|
objectHeader
|
||||||
|
} else {
|
||||||
|
allocInstance(codegen.typeInfoForAllocation(irClass), lifetime)
|
||||||
|
}
|
||||||
|
|
||||||
fun allocArray(typeInfo: LLVMValueRef,
|
// TODO: find better place?
|
||||||
|
val arrayToElementType = mapOf(
|
||||||
|
"kotlin.ByteArray" to int8Type,
|
||||||
|
"kotlin.CharArray" to int16Type,
|
||||||
|
"kotlin.ShortArray" to int16Type,
|
||||||
|
"kotlin.IntArray" to int32Type,
|
||||||
|
"kotlin.LongArray" to int64Type,
|
||||||
|
"kotlin.FloatArray" to floatType,
|
||||||
|
"kotlin.DoubleArray" to doubleType,
|
||||||
|
"kotlin.BooleanArray" to int8Type
|
||||||
|
)
|
||||||
|
|
||||||
|
// Returns generated special type for local array.
|
||||||
|
// It's needed to prevent changing variables order on stack.
|
||||||
|
// TODO: add alignment calculation. Now it isn't needed, because of working only with primitive types.
|
||||||
|
private fun localArrayType(className: String, count: Int): LLVMTypeRef {
|
||||||
|
val name = "local#${className}${count}#internal"
|
||||||
|
// Create new type or get already created.
|
||||||
|
return context.declaredLocalArrays.getOrPut(name) {
|
||||||
|
val fieldTypes = listOf(kArrayHeader, LLVMArrayType(arrayToElementType[className]!!, count))
|
||||||
|
val classType = LLVMStructCreateNamed(LLVMGetModuleContext(context.llvmModule), name)!!
|
||||||
|
LLVMStructSetBody(classType, fieldTypes.toCValues(), fieldTypes.size, 1)
|
||||||
|
classType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun allocArray(irClass: IrClass,
|
||||||
count: LLVMValueRef,
|
count: LLVMValueRef,
|
||||||
lifetime: Lifetime,
|
lifetime: Lifetime,
|
||||||
exceptionHandler: ExceptionHandler): LLVMValueRef =
|
exceptionHandler: ExceptionHandler): LLVMValueRef =
|
||||||
call(context.llvm.allocArrayFunction, listOf(typeInfo, count), lifetime, exceptionHandler)
|
if (lifetime == Lifetime.LOCAL) {
|
||||||
|
val className = irClass.fqNameForIrSerialization.asString()
|
||||||
|
assert(className in arrayToElementType)
|
||||||
|
allocArrayOnStack(className, count)
|
||||||
|
} else {
|
||||||
|
call(context.llvm.allocArrayFunction, listOf(codegen.typeInfoValue(irClass), count),
|
||||||
|
lifetime, exceptionHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTypeInfoForLocalObject(objectHeader: LLVMValueRef) {
|
||||||
|
val typeInfo = structGep(objectHeader, 0, "typeInfoOrMeta_")
|
||||||
|
// Set tag OBJECT_TAG_PERMANENT_CONTAINER.
|
||||||
|
val typeInfoValue = intToPtr(or(ptrToInt(alloca(kTypeInfo), int32Type), Int32(1).llvm), kTypeInfoPtr)
|
||||||
|
store(typeInfoValue, typeInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun allocArrayOnStack(arrayTypeName: String, count: LLVMValueRef): LLVMValueRef {
|
||||||
|
val arraySlot = alloca(localArrayType(arrayTypeName, extractConstUnsignedInt(count).toInt()))
|
||||||
|
// Set array size in ArrayHeader.
|
||||||
|
val arrayHeaderSlot = structGep(arraySlot, 0, "arrayHeader")
|
||||||
|
setTypeInfoForLocalObject(arrayHeaderSlot)
|
||||||
|
val sizeField = structGep(arrayHeaderSlot, 1, "count_")
|
||||||
|
store(count, sizeField)
|
||||||
|
return bitcast(kObjHeaderPtr, arrayHeaderSlot)
|
||||||
|
}
|
||||||
|
|
||||||
fun unreachable(): LLVMValueRef? {
|
fun unreachable(): LLVMValueRef? {
|
||||||
if (context.config.debug) {
|
if (context.config.debug) {
|
||||||
@@ -973,8 +1037,8 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
|||||||
positionAtEnd(localsInitBb)
|
positionAtEnd(localsInitBb)
|
||||||
slotsPhi = phi(kObjHeaderPtrPtr)
|
slotsPhi = phi(kObjHeaderPtrPtr)
|
||||||
// Is removed by DCE trivially, if not needed.
|
// Is removed by DCE trivially, if not needed.
|
||||||
arenaSlot = intToPtr(
|
/*arenaSlot = intToPtr(
|
||||||
or(ptrToInt(slotsPhi, codegen.intPtrType), codegen.immOneIntPtrType), kObjHeaderPtrPtr)
|
or(ptrToInt(slotsPhi, codegen.intPtrType), codegen.immOneIntPtrType), kObjHeaderPtrPtr)*/
|
||||||
positionAtEnd(entryBb)
|
positionAtEnd(entryBb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
-5
@@ -417,11 +417,6 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv
|
|||||||
TODO("Implement me")
|
TODO("Implement me")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun extractConstUnsignedInt(value: LLVMValueRef): Long {
|
|
||||||
assert(LLVMIsConstant(value) != 0)
|
|
||||||
return LLVMConstIntGetZExtValue(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun FunctionGenerationContext.emitGetClassTypeInfo(callSite: IrCall): LLVMValueRef {
|
private fun FunctionGenerationContext.emitGetClassTypeInfo(callSite: IrCall): LLVMValueRef {
|
||||||
val typeArgument = callSite.getTypeArgument(0)!!
|
val typeArgument = callSite.getTypeArgument(0)!!
|
||||||
val typeArgumentClass = typeArgument.getClass()
|
val typeArgumentClass = typeArgument.getClass()
|
||||||
|
|||||||
+2
-3
@@ -2097,14 +2097,13 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
val thisValue = when {
|
val thisValue = when {
|
||||||
constructedClass.isArray -> {
|
constructedClass.isArray -> {
|
||||||
assert(args.isNotEmpty() && args[0].type == int32Type)
|
assert(args.isNotEmpty() && args[0].type == int32Type)
|
||||||
functionGenerationContext.allocArray(codegen.typeInfoValue(constructedClass), args[0],
|
functionGenerationContext.allocArray(constructedClass, args[0],
|
||||||
resultLifetime(callee), currentCodeContext.exceptionHandler)
|
resultLifetime(callee), currentCodeContext.exceptionHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructedClass == context.ir.symbols.string.owner -> {
|
constructedClass == context.ir.symbols.string.owner -> {
|
||||||
// TODO: consider returning the empty string literal instead.
|
// TODO: consider returning the empty string literal instead.
|
||||||
assert(args.isEmpty())
|
assert(args.isEmpty())
|
||||||
functionGenerationContext.allocArray(codegen.typeInfoValue(constructedClass), count = kImmZero,
|
functionGenerationContext.allocArray(constructedClass, count = kImmZero,
|
||||||
lifetime = resultLifetime(callee), exceptionHandler = currentCodeContext.exceptionHandler)
|
lifetime = resultLifetime(callee), exceptionHandler = currentCodeContext.exceptionHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
@@ -204,6 +204,11 @@ internal fun ContextUtils.numParameters(functionType: LLVMTypeRef) : Int {
|
|||||||
return LLVMCountParamTypes(LLVMGetElementType(functionType))
|
return LLVMCountParamTypes(LLVMGetElementType(functionType))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun extractConstUnsignedInt(value: LLVMValueRef): Long {
|
||||||
|
assert(LLVMIsConstant(value) != 0)
|
||||||
|
return LLVMConstIntGetZExtValue(value)
|
||||||
|
}
|
||||||
|
|
||||||
internal fun ContextUtils.isObjectReturn(functionType: LLVMTypeRef) : Boolean {
|
internal fun ContextUtils.isObjectReturn(functionType: LLVMTypeRef) : Boolean {
|
||||||
// Note that type is usually function pointer, so we have to dereference it.
|
// Note that type is usually function pointer, so we have to dereference it.
|
||||||
val returnType = LLVMGetReturnType(LLVMGetElementType(functionType))!!
|
val returnType = LLVMGetReturnType(LLVMGetElementType(functionType))!!
|
||||||
|
|||||||
+2
-2
@@ -129,7 +129,7 @@ internal class CallGraphBuilder(val context: Context,
|
|||||||
|
|
||||||
is DataFlowIR.Node.ArrayRead ->
|
is DataFlowIR.Node.ArrayRead ->
|
||||||
block(DataFlowIR.Node.Call(
|
block(DataFlowIR.Node.Call(
|
||||||
callee = moduleDFG.symbolTable.mapFunction(arrayGet),
|
callee = node.callee,
|
||||||
arguments = listOf(node.array, node.index),
|
arguments = listOf(node.array, node.index),
|
||||||
returnType = node.type,
|
returnType = node.type,
|
||||||
irCallSite = null)
|
irCallSite = null)
|
||||||
@@ -137,7 +137,7 @@ internal class CallGraphBuilder(val context: Context,
|
|||||||
|
|
||||||
is DataFlowIR.Node.ArrayWrite ->
|
is DataFlowIR.Node.ArrayWrite ->
|
||||||
block(DataFlowIR.Node.Call(
|
block(DataFlowIR.Node.Call(
|
||||||
callee = moduleDFG.symbolTable.mapFunction(arraySet),
|
callee = node.callee,
|
||||||
arguments = listOf(node.array, node.index, node.value),
|
arguments = listOf(node.array, node.index, node.value),
|
||||||
returnType = moduleDFG.symbolTable.mapType(context.irBuiltIns.unitType),
|
returnType = moduleDFG.symbolTable.mapType(context.irBuiltIns.unitType),
|
||||||
irCallSite = null)
|
irCallSite = null)
|
||||||
|
|||||||
+26
-13
@@ -404,8 +404,8 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
|
|||||||
private val getContinuationSymbol = symbols.getContinuation
|
private val getContinuationSymbol = symbols.getContinuation
|
||||||
private val continuationType = getContinuationSymbol.owner.returnType
|
private val continuationType = getContinuationSymbol.owner.returnType
|
||||||
|
|
||||||
private val arrayGetSymbol = symbols.arrayGet[symbols.array]
|
private val arrayGetSymbols = symbols.arrayGet.values
|
||||||
private val arraySetSymbol = symbols.arraySet[symbols.array]
|
private val arraySetSymbols = symbols.arraySet.values
|
||||||
private val createUninitializedInstanceSymbol = symbols.createUninitializedInstance
|
private val createUninitializedInstanceSymbol = symbols.createUninitializedInstance
|
||||||
private val initInstanceSymbol = symbols.initInstance
|
private val initInstanceSymbol = symbols.initInstance
|
||||||
private val executeImplSymbol = symbols.executeImpl
|
private val executeImplSymbol = symbols.executeImpl
|
||||||
@@ -574,7 +574,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
|
|||||||
if (value.value == null)
|
if (value.value == null)
|
||||||
DataFlowIR.Node.Null
|
DataFlowIR.Node.Null
|
||||||
else
|
else
|
||||||
DataFlowIR.Node.Const(symbolTable.mapType(value.type))
|
DataFlowIR.Node.SimpleConst(symbolTable.mapType(value.type), value.value!!)
|
||||||
|
|
||||||
is IrGetObjectValue -> DataFlowIR.Node.Singleton(
|
is IrGetObjectValue -> DataFlowIR.Node.Singleton(
|
||||||
symbolTable.mapType(value.type),
|
symbolTable.mapType(value.type),
|
||||||
@@ -597,17 +597,30 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
|
|||||||
is IrCall -> when (value.symbol) {
|
is IrCall -> when (value.symbol) {
|
||||||
getContinuationSymbol -> getContinuation()
|
getContinuationSymbol -> getContinuation()
|
||||||
|
|
||||||
arrayGetSymbol -> DataFlowIR.Node.ArrayRead(
|
in arrayGetSymbols -> {
|
||||||
array = expressionToEdge(value.dispatchReceiver!!),
|
val callee = value.symbol.owner
|
||||||
index = expressionToEdge(value.getValueArgument(0)!!),
|
val actualCallee = (value.superQualifierSymbol?.owner?.getOverridingOf(callee)
|
||||||
type = mapReturnType(value.type, context.irBuiltIns.anyType),
|
?: callee).target
|
||||||
irCallSite = value)
|
|
||||||
|
|
||||||
arraySetSymbol -> DataFlowIR.Node.ArrayWrite(
|
DataFlowIR.Node.ArrayRead(
|
||||||
array = expressionToEdge(value.dispatchReceiver!!),
|
symbolTable.mapFunction(actualCallee),
|
||||||
index = expressionToEdge(value.getValueArgument(0)!!),
|
array = expressionToEdge(value.dispatchReceiver!!),
|
||||||
value = expressionToEdge(value.getValueArgument(1)!!),
|
index = expressionToEdge(value.getValueArgument(0)!!),
|
||||||
type = mapReturnType(value.getValueArgument(1)!!.type, context.irBuiltIns.anyType))
|
type = mapReturnType(value.type, context.irBuiltIns.anyType),
|
||||||
|
irCallSite = value)
|
||||||
|
}
|
||||||
|
|
||||||
|
in arraySetSymbols -> {
|
||||||
|
val callee = value.symbol.owner
|
||||||
|
val actualCallee = (value.superQualifierSymbol?.owner?.getOverridingOf(callee)
|
||||||
|
?: callee).target
|
||||||
|
DataFlowIR.Node.ArrayWrite(
|
||||||
|
symbolTable.mapFunction(actualCallee),
|
||||||
|
array = expressionToEdge(value.dispatchReceiver!!),
|
||||||
|
index = expressionToEdge(value.getValueArgument(0)!!),
|
||||||
|
value = expressionToEdge(value.getValueArgument(1)!!),
|
||||||
|
type = mapReturnType(value.getValueArgument(1)!!.type, context.irBuiltIns.anyType))
|
||||||
|
}
|
||||||
|
|
||||||
createUninitializedInstanceSymbol ->
|
createUninitializedInstanceSymbol ->
|
||||||
DataFlowIR.Node.AllocInstance(symbolTable.mapClassReferenceType(
|
DataFlowIR.Node.AllocInstance(symbolTable.mapClassReferenceType(
|
||||||
|
|||||||
+14
-12
@@ -565,22 +565,24 @@ internal object DFGSerializer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ArrayRead(val array: Edge, val index: Edge, val type: Int) {
|
class ArrayRead(val callee: Int, val array: Edge, val index: Edge, val type: Int) {
|
||||||
|
|
||||||
constructor(data: ArraySlice) : this(Edge(data), Edge(data), data.readInt())
|
constructor(data: ArraySlice) : this(data.readInt(), Edge(data), Edge(data), data.readInt())
|
||||||
|
|
||||||
fun write(result: ArraySlice) {
|
fun write(result: ArraySlice) {
|
||||||
|
result.writeInt(callee)
|
||||||
array.write(result)
|
array.write(result)
|
||||||
index.write(result)
|
index.write(result)
|
||||||
result.writeInt(type)
|
result.writeInt(type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ArrayWrite(val array: Edge, val index: Edge, val value: Edge, val type: Int) {
|
class ArrayWrite(val callee: Int, val array: Edge, val index: Edge, val value: Edge, val type: Int) {
|
||||||
|
|
||||||
constructor(data: ArraySlice) : this(Edge(data), Edge(data), Edge(data), data.readInt())
|
constructor(data: ArraySlice) : this(data.readInt(), Edge(data), Edge(data), Edge(data), data.readInt())
|
||||||
|
|
||||||
fun write(result: ArraySlice) {
|
fun write(result: ArraySlice) {
|
||||||
|
result.writeInt(callee)
|
||||||
array.write(result)
|
array.write(result)
|
||||||
index.write(result)
|
index.write(result)
|
||||||
value.write(result)
|
value.write(result)
|
||||||
@@ -708,11 +710,11 @@ internal object DFGSerializer {
|
|||||||
fun fieldWrite(receiver: Edge?, field: Field, value: Edge, type: Int) =
|
fun fieldWrite(receiver: Edge?, field: Field, value: Edge, type: Int) =
|
||||||
Node().also { it.fieldWrite = FieldWrite(receiver, field, value, type) }
|
Node().also { it.fieldWrite = FieldWrite(receiver, field, value, type) }
|
||||||
|
|
||||||
fun arrayRead(array: Edge, index: Edge, type: Int) =
|
fun arrayRead(callee: Int, array: Edge, index: Edge, type: Int) =
|
||||||
Node().also { it.arrayRead = ArrayRead(array, index, type) }
|
Node().also { it.arrayRead = ArrayRead(callee, array, index, type) }
|
||||||
|
|
||||||
fun arrayWrite(array: Edge, index: Edge, value: Edge, type: Int) =
|
fun arrayWrite(callee: Int, array: Edge, index: Edge, value: Edge, type: Int) =
|
||||||
Node().also { it.arrayWrite = ArrayWrite(array, index, value, type) }
|
Node().also { it.arrayWrite = ArrayWrite(callee, array, index, value, type) }
|
||||||
|
|
||||||
fun variable(values: Array<Edge>, type: Int, kind: DataFlowIR.VariableKind) =
|
fun variable(values: Array<Edge>, type: Int, kind: DataFlowIR.VariableKind) =
|
||||||
Node().also { it.variable = Variable(values, type, kind.ordinal.toByte()) }
|
Node().also { it.variable = Variable(values, type, kind.ordinal.toByte()) }
|
||||||
@@ -948,10 +950,10 @@ internal object DFGSerializer {
|
|||||||
Node.fieldWrite(node.receiver?.let { buildEdge(it) }, buildField(node.field), buildEdge(node.value), typeMap[node.type]!!)
|
Node.fieldWrite(node.receiver?.let { buildEdge(it) }, buildField(node.field), buildEdge(node.value), typeMap[node.type]!!)
|
||||||
|
|
||||||
is DataFlowIR.Node.ArrayRead ->
|
is DataFlowIR.Node.ArrayRead ->
|
||||||
Node.arrayRead(buildEdge(node.array), buildEdge(node.index), typeMap[node.type]!!)
|
Node.arrayRead(functionSymbolMap[node.callee]!!, buildEdge(node.array), buildEdge(node.index), typeMap[node.type]!!)
|
||||||
|
|
||||||
is DataFlowIR.Node.ArrayWrite ->
|
is DataFlowIR.Node.ArrayWrite ->
|
||||||
Node.arrayWrite(buildEdge(node.array), buildEdge(node.index), buildEdge(node.value), typeMap[node.type]!!)
|
Node.arrayWrite(functionSymbolMap[node.callee]!!, buildEdge(node.array), buildEdge(node.index), buildEdge(node.value), typeMap[node.type]!!)
|
||||||
|
|
||||||
is DataFlowIR.Node.Variable ->
|
is DataFlowIR.Node.Variable ->
|
||||||
Node.variable(node.values.map { buildEdge(it) }.toTypedArray(), typeMap[node.type]!!, node.kind)
|
Node.variable(node.values.map { buildEdge(it) }.toTypedArray(), typeMap[node.type]!!, node.kind)
|
||||||
@@ -1213,12 +1215,12 @@ internal object DFGSerializer {
|
|||||||
|
|
||||||
NodeType.ARRAY_READ -> {
|
NodeType.ARRAY_READ -> {
|
||||||
val arrayRead = it.arrayRead!!
|
val arrayRead = it.arrayRead!!
|
||||||
DataFlowIR.Node.ArrayRead(deserializeEdge(arrayRead.array), deserializeEdge(arrayRead.index), types[arrayRead.type], null)
|
DataFlowIR.Node.ArrayRead(functionSymbols[arrayRead.callee], deserializeEdge(arrayRead.array), deserializeEdge(arrayRead.index), types[arrayRead.type], null)
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeType.ARRAY_WRITE -> {
|
NodeType.ARRAY_WRITE -> {
|
||||||
val arrayWrite = it.arrayWrite!!
|
val arrayWrite = it.arrayWrite!!
|
||||||
DataFlowIR.Node.ArrayWrite(deserializeEdge(arrayWrite.array), deserializeEdge(arrayWrite.index), deserializeEdge(arrayWrite.value), types[arrayWrite.type])
|
DataFlowIR.Node.ArrayWrite(functionSymbols[arrayWrite.callee], deserializeEdge(arrayWrite.array), deserializeEdge(arrayWrite.index), deserializeEdge(arrayWrite.value), types[arrayWrite.type])
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeType.VARIABLE -> {
|
NodeType.VARIABLE -> {
|
||||||
|
|||||||
+5
-4
@@ -217,7 +217,9 @@ internal object DataFlowIR {
|
|||||||
sealed class Node {
|
sealed class Node {
|
||||||
class Parameter(val index: Int) : Node()
|
class Parameter(val index: Int) : Node()
|
||||||
|
|
||||||
class Const(val type: Type) : Node()
|
open class Const(val type: Type) : Node()
|
||||||
|
|
||||||
|
class SimpleConst<T : Any>(type: Type, val value: T) : Const(type)
|
||||||
|
|
||||||
object Null : Node()
|
object Null : Node()
|
||||||
|
|
||||||
@@ -255,9 +257,9 @@ internal object DataFlowIR {
|
|||||||
|
|
||||||
class FieldWrite(val receiver: Edge?, val field: Field, val value: Edge, val type: Type) : Node()
|
class FieldWrite(val receiver: Edge?, val field: Field, val value: Edge, val type: Type) : Node()
|
||||||
|
|
||||||
class ArrayRead(val array: Edge, val index: Edge, val type: Type, val irCallSite: IrCall?) : Node()
|
class ArrayRead(val callee: FunctionSymbol, val array: Edge, val index: Edge, val type: Type, val irCallSite: IrCall?) : Node()
|
||||||
|
|
||||||
class ArrayWrite(val array: Edge, val index: Edge, val value: Edge, val type: Type) : Node()
|
class ArrayWrite(val callee: FunctionSymbol, val array: Edge, val index: Edge, val value: Edge, val type: Type) : Node()
|
||||||
|
|
||||||
class Variable(values: List<Edge>, val type: Type, val kind: VariableKind) : Node() {
|
class Variable(values: List<Edge>, val type: Type, val kind: VariableKind) : Node() {
|
||||||
val values = mutableListOf<Edge>().also { it += values }
|
val values = mutableListOf<Edge>().also { it += values }
|
||||||
@@ -506,7 +508,6 @@ internal object DataFlowIR {
|
|||||||
val isFinal = irClass.isFinal()
|
val isFinal = irClass.isFinal()
|
||||||
val isAbstract = irClass.isAbstract()
|
val isAbstract = irClass.isAbstract()
|
||||||
val name = irClass.fqNameForIrSerialization.asString()
|
val name = irClass.fqNameForIrSerialization.asString()
|
||||||
|
|
||||||
classMap[irClass]?.let { return it }
|
classMap[irClass]?.let { return it }
|
||||||
|
|
||||||
val placeToClassTable = true
|
val placeToClassTable = true
|
||||||
|
|||||||
+209
@@ -0,0 +1,209 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.backend.konan.optimizations
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.konan.llvm.Lifetime
|
||||||
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
|
import org.jetbrains.kotlin.backend.konan.descriptors.isArrayWithFixedSizeItems
|
||||||
|
import org.jetbrains.kotlin.backend.konan.descriptors.isBuiltInOperator
|
||||||
|
|
||||||
|
internal object LocalEscapeAnalysis {
|
||||||
|
private val DEBUG = 0
|
||||||
|
|
||||||
|
private inline fun DEBUG_OUTPUT(severity: Int, block: () -> Unit) {
|
||||||
|
if (DEBUG > severity) block()
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum class EscapeState {
|
||||||
|
GLOBAL_ESCAPE, // escape through global reference
|
||||||
|
ARG_ESCAPE, // escapes through function arguments, return or throw
|
||||||
|
NO_ESCAPE // object does not escape
|
||||||
|
}
|
||||||
|
|
||||||
|
class FunctionAnalyzer(val function: DataFlowIR.Function, val context: Context) {
|
||||||
|
// Escape states of nodes.
|
||||||
|
private val nodesEscapeStates = mutableMapOf<DataFlowIR.Node, EscapeState>()
|
||||||
|
// Connected objects map, escape state change of key node influences on all connected nodes states.
|
||||||
|
private val connectedObjects = mutableMapOf<DataFlowIR.Node, MutableSet<DataFlowIR.Node>>()
|
||||||
|
// Maximum size of array which is allowed to allocate on stack.
|
||||||
|
// TODO: replace into KonanConfigKeys?
|
||||||
|
private val stackAllocationArraySizeLimit = 64
|
||||||
|
|
||||||
|
private var DataFlowIR.Node.escapeState: EscapeState
|
||||||
|
set(value) {
|
||||||
|
// Write state only if it has more limitations.
|
||||||
|
val writeState = nodesEscapeStates[this]?.let {
|
||||||
|
value < it
|
||||||
|
} ?: true
|
||||||
|
if (writeState) {
|
||||||
|
nodesEscapeStates[this] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get() = nodesEscapeStates.getOrDefault(this, EscapeState.NO_ESCAPE)
|
||||||
|
|
||||||
|
private fun connectObjects(node: DataFlowIR.Node, connectedNode: DataFlowIR.Node) {
|
||||||
|
connectedObjects.getOrPut(node) { mutableSetOf() }.add(connectedNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findOutArraySize(node: DataFlowIR.Node): Int? {
|
||||||
|
if (node is DataFlowIR.Node.SimpleConst<*>) {
|
||||||
|
return node.value as? Int
|
||||||
|
}
|
||||||
|
if (node is DataFlowIR.Node.Variable) {
|
||||||
|
// In case of several possible values, it's unknown what is used.
|
||||||
|
// TODO: if all values are constants which are less limit?
|
||||||
|
if (node.values.size == 1) {
|
||||||
|
return findOutArraySize(node.values.first().node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun evaluateEscapeState(node: DataFlowIR.Node) {
|
||||||
|
node.escapeState = EscapeState.NO_ESCAPE
|
||||||
|
when (node) {
|
||||||
|
is DataFlowIR.Node.Call -> {
|
||||||
|
val pointsToMasks = (0..node.callee.parameters.size)
|
||||||
|
.map { node.callee.pointsTo?.elementAtOrNull(it) ?: 0 }
|
||||||
|
val returnPointsToMask = pointsToMasks[node.callee.parameters.size]
|
||||||
|
node.arguments.forEachIndexed { index, arg ->
|
||||||
|
// Check information about arguments escaping.
|
||||||
|
val escapes = node.callee.escapes?.let {
|
||||||
|
it and (1 shl index) != 0
|
||||||
|
} ?: node.callee !is DataFlowIR.FunctionSymbol.External
|
||||||
|
|
||||||
|
// Connect with all arguments that return value points to.
|
||||||
|
if (returnPointsToMask and (1 shl index) != 0) {
|
||||||
|
connectObjects(node, arg.node)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect current argument with other it points to.
|
||||||
|
(0..node.callee.parameters.size).filter { pointsToMasks[index] and (1 shl it) != 0 }.forEach {
|
||||||
|
if (it == node.callee.parameters.size) {
|
||||||
|
// Argument points to this.
|
||||||
|
connectObjects(arg.node, node)
|
||||||
|
} else {
|
||||||
|
connectObjects(arg.node, node.arguments[it].node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
arg.node.escapeState = if (escapes) EscapeState.ARG_ESCAPE else EscapeState.NO_ESCAPE
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check size for array allocation.
|
||||||
|
if (node is DataFlowIR.Node.NewObject && node.constructedType is DataFlowIR.Type.Declared) {
|
||||||
|
node.constructedType.irClass?.let { irClass ->
|
||||||
|
// Work only with arrays which elements size is known.
|
||||||
|
if (irClass.isArrayWithFixedSizeItems) {
|
||||||
|
val sizeArgument = node.arguments.first().node
|
||||||
|
val arraySize = findOutArraySize(sizeArgument)
|
||||||
|
if (arraySize == null || arraySize > stackAllocationArraySizeLimit) {
|
||||||
|
node.escapeState = EscapeState.GLOBAL_ESCAPE
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
node.escapeState = EscapeState.GLOBAL_ESCAPE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is DataFlowIR.Node.Singleton -> {
|
||||||
|
node.escapeState = EscapeState.GLOBAL_ESCAPE
|
||||||
|
}
|
||||||
|
is DataFlowIR.Node.FieldRead -> {
|
||||||
|
node.receiver?.let { obj ->
|
||||||
|
connectObjects(node, obj.node)
|
||||||
|
} ?: run { node.escapeState = EscapeState.GLOBAL_ESCAPE }
|
||||||
|
}
|
||||||
|
is DataFlowIR.Node.FieldWrite -> {
|
||||||
|
node.receiver?.let { obj ->
|
||||||
|
connectObjects(obj.node, node.value.node)
|
||||||
|
} ?: run {
|
||||||
|
node.escapeState = EscapeState.GLOBAL_ESCAPE
|
||||||
|
node.value.node.escapeState = EscapeState.GLOBAL_ESCAPE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is DataFlowIR.Node.ArrayWrite -> {
|
||||||
|
connectObjects(node.array.node, node.value.node)
|
||||||
|
}
|
||||||
|
is DataFlowIR.Node.Variable -> {
|
||||||
|
node.values.forEach {
|
||||||
|
connectObjects(node, it.node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is DataFlowIR.Node.ArrayRead -> {
|
||||||
|
// If element of array escapes then array also should escape.
|
||||||
|
connectObjects(node, node.array.node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class ConnectedObjectsVisitor {
|
||||||
|
val visitedObjects = mutableSetOf<DataFlowIR.Node>()
|
||||||
|
|
||||||
|
fun visit(node: DataFlowIR.Node, action: (DataFlowIR.Node) -> Unit) {
|
||||||
|
action(node)
|
||||||
|
visitedObjects.add(node)
|
||||||
|
connectedObjects[node]?.forEach {
|
||||||
|
if (!visitedObjects.contains(it)) {
|
||||||
|
visit(it, action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun propagateState(state: EscapeState, visitor: ConnectedObjectsVisitor) {
|
||||||
|
connectedObjects.filter { it.key.escapeState == state }.forEach { (key, value) ->
|
||||||
|
value.forEach { obj ->
|
||||||
|
visitor.visit(obj) { it.escapeState = key.escapeState }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun analyze(lifetimes: MutableMap<IrElement, Lifetime>) {
|
||||||
|
function.body.nodes.forEach { node ->
|
||||||
|
evaluateEscapeState(node)
|
||||||
|
}
|
||||||
|
function.body.returns.escapeState = EscapeState.ARG_ESCAPE
|
||||||
|
function.body.throws.escapeState = EscapeState.ARG_ESCAPE
|
||||||
|
|
||||||
|
// Change state of connected objects.
|
||||||
|
val visitor = ConnectedObjectsVisitor()
|
||||||
|
propagateState(EscapeState.GLOBAL_ESCAPE, visitor)
|
||||||
|
propagateState(EscapeState.ARG_ESCAPE, visitor)
|
||||||
|
|
||||||
|
nodesEscapeStates.filter {
|
||||||
|
it.value == EscapeState.NO_ESCAPE
|
||||||
|
}.forEach { (irNode, _) ->
|
||||||
|
val ir = when (irNode) {
|
||||||
|
is DataFlowIR.Node.Call -> irNode.irCallSite
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
ir?.let {
|
||||||
|
lifetimes.put(it, Lifetime.LOCAL)
|
||||||
|
DEBUG_OUTPUT(3) { println("${ir} does not escape") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun analyze(context: Context, moduleDFG: ModuleDFG, lifetimes: MutableMap<IrElement, Lifetime>) {
|
||||||
|
moduleDFG.functions.forEach { (name, function) ->
|
||||||
|
DEBUG_OUTPUT(5) {
|
||||||
|
println("===============================================")
|
||||||
|
println("Visiting $name")
|
||||||
|
println("DATA FLOW IR:")
|
||||||
|
function.debugOutput()
|
||||||
|
}
|
||||||
|
FunctionAnalyzer(function, context).analyze(lifetimes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun computeLifetimes(context: Context, moduleDFG: ModuleDFG, lifetimes: MutableMap<IrElement, Lifetime>) {
|
||||||
|
DEBUG_OUTPUT(1) { println("In local EA") }
|
||||||
|
assert(lifetimes.isEmpty())
|
||||||
|
analyze(context, moduleDFG, lifetimes)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -204,7 +204,8 @@ class RingLauncher : Launcher() {
|
|||||||
"CoordinatesSolver.solve" to BenchmarkEntryWithInit.create(::CoordinatesSolverBenchmark, { solve() }),
|
"CoordinatesSolver.solve" to BenchmarkEntryWithInit.create(::CoordinatesSolverBenchmark, { solve() }),
|
||||||
"GraphSolver.solve" to BenchmarkEntryWithInit.create(::GraphSolverBenchmark, { solve() }),
|
"GraphSolver.solve" to BenchmarkEntryWithInit.create(::GraphSolverBenchmark, { solve() }),
|
||||||
"Casts.classCast" to BenchmarkEntryWithInit.create(::CastsBenchmark, { classCast() }),
|
"Casts.classCast" to BenchmarkEntryWithInit.create(::CastsBenchmark, { classCast() }),
|
||||||
"Casts.interfaceCast" to BenchmarkEntryWithInit.create(::CastsBenchmark, { interfaceCast() })
|
"Casts.interfaceCast" to BenchmarkEntryWithInit.create(::CastsBenchmark, { interfaceCast() }),
|
||||||
|
"LocalObjects.localArray" to BenchmarkEntryWithInit.create(::LocalObjectsBenchmark, { localArray() })
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.ring
|
||||||
|
|
||||||
|
import org.jetbrains.benchmarksLauncher.Blackhole
|
||||||
|
|
||||||
|
open class LocalObjectsBenchmark {
|
||||||
|
//Benchmark
|
||||||
|
fun localArray(): Int {
|
||||||
|
val size = 48
|
||||||
|
val array = IntArray(size)
|
||||||
|
for (i in 1..size) {
|
||||||
|
array[i - 1] = i * 2
|
||||||
|
}
|
||||||
|
var result = 0
|
||||||
|
for (i in 0 until size) {
|
||||||
|
result += array[i]
|
||||||
|
}
|
||||||
|
if (result > 10) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,7 +29,7 @@ namespace {
|
|||||||
|
|
||||||
ALWAYS_INLINE inline void mutabilityCheck(KConstRef thiz) {
|
ALWAYS_INLINE inline void mutabilityCheck(KConstRef thiz) {
|
||||||
// TODO: optimize it!
|
// TODO: optimize it!
|
||||||
if (thiz->container()->frozen()) {
|
if (thiz->container() != nullptr && thiz->container()->frozen()) {
|
||||||
ThrowInvalidMutabilityException(thiz);
|
ThrowInvalidMutabilityException(thiz);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import kotlin.internal.PureReifiable
|
|||||||
import kotlin.native.internal.ExportTypeInfo
|
import kotlin.native.internal.ExportTypeInfo
|
||||||
import kotlin.native.internal.IntrinsicType
|
import kotlin.native.internal.IntrinsicType
|
||||||
import kotlin.native.internal.TypedIntrinsic
|
import kotlin.native.internal.TypedIntrinsic
|
||||||
|
import kotlin.native.internal.PointsTo
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array of bytes.
|
* An array of bytes.
|
||||||
@@ -527,6 +528,7 @@ public inline fun <reified @PureReifiable T> arrayOfNulls(size: Int): Array<T?>
|
|||||||
* Returns an array containing the specified elements.
|
* Returns an array containing the specified elements.
|
||||||
*/
|
*/
|
||||||
@TypedIntrinsic(IntrinsicType.IDENTITY)
|
@TypedIntrinsic(IntrinsicType.IDENTITY)
|
||||||
|
@PointsTo(0, 1) // <return> points to <array> argument.
|
||||||
public external inline fun <reified @PureReifiable T> arrayOf(vararg elements: T): Array<T>
|
public external inline fun <reified @PureReifiable T> arrayOf(vararg elements: T): Array<T>
|
||||||
|
|
||||||
@SymbolName("Kotlin_emptyArray")
|
@SymbolName("Kotlin_emptyArray")
|
||||||
|
|||||||
Reference in New Issue
Block a user