[Wasm] Fix unit issues
- Materialize unit when its value is actually needed. - Special-case Unit_getInstance return type at codegen. It should be a proper Unit object instead of a "void"
This commit is contained in:
committed by
TeamCityServer
parent
20ddaa9ebf
commit
b79719d6f5
@@ -328,6 +328,12 @@ private val wasmNullSpecializationLowering = makeWasmModulePhase(
|
||||
description = "Specialize assigning Nothing? values to other types."
|
||||
)
|
||||
|
||||
private val unitMaterializationPass = makeWasmModulePhase(
|
||||
{ context -> UnitMaterializationPass(context) },
|
||||
name = "UnitLoweringPass",
|
||||
description = "Materialize unit instances"
|
||||
)
|
||||
|
||||
private val staticMembersLoweringPhase = makeWasmModulePhase(
|
||||
::StaticMembersLowering,
|
||||
name = "StaticMembersLowering",
|
||||
@@ -499,5 +505,6 @@ val wasmPhases = NamedCompilerPhase(
|
||||
wasmThrowDebugLoweringPhase then
|
||||
staticMembersLoweringPhase then
|
||||
wasmNullSpecializationLowering then
|
||||
unitMaterializationPass then
|
||||
validateIrAfterLowering
|
||||
)
|
||||
|
||||
+58
-41
@@ -20,10 +20,7 @@ import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.defaultType
|
||||
import org.jetbrains.kotlin.ir.util.getInlineClassBackingField
|
||||
import org.jetbrains.kotlin.ir.util.isInterface
|
||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
import org.jetbrains.kotlin.wasm.ir.*
|
||||
@@ -40,6 +37,13 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
|
||||
error("Unexpected element of type ${element::class}")
|
||||
}
|
||||
|
||||
val unitGetInstance by lazy { backendContext.mapping.objectToGetInstanceFunction[irBuiltIns.unitClass.owner]!! }
|
||||
|
||||
override fun visitGetObjectValue(expression: IrGetObjectValue) {
|
||||
require(expression.symbol == irBuiltIns.unitClass)
|
||||
body.buildCall(context.referenceFunction(unitGetInstance.symbol))
|
||||
}
|
||||
|
||||
override fun visitTypeOperator(expression: IrTypeOperatorCall) {
|
||||
require(expression.operator == IrTypeOperator.REINTERPRET_CAST) { "Other types of casts must be lowered" }
|
||||
generateExpression(expression.argument)
|
||||
@@ -329,11 +333,7 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
|
||||
statementToWasmInstruction(it)
|
||||
}
|
||||
|
||||
if (expression.type != irBuiltIns.unitType) {
|
||||
generateExpression(statements.last() as IrExpression)
|
||||
} else {
|
||||
statementToWasmInstruction(statements.last())
|
||||
}
|
||||
generateExpression(statements.last() as IrExpression)
|
||||
}
|
||||
|
||||
override fun visitBreak(jump: IrBreak) {
|
||||
@@ -345,18 +345,17 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
|
||||
}
|
||||
|
||||
override fun visitReturn(expression: IrReturn) {
|
||||
generateExpression(expression.value)
|
||||
|
||||
// FIXME: Hack for "returning" Unit from functions with generic return type.
|
||||
// Common case -- lambdas returning unit.
|
||||
if (expression.value.type == irBuiltIns.unitType &&
|
||||
expression.returnTargetSymbol.owner.returnType(backendContext) != irBuiltIns.unitType
|
||||
if (
|
||||
expression.value.type == irBuiltIns.unitType &&
|
||||
expression.returnTargetSymbol.owner.returnType(backendContext) == irBuiltIns.unitType
|
||||
) {
|
||||
val irReturnType = expression.returnTargetSymbol.owner.returnType(backendContext)
|
||||
statementToWasmInstruction(expression.value)
|
||||
} else {
|
||||
generateExpression(expression.value)
|
||||
}
|
||||
|
||||
if (irReturnType != irBuiltIns.unitType) {
|
||||
generateDefaultInitializerForType(context.transformType(irReturnType), body)
|
||||
}
|
||||
if (context.irFunction is IrConstructor) {
|
||||
body.buildGetLocal(context.referenceLocal(0))
|
||||
}
|
||||
|
||||
// Handle complex exported parameters.
|
||||
@@ -371,25 +370,6 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
|
||||
}
|
||||
|
||||
override fun visitWhen(expression: IrWhen) {
|
||||
if (expression.type == irBuiltIns.unitType) {
|
||||
var ifCount = 0
|
||||
for (branch in expression.branches) {
|
||||
if (!isElseBranch(branch)) {
|
||||
generateExpression(branch.condition)
|
||||
body.buildIf(label = null, resultType = null)
|
||||
statementToWasmInstruction(branch.result)
|
||||
body.buildElse()
|
||||
ifCount++
|
||||
} else {
|
||||
statementToWasmInstruction(branch.result)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
repeat(ifCount) { body.buildEnd() }
|
||||
return
|
||||
}
|
||||
|
||||
val resultType = context.transformBlockResultType(expression.type)
|
||||
var ifCount = 0
|
||||
for (branch in expression.branches) {
|
||||
@@ -489,10 +469,47 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
|
||||
return
|
||||
}
|
||||
|
||||
generateExpression(statement as IrExpression)
|
||||
if (statement is IrContainerExpression) {
|
||||
statement.statements.forEach { it ->
|
||||
statementToWasmInstruction(it)
|
||||
}
|
||||
} else if (statement is IrWhen) {
|
||||
var ifCount = 0
|
||||
for (branch in statement.branches) {
|
||||
if (!isElseBranch(branch)) {
|
||||
generateExpression(branch.condition)
|
||||
body.buildIf(label = null, resultType = null)
|
||||
statementToWasmInstruction(branch.result)
|
||||
body.buildElse()
|
||||
ifCount++
|
||||
} else {
|
||||
statementToWasmInstruction(branch.result)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (statement.type != irBuiltIns.unitType && statement.type != irBuiltIns.nothingType) {
|
||||
body.buildInstr(WasmOp.DROP)
|
||||
repeat(ifCount) { body.buildEnd() }
|
||||
} else {
|
||||
generateExpression(statement as IrExpression)
|
||||
|
||||
var needDrop = true
|
||||
if (statement.type == irBuiltIns.nothingType)
|
||||
needDrop = false
|
||||
|
||||
if (statement is IrSetValue || statement is IrBreakContinue || statement is IrSetField || statement is IrLoop || statement is IrDelegatingConstructorCall) {
|
||||
needDrop = false
|
||||
}
|
||||
|
||||
if (statement is IrCall || statement is IrConstructorCall) {
|
||||
val unitGetInstanceCall = statement is IrCall && statement.symbol.owner == unitGetInstance
|
||||
if (statement.type == irBuiltIns.unitType && !unitGetInstanceCall) {
|
||||
needDrop = false
|
||||
}
|
||||
}
|
||||
|
||||
if (needDrop) {
|
||||
body.buildInstr(WasmOp.DROP)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+15
-2
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.findUnitGetInstanceFunction
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
|
||||
@@ -29,6 +30,8 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext) : IrElementVis
|
||||
private val backendContext: WasmBackendContext = context.backendContext
|
||||
private val irBuiltIns: IrBuiltIns = backendContext.irBuiltIns
|
||||
|
||||
private val unitGetInstanceFunction: IrSimpleFunction by lazy { backendContext.findUnitGetInstanceFunction() }
|
||||
|
||||
override fun visitElement(element: IrElement) {
|
||||
error("Unexpected element of type ${element::class}")
|
||||
}
|
||||
@@ -84,8 +87,18 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext) : IrElementVis
|
||||
}
|
||||
},
|
||||
resultTypes = listOfNotNull(
|
||||
resultType.let {
|
||||
if (importedName != null && it is WasmRefNullType) WasmEqRef else it
|
||||
run {
|
||||
val type = if (declaration == unitGetInstanceFunction) {
|
||||
// Unit_getInstance returns true Unit reference instead of "void"
|
||||
context.transformType(declaration.returnType)
|
||||
} else {
|
||||
resultType
|
||||
}
|
||||
|
||||
if (importedName != null && type is WasmRefNullType)
|
||||
WasmEqRef
|
||||
else
|
||||
type
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
+21
-13
@@ -14,9 +14,12 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.irNot
|
||||
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
|
||||
import org.jetbrains.kotlin.backend.wasm.ir2wasm.erasedUpperBound
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.findUnitGetInstanceFunction
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
|
||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
|
||||
@@ -35,6 +38,7 @@ class WasmTypeOperatorLowering(val context: WasmBackendContext) : FileLoweringPa
|
||||
class WasmBaseTypeOperatorTransformer(val context: WasmBackendContext) : IrElementTransformerVoidWithContext() {
|
||||
private val symbols = context.wasmSymbols
|
||||
private val builtIns = context.irBuiltIns
|
||||
private val unitGetInstance = context.findUnitGetInstanceFunction()
|
||||
|
||||
private lateinit var builder: DeclarationIrBuilder
|
||||
|
||||
@@ -45,7 +49,7 @@ class WasmBaseTypeOperatorTransformer(val context: WasmBackendContext) : IrEleme
|
||||
return when (expression.operator) {
|
||||
IrTypeOperator.IMPLICIT_CAST -> lowerImplicitCast(expression)
|
||||
IrTypeOperator.IMPLICIT_DYNAMIC_CAST -> error("Dynamic casts are not supported in Wasm backend")
|
||||
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> expression.argument
|
||||
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> builder.irComposite(resultType = builtIns.unitType) { +expression.argument }
|
||||
IrTypeOperator.IMPLICIT_INTEGER_COERCION -> lowerIntegerCoercion(expression)
|
||||
IrTypeOperator.IMPLICIT_NOTNULL -> lowerImplicitCast(expression)
|
||||
IrTypeOperator.INSTANCEOF -> lowerInstanceOf(expression, inverted = false)
|
||||
@@ -57,6 +61,19 @@ class WasmBaseTypeOperatorTransformer(val context: WasmBackendContext) : IrEleme
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitVariable(declaration: IrVariable): IrStatement {
|
||||
// Some IR passes, notable for-loops-lowering assumes implicit cast during variable initialization
|
||||
val initializer = declaration.initializer
|
||||
if (initializer != null &&
|
||||
initializer.type != declaration.type
|
||||
) {
|
||||
builder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol).at(declaration)
|
||||
declaration.initializer = narrowType(initializer.type, declaration.type, initializer)
|
||||
}
|
||||
|
||||
return super.visitVariable(declaration)
|
||||
}
|
||||
|
||||
private fun lowerInstanceOf(
|
||||
expression: IrTypeOperatorCall,
|
||||
inverted: Boolean
|
||||
@@ -135,10 +152,7 @@ class WasmBaseTypeOperatorTransformer(val context: WasmBackendContext) : IrEleme
|
||||
private fun generateTypeCheckNonNull(argument: IrExpression, toType: IrType): IrExpression {
|
||||
assert(!toType.isMarkedNullable())
|
||||
return when {
|
||||
toType.isNothing() -> builder.irComposite(resultType = builtIns.booleanType) {
|
||||
+(argument)
|
||||
+builder.irFalse()
|
||||
}
|
||||
toType.isNothing() -> builder.irFalse()
|
||||
toType.isTypeParameter() -> generateTypeCheckWithTypeParameter(argument, toType)
|
||||
toType.isInterface() -> generateIsInterface(argument, toType)
|
||||
else -> generateIsSubClass(argument, toType)
|
||||
@@ -281,16 +295,10 @@ class WasmBaseTypeOperatorTransformer(val context: WasmBackendContext) : IrEleme
|
||||
val fromTypeErased = fromType.erasedType
|
||||
val toTypeErased = toType.erasedType
|
||||
if (fromTypeErased.isSubtypeOfClass(toTypeErased.classOrNull!!)) {
|
||||
return builder.irComposite {
|
||||
+argument
|
||||
+builder.irTrue()
|
||||
}
|
||||
return builder.irTrue()
|
||||
}
|
||||
if (!toTypeErased.isSubtypeOfClass(fromTypeErased.classOrNull!!)) {
|
||||
return builder.irComposite {
|
||||
+argument
|
||||
+builder.irFalse()
|
||||
}
|
||||
return builder.irFalse()
|
||||
}
|
||||
|
||||
return builder.irCall(symbols.refTest).apply {
|
||||
|
||||
Reference in New Issue
Block a user