[WASM] Native support of returnable blocks
This commit is contained in:
+2
-10
@@ -144,8 +144,8 @@ private val tailrecLoweringPhase = makeWasmModulePhase(
|
|||||||
|
|
||||||
private val wasmStringSwitchOptimizerLowering = makeWasmModulePhase(
|
private val wasmStringSwitchOptimizerLowering = makeWasmModulePhase(
|
||||||
::WasmStringSwitchOptimizerLowering,
|
::WasmStringSwitchOptimizerLowering,
|
||||||
name = "!!!",
|
name = "WasmStringSwitchOptimizerLowering",
|
||||||
description = "!!!"
|
description = "Replace when with constant string cases to binary search by string hashcodes"
|
||||||
)
|
)
|
||||||
|
|
||||||
private val complexExternalDeclarationsToTopLevelFunctionsLowering = makeWasmModulePhase(
|
private val complexExternalDeclarationsToTopLevelFunctionsLowering = makeWasmModulePhase(
|
||||||
@@ -403,13 +403,6 @@ private val tryCatchCanonicalization = makeWasmModulePhase(
|
|||||||
prerequisite = setOf(functionInliningPhase)
|
prerequisite = setOf(functionInliningPhase)
|
||||||
)
|
)
|
||||||
|
|
||||||
private val returnableBlockLoweringPhase = makeWasmModulePhase(
|
|
||||||
::ReturnableBlockLowering,
|
|
||||||
name = "ReturnableBlockLowering",
|
|
||||||
description = "Replace returnable block with do-while loop",
|
|
||||||
prerequisite = setOf(functionInliningPhase)
|
|
||||||
)
|
|
||||||
|
|
||||||
private val bridgesConstructionPhase = makeWasmModulePhase(
|
private val bridgesConstructionPhase = makeWasmModulePhase(
|
||||||
::WasmBridgesConstruction,
|
::WasmBridgesConstruction,
|
||||||
name = "BridgesConstruction",
|
name = "BridgesConstruction",
|
||||||
@@ -627,7 +620,6 @@ val wasmPhases = NamedCompilerPhase(
|
|||||||
unhandledExceptionLowering then
|
unhandledExceptionLowering then
|
||||||
|
|
||||||
tryCatchCanonicalization then
|
tryCatchCanonicalization then
|
||||||
returnableBlockLoweringPhase then
|
|
||||||
|
|
||||||
forLoopsLoweringPhase then
|
forLoopsLoweringPhase then
|
||||||
propertyLazyInitLoweringPhase then
|
propertyLazyInitLoweringPhase then
|
||||||
|
|||||||
+131
-29
@@ -3,8 +3,6 @@
|
|||||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@file:OptIn(ExperimentalUnsignedTypes::class)
|
|
||||||
|
|
||||||
package org.jetbrains.kotlin.backend.wasm.ir2wasm
|
package org.jetbrains.kotlin.backend.wasm.ir2wasm
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.ir.returnType
|
import org.jetbrains.kotlin.backend.common.ir.returnType
|
||||||
@@ -21,6 +19,7 @@ import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
|
|||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol
|
||||||
import org.jetbrains.kotlin.ir.types.*
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||||
@@ -30,7 +29,8 @@ import org.jetbrains.kotlin.wasm.ir.*
|
|||||||
|
|
||||||
class BodyGenerator(
|
class BodyGenerator(
|
||||||
val context: WasmFunctionCodegenContext,
|
val context: WasmFunctionCodegenContext,
|
||||||
private val hierarchyDisjointUnions: DisjointUnions<IrClassSymbol>
|
private val hierarchyDisjointUnions: DisjointUnions<IrClassSymbol>,
|
||||||
|
private val isGetUnitFunction: Boolean,
|
||||||
) : IrElementVisitorVoid {
|
) : IrElementVisitorVoid {
|
||||||
val body: WasmExpressionBuilder = context.bodyGen
|
val body: WasmExpressionBuilder = context.bodyGen
|
||||||
|
|
||||||
@@ -45,12 +45,12 @@ class BodyGenerator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generates code for the given IR element. Leaves something on the stack unless expression was of the type Void.
|
// Generates code for the given IR element. Leaves something on the stack unless expression was of the type Void.
|
||||||
private fun generateExpression(elem: IrElement) {
|
internal fun generateExpression(elem: IrElement) {
|
||||||
assert(elem is IrExpression || elem is IrVariable) { "Unsupported statement kind" }
|
assert(elem is IrExpression || elem is IrVariable) { "Unsupported statement kind" }
|
||||||
|
|
||||||
elem.acceptVoid(this)
|
elem.acceptVoid(this)
|
||||||
|
|
||||||
if (elem is IrExpression && elem.type == irBuiltIns.nothingType) {
|
if (elem is IrExpression && elem.type.isNothing()) {
|
||||||
body.buildUnreachable()
|
body.buildUnreachable()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -296,7 +296,7 @@ class BodyGenerator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (tryToGenerateIntrinsicCall(call, function)) {
|
if (tryToGenerateIntrinsicCall(call, function)) {
|
||||||
if (function.returnType == irBuiltIns.unitType)
|
if (function.returnType.isUnit())
|
||||||
body.buildGetUnit()
|
body.buildGetUnit()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -346,7 +346,7 @@ class BodyGenerator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unit types don't cross function boundaries
|
// Unit types don't cross function boundaries
|
||||||
if (function.returnType == irBuiltIns.unitType)
|
if (function.returnType.isUnit())
|
||||||
body.buildGetUnit()
|
body.buildGetUnit()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -498,21 +498,36 @@ class BodyGenerator(
|
|||||||
val statements = expression.statements
|
val statements = expression.statements
|
||||||
|
|
||||||
if (statements.isEmpty()) {
|
if (statements.isEmpty()) {
|
||||||
if (expression.type == irBuiltIns.unitType)
|
if (expression.type == irBuiltIns.unitType) {
|
||||||
body.buildGetUnit()
|
body.buildGetUnit()
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
statements.dropLast(1).forEach {
|
if (expression is IrReturnableBlock) {
|
||||||
generateStatement(it)
|
context.defineNonLocalReturnLevel(
|
||||||
|
expression.symbol,
|
||||||
|
body.buildBlock(context.transformBlockResultType(expression.type))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
generateExpression(statements.last())
|
statements.forEachIndexed { i, statement ->
|
||||||
|
if (i != statements.lastIndex) {
|
||||||
|
generateStatement(statement)
|
||||||
|
} else {
|
||||||
|
if (statement is IrExpression) {
|
||||||
|
generateWithExpectedType(statement, expression.type)
|
||||||
|
} else {
|
||||||
|
generateStatement(statement)
|
||||||
|
if (expression.type != wasmSymbols.voidType) {
|
||||||
|
body.buildGetUnit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This handles cases where the last statement of a block is declaration which doesn't produce any value,
|
if (expression is IrReturnableBlock) {
|
||||||
// but the block itself marked with the unit type.
|
body.buildEnd()
|
||||||
if (statements.last() !is IrExpression && expression.type != wasmSymbols.voidType) {
|
|
||||||
body.buildGetUnit()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -526,13 +541,16 @@ class BodyGenerator(
|
|||||||
body.buildBr(context.referenceLoopLevel(jump.loop, LoopLabelType.CONTINUE))
|
body.buildBr(context.referenceLoopLevel(jump.loop, LoopLabelType.CONTINUE))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitReturn(expression: IrReturn) {
|
private fun visitFunctionReturn(expression: IrReturn) {
|
||||||
if (expression.returnTargetSymbol.owner.returnType(backendContext) == irBuiltIns.unitType &&
|
val returnType = expression.returnTargetSymbol.owner.returnType(backendContext)
|
||||||
expression.returnTargetSymbol.owner != unitGetInstance
|
if (returnType == irBuiltIns.unitType && expression.returnTargetSymbol.owner != unitGetInstance) {
|
||||||
) {
|
|
||||||
generateStatement(expression.value)
|
generateStatement(expression.value)
|
||||||
} else {
|
} else {
|
||||||
generateExpression(expression.value)
|
if (isGetUnitFunction) {
|
||||||
|
generateExpression(expression.value)
|
||||||
|
} else {
|
||||||
|
generateWithExpectedType(expression.value, returnType)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.irFunction is IrConstructor) {
|
if (context.irFunction is IrConstructor) {
|
||||||
@@ -542,6 +560,88 @@ class BodyGenerator(
|
|||||||
body.buildInstr(WasmOp.RETURN)
|
body.buildInstr(WasmOp.RETURN)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun generateWithExpectedType(expression: IrExpression, expectedType: IrType) {
|
||||||
|
val actualType = expression.type
|
||||||
|
|
||||||
|
if (expectedType == wasmSymbols.voidType) {
|
||||||
|
generateStatement(expression)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expectedType.isUnit() && !actualType.isUnit()) {
|
||||||
|
generateStatement(expression)
|
||||||
|
body.buildGetUnit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
generateExpression(expression)
|
||||||
|
recoverToExpectedType(actualType = actualType, expectedType = expectedType)
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: This method needed because of IR has type inconsistency. We need to discover why is it and fix
|
||||||
|
private fun recoverToExpectedType(actualType: IrType, expectedType: IrType) {
|
||||||
|
// TYPE -> NOTHING -> FALSE
|
||||||
|
if (expectedType.isNothing()) {
|
||||||
|
body.buildUnreachable()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTHING -> TYPE -> TRUE
|
||||||
|
if (actualType.isNothing()) return
|
||||||
|
|
||||||
|
// NOTHING? -> TYPE? -> TRUE
|
||||||
|
if (actualType.isNullableNothing() && expectedType.isNullable()) return
|
||||||
|
|
||||||
|
val expectedClassErased = expectedType.getRuntimeClass(irBuiltIns)
|
||||||
|
|
||||||
|
// TYPE -> EXTERNAL -> TRUE
|
||||||
|
if (expectedClassErased.isExternal) return
|
||||||
|
|
||||||
|
val actualClassErased = actualType.getRuntimeClass(irBuiltIns)
|
||||||
|
val expectedTypeErased = expectedClassErased.defaultType
|
||||||
|
val actualTypeErased = actualClassErased.defaultType
|
||||||
|
|
||||||
|
// TYPE -> TYPE -> TRUE
|
||||||
|
if (expectedTypeErased == actualTypeErased) return
|
||||||
|
|
||||||
|
// NOT_NOTHING_TYPE -> NOTHING -> FALSE
|
||||||
|
if (expectedTypeErased.isNothing() && !actualTypeErased.isNothing()) {
|
||||||
|
body.buildUnreachable()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TYPE -> BASE -> TRUE
|
||||||
|
if (actualClassErased.isSubclassOf(expectedClassErased)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val expectedIsPrimitive = expectedTypeErased.isPrimitiveType()
|
||||||
|
val actualIsPrimitive = actualTypeErased.isPrimitiveType()
|
||||||
|
|
||||||
|
// PRIMITIVE -> REF -> FALSE
|
||||||
|
// REF -> PRIMITIVE -> FALSE
|
||||||
|
if (expectedIsPrimitive != actualIsPrimitive) {
|
||||||
|
body.buildUnreachable()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// REF -> REF -> REF_CAST
|
||||||
|
if (!expectedIsPrimitive) {
|
||||||
|
generateRefCast(actualTypeErased, expectedTypeErased)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitReturn(expression: IrReturn) {
|
||||||
|
val nonLocalReturnSymbol = expression.returnTargetSymbol as? IrReturnableBlockSymbol
|
||||||
|
if (nonLocalReturnSymbol != null) {
|
||||||
|
generateWithExpectedType(expression.value, nonLocalReturnSymbol.owner.type)
|
||||||
|
body.buildBr(context.referenceNonLocalReturnLevel(nonLocalReturnSymbol))
|
||||||
|
body.buildUnreachable()
|
||||||
|
} else {
|
||||||
|
visitFunctionReturn(expression)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun visitWhen(expression: IrWhen) {
|
override fun visitWhen(expression: IrWhen) {
|
||||||
if (tryGenerateOptimisedWhen(expression, context.backendContext.wasmSymbols)) {
|
if (tryGenerateOptimisedWhen(expression, context.backendContext.wasmSymbols)) {
|
||||||
return
|
return
|
||||||
@@ -555,17 +655,11 @@ class BodyGenerator(
|
|||||||
if (!isElseBranch(branch)) {
|
if (!isElseBranch(branch)) {
|
||||||
generateExpression(branch.condition)
|
generateExpression(branch.condition)
|
||||||
body.buildIf(null, resultType)
|
body.buildIf(null, resultType)
|
||||||
generateExpression(branch.result)
|
generateWithExpectedType(branch.result, expression.type)
|
||||||
if (expression.type == irBuiltIns.nothingType) {
|
|
||||||
body.buildUnreachable()
|
|
||||||
}
|
|
||||||
body.buildElse()
|
body.buildElse()
|
||||||
ifCount++
|
ifCount++
|
||||||
} else {
|
} else {
|
||||||
generateExpression(branch.result)
|
generateWithExpectedType(branch.result, expression.type)
|
||||||
if (expression.type == irBuiltIns.nothingType) {
|
|
||||||
body.buildUnreachable()
|
|
||||||
}
|
|
||||||
seenElse = true
|
seenElse = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -575,7 +669,15 @@ class BodyGenerator(
|
|||||||
// If it's not exhaustive it must be used as a statement (per kotlin spec) and so the result value of the last else will never be used.
|
// If it's not exhaustive it must be used as a statement (per kotlin spec) and so the result value of the last else will never be used.
|
||||||
if (!seenElse && resultType != null) {
|
if (!seenElse && resultType != null) {
|
||||||
assert(expression.type != irBuiltIns.nothingType)
|
assert(expression.type != irBuiltIns.nothingType)
|
||||||
generateDefaultInitializerForType(resultType, body)
|
if (expression.type.isUnit()) {
|
||||||
|
if (isGetUnitFunction) {
|
||||||
|
generateDefaultInitializerForType(resultType, body)
|
||||||
|
} else {
|
||||||
|
body.buildGetUnit()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
body.buildUnreachable()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
repeat(ifCount) { body.buildEnd() }
|
repeat(ifCount) { body.buildEnd() }
|
||||||
|
|||||||
+5
-1
@@ -126,7 +126,11 @@ class DeclarationGenerator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val exprGen = functionCodegenContext.bodyGen
|
val exprGen = functionCodegenContext.bodyGen
|
||||||
val bodyBuilder = BodyGenerator(functionCodegenContext, hierarchyDisjointUnions)
|
val bodyBuilder = BodyGenerator(
|
||||||
|
context = functionCodegenContext,
|
||||||
|
hierarchyDisjointUnions = hierarchyDisjointUnions,
|
||||||
|
isGetUnitFunction = declaration == unitGetInstanceFunction
|
||||||
|
)
|
||||||
|
|
||||||
require(declaration.body is IrBlockBody) { "Only IrBlockBody is supported" }
|
require(declaration.body is IrBlockBody) { "Only IrBlockBody is supported" }
|
||||||
declaration.body?.acceptVoid(bodyBuilder)
|
declaration.body?.acceptVoid(bodyBuilder)
|
||||||
|
|||||||
+27
-27
@@ -9,8 +9,8 @@ import org.jetbrains.kotlin.backend.common.IrWhenUtils
|
|||||||
import org.jetbrains.kotlin.backend.wasm.WasmSymbols
|
import org.jetbrains.kotlin.backend.wasm.WasmSymbols
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
|
import org.jetbrains.kotlin.ir.types.isUnit
|
||||||
import org.jetbrains.kotlin.ir.util.isElseBranch
|
import org.jetbrains.kotlin.ir.util.isElseBranch
|
||||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
|
||||||
import org.jetbrains.kotlin.wasm.ir.*
|
import org.jetbrains.kotlin.wasm.ir.*
|
||||||
|
|
||||||
private class ExtractedWhenCondition<T>(val condition: IrCall, val const: IrConst<T>)
|
private class ExtractedWhenCondition<T>(val condition: IrCall, val const: IrConst<T>)
|
||||||
@@ -58,7 +58,7 @@ internal fun BodyGenerator.tryGenerateOptimisedWhen(expression: IrWhen, symbols:
|
|||||||
if (minValue == maxValue) return false
|
if (minValue == maxValue) return false
|
||||||
|
|
||||||
val selectorLocal = context.referenceLocal(SyntheticLocalType.TABLE_SWITCH_SELECTOR)
|
val selectorLocal = context.referenceLocal(SyntheticLocalType.TABLE_SWITCH_SELECTOR)
|
||||||
subject.accept(this, null)
|
generateExpression(subject)
|
||||||
body.buildSetLocal(selectorLocal)
|
body.buildSetLocal(selectorLocal)
|
||||||
|
|
||||||
val resultType = context.transformBlockResultType(expression.type)
|
val resultType = context.transformBlockResultType(expression.type)
|
||||||
@@ -71,7 +71,7 @@ internal fun BodyGenerator.tryGenerateOptimisedWhen(expression: IrWhen, symbols:
|
|||||||
intBranches = intBranches,
|
intBranches = intBranches,
|
||||||
elseExpression = elseExpression,
|
elseExpression = elseExpression,
|
||||||
resultType = resultType,
|
resultType = resultType,
|
||||||
nothingType = symbols.irBuiltIns.nothingType
|
expectedType = expression.type,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
createBinaryTable(selectorLocal, intBranches)
|
createBinaryTable(selectorLocal, intBranches)
|
||||||
@@ -83,7 +83,7 @@ internal fun BodyGenerator.tryGenerateOptimisedWhen(expression: IrWhen, symbols:
|
|||||||
elseExpression = elseExpression,
|
elseExpression = elseExpression,
|
||||||
shift = 0,
|
shift = 0,
|
||||||
brTable = intBranches.indices.toList(),
|
brTable = intBranches.indices.toList(),
|
||||||
nothingType = symbols.irBuiltIns.nothingType
|
expectedType = expression.type,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -100,19 +100,12 @@ internal fun BodyGenerator.tryGenerateOptimisedWhen(expression: IrWhen, symbols:
|
|||||||
elseExpression = elseExpression,
|
elseExpression = elseExpression,
|
||||||
shift = minValue,
|
shift = minValue,
|
||||||
brTable = brTable,
|
brTable = brTable,
|
||||||
nothingType = symbols.irBuiltIns.nothingType
|
expectedType = expression.type,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IrExpression.safeAccept(bodyGenerator: BodyGenerator, nothingType: IrType) {
|
|
||||||
acceptVoid(bodyGenerator)
|
|
||||||
if (type == nothingType) {
|
|
||||||
bodyGenerator.body.buildUnreachable()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create binary search for when that emit branches index in leafs
|
* Create binary search for when that emit branches index in leafs
|
||||||
* when (a) {
|
* when (a) {
|
||||||
@@ -202,7 +195,7 @@ private fun BodyGenerator.createBinaryTable(
|
|||||||
intBranches: List<ExtractedWhenBranch<Int>>,
|
intBranches: List<ExtractedWhenBranch<Int>>,
|
||||||
elseExpression: IrExpression?,
|
elseExpression: IrExpression?,
|
||||||
resultType: WasmType?,
|
resultType: WasmType?,
|
||||||
nothingType: IrType,
|
expectedType: IrType,
|
||||||
) {
|
) {
|
||||||
val sortedCaseToBranchIndex = mutableListOf<Pair<Int, IrExpression>>()
|
val sortedCaseToBranchIndex = mutableListOf<Pair<Int, IrExpression>>()
|
||||||
intBranches.mapTo(sortedCaseToBranchIndex) { branch -> branch.conditions[0].const.value to branch.expression }
|
intBranches.mapTo(sortedCaseToBranchIndex) { branch -> branch.conditions[0].const.value to branch.expression }
|
||||||
@@ -210,26 +203,31 @@ private fun BodyGenerator.createBinaryTable(
|
|||||||
|
|
||||||
body.buildBlock("when_block", resultType) { currentBlock ->
|
body.buildBlock("when_block", resultType) { currentBlock ->
|
||||||
val thenBody = { result: IrExpression ->
|
val thenBody = { result: IrExpression ->
|
||||||
result.safeAccept(this, nothingType)
|
generateWithExpectedType(result, expectedType)
|
||||||
body.buildBr(currentBlock)
|
body.buildBr(currentBlock)
|
||||||
}
|
}
|
||||||
val elseBody: () -> Unit = {
|
|
||||||
resultType?.let { generateDefaultInitializerForType(it, body) }
|
|
||||||
}
|
|
||||||
createBinaryTable(
|
createBinaryTable(
|
||||||
selectorLocal = selectorLocal,
|
selectorLocal = selectorLocal,
|
||||||
resultType = resultType,
|
resultType = null,
|
||||||
sortedCases = sortedCaseToBranchIndex,
|
sortedCases = sortedCaseToBranchIndex,
|
||||||
fromIncl = 0,
|
fromIncl = 0,
|
||||||
toExcl = sortedCaseToBranchIndex.size,
|
toExcl = sortedCaseToBranchIndex.size,
|
||||||
thenBody = thenBody,
|
thenBody = thenBody,
|
||||||
elseBody = elseBody
|
elseBody = { }
|
||||||
)
|
)
|
||||||
|
|
||||||
if (resultType != null) {
|
if (elseExpression != null) {
|
||||||
body.buildDrop()
|
generateWithExpectedType(elseExpression, expectedType)
|
||||||
|
} else {
|
||||||
|
// default else block
|
||||||
|
if (resultType != null) {
|
||||||
|
if (expectedType.isUnit()) {
|
||||||
|
body.buildGetUnit()
|
||||||
|
} else {
|
||||||
|
body.buildUnreachable()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
elseExpression?.safeAccept(this, nothingType)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,7 +297,7 @@ private fun BodyGenerator.genTableIntSwitch(
|
|||||||
elseExpression: IrExpression?,
|
elseExpression: IrExpression?,
|
||||||
shift: Int,
|
shift: Int,
|
||||||
brTable: List<Int>,
|
brTable: List<Int>,
|
||||||
nothingType: IrType,
|
expectedType: IrType,
|
||||||
) {
|
) {
|
||||||
val baseBlockIndex = body.numberOfNestedBlocks
|
val baseBlockIndex = body.numberOfNestedBlocks
|
||||||
//expressions + else branch + br_table
|
//expressions + else branch + br_table
|
||||||
@@ -324,16 +322,18 @@ private fun BodyGenerator.genTableIntSwitch(
|
|||||||
if (resultType != null) {
|
if (resultType != null) {
|
||||||
body.buildDrop()
|
body.buildDrop()
|
||||||
}
|
}
|
||||||
expression.expression.safeAccept(this, nothingType)
|
generateWithExpectedType(expression.expression, expectedType)
|
||||||
|
|
||||||
body.buildBr(baseBlockIndex + 1)
|
body.buildBr(baseBlockIndex + 1)
|
||||||
body.buildEnd()
|
body.buildEnd()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resultType != null) {
|
if (elseExpression != null) {
|
||||||
body.buildDrop()
|
if (resultType != null) {
|
||||||
|
body.buildDrop()
|
||||||
|
}
|
||||||
|
generateWithExpectedType(elseExpression, expectedType)
|
||||||
}
|
}
|
||||||
elseExpression?.safeAccept(this, nothingType)
|
|
||||||
|
|
||||||
body.buildEnd()
|
body.buildEnd()
|
||||||
check(baseBlockIndex == body.numberOfNestedBlocks)
|
check(baseBlockIndex == body.numberOfNestedBlocks)
|
||||||
|
|||||||
+10
-12
@@ -80,19 +80,17 @@ class UnitToVoidLowering(val context: WasmBackendContext) : FileLoweringPass, Ab
|
|||||||
if (expr.type == symbols.voidType || expr.type == builtIns.nothingType)
|
if (expr.type == symbols.voidType || expr.type == builtIns.nothingType)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
when (expr) {
|
return when (expr) {
|
||||||
is IrContainerExpression -> {
|
is IrContainerExpression ->
|
||||||
if (expr.statements.isEmpty())
|
expr.statements.isNotEmpty() && expr !is IrReturnableBlock && shouldVoidify(expr.statements.last())
|
||||||
return false
|
|
||||||
return shouldVoidify(expr.statements.last())
|
is IrWhen, is IrTry ->
|
||||||
}
|
true
|
||||||
is IrWhen ->
|
|
||||||
return true
|
|
||||||
is IrTry ->
|
|
||||||
return true
|
|
||||||
is IrTypeOperatorCall ->
|
is IrTypeOperatorCall ->
|
||||||
return expr.operator == IrTypeOperator.IMPLICIT_COERCION_TO_UNIT
|
expr.operator == IrTypeOperator.IMPLICIT_COERCION_TO_UNIT
|
||||||
else -> return false
|
|
||||||
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-3
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.backend.wasm.lower
|
package org.jetbrains.kotlin.backend.wasm.lower
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
|
||||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||||
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
|
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
|
||||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||||
@@ -22,11 +23,11 @@ import org.jetbrains.kotlin.ir.types.makeNotNull
|
|||||||
*
|
*
|
||||||
* Wasm GC doesn't have a nullref type anymore.
|
* Wasm GC doesn't have a nullref type anymore.
|
||||||
*/
|
*/
|
||||||
class WasmNullCoercingLowering(context: JsCommonBackendContext) : AbstractValueUsageLowering(context) {
|
class WasmNullCoercingLowering(private val contextx: WasmBackendContext) : AbstractValueUsageLowering(contextx) {
|
||||||
override fun IrExpression.useExpressionAsType(actualType: IrType, expectedType: IrType): IrExpression =
|
override fun IrExpression.useExpressionAsType(actualType: IrType, expectedType: IrType): IrExpression =
|
||||||
if (actualType.makeNotNull().isNothing() && actualType.isNullable() && !expectedType.makeNotNull().isNothing())
|
if (actualType.makeNotNull().isNothing() && actualType.isNullable() && !expectedType.makeNotNull().isNothing() && expectedType != contextx.wasmSymbols.voidType)
|
||||||
JsIrBuilder.buildComposite(
|
JsIrBuilder.buildComposite(
|
||||||
type,
|
expectedType,
|
||||||
listOf(this, IrConstImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, expectedType, IrConstKind.Null, null))
|
listOf(this, IrConstImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, expectedType, IrConstKind.Null, null))
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// IGNORE_BACKEND: WASM
|
|
||||||
// WASM_MUTE_REASON: TYPE_ISSUES
|
|
||||||
// WITH_STDLIB
|
// WITH_STDLIB
|
||||||
// WORKS_WHEN_VALUE_CLASS
|
// WORKS_WHEN_VALUE_CLASS
|
||||||
// LANGUAGE: +ValueClasses
|
// LANGUAGE: +ValueClasses
|
||||||
|
|||||||
Reference in New Issue
Block a user