WASM: Canonicalize catches without finally blocks
This commit is contained in:
committed by
TeamCityServer
parent
02aed80e18
commit
d99473fe4d
@@ -298,6 +298,13 @@ private val returnableBlockLoweringPhase = makeWasmModulePhase(
|
|||||||
prerequisite = setOf(functionInliningPhase)
|
prerequisite = setOf(functionInliningPhase)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private val tryCatchCanonicalization = makeWasmModulePhase(
|
||||||
|
::TryCatchCanonicalization,
|
||||||
|
name = "TryCatchCanonicalization",
|
||||||
|
description = "Transforms try/catch statements into canonical form supported by the wasm codegen",
|
||||||
|
prerequisite = setOf(functionInliningPhase)
|
||||||
|
)
|
||||||
|
|
||||||
private val bridgesConstructionPhase = makeWasmModulePhase(
|
private val bridgesConstructionPhase = makeWasmModulePhase(
|
||||||
::WasmBridgesConstruction,
|
::WasmBridgesConstruction,
|
||||||
name = "BridgesConstruction",
|
name = "BridgesConstruction",
|
||||||
@@ -461,6 +468,7 @@ val wasmPhases = NamedCompilerPhase(
|
|||||||
|
|
||||||
stringConstructorLowering then
|
stringConstructorLowering then
|
||||||
returnableBlockLoweringPhase then
|
returnableBlockLoweringPhase then
|
||||||
|
tryCatchCanonicalization then
|
||||||
|
|
||||||
forLoopsLoweringPhase then
|
forLoopsLoweringPhase then
|
||||||
propertyAccessorInlinerLoweringPhase then
|
propertyAccessorInlinerLoweringPhase then
|
||||||
|
|||||||
+104
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* 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.backend.wasm.lower
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||||
|
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
||||||
|
import org.jetbrains.kotlin.backend.common.lower.*
|
||||||
|
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
|
||||||
|
import org.jetbrains.kotlin.backend.wasm.utils.isCanonical
|
||||||
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
|
import org.jetbrains.kotlin.ir.builders.declarations.buildVariable
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrTry
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.*
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
|
||||||
|
// This pass transforms try/catch statements into a simple canonical form which is easily mapped into wasm instruction set.
|
||||||
|
// From this:
|
||||||
|
// try {
|
||||||
|
// ...exprs
|
||||||
|
// } catch (e: Foo) {
|
||||||
|
// ...exprs
|
||||||
|
// } catch (e: Bar) {
|
||||||
|
// ...exprs
|
||||||
|
// } finally {
|
||||||
|
// ...exprs
|
||||||
|
// }
|
||||||
|
// We get this:
|
||||||
|
// try {
|
||||||
|
// ...exprs
|
||||||
|
// } catch (e: Throwable) {
|
||||||
|
// when (e) {
|
||||||
|
// is Foo -> ...exprs
|
||||||
|
// is Bar -> ...exprs
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// TODO: Describe finally transformation
|
||||||
|
|
||||||
|
internal class TryCatchCanonicalization(private val ctx: WasmBackendContext) : FileLoweringPass {
|
||||||
|
override fun lower(irFile: IrFile) {
|
||||||
|
irFile.transformChildrenVoid(CatchMerger(ctx))
|
||||||
|
|
||||||
|
irFile.acceptVoid(object : IrElementVisitorVoid {
|
||||||
|
override fun visitElement(element: IrElement) {
|
||||||
|
element.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitTry(aTry: IrTry) {
|
||||||
|
check(aTry.isCanonical(ctx.irBuiltIns)) { "Found non canonical try/catch $aTry" }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class CatchMerger(private val ctx: WasmBackendContext): IrElementTransformerVoidWithContext() {
|
||||||
|
override fun visitTry(aTry: IrTry): IrExpression {
|
||||||
|
// First, handle all nested constructs
|
||||||
|
aTry.transformChildrenVoid(this)
|
||||||
|
|
||||||
|
// Nothing to do
|
||||||
|
if (aTry.catches.isEmpty() ||
|
||||||
|
aTry.catches.singleOrNull()?.catchParameter?.symbol?.owner?.type == ctx.irBuiltIns.throwableType)
|
||||||
|
return aTry
|
||||||
|
|
||||||
|
ctx.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol).run {
|
||||||
|
val newCatchParameter = buildVariable(
|
||||||
|
currentScope!!.scope.getLocalDeclarationParent(),
|
||||||
|
startOffset,
|
||||||
|
endOffset,
|
||||||
|
IrDeclarationOrigin.CATCH_PARAMETER,
|
||||||
|
Name.identifier("merged_catch_param"),
|
||||||
|
ctx.irBuiltIns.throwableType
|
||||||
|
)
|
||||||
|
|
||||||
|
val newCatchBody = irBlock(aTry) {
|
||||||
|
+irWhen(
|
||||||
|
aTry.type,
|
||||||
|
aTry.catches.map {
|
||||||
|
irBranch(
|
||||||
|
irIs(irGet(newCatchParameter), it.catchParameter.type),
|
||||||
|
irBlock(it.result) {
|
||||||
|
it.catchParameter.initializer = irImplicitCast(irGet(newCatchParameter), it.catchParameter.type)
|
||||||
|
it.catchParameter.origin = IrDeclarationOrigin.DEFINED
|
||||||
|
+it.catchParameter
|
||||||
|
+it.result
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} + irElseBranch(irThrow(irGet(newCatchParameter)))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val newCatch = irCatch(newCatchParameter, newCatchBody)
|
||||||
|
|
||||||
|
return irTry(aTry.type, aTry.tryResult, listOf(newCatch), aTry.finallyExpression)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -336,6 +336,9 @@ fun IrBuilderWithScope.irVararg(elementType: IrType, values: List<IrExpression>)
|
|||||||
fun IrBuilderWithScope.irRawFunctionReferefence(type: IrType, symbol: IrFunctionSymbol) =
|
fun IrBuilderWithScope.irRawFunctionReferefence(type: IrType, symbol: IrFunctionSymbol) =
|
||||||
IrRawFunctionReferenceImpl(startOffset, endOffset, type, symbol)
|
IrRawFunctionReferenceImpl(startOffset, endOffset, type, symbol)
|
||||||
|
|
||||||
|
fun IrBuilderWithScope.irTry(type: IrType, tryResult: IrExpression, catches: List<IrCatch>, finallyExpression: IrExpression?) =
|
||||||
|
IrTryImpl(startOffset, endOffset, type, tryResult, catches, finallyExpression)
|
||||||
|
|
||||||
inline fun IrBuilderWithScope.irBlock(
|
inline fun IrBuilderWithScope.irBlock(
|
||||||
startOffset: Int = this.startOffset,
|
startOffset: Int = this.startOffset,
|
||||||
endOffset: Int = this.endOffset,
|
endOffset: Int = this.endOffset,
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// IGNORE_BACKEND: WASM
|
|
||||||
|
|
||||||
fun foo() {}
|
fun foo() {}
|
||||||
|
|
||||||
inline fun test1(v: Int) {
|
inline fun test1(v: Int) {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// IGNORE_BACKEND: JS
|
// IGNORE_BACKEND: JS
|
||||||
// IGNORE_BACKEND: WASM
|
|
||||||
|
|
||||||
var l = ""
|
var l = ""
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// IGNORE_BACKEND: JS
|
// IGNORE_BACKEND: JS
|
||||||
// IGNORE_BACKEND: WASM
|
|
||||||
|
|
||||||
var l = ""
|
var l = ""
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// IGNORE_BACKEND: WASM
|
|
||||||
// WASM_MUTE_REASON: EXCEPTIONS_NOT_IMPLEMENTED
|
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// KT-44496
|
// KT-44496
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user