From 856a1649c63554e3bad26e1403f7cbf925dd4a56 Mon Sep 17 00:00:00 2001 From: max-kammerer Date: Thu, 9 May 2019 11:48:58 +0200 Subject: [PATCH] Revert "JVM_IR: implement IrReturnableBlock codegen" This reverts commit 530ad368fe36c36793790cbd5f2276fc850b2155. --- .../common/lower/ArrayConstructorLowering.kt | 111 +++++++++++------- .../kotlin/backend/common/lower/LowerUtils.kt | 6 +- .../backend/jvm/codegen/ExpressionCodegen.kt | 109 +++++++---------- .../backend/jvm/codegen/IrInlineCodegen.kt | 8 +- 4 files changed, 115 insertions(+), 119 deletions(-) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ArrayConstructorLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ArrayConstructorLowering.kt index fed0d94a27b..cd13cd84c74 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ArrayConstructorLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ArrayConstructorLowering.kt @@ -11,10 +11,8 @@ import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrReturnableBlockImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol -import org.jetbrains.kotlin.ir.symbols.impl.IrReturnableBlockSymbolImpl import org.jetbrains.kotlin.ir.types.getClass import org.jetbrains.kotlin.ir.types.toKotlinType import org.jetbrains.kotlin.ir.util.* @@ -35,6 +33,31 @@ class ArrayConstructorLowering(val context: CommonBackendContext) : IrElementTra fromInit to fromSize } + // Generate `array[index] = value`. + private fun IrBuilderWithScope.setItem(array: IrVariable, index: IrVariable, value: IrExpression) = + irCall(array.type.getClass()!!.functions.single { it.name.toString() == "set" }).apply { + dispatchReceiver = irGet(array) + putValueArgument(0, irGet(index)) + putValueArgument(1, value) + } + + // Generate `for (index in 0 until end) { element }`. + private fun IrBlockBuilder.fromZeroTo(end: IrVariable, element: IrBlockBuilder.(IrLoop, IrVariable) -> Unit) { + val index = irTemporaryVar(irInt(0)) + +irWhile().apply { + condition = irCall(context.irBuiltIns.lessFunByOperandType[index.type.toKotlinType()]!!).apply { + putValueArgument(0, irGet(index)) + putValueArgument(1, irGet(end)) + } + body = irBlock { + val currentIndex = irTemporary(irGet(index)) + val inc = index.type.getClass()!!.functions.single { it.name.asString() == "inc" } + +irSetVar(index.symbol, irCall(inc).apply { dispatchReceiver = irGet(index) }) + element(this@apply, currentIndex) + } + } + } + private fun IrExpression.asSingleArgumentLambda(): IrSimpleFunction? { // A lambda is represented as a block with a function declaration and a reference to it. if (this !is IrBlock || statements.size != 2) @@ -52,6 +75,8 @@ class ArrayConstructorLowering(val context: CommonBackendContext) : IrElementTra override fun visitConstructorCall(expression: IrConstructorCall): IrExpression { val sizeConstructor = arrayInlineToSizeCtor[expression.symbol] ?: return super.visitConstructorCall(expression) + val arrayOfReferences = sizeConstructor == context.ir.symbols.arrayOfNulls + // inline fun Array(size: Int, invokable: (Int) -> T): Array { // val result = arrayOfNulls(size) // for (i in 0 until size) { @@ -60,61 +85,59 @@ class ArrayConstructorLowering(val context: CommonBackendContext) : IrElementTra // return result as Array // } // (and similar for primitive arrays) + val loweringContext = context val size = expression.getValueArgument(0)!!.transform(this, null) val invokable = expression.getValueArgument(1)!!.transform(this, null) return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol).irBlock(expression.startOffset, expression.endOffset) { - val index = irTemporaryVar(irInt(0)) val sizeVar = irTemporary(size) val result = irTemporary(irCall(sizeConstructor, expression.type).apply { - copyTypeArgumentsFrom(expression) + if (arrayOfReferences) { + putTypeArgument(0, expression.getTypeArgument(0)) + } putValueArgument(0, irGet(sizeVar)) }) val lambda = invokable.asSingleArgumentLambda() - val invoke = invokable.type.getClass()!!.functions.single { it.name == OperatorNameConventions.INVOKE } - val invokableVar = if (lambda == null) irTemporary(invokable) else null - +irWhile().apply { - condition = irCall(context.irBuiltIns.lessFunByOperandType[index.type.toKotlinType()]!!).apply { - putValueArgument(0, irGet(index)) - putValueArgument(1, irGet(sizeVar)) - } - body = irBlock { - val value = - lambda?.inline(listOf(index)) ?: irCallOp(invoke.symbol, invoke.returnType, irGet(invokableVar!!), irGet(index)) - +irCall(result.type.getClass()!!.functions.single { it.name == OperatorNameConventions.SET }).apply { - dispatchReceiver = irGet(result) + if (lambda == null) { + val invokableVar = irTemporary(invokable) + val invoke = invokable.type.getClass()!!.functions.single { it.name == OperatorNameConventions.INVOKE } + fromZeroTo(sizeVar) { _, index -> + +setItem(result, index, irCall(invoke).apply { + dispatchReceiver = irGet(invokableVar) putValueArgument(0, irGet(index)) - putValueArgument(1, value) + }) + } + } else { + // Inline `invokable` by replacing the argument with `i` and `return x` with `result[i] = x; continue`. + fromZeroTo(sizeVar) { loop, index -> + val body = lambda.body!!.transform(object : IrElementTransformerVoidWithContext() { + override fun visitGetValue(expression: IrGetValue) = + if (expression.symbol == lambda.valueParameters[0].symbol) + IrGetValueImpl(expression.startOffset, expression.endOffset, index.symbol) + else + super.visitGetValue(expression) + + override fun visitReturn(expression: IrReturn) = + if (expression.returnTargetSymbol == lambda.symbol) { + val value = expression.value.transform(this, null) + val scope = currentScope?.scope?.scopeOwnerSymbol ?: lambda.symbol + loweringContext.createIrBuilder(scope).irBlock(expression.startOffset, expression.endOffset) { + +setItem(result, index, value) + +irContinue(loop) + } + } else { + super.visitReturn(expression) + } + }, null) + + when (body) { + is IrExpressionBody -> +setItem(result, index, body.expression) + is IrBlockBody -> body.statements.forEach { +it } + else -> throw AssertionError("unexpected function body type: $body") } - val inc = index.type.getClass()!!.functions.single { it.name == OperatorNameConventions.INC } - +irSetVar(index.symbol, irCallOp(inc.symbol, index.type, irGet(index))) } } +irGet(result) } } - - // TODO use a generic inliner (e.g. JS/Native's FunctionInlining.Inliner) - private fun IrFunction.inline(arguments: List): IrReturnableBlock { - val argumentMap = valueParameters.zip(arguments).toMap() - val blockSymbol = IrReturnableBlockSymbolImpl(descriptor) - val block = IrReturnableBlockImpl(startOffset, endOffset, returnType, blockSymbol, null, symbol) - val remapper = object : AbstractVariableRemapper() { - override fun remapVariable(value: IrValueDeclaration): IrValueDeclaration? = - argumentMap[value] - - override fun visitReturn(expression: IrReturn): IrExpression = super.visitReturn( - if (expression.returnTargetSymbol == symbol) - IrReturnImpl(expression.startOffset, expression.endOffset, expression.type, blockSymbol, expression.value) - else - expression - ) - } - when (val transformed = body?.transform(remapper, null)) { - is IrBlockBody -> block.statements += transformed.statements - is IrExpressionBody -> block.statements += transformed.expression - else -> throw AssertionError("unexpected body type: $this") - } - return block - } } diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt index 1e0668b09c3..657593eb91c 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt @@ -59,7 +59,7 @@ class DeclarationIrBuilder( ) abstract class AbstractVariableRemapper : IrElementTransformerVoid() { - protected abstract fun remapVariable(value: IrValueDeclaration): IrValueDeclaration? + protected abstract fun remapVariable(value: IrValueDeclaration): IrValueParameter? override fun visitGetValue(expression: IrGetValue): IrExpression = remapVariable(expression.symbol.owner)?.let { @@ -68,12 +68,12 @@ abstract class AbstractVariableRemapper : IrElementTransformerVoid() { } class VariableRemapper(val mapping: Map) : AbstractVariableRemapper() { - override fun remapVariable(value: IrValueDeclaration): IrValueDeclaration? = + override fun remapVariable(value: IrValueDeclaration): IrValueParameter? = mapping[value] } class VariableRemapperDesc(val mapping: Map) : AbstractVariableRemapper() { - override fun remapVariable(value: IrValueDeclaration): IrValueDeclaration? = + override fun remapVariable(value: IrValueDeclaration): IrValueParameter? = mapping[value.descriptor] } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt index 142767bbfb0..697b3b456b3 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt @@ -52,22 +52,17 @@ class TryInfo(val onExit: IrExpression) : ExpressionInfo() { val gaps = mutableListOf>() } -class ReturnableBlockInfo( - val returnLabel: Label, - val returnSymbol: IrSymbol, - val returnTemporary: Int? = null -) : ExpressionInfo() - -class BlockInfo(val parent: BlockInfo? = null) { +class BlockInfo private constructor(val parent: BlockInfo?) { val variables = mutableListOf() - private val infos: Stack = parent?.infos ?: Stack() + val infos = Stack() + + fun create() = BlockInfo(this).apply { + this@apply.infos.addAll(this@BlockInfo.infos) + } fun hasFinallyBlocks(): Boolean = infos.firstIsInstanceOrNull() != null - internal inline fun findBlock(predicate: (T) -> Boolean): T? = - infos.find { it is T && predicate(it) } as? T - - internal inline fun withBlock(info: T, f: (T) -> R): R { + inline fun withBlock(info: T, f: (T) -> R): R { infos.add(info) try { return f(info) @@ -76,7 +71,7 @@ class BlockInfo(val parent: BlockInfo? = null) { } } - internal inline fun handleBlock(f: (ExpressionInfo) -> R): R? { + inline fun handleBlock(f: (ExpressionInfo) -> R): R? { if (infos.isEmpty()) { return null } @@ -87,6 +82,10 @@ class BlockInfo(val parent: BlockInfo? = null) { infos.add(top) } } + + companion object { + fun create() = BlockInfo(null) + } } class VariableInfo(val declaration: IrVariable, val index: Int, val type: Type, val startLabel: Label) @@ -154,7 +153,7 @@ class ExpressionCodegen( fun generate() { mv.visitCode() val startLabel = markNewLabel() - val info = BlockInfo() + val info = BlockInfo.create() val body = irFunction.body!! val result = body.accept(this, info) // If this function has an expression body, return the result of that expression. @@ -170,26 +169,25 @@ class ExpressionCodegen( result.coerce(returnType).materialize() mv.areturn(returnType) } - val endLabel = markNewLabel() - writeLocalVariablesInTable(info, endLabel) - writeParameterInLocalVariableTable(startLabel, endLabel) + writeLocalVariablesInTable(info) + writeParameterInLocalVariableTable(startLabel) mv.visitEnd() } - private fun writeParameterInLocalVariableTable(startLabel: Label, endLabel: Label) { + private fun writeParameterInLocalVariableTable(startLabel: Label) { if (!irFunction.isStatic) { - mv.visitLocalVariable("this", classCodegen.type.descriptor, null, startLabel, endLabel, 0) + mv.visitLocalVariable("this", classCodegen.type.descriptor, null, startLabel, markNewLabel(), 0) } val extensionReceiverParameter = irFunction.extensionReceiverParameter if (extensionReceiverParameter != null) { - writeValueParameterInLocalVariableTable(extensionReceiverParameter, startLabel, endLabel) + writeValueParameterInLocalVariableTable(extensionReceiverParameter, startLabel) } for (param in irFunction.valueParameters) { - writeValueParameterInLocalVariableTable(param, startLabel, endLabel) + writeValueParameterInLocalVariableTable(param, startLabel) } } - private fun writeValueParameterInLocalVariableTable(param: IrValueParameter, startLabel: Label, endLabel: Label) { + private fun writeValueParameterInLocalVariableTable(param: IrValueParameter, startLabel: Label) { // TODO: old code has a special treatment for destructuring lambda parameters. // There is no (easy) way to reproduce it with IR structures. // Does not show up in tests, but might come to bite us at some point. @@ -198,43 +196,22 @@ class ExpressionCodegen( val type = typeMapper.mapType(param) // NOTE: we expect all value parameters to be present in the frame. mv.visitLocalVariable( - name, type.descriptor, null, startLabel, endLabel, findLocalIndex(param.symbol) + name, type.descriptor, null, startLabel, markNewLabel(), findLocalIndex(param.symbol) ) } override fun visitBlock(expression: IrBlock, data: BlockInfo): PromisedValue { if (expression.isTransparentScope) return super.visitBlock(expression, data) - val info = BlockInfo(data) - return if (expression is IrReturnableBlock) { - val returnType = expression.asmType - val returnLabel = Label() - // Because the return might be inside an expression, it will need to pop excess items - // before jumping and store the result in a temporary variable. - val returnTemporary = if (returnType != Type.VOID_TYPE) frameMap.enterTemp(returnType) else null - info.withBlock(ReturnableBlockInfo(returnLabel, expression.symbol, returnTemporary)) { - // Remember current stack depth. - mv.fakeAlwaysFalseIfeq(returnLabel) - super.visitBlock(expression, info).materialized.also { - returnTemporary?.let { mv.store(it, returnType) } - // Variables leave the scope in reverse order, so must write locals first. - mv.mark(returnLabel) - writeLocalVariablesInTable(info, returnLabel) - returnTemporary?.let { - mv.load(it, returnType) - frameMap.leaveTemp(returnType) - } - } - } - } else { - // Force materialization to avoid reading from out-of-scope variables. - super.visitBlock(expression, info).materialized.also { - writeLocalVariablesInTable(info, markNewLabel()) - } + val info = data.create() + // Force materialization to avoid reading from out-of-scope variables. + return super.visitBlock(expression, info).materialized.apply { + writeLocalVariablesInTable(info) } } - private fun writeLocalVariablesInTable(info: BlockInfo, endLabel: Label) { + private fun writeLocalVariablesInTable(info: BlockInfo) { + val endLabel = markNewLabel() info.variables.forEach { when (it.declaration.origin) { IrDeclarationOrigin.IR_TEMPORARY_VARIABLE, @@ -624,21 +601,15 @@ class ExpressionCodegen( return voidValue } - val target = data.findBlock { it.returnSymbol == expression.returnTargetSymbol } val returnType = typeMapper.mapReturnType(owner) val afterReturnLabel = Label() expression.value.accept(this, data).coerce(returnType).materialize() - generateFinallyBlocksIfNeeded(returnType, afterReturnLabel, data, target) + generateFinallyBlocksIfNeeded(returnType, afterReturnLabel, data) expression.markLineNumber(startOffset = true) - if (target != null) { - target.returnTemporary?.let { mv.store(it, returnType) } - mv.fixStackAndJump(target.returnLabel) - } else { - if (isNonLocalReturn) { - generateGlobalReturnFlag(mv, (owner as IrFunction).name.asString()) - } - mv.areturn(returnType) + if (isNonLocalReturn) { + generateGlobalReturnFlag(mv, (owner as IrFunction).name.asString()) } + mv.areturn(returnType) mv.mark(afterReturnLabel) mv.nop()/*TODO check RESTORE_STACK_IN_TRY_CATCH processor*/ return voidValue @@ -826,18 +797,20 @@ class ExpressionCodegen( return voidValue } - private fun unwindBlockStack(endLabel: Label, data: BlockInfo, stop: (ExpressionInfo) -> Boolean): ExpressionInfo? { + private fun unwindBlockStack(endLabel: Label, data: BlockInfo, loop: IrLoop? = null): LoopInfo? { return data.handleBlock { - if (it is TryInfo) - genFinallyBlock(it, null, endLabel, data) - return if (stop(it)) it else unwindBlockStack(endLabel, data, stop) + when { + it is TryInfo -> genFinallyBlock(it, null, endLabel, data) + it is LoopInfo && it.loop == loop -> return it + } + unwindBlockStack(endLabel, data, loop) } } override fun visitBreakContinue(jump: IrBreakContinue, data: BlockInfo): PromisedValue { jump.markLineNumber(startOffset = true) val endLabel = Label() - val stackElement = unwindBlockStack(endLabel, data) { it is LoopInfo && it.loop == jump.loop } as LoopInfo? + val stackElement = unwindBlockStack(endLabel, data, jump.loop) ?: throw AssertionError("Target label for break/continue not found") mv.fixStackAndJump(if (jump is IrBreak) stackElement.breakLabel else stackElement.continueLabel) mv.mark(endLabel) @@ -969,16 +942,16 @@ class ExpressionCodegen( tryInfo.gaps.add(gapStart to (afterJumpLabel ?: markNewLabel())) } - fun generateFinallyBlocksIfNeeded(returnType: Type, afterReturnLabel: Label, data: BlockInfo, target: ReturnableBlockInfo? = null) { + fun generateFinallyBlocksIfNeeded(returnType: Type, afterReturnLabel: Label, data: BlockInfo) { if (data.hasFinallyBlocks()) { if (Type.VOID_TYPE != returnType) { val returnValIndex = frameMap.enterTemp(returnType) mv.store(returnValIndex, returnType) - unwindBlockStack(afterReturnLabel, data) { it == target } + unwindBlockStack(afterReturnLabel, data, null) mv.load(returnValIndex, returnType) frameMap.leaveTemp(returnType) } else { - unwindBlockStack(afterReturnLabel, data) { it == target } + unwindBlockStack(afterReturnLabel, data, null) } } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt index e6909efc72d..58c0c7430d9 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt @@ -54,7 +54,7 @@ class IrInlineCodegen( (argumentExpression as IrBlock).statements.filterIsInstance().single() rememberClosure(irReference, parameterType, irValueParameter) as IrExpressionLambdaImpl } else { - putValueOnStack(argumentExpression, parameterType, irValueParameter?.index ?: -1, blockInfo) + putValueOnStack(argumentExpression, parameterType, irValueParameter?.index ?: -1) } } @@ -71,14 +71,14 @@ class IrInlineCodegen( } private fun putCapturedValueOnStack(argumentExpression: IrExpression, valueType: Type, capturedParamIndex: Int) { - val onStack = codegen.gen(argumentExpression, valueType, BlockInfo()) + val onStack = codegen.gen(argumentExpression, valueType, BlockInfo.create()) putArgumentOrCapturedToLocalVal( JvmKotlinType(onStack.type, onStack.kotlinType), onStack, capturedParamIndex, capturedParamIndex, ValueKind.CAPTURED ) } - private fun putValueOnStack(argumentExpression: IrExpression, valueType: Type, paramIndex: Int, blockInfo: BlockInfo) { - val onStack = codegen.gen(argumentExpression, valueType, blockInfo) + private fun putValueOnStack(argumentExpression: IrExpression, valueType: Type, paramIndex: Int) { + val onStack = codegen.gen(argumentExpression, valueType, BlockInfo.create()) putArgumentOrCapturedToLocalVal(JvmKotlinType(onStack.type, onStack.kotlinType), onStack, -1, paramIndex, ValueKind.CAPTURED) }