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)
|
||||
)
|
||||
|
||||
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(
|
||||
::WasmBridgesConstruction,
|
||||
name = "BridgesConstruction",
|
||||
@@ -461,6 +468,7 @@ val wasmPhases = NamedCompilerPhase(
|
||||
|
||||
stringConstructorLowering then
|
||||
returnableBlockLoweringPhase then
|
||||
tryCatchCanonicalization then
|
||||
|
||||
forLoopsLoweringPhase 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) =
|
||||
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(
|
||||
startOffset: Int = this.startOffset,
|
||||
endOffset: Int = this.endOffset,
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// IGNORE_BACKEND: WASM
|
||||
|
||||
fun foo() {}
|
||||
|
||||
inline fun test1(v: Int) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// IGNORE_BACKEND: JS
|
||||
// IGNORE_BACKEND: WASM
|
||||
|
||||
var l = ""
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// IGNORE_BACKEND: JS
|
||||
// IGNORE_BACKEND: WASM
|
||||
|
||||
var l = ""
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// IGNORE_BACKEND: WASM
|
||||
// WASM_MUTE_REASON: EXCEPTIONS_NOT_IMPLEMENTED
|
||||
// WITH_RUNTIME
|
||||
// KT-44496
|
||||
|
||||
|
||||
Reference in New Issue
Block a user