Split voidValue into immaterialUnitValue and defaultValue

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