Split voidValue into immaterialUnitValue and defaultValue
This commit is contained in:
committed by
Alexander Udalov
parent
27a850be92
commit
ea3bae5fc9
+26
-22
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.DEFAULT_CONSTRUCTOR_MARKER
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
@@ -169,7 +170,8 @@ class ExpressionCodegen(
|
||||
irFunction.markLineNumber(startOffset = irFunction is IrConstructor && irFunction.isPrimary)
|
||||
}
|
||||
val returnType = typeMapper.mapReturnType(irFunction)
|
||||
result.coerce(returnType, if (irFunction !is IrConstructor) irFunction.returnType else null).materialize()
|
||||
val returnIrType = if (irFunction !is IrConstructor) irFunction.returnType else context.irBuiltIns.unitType
|
||||
result.coerce(returnType, returnIrType).materialize()
|
||||
mv.areturn(returnType)
|
||||
}
|
||||
val endLabel = markNewLabel()
|
||||
@@ -278,7 +280,7 @@ class ExpressionCodegen(
|
||||
}
|
||||
|
||||
private fun visitStatementContainer(container: IrStatementContainer, data: BlockInfo) =
|
||||
container.statements.fold(voidValue as PromisedValue) { prev, exp ->
|
||||
container.statements.fold(immaterialUnitValue as PromisedValue) { prev, exp ->
|
||||
prev.discard()
|
||||
exp.accept(this, data).also { (exp as? IrExpression)?.markEndOfStatementIfNeeded() }
|
||||
}
|
||||
@@ -396,12 +398,12 @@ class ExpressionCodegen(
|
||||
returnType.substitute(typeSubstitutionMap).isNothing() -> {
|
||||
mv.aconst(null)
|
||||
mv.athrow()
|
||||
voidValue
|
||||
immaterialUnitValue
|
||||
}
|
||||
expression is IrConstructorCall ->
|
||||
MaterialValue(this, asmType, expression.type)
|
||||
expression is IrDelegatingConstructorCall ->
|
||||
voidValue
|
||||
immaterialUnitValue
|
||||
expression.type.isUnit() ->
|
||||
// NewInference allows casting `() -> T` to `() -> Unit`. A CHECKCAST here will fail.
|
||||
MaterialValue(this, callable.returnType, returnType).discard().coerce(expression.type)
|
||||
@@ -423,7 +425,7 @@ class ExpressionCodegen(
|
||||
}
|
||||
|
||||
data.variables.add(VariableInfo(declaration, index, varType, markNewLabel()))
|
||||
return voidValue
|
||||
return immaterialUnitValue
|
||||
}
|
||||
|
||||
override fun visitGetValue(expression: IrGetValue, data: BlockInfo): PromisedValue {
|
||||
@@ -442,7 +444,7 @@ class ExpressionCodegen(
|
||||
assert(expression is IrSetField) { "read of const val ${expression.symbol.owner.name} not inlined by ConstLowering" }
|
||||
// This can only be the field's initializer; JVM implementations are required
|
||||
// to generate those for ConstantValue-marked fields automatically, so this is redundant.
|
||||
return voidValue.coerce(expression.type)
|
||||
return defaultValue(expression.type)
|
||||
}
|
||||
|
||||
val realField = expression.symbol.owner.resolveFakeOverride()!!
|
||||
@@ -458,7 +460,7 @@ class ExpressionCodegen(
|
||||
isStatic -> mv.putstatic(ownerType, fieldName, fieldType.descriptor)
|
||||
else -> mv.putfield(ownerType, fieldName, fieldType.descriptor)
|
||||
}
|
||||
voidValue.coerce(expression.type)
|
||||
defaultValue(expression.type)
|
||||
} else {
|
||||
when {
|
||||
isStatic ->
|
||||
@@ -480,7 +482,7 @@ class ExpressionCodegen(
|
||||
val isFieldInitializer = expression.origin == null
|
||||
val skip = (inPrimaryConstructor || inClassInit) && isFieldInitializer && expressionValue is IrConst<*> &&
|
||||
isDefaultValueForType(expression.symbol.owner.type.asmType, expressionValue.value)
|
||||
return if (skip) voidValue.coerce(expression.type) else super.visitSetField(expression, data)
|
||||
return if (skip) defaultValue(expression.type) else super.visitSetField(expression, data)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -511,7 +513,7 @@ class ExpressionCodegen(
|
||||
expression.value.markLineNumber(startOffset = true)
|
||||
expression.value.accept(this, data).coerce(expression.symbol.owner.type).materialize()
|
||||
mv.store(findLocalIndex(expression.symbol), expression.symbol.owner.asmType)
|
||||
return voidValue.coerce(expression.type)
|
||||
return defaultValue(expression.type)
|
||||
}
|
||||
|
||||
override fun <T> visitConst(expression: IrConst<T>, data: BlockInfo): PromisedValue {
|
||||
@@ -544,7 +546,7 @@ class ExpressionCodegen(
|
||||
// TODO maybe remove?
|
||||
override fun visitClass(declaration: IrClass, data: BlockInfo): PromisedValue {
|
||||
classCodegen.generateLocalClass(declaration)
|
||||
return voidValue
|
||||
return immaterialUnitValue
|
||||
}
|
||||
|
||||
override fun visitVararg(expression: IrVararg, data: BlockInfo): PromisedValue {
|
||||
@@ -662,7 +664,7 @@ class ExpressionCodegen(
|
||||
mv, "java/lang/UnsupportedOperationException",
|
||||
"Non-local returns are not allowed with inlining disabled"
|
||||
)
|
||||
return voidValue
|
||||
return immaterialUnitValue
|
||||
}
|
||||
|
||||
val target = data.findBlock<ReturnableBlockInfo> { it.returnSymbol == expression.returnTargetSymbol }
|
||||
@@ -682,7 +684,7 @@ class ExpressionCodegen(
|
||||
}
|
||||
mv.mark(afterReturnLabel)
|
||||
mv.nop()/*TODO check RESTORE_STACK_IN_TRY_CATCH processor*/
|
||||
return voidValue
|
||||
return immaterialUnitValue
|
||||
}
|
||||
|
||||
override fun visitWhen(expression: IrWhen, data: BlockInfo): PromisedValue {
|
||||
@@ -715,7 +717,7 @@ class ExpressionCodegen(
|
||||
}
|
||||
// Produce the default value for the type. Doesn't really matter right now, as non-exhaustive
|
||||
// conditionals cannot be used as expressions.
|
||||
val result = voidValue.coerce(expression.type).materialized
|
||||
val result = defaultValue(expression.type).materialized
|
||||
mv.mark(endLabel)
|
||||
return result
|
||||
}
|
||||
@@ -727,7 +729,7 @@ class ExpressionCodegen(
|
||||
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> {
|
||||
expression.argument.accept(this, data).discard()
|
||||
expression.argument.markEndOfStatementIfNeeded()
|
||||
voidValue.coerce(expression.asmType)
|
||||
defaultValue(expression.type)
|
||||
}
|
||||
|
||||
IrTypeOperator.IMPLICIT_CAST, IrTypeOperator.IMPLICIT_INTEGER_COERCION -> {
|
||||
@@ -736,7 +738,7 @@ class ExpressionCodegen(
|
||||
|
||||
IrTypeOperator.CAST, IrTypeOperator.SAFE_CAST -> {
|
||||
val result = expression.argument.accept(this, data)
|
||||
val boxedLeftType = result.irType?.let { typeMapper.boxType(it) } ?: OBJECT_TYPE
|
||||
val boxedLeftType = typeMapper.boxType(result.irType)
|
||||
result.coerce(boxedLeftType, expression.argument.type).materialize()
|
||||
val boxedRightType = typeMapper.boxType(typeOperand)
|
||||
|
||||
@@ -851,7 +853,7 @@ class ExpressionCodegen(
|
||||
}
|
||||
mv.goTo(continueLabel)
|
||||
mv.mark(endLabel)
|
||||
return voidValue
|
||||
return immaterialUnitValue
|
||||
}
|
||||
|
||||
override fun visitDoWhileLoop(loop: IrDoWhileLoop, data: BlockInfo): PromisedValue {
|
||||
@@ -868,7 +870,7 @@ class ExpressionCodegen(
|
||||
loop.condition.markLineNumber(true)
|
||||
loop.condition.accept(this, data).coerceToBoolean().jumpIfTrue(entry)
|
||||
mv.mark(endLabel)
|
||||
return voidValue
|
||||
return immaterialUnitValue
|
||||
}
|
||||
|
||||
private fun unwindBlockStack(endLabel: Label, data: BlockInfo, stop: (ExpressionInfo) -> Boolean): ExpressionInfo? {
|
||||
@@ -886,7 +888,7 @@ class ExpressionCodegen(
|
||||
?: throw AssertionError("Target label for break/continue not found")
|
||||
mv.fixStackAndJump(if (jump is IrBreak) stackElement.breakLabel else stackElement.continueLabel)
|
||||
mv.mark(endLabel)
|
||||
return voidValue
|
||||
return immaterialUnitValue
|
||||
}
|
||||
|
||||
override fun visitTry(aTry: IrTry, data: BlockInfo): PromisedValue {
|
||||
@@ -985,7 +987,7 @@ class ExpressionCodegen(
|
||||
frameMap.leaveTemp(tryAsmType)
|
||||
return aTry.onStack
|
||||
}
|
||||
return voidValue
|
||||
return immaterialUnitValue
|
||||
}
|
||||
|
||||
private fun genTryCatchCover(catchStart: Label, tryStart: Label, tryEnd: Label, tryGaps: List<Pair<Label, Label>>, type: String?) {
|
||||
@@ -1030,9 +1032,11 @@ class ExpressionCodegen(
|
||||
|
||||
override fun visitThrow(expression: IrThrow, data: BlockInfo): PromisedValue {
|
||||
expression.markLineNumber(startOffset = true)
|
||||
expression.value.accept(this, data).coerce(AsmTypes.JAVA_THROWABLE_TYPE).materialize()
|
||||
expression.value.accept(this, data)
|
||||
.coerce(AsmTypes.JAVA_THROWABLE_TYPE, context.irBuiltIns.throwableType)
|
||||
.materialize()
|
||||
mv.athrow()
|
||||
return voidValue
|
||||
return immaterialUnitValue
|
||||
}
|
||||
|
||||
override fun visitGetClass(expression: IrGetClass, data: BlockInfo) =
|
||||
@@ -1213,7 +1217,7 @@ fun DefaultCallArgs.generateOnStackIfNeeded(callGenerator: IrCallGenerator, isCo
|
||||
callGenerator.putValueIfNeeded(Type.INT_TYPE, StackValue.constant(mask, Type.INT_TYPE), ValueKind.DEFAULT_MASK, -1, codegen)
|
||||
}
|
||||
|
||||
val parameterType = if (isConstructor) AsmTypes.DEFAULT_CONSTRUCTOR_MARKER else OBJECT_TYPE
|
||||
val parameterType = if (isConstructor) DEFAULT_CONSTRUCTOR_MARKER else OBJECT_TYPE
|
||||
callGenerator.putValueIfNeeded(
|
||||
parameterType,
|
||||
StackValue.constant(null, parameterType),
|
||||
|
||||
+34
-18
@@ -19,7 +19,7 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
// A value that may not have been fully constructed yet. The ability to "roll back" code generation
|
||||
// is useful for certain optimizations.
|
||||
abstract class PromisedValue(val codegen: ExpressionCodegen, val type: Type, val irType: IrType?) {
|
||||
abstract class PromisedValue(val codegen: ExpressionCodegen, val type: Type, val irType: IrType) {
|
||||
// If this value is immaterial, construct an object on the top of the stack. This
|
||||
// must always be done before generating other values or emitting raw bytecode.
|
||||
abstract fun materialize()
|
||||
@@ -30,12 +30,17 @@ abstract class PromisedValue(val codegen: ExpressionCodegen, val type: Type, val
|
||||
val typeMapper: IrTypeMapper
|
||||
get() = codegen.typeMapper
|
||||
|
||||
val kotlinType: KotlinType?
|
||||
get() = irType?.toKotlinType()
|
||||
val kotlinType: KotlinType
|
||||
get() = irType.toKotlinType()
|
||||
}
|
||||
|
||||
// A value that *has* been fully constructed.
|
||||
class MaterialValue(codegen: ExpressionCodegen, type: Type, irType: IrType?) : PromisedValue(codegen, type, irType) {
|
||||
class MaterialValue(codegen: ExpressionCodegen, type: Type, irType: IrType) : PromisedValue(codegen, type, irType) {
|
||||
override fun materialize() {}
|
||||
}
|
||||
|
||||
// A value that is only materialized through coercion.
|
||||
class ImmaterialValue(codegen: ExpressionCodegen, type: Type, irType: IrType) : PromisedValue(codegen, type, irType) {
|
||||
override fun materialize() {}
|
||||
}
|
||||
|
||||
@@ -66,11 +71,11 @@ val PromisedValue.materialized: MaterialValue
|
||||
|
||||
// Materialize and disregard this value. Materialization is forced because, presumably,
|
||||
// we only wanted the side effects anyway.
|
||||
fun PromisedValue.discard(): MaterialValue {
|
||||
fun PromisedValue.discard(): PromisedValue {
|
||||
materialize()
|
||||
if (type !== Type.VOID_TYPE)
|
||||
AsmUtil.pop(mv, type)
|
||||
return codegen.voidValue
|
||||
return codegen.immaterialUnitValue
|
||||
}
|
||||
|
||||
private val IrType.unboxed: IrType
|
||||
@@ -103,9 +108,8 @@ fun PromisedValue.coerceInlineClasses(type: Type, irType: IrType, target: Type,
|
||||
}
|
||||
|
||||
// On materialization, cast the value to a different type.
|
||||
fun PromisedValue.coerce(target: Type, irTarget: IrType? = null): PromisedValue {
|
||||
if (irType != null && irTarget != null)
|
||||
coerceInlineClasses(type, irType, target, irTarget)?.let { return it }
|
||||
fun PromisedValue.coerce(target: Type, irTarget: IrType): PromisedValue {
|
||||
coerceInlineClasses(type, irType, target, irTarget)?.let { return it }
|
||||
return when (type) {
|
||||
// All unsafe coercions between irTypes should use the UnsafeCoerce intrinsic
|
||||
target -> this
|
||||
@@ -129,14 +133,26 @@ fun PromisedValue.boxInlineClasses(irTarget: IrType) =
|
||||
coerceToBoxed(irTarget) else this
|
||||
|
||||
// Same as above, but with a return type that allows conditional jumping.
|
||||
fun PromisedValue.coerceToBoolean() = when (val coerced = coerce(Type.BOOLEAN_TYPE)) {
|
||||
is BooleanValue -> coerced
|
||||
else -> object : BooleanValue(codegen) {
|
||||
override fun jumpIfFalse(target: Label) = coerced.materialize().also { mv.ifeq(target) }
|
||||
override fun jumpIfTrue(target: Label) = coerced.materialize().also { mv.ifne(target) }
|
||||
override fun materialize() = coerced.materialize()
|
||||
fun PromisedValue.coerceToBoolean() =
|
||||
when (val coerced = coerce(Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType)) {
|
||||
is BooleanValue -> coerced
|
||||
else -> object : BooleanValue(codegen) {
|
||||
override fun jumpIfFalse(target: Label) = coerced.materialize().also { mv.ifeq(target) }
|
||||
override fun jumpIfTrue(target: Label) = coerced.materialize().also { mv.ifne(target) }
|
||||
override fun materialize() = coerced.materialize()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val ExpressionCodegen.voidValue: MaterialValue
|
||||
get() = MaterialValue(this, Type.VOID_TYPE, null)
|
||||
// Non-materialized value of Unit type
|
||||
// This value is only materialized when calling coerce, which is why it is represented
|
||||
// as a MaterialValue even though there is nothing on the stack.
|
||||
val ExpressionCodegen.immaterialUnitValue: ImmaterialValue
|
||||
get() = ImmaterialValue(this, Type.VOID_TYPE, context.irBuiltIns.unitType)
|
||||
|
||||
// Non-materialized default value for the given type
|
||||
fun ExpressionCodegen.defaultValue(irType: IrType): PromisedValue =
|
||||
object : PromisedValue(this, typeMapper.mapType(irType), irType) {
|
||||
override fun materialize() {
|
||||
StackValue.coerce(Type.VOID_TYPE, type, codegen.mv)
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -217,7 +217,7 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
|
||||
}
|
||||
}
|
||||
|
||||
protected fun genBranchTargets(): PromisedValue {
|
||||
private fun genBranchTargets(): PromisedValue {
|
||||
with(codegen) {
|
||||
val endLabel = Label()
|
||||
for ((thenExpression, label) in expressionToLabels) {
|
||||
@@ -226,8 +226,8 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
|
||||
mv.goTo(endLabel)
|
||||
}
|
||||
mv.visitLabel(defaultLabel)
|
||||
val stackValue = elseExpression?.accept(codegen, data) ?: voidValue
|
||||
val result = stackValue.coerce(expression.type).materialized
|
||||
val stackValue = elseExpression?.accept(codegen, data)?.coerce(expression.type) ?: defaultValue(expression.type)
|
||||
val result = stackValue.materialized
|
||||
mv.mark(endLabel)
|
||||
return result
|
||||
}
|
||||
|
||||
+2
-2
@@ -33,8 +33,8 @@ object JavaClassProperty : IntrinsicMethod() {
|
||||
fun invokeWith(value: PromisedValue) =
|
||||
when {
|
||||
value.type == Type.VOID_TYPE ->
|
||||
invokeGetClass(value.coerce(AsmTypes.UNIT_TYPE))
|
||||
value.irType?.isInlined() == true ->
|
||||
invokeGetClass(value.coerce(AsmTypes.UNIT_TYPE, value.codegen.context.irBuiltIns.unitType))
|
||||
value.irType.isInlined() ->
|
||||
invokeGetClass(value.coerceToBoxed(value.irType))
|
||||
isPrimitive(value.type) -> {
|
||||
value.discard()
|
||||
|
||||
Reference in New Issue
Block a user