Make ReturnableBlockLowering common

and remove special handling in JVM_IR codegen.
This commit is contained in:
pyos
2019-06-25 14:42:53 +02:00
committed by max-kammerer
parent ae8c93de6a
commit 20626e4aaf
5 changed files with 51 additions and 104 deletions
@@ -3,14 +3,15 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.backend.js.lower.inline
package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrDeclarationParent
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.ir.builders.irBoolean
import org.jetbrains.kotlin.ir.builders.irBreak
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irSetVar
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.IrContainerExpression
import org.jetbrains.kotlin.ir.expressions.IrExpression
@@ -20,7 +21,6 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrDoWhileLoopImpl
import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
/**
* Replaces returnable blocks and `return`'s with loops and `break`'s correspondingly.
@@ -71,38 +71,25 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
*/
class ReturnableBlockLowering(val context: CommonBackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.transform(ReturnableBlockTransformer(context), ReturnableBlockLoweringContext(irFile))
irFile.transform(ReturnableBlockTransformer(context), null)
}
}
private class ReturnableBlockLoweringContext(val containingDeclaration: IrDeclarationParent) {
var labelCnt = 0
val returnMap = mutableMapOf<IrReturnableBlockSymbol, (IrReturn) -> IrExpression>()
}
private class ReturnableBlockTransformer(val context: CommonBackendContext) : IrElementTransformerVoidWithContext() {
private var labelCnt = 0
private val returnMap = mutableMapOf<IrReturnableBlockSymbol, (IrReturn) -> IrExpression>()
private class ReturnableBlockTransformer(
val context: CommonBackendContext
) : IrElementTransformer<ReturnableBlockLoweringContext> {
override fun visitReturn(expression: IrReturn, data: ReturnableBlockLoweringContext): IrExpression {
expression.transformChildren(this, data)
return data.returnMap[expression.returnTargetSymbol]?.invoke(expression) ?: expression
override fun visitReturn(expression: IrReturn): IrExpression {
expression.transformChildrenVoid()
return returnMap[expression.returnTargetSymbol]?.invoke(expression) ?: expression
}
override fun visitDeclaration(declaration: IrDeclaration, data: ReturnableBlockLoweringContext): IrStatement {
if (declaration is IrDeclarationParent) {
declaration.transformChildren(this, ReturnableBlockLoweringContext(declaration))
}
return super.visitDeclaration(declaration, data)
}
private val constFalse get() = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, false)
override fun visitContainerExpression(expression: IrContainerExpression, data: ReturnableBlockLoweringContext): IrExpression {
if (expression !is IrReturnableBlock) return super.visitContainerExpression(expression, data)
override fun visitContainerExpression(expression: IrContainerExpression): IrExpression {
if (expression !is IrReturnableBlock) return super.visitContainerExpression(expression)
val builder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol)
val variable by lazy {
JsIrBuilder.buildVar(expression.type, data.containingDeclaration, "tmp\$ret\$${data.labelCnt++}", true)
builder.scope.createTemporaryVariableDeclaration(expression.type, "tmp\$ret\$${labelCnt++}", true)
}
val loop by lazy {
@@ -112,38 +99,33 @@ private class ReturnableBlockTransformer(
context.irBuiltIns.unitType,
expression.origin
).apply {
label = "l\$ret\$${data.labelCnt++}"
condition = constFalse
label = "l\$ret\$${labelCnt++}"
condition = builder.irBoolean(false)
}
}
var hasReturned = false
data.returnMap[expression.symbol] = { returnExpression ->
returnMap[expression.symbol] = { returnExpression ->
hasReturned = true
IrCompositeImpl(
returnExpression.startOffset,
returnExpression.endOffset,
context.irBuiltIns.unitType
).apply {
statements += JsIrBuilder.buildSetVariable(variable.symbol, returnExpression.value, context.irBuiltIns.unitType)
statements += JsIrBuilder.buildBreak(context.irBuiltIns.unitType, loop)
builder.irComposite(returnExpression) {
+irSetVar(variable.symbol, returnExpression.value)
+irBreak(loop)
}
}
val newStatements = expression.statements.mapIndexed { i, s ->
if (i == expression.statements.lastIndex && s is IrReturn && s.returnTargetSymbol == expression.symbol) {
s.transformChildren(this, data)
s.transformChildrenVoid()
if (!hasReturned) s.value else {
JsIrBuilder.buildSetVariable(variable.symbol, s.value, context.irBuiltIns.unitType)
builder.irSetVar(variable.symbol, s.value)
}
} else {
s.transform(this, data)
s.transform(this, null)
}
}
data.returnMap.remove(expression.symbol)
returnMap.remove(expression.symbol)
if (!hasReturned) {
return IrCompositeImpl(
@@ -162,15 +144,10 @@ private class ReturnableBlockTransformer(
newStatements
)
return IrCompositeImpl(
expression.startOffset,
expression.endOffset,
expression.type,
expression.origin
).apply {
statements += variable
statements += loop
statements += JsIrBuilder.buildGetValue(variable.symbol)
return builder.irComposite(expression, expression.origin) {
+variable
+loop
+irGet(variable)
}
}
}
@@ -13,7 +13,6 @@ import org.jetbrains.kotlin.ir.backend.js.lower.*
import org.jetbrains.kotlin.ir.backend.js.lower.calls.CallsLowering
import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.JsSuspendFunctionsLowering
import org.jetbrains.kotlin.ir.backend.js.lower.inline.RemoveInlineFunctionsWithReifiedTypeParametersLowering
import org.jetbrains.kotlin.ir.backend.js.lower.inline.ReturnableBlockLowering
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
@@ -121,6 +121,13 @@ private val innerClassesPhase = makeIrFilePhase(
prerequisite = setOf(localDeclarationsPhase)
)
private val returnableBlocksPhase = makeIrFilePhase(
::ReturnableBlockLowering,
name = "ReturnableBlock",
description = "Replace returnable blocks with do-while(false) loops",
prerequisite = setOf(arrayConstructorPhase, assertionPhase)
)
private val jvmFilePhases =
stripTypeAliasDeclarationsPhase then
provisionalFunctionExpressionPhase then
@@ -141,6 +148,7 @@ private val jvmFilePhases =
renameFieldsPhase then
assertionPhase then
tailrecPhase then
returnableBlocksPhase then
jvmInlineClassPhase then
@@ -62,21 +62,12 @@ class TryInfo(val onExit: IrExpression) : ExpressionInfo() {
val gaps = mutableListOf<Pair<Label, Label>>()
}
class ReturnableBlockInfo(
val returnLabel: Label,
val returnSymbol: IrSymbol,
val returnTemporary: Int? = null
) : ExpressionInfo()
class BlockInfo(val parent: BlockInfo? = null) {
val variables = mutableListOf<VariableInfo>()
private val infos: Stack<ExpressionInfo> = parent?.infos ?: Stack()
fun hasFinallyBlocks(): Boolean = infos.firstIsInstanceOrNull<TryInfo>() != null
internal inline fun <reified T : ExpressionInfo> findBlock(predicate: (T) -> Boolean): T? =
infos.find { it is T && predicate(it) } as? T
internal inline fun <T : ExpressionInfo, R> withBlock(info: T, f: (T) -> R): R {
infos.add(info)
try {
@@ -257,34 +248,13 @@ class ExpressionCodegen(
}
override fun visitBlock(expression: IrBlock, data: BlockInfo): PromisedValue {
assert(expression !is IrReturnableBlock) { "unlowered returnable block: ${expression.dump()}" }
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())
}
// Force materialization to avoid reading from out-of-scope variables.
return super.visitBlock(expression, info).materialized.also {
writeLocalVariablesInTable(info, markNewLabel())
}
}
@@ -571,21 +541,15 @@ class ExpressionCodegen(
return immaterialUnitValue
}
val target = data.findBlock<ReturnableBlockInfo> { it.returnSymbol == expression.returnTargetSymbol }
val returnType = methodSignatureMapper.mapReturnType(owner)
val afterReturnLabel = Label()
expression.value.accept(this, data).coerce(returnType, owner.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.name.asString())
}
mv.areturn(returnType)
if (isNonLocalReturn) {
generateGlobalReturnFlag(mv, owner.name.asString())
}
mv.areturn(returnType)
mv.mark(afterReturnLabel)
mv.nop()/*TODO check RESTORE_STACK_IN_TRY_CATCH processor*/
return immaterialUnitValue
@@ -757,7 +721,7 @@ class ExpressionCodegen(
return immaterialUnitValue
}
private fun unwindBlockStack(endLabel: Label, data: BlockInfo, stop: (ExpressionInfo) -> Boolean): ExpressionInfo? {
private fun unwindBlockStack(endLabel: Label, data: BlockInfo, stop: (ExpressionInfo) -> Boolean = { false }): ExpressionInfo? {
return data.handleBlock {
if (it is TryInfo)
genFinallyBlock(it, null, endLabel, data)
@@ -900,16 +864,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)
mv.load(returnValIndex, returnType)
frameMap.leaveTemp(returnType)
} else {
unwindBlockStack(afterReturnLabel, data) { it == target }
unwindBlockStack(afterReturnLabel, data)
}
}
}
@@ -15,7 +15,6 @@ import org.jetbrains.kotlin.backend.wasm.lower.excludeDeclarationsFromCodegen
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
import org.jetbrains.kotlin.ir.backend.js.lower.*
import org.jetbrains.kotlin.ir.backend.js.lower.inline.RemoveInlineFunctionsWithReifiedTypeParametersLowering
import org.jetbrains.kotlin.ir.backend.js.lower.inline.ReturnableBlockLowering
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.util.patchDeclarationParents