[JVM IR] Refactor PromisedValue...

... for an _even leaner_ codegen!

- move discard from extention method to abstract method. This will at
  length avoid some calls to materialize.

- use newly abstract method to specialize discard in cases where it
  would introduce _and_ coerce values only to be popped by discard.

- move coerce to member method, introduce materializeAt with a view to
  eliminate calls to coerce outside of PromisedValue. This will allow
  us to specilaize materialization at particular types in some
  instances, at length avoiding casts etc.

- no calls to coerce outside of PromisedValue.kt! Progress.

- inlined coerce into materializeAt, anticipating specializing
  materializeAt across implementations of PromisedValue

- made materializeAt abstract, copied implementation everywhere!

- made materialize an extention function! Ready to specialize materializeAt

- coerce is no more!

- simplified and specialized all inlinings of materializeAt.

- adjust docs to match refactoring
This commit is contained in:
Kristoffer Andersen
2020-02-13 14:18:45 +01:00
committed by Alexander Udalov
parent ad988dbf43
commit 6e40117116
18 changed files with 245 additions and 200 deletions
@@ -7,15 +7,18 @@ package org.jetbrains.kotlin.backend.jvm.codegen
import org.jetbrains.kotlin.backend.common.lower.BOUND_RECEIVER_PARAMETER import org.jetbrains.kotlin.backend.common.lower.BOUND_RECEIVER_PARAMETER
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin.* import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE
import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods
import org.jetbrains.kotlin.backend.jvm.intrinsics.JavaClassProperty import org.jetbrains.kotlin.backend.jvm.intrinsics.JavaClassProperty
import org.jetbrains.kotlin.backend.jvm.lower.MultifileFacadeFileEntry import org.jetbrains.kotlin.backend.jvm.lower.MultifileFacadeFileEntry
import org.jetbrains.kotlin.backend.jvm.lower.constantValue import org.jetbrains.kotlin.backend.jvm.lower.constantValue
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.unboxInlineClass import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.unboxInlineClass
import org.jetbrains.kotlin.backend.jvm.lower.suspendFunctionOriginal
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.AsmUtil.* import org.jetbrains.kotlin.codegen.AsmUtil.*
import org.jetbrains.kotlin.codegen.BaseExpressionCodegen
import org.jetbrains.kotlin.codegen.CallGenerator
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.codegen.extractReificationArgument
import org.jetbrains.kotlin.codegen.inline.* import org.jetbrains.kotlin.codegen.inline.*
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.Companion.putNeedClassReificationMarker import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.Companion.putNeedClassReificationMarker
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.OperationKind.AS import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.OperationKind.AS
@@ -168,7 +171,7 @@ class ExpressionCodegen(
if (expression.attributeOwnerId === context.fakeContinuation) { if (expression.attributeOwnerId === context.fakeContinuation) {
addFakeContinuationMarker(mv) addFakeContinuationMarker(mv)
} else { } else {
expression.accept(this, data).coerce(type, irType).materialize() expression.accept(this, data).materializeAt(type, irType)
} }
return StackValue.onStack(type, irType.toKotlinType()) return StackValue.onStack(type, irType.toKotlinType())
} }
@@ -209,7 +212,7 @@ class ExpressionCodegen(
} }
val returnType = signature.returnType val returnType = signature.returnType
val returnIrType = if (irFunction !is IrConstructor) irFunction.returnType else context.irBuiltIns.unitType val returnIrType = if (irFunction !is IrConstructor) irFunction.returnType else context.irBuiltIns.unitType
result.coerce(returnType, returnIrType).materialize() result.materializeAt(returnType, returnIrType)
mv.areturn(returnType) mv.areturn(returnType)
} }
val endLabel = markNewLabel() val endLabel = markNewLabel()
@@ -315,7 +318,7 @@ class ExpressionCodegen(
return super.visitBlock(expression, data) return super.visitBlock(expression, data)
val info = BlockInfo(data) val info = BlockInfo(data)
// Force materialization to avoid reading from out-of-scope variables. // Force materialization to avoid reading from out-of-scope variables.
return super.visitBlock(expression, info).materialized.also { return super.visitBlock(expression, info).materialized().also {
if (info.variables.isNotEmpty()) { if (info.variables.isNotEmpty()) {
writeLocalVariablesInTable(info, markNewLabel()) writeLocalVariablesInTable(info, markNewLabel())
} }
@@ -339,13 +342,15 @@ class ExpressionCodegen(
} }
private fun visitStatementContainer(container: IrStatementContainer, data: BlockInfo) = private fun visitStatementContainer(container: IrStatementContainer, data: BlockInfo) =
container.statements.fold(immaterialUnitValue as PromisedValue) { prev, exp -> container.statements.fold(unitValue) { prev, exp ->
prev.discard() prev.discard()
exp.accept(this, data).also { (exp as? IrExpression)?.markEndOfStatementIfNeeded() } exp.accept(this, data).also { (exp as? IrExpression)?.markEndOfStatementIfNeeded() }
} }
override fun visitBlockBody(body: IrBlockBody, data: BlockInfo) = override fun visitBlockBody(body: IrBlockBody, data: BlockInfo): PromisedValue {
visitStatementContainer(body, data).discard() visitStatementContainer(body, data).discard()
return unitValue
}
override fun visitContainerExpression(expression: IrContainerExpression, data: BlockInfo) = override fun visitContainerExpression(expression: IrContainerExpression, data: BlockInfo) =
visitStatementContainer(expression, data) visitStatementContainer(expression, data)
@@ -435,7 +440,7 @@ class ExpressionCodegen(
expression.type.isNothing() -> { expression.type.isNothing() -> {
mv.aconst(null) mv.aconst(null)
mv.athrow() mv.athrow()
immaterialUnitValue unitValue
} }
expression is IrConstructorCall -> expression is IrConstructorCall ->
MaterialValue(this, asmType, expression.type) MaterialValue(this, asmType, expression.type)
@@ -448,7 +453,7 @@ class ExpressionCodegen(
if (callable.asmMethod.returnType != Type.VOID_TYPE) if (callable.asmMethod.returnType != Type.VOID_TYPE)
MaterialValue(this, callable.asmMethod.returnType, callee.returnType).discard() MaterialValue(this, callable.asmMethod.returnType, callee.returnType).discard()
// don't generate redundant UNIT/pop instructions // don't generate redundant UNIT/pop instructions
immaterialUnitValue unitValue
} }
callee.parentAsClass.isAnnotationClass && callable.asmMethod.returnType == AsmTypes.JAVA_CLASS_TYPE -> { callee.parentAsClass.isAnnotationClass && callable.asmMethod.returnType == AsmTypes.JAVA_CLASS_TYPE -> {
wrapJavaClassIntoKClass(mv) wrapJavaClassIntoKClass(mv)
@@ -483,7 +488,7 @@ class ExpressionCodegen(
val initializer = declaration.initializer val initializer = declaration.initializer
if (initializer != null) { if (initializer != null) {
initializer.accept(this, data).coerce(varType, declaration.type).materialize() initializer.accept(this, data).materializeAt(varType, declaration.type)
initializer.markLineNumber(startOffset = true) initializer.markLineNumber(startOffset = true)
mv.store(index, varType) mv.store(index, varType)
} else if (declaration.isVisibleInLVT) { } else if (declaration.isVisibleInLVT) {
@@ -492,7 +497,7 @@ class ExpressionCodegen(
} }
data.variables.add(VariableInfo(declaration, index, varType, markNewLabel())) data.variables.add(VariableInfo(declaration, index, varType, markNewLabel()))
return immaterialUnitValue return unitValue
} }
override fun visitGetValue(expression: IrGetValue, data: BlockInfo): PromisedValue { override fun visitGetValue(expression: IrGetValue, data: BlockInfo): PromisedValue {
@@ -519,17 +524,17 @@ class ExpressionCodegen(
expression.markLineNumber(startOffset = true) expression.markLineNumber(startOffset = true)
val ownerName = expression.receiver?.let { receiver -> val ownerName = expression.receiver?.let { receiver ->
val ownerType = typeMapper.mapTypeAsDeclaration(receiver.type) val ownerType = typeMapper.mapTypeAsDeclaration(receiver.type)
receiver.accept(this, data).coerce(ownerType, receiver.type).materialize() receiver.accept(this, data).materializeAt(ownerType, receiver.type)
ownerType.internalName ownerType.internalName
} ?: typeMapper.mapClass(callee.parentAsClass).internalName } ?: typeMapper.mapClass(callee.parentAsClass).internalName
return if (expression is IrSetField) { return if (expression is IrSetField) {
expression.value.accept(this, data).coerce(fieldType, callee.type).materialize() expression.value.accept(this, data).materializeAt(fieldType, callee.type)
when { when {
isStatic -> mv.putstatic(ownerName, fieldName, fieldType.descriptor) isStatic -> mv.putstatic(ownerName, fieldName, fieldType.descriptor)
else -> mv.putfield(ownerName, fieldName, fieldType.descriptor) else -> mv.putfield(ownerName, fieldName, fieldType.descriptor)
} }
assert(expression.type.isUnit()) assert(expression.type.isUnit())
immaterialUnitValue unitValue
} else { } else {
when { when {
isStatic -> mv.getstatic(ownerName, fieldName, fieldType.descriptor) isStatic -> mv.getstatic(ownerName, fieldName, fieldType.descriptor)
@@ -547,7 +552,7 @@ class ExpressionCodegen(
val isFieldInitializer = expression.origin == IrStatementOrigin.INITIALIZE_FIELD val isFieldInitializer = expression.origin == IrStatementOrigin.INITIALIZE_FIELD
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) defaultValue(expression.type) else super.visitSetField(expression, data) return if (skip) unitValue else super.visitSetField(expression, data)
} }
/** /**
@@ -576,22 +581,22 @@ class ExpressionCodegen(
override fun visitSetVariable(expression: IrSetVariable, data: BlockInfo): PromisedValue { override fun visitSetVariable(expression: IrSetVariable, data: BlockInfo): PromisedValue {
expression.markLineNumber(startOffset = true) expression.markLineNumber(startOffset = true)
setVariable(expression.symbol, expression.value, data) setVariable(expression.symbol, expression.value, data)
return immaterialUnitValue return unitValue
} }
fun setVariable(symbol: IrValueSymbol, value: IrExpression, data: BlockInfo) { fun setVariable(symbol: IrValueSymbol, value: IrExpression, data: BlockInfo) {
value.markLineNumber(startOffset = true) value.markLineNumber(startOffset = true)
value.accept(this, data).coerce(symbol.owner.type).materialize() value.accept(this, data).materializeAt(symbol.owner.type)
mv.store(findLocalIndex(symbol), symbol.owner.asmType) mv.store(findLocalIndex(symbol), symbol.owner.asmType)
} }
override fun <T> visitConst(expression: IrConst<T>, data: BlockInfo): PromisedValue { override fun <T> visitConst(expression: IrConst<T>, data: BlockInfo): PromisedValue {
expression.markLineNumber(startOffset = true) expression.markLineNumber(startOffset = true)
when (val value = expression.value) { when (val value = expression.value) {
is Boolean -> return object : BooleanValue(this) { is Boolean -> {
override fun jumpIfFalse(target: Label) = if (value) Unit else mv.goTo(target) // BooleanConstants _may not_ be materialized, so we ensure an instruction for the line number.
override fun jumpIfTrue(target: Label) = if (value) mv.goTo(target) else Unit mv.nop()
override fun materialize() = mv.iconst(if (value) 1 else 0) return BooleanConstant(this, value)
} }
is Char -> mv.iconst(value.toInt()) is Char -> mv.iconst(value.toInt())
is Long -> mv.lconst(value) is Long -> mv.lconst(value)
@@ -618,7 +623,7 @@ class ExpressionCodegen(
closureReifiedMarkers[declaration] = it closureReifiedMarkers[declaration] = it
} }
} }
return immaterialUnitValue return unitValue
} }
override fun visitReturn(expression: IrReturn, data: BlockInfo): PromisedValue { override fun visitReturn(expression: IrReturn, data: BlockInfo): PromisedValue {
@@ -636,12 +641,12 @@ 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 immaterialUnitValue return unitValue
} }
val returnType = if (owner == irFunction) signature.returnType else methodSignatureMapper.mapReturnType(owner) val returnType = if (owner == irFunction) signature.returnType else methodSignatureMapper.mapReturnType(owner)
val afterReturnLabel = Label() val afterReturnLabel = Label()
expression.value.accept(this, data).coerce(returnType, owner.returnType).materialize() expression.value.accept(this, data).materializeAt(returnType, owner.returnType)
generateFinallyBlocksIfNeeded(returnType, afterReturnLabel, data) generateFinallyBlocksIfNeeded(returnType, afterReturnLabel, data)
expression.markLineNumber(startOffset = true) expression.markLineNumber(startOffset = true)
if (isNonLocalReturn) { if (isNonLocalReturn) {
@@ -650,7 +655,7 @@ class ExpressionCodegen(
mv.areturn(returnType) mv.areturn(returnType)
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 immaterialUnitValue return unitValue
} }
override fun visitWhen(expression: IrWhen, data: BlockInfo): PromisedValue { override fun visitWhen(expression: IrWhen, data: BlockInfo): PromisedValue {
@@ -681,7 +686,7 @@ class ExpressionCodegen(
if (!exhaustive) { if (!exhaustive) {
result.discard() result.discard()
} else { } else {
val materializedResult = result.coerce(expression.type).materialized val materializedResult = result.materializedAt(expression.type)
if (branch.condition.isTrueConst()) { if (branch.condition.isTrueConst()) {
// The rest of the expression is dead code. // The rest of the expression is dead code.
mv.mark(endLabel) mv.mark(endLabel)
@@ -695,7 +700,7 @@ class ExpressionCodegen(
mv.mark(elseLabel) mv.mark(elseLabel)
} }
mv.mark(endLabel) mv.mark(endLabel)
return immaterialUnitValue return unitValue
} }
override fun visitTypeOperator(expression: IrTypeOperatorCall, data: BlockInfo): PromisedValue { override fun visitTypeOperator(expression: IrTypeOperatorCall, data: BlockInfo): PromisedValue {
@@ -708,7 +713,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 = typeMapper.boxType(result.irType) val boxedLeftType = typeMapper.boxType(result.irType)
result.coerce(boxedLeftType, expression.argument.type).materialize() result.materializeAt(boxedLeftType, expression.argument.type)
val boxedRightType = typeMapper.boxType(typeOperand) val boxedRightType = typeMapper.boxType(typeOperand)
if (typeOperand.isReifiedTypeParameter) { if (typeOperand.isReifiedTypeParameter) {
@@ -723,7 +728,7 @@ class ExpressionCodegen(
} }
IrTypeOperator.INSTANCEOF -> { IrTypeOperator.INSTANCEOF -> {
expression.argument.accept(this, data).coerce(context.irBuiltIns.anyNType).materialize() expression.argument.accept(this, data).materializeAt(context.irBuiltIns.anyNType)
val type = typeMapper.boxType(typeOperand) val type = typeMapper.boxType(typeOperand)
if (typeOperand.isReifiedTypeParameter) { if (typeOperand.isReifiedTypeParameter) {
putReifiedOperationMarkerIfTypeIsReifiedParameter(typeOperand, ReifiedTypeInliner.OperationKind.IS) putReifiedOperationMarkerIfTypeIsReifiedParameter(typeOperand, ReifiedTypeInliner.OperationKind.IS)
@@ -763,7 +768,7 @@ class ExpressionCodegen(
} }
mv.goTo(continueLabel) mv.goTo(continueLabel)
mv.mark(endLabel) mv.mark(endLabel)
return immaterialUnitValue return unitValue
} }
override fun visitDoWhileLoop(loop: IrDoWhileLoop, data: BlockInfo): PromisedValue { override fun visitDoWhileLoop(loop: IrDoWhileLoop, data: BlockInfo): PromisedValue {
@@ -780,7 +785,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 immaterialUnitValue return unitValue
} }
private fun unwindBlockStack( private fun unwindBlockStack(
@@ -807,7 +812,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 immaterialUnitValue return unitValue
} }
override fun visitTry(aTry: IrTry, data: BlockInfo): PromisedValue { override fun visitTry(aTry: IrTry, data: BlockInfo): PromisedValue {
@@ -826,7 +831,7 @@ class ExpressionCodegen(
val isExpression = !aTry.type.isUnit() val isExpression = !aTry.type.isUnit()
var savedValue: Int? = null var savedValue: Int? = null
if (isExpression) { if (isExpression) {
tryResult.coerce(tryAsmType, aTry.type).materialize() tryResult.materializeAt(tryAsmType, aTry.type)
savedValue = frameMap.enterTemp(tryAsmType) savedValue = frameMap.enterTemp(tryAsmType)
mv.store(savedValue, tryAsmType) mv.store(savedValue, tryAsmType)
} else { } else {
@@ -855,7 +860,7 @@ class ExpressionCodegen(
val catchBody = clause.result val catchBody = clause.result
val catchResult = catchBody.accept(this, data) val catchResult = catchBody.accept(this, data)
if (savedValue != null) { if (savedValue != null) {
catchResult.coerce(tryAsmType, aTry.type).materialize() catchResult.materializeAt(tryAsmType, aTry.type)
mv.store(savedValue, tryAsmType) mv.store(savedValue, tryAsmType)
} else { } else {
catchResult.discard() catchResult.discard()
@@ -903,7 +908,7 @@ class ExpressionCodegen(
frameMap.leaveTemp(tryAsmType) frameMap.leaveTemp(tryAsmType)
return aTry.onStack return aTry.onStack
} }
return immaterialUnitValue return unitValue
} }
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?) {
@@ -964,11 +969,11 @@ class ExpressionCodegen(
// Avoid unecessary CHECKCASTs to java/lang/Throwable. If the exception is not of type Object // Avoid unecessary CHECKCASTs to java/lang/Throwable. If the exception is not of type Object
// then it must be some subtype of throwable and we don't need to coerce it. // then it must be some subtype of throwable and we don't need to coerce it.
if (exception.type == OBJECT_TYPE) if (exception.type == OBJECT_TYPE)
exception.coerce(context.irBuiltIns.throwableType).materialize() exception.materializeAt(context.irBuiltIns.throwableType)
else else
exception.materialize() exception.materialize()
mv.athrow() mv.athrow()
return immaterialUnitValue return unitValue
} }
override fun visitGetClass(expression: IrGetClass, data: BlockInfo) = override fun visitGetClass(expression: IrGetClass, data: BlockInfo) =
@@ -13,7 +13,6 @@ import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.StackValue import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.parentAsClass import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.Type
@@ -24,7 +23,42 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
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() open fun materializeAt(target: Type, irTarget: IrType) {
val erasedSourceType = irType.eraseTypeParameters()
val erasedTargetType = irTarget.eraseTypeParameters()
val isFromTypeInlineClass = erasedSourceType.classOrNull!!.owner.isInline
val isToTypeInlineClass = erasedTargetType.classOrNull!!.owner.isInline
// Boxing and unboxing kotlin.Result leads to CCE in generated code
val doNotCoerceKotlinResultInContinuation =
(codegen.irFunction.parentAsClass.origin == JvmLoweredDeclarationOrigin.CONTINUATION_CLASS ||
codegen.irFunction.parentAsClass.origin == JvmLoweredDeclarationOrigin.SUSPEND_LAMBDA)
&& (irType.isKotlinResult() || irTarget.isKotlinResult())
// Coerce inline classes
if ((isFromTypeInlineClass || isToTypeInlineClass) && !doNotCoerceKotlinResultInContinuation) {
val isFromTypeUnboxed = isFromTypeInlineClass && typeMapper.mapType(erasedSourceType.unboxed) == type
val isToTypeUnboxed = isToTypeInlineClass && typeMapper.mapType(erasedTargetType.unboxed) == target
when {
isFromTypeUnboxed && !isToTypeUnboxed -> {
StackValue.boxInlineClass(erasedSourceType.toKotlinType(), mv)
return
}
!isFromTypeUnboxed && isToTypeUnboxed -> {
StackValue.unboxInlineClass(type, erasedTargetType.toKotlinType(), mv)
return
}
}
}
if (type != target) {
StackValue.coerce(type, target, mv)
}
}
abstract fun discard()
val mv: InstructionAdapter val mv: InstructionAdapter
get() = codegen.mv get() = codegen.mv
@@ -38,21 +72,20 @@ abstract class PromisedValue(val codegen: ExpressionCodegen, val type: Type, val
// 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() {} override fun discard() {
} if (type !== Type.VOID_TYPE)
AsmUtil.pop(mv, type)
// A value that is only materialized through coercion. }
class ImmaterialValue(codegen: ExpressionCodegen, type: Type, irType: IrType) : PromisedValue(codegen, type, irType) {
override fun materialize() {}
} }
// A value that can be branched on. JVM has certain branching instructions which can be used // A value that can be branched on. JVM has certain branching instructions which can be used
// to optimize these. // to optimize these.
abstract class BooleanValue(codegen: ExpressionCodegen) : PromisedValue(codegen, Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType) { abstract class BooleanValue(codegen: ExpressionCodegen) :
PromisedValue(codegen, Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType) {
abstract fun jumpIfFalse(target: Label) abstract fun jumpIfFalse(target: Label)
abstract fun jumpIfTrue(target: Label) abstract fun jumpIfTrue(target: Label)
override fun materialize() { override fun materializeAt(target: Type, irTarget: IrType) {
val const0 = Label() val const0 = Label()
val end = Label() val end = Label()
jumpIfFalse(const0) jumpIfFalse(const0)
@@ -61,103 +94,71 @@ abstract class BooleanValue(codegen: ExpressionCodegen) : PromisedValue(codegen,
mv.mark(const0) mv.mark(const0)
mv.iconst(0) mv.iconst(0)
mv.mark(end) mv.mark(end)
if (Type.BOOLEAN_TYPE != target) {
StackValue.coerce(Type.BOOLEAN_TYPE, target, mv)
}
} }
} }
// Same as materialize(), but return a representation of the result. class BooleanConstant(codegen: ExpressionCodegen, val value: Boolean) : BooleanValue(codegen) {
val PromisedValue.materialized: MaterialValue override fun jumpIfFalse(target: Label) = if (value) Unit else mv.goTo(target)
get() { override fun jumpIfTrue(target: Label) = if (value) mv.goTo(target) else Unit
materialize() override fun materializeAt(target: Type, irTarget: IrType) {
return MaterialValue(codegen, type, irType) mv.iconst(if (value) 1 else 0)
if (Type.BOOLEAN_TYPE != target) {
StackValue.coerce(Type.BOOLEAN_TYPE, target, mv)
}
} }
// Materialize and disregard this value. Materialization is forced because, presumably, override fun discard() {}
// we only wanted the side effects anyway.
fun PromisedValue.discard(): PromisedValue {
materialize()
if (type !== Type.VOID_TYPE)
AsmUtil.pop(mv, type)
return codegen.immaterialUnitValue
} }
private val IrType.unboxed: IrType fun PromisedValue.coerceToBoolean(): BooleanValue =
when (this) {
is BooleanValue -> this
else -> object : BooleanValue(codegen) {
override fun jumpIfFalse(target: Label) =
this@coerceToBoolean.materializeAt(Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType).also { mv.ifeq(target) }
override fun jumpIfTrue(target: Label) =
this@coerceToBoolean.materializeAt(Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType).also { mv.ifne(target) }
override fun discard() {
this@coerceToBoolean.discard()
}
}
}
fun PromisedValue.materializedAt(target: Type, irTarget: IrType): MaterialValue {
materializeAt(target, irTarget)
return MaterialValue(codegen, target, irTarget)
}
fun PromisedValue.materialized(): MaterialValue =
materializedAt(type, irType)
fun PromisedValue.materializedAt(irTarget: IrType): MaterialValue =
materializedAt(typeMapper.mapType(irTarget), irTarget)
fun PromisedValue.materializedAtBoxed(irTarget: IrType): MaterialValue =
materializedAt(typeMapper.boxType(irTarget), irTarget)
fun PromisedValue.materialize() {
materializeAt(type, irType)
}
fun PromisedValue.materializeAt(irTarget: IrType) {
materializeAt(typeMapper.mapType(irTarget), irTarget)
}
fun PromisedValue.materializeAtBoxed(irTarget: IrType) {
materializeAt(typeMapper.boxType(irTarget), irTarget)
}
val IrType.unboxed: IrType
get() = InlineClassAbi.getUnderlyingType(erasedUpperBound) get() = InlineClassAbi.getUnderlyingType(erasedUpperBound)
// On materialization, cast the value to a different type. // A Non-materialized value of Unit type that is only materialized through coercion.
fun PromisedValue.coerce(target: Type, irTarget: IrType): PromisedValue { val ExpressionCodegen.unitValue: PromisedValue
val erasedSourceType = irType.eraseTypeParameters() get() = MaterialValue(this, Type.VOID_TYPE, context.irBuiltIns.unitType)
val erasedTargetType = irTarget.eraseTypeParameters()
val isFromTypeInlineClass = erasedSourceType.classOrNull!!.owner.isInline
val isToTypeInlineClass = erasedTargetType.classOrNull!!.owner.isInline
// Boxing and unboxing kotlin.Result leads to CCE in generated code
val doNotCoerceKotlinResultInContinuation =
(codegen.irFunction.parentAsClass.origin == JvmLoweredDeclarationOrigin.CONTINUATION_CLASS ||
codegen.irFunction.parentAsClass.origin == JvmLoweredDeclarationOrigin.SUSPEND_LAMBDA)
&& (irType.isKotlinResult() || irTarget.isKotlinResult())
// Coerce inline classes
if ((isFromTypeInlineClass || isToTypeInlineClass) && !doNotCoerceKotlinResultInContinuation) {
val isFromTypeUnboxed = isFromTypeInlineClass && typeMapper.mapType(erasedSourceType.unboxed) == type
val isToTypeUnboxed = isToTypeInlineClass && typeMapper.mapType(erasedTargetType.unboxed) == target
when {
isFromTypeUnboxed && !isToTypeUnboxed -> return object : PromisedValue(codegen, target, irTarget) {
override fun materialize() {
this@coerce.materialize()
StackValue.boxInlineClass(erasedSourceType.toKotlinType(), mv)
}
}
!isFromTypeUnboxed && isToTypeUnboxed -> return object : PromisedValue(codegen, target, irTarget) {
override fun materialize() {
val value = this@coerce.materialized
StackValue.unboxInlineClass(value.type, erasedTargetType.toKotlinType(), mv)
}
}
}
}
// All unsafe coercions between irTypes should use the UnsafeCoerce intrinsic
return if (type == target) this else object : PromisedValue(codegen, target, irTarget) {
override fun materialize() {
val value = this@coerce.materialized
StackValue.coerce(value.type, type, mv)
}
}
}
fun PromisedValue.coerce(irTarget: IrType) =
coerce(typeMapper.mapType(irTarget), irTarget)
fun PromisedValue.coerceToBoxed(irTarget: IrType) =
coerce(typeMapper.boxType(irTarget), irTarget)
// Same as above, but with a return type that allows conditional jumping.
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()
}
}
// 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)
}
}
private fun IrType.isArrayOfKClass(): Boolean =
isArray() && this is IrSimpleType && arguments.singleOrNull().let { argument ->
argument is IrTypeProjection && argument.type.isKClass()
}
@@ -160,13 +160,13 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
// //
// Namely, the structure which returns true if any one of the condition is true. // Namely, the structure which returns true if any one of the condition is true.
for (branch in condition.branches) { for (branch in condition.branches) {
if (branch is IrElseBranch) { candidates += if (branch is IrElseBranch) {
assert(branch.condition.isTrueConst()) { "IrElseBranch.condition should be const true: ${branch.condition.dump()}" } assert(branch.condition.isTrueConst()) { "IrElseBranch.condition should be const true: ${branch.condition.dump()}" }
candidates += matchConditions(branch.result) ?: return null matchConditions(branch.result) ?: return null
} else { } else {
if (!branch.result.isTrueConst()) if (!branch.result.isTrueConst())
return null return null
candidates += matchConditions(branch.condition) ?: return null matchConditions(branch.condition) ?: return null
} }
} }
@@ -220,14 +220,21 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
private 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) {
mv.visitLabel(label) mv.visitLabel(label)
thenExpression.accept(codegen, data).coerce(expression.type).materialized thenExpression.accept(codegen, data).also {
if (elseExpression != null) {
it.materializedAt(expression.type)
} else {
it.discard()
}
}
mv.goTo(endLabel) mv.goTo(endLabel)
} }
mv.visitLabel(defaultLabel) mv.visitLabel(defaultLabel)
val stackValue = elseExpression?.accept(codegen, data)?.coerce(expression.type) ?: defaultValue(expression.type) val result = elseExpression?.accept(codegen, data)?.materializedAt(expression.type) ?: unitValue
val result = stackValue.materialized
mv.mark(endLabel) mv.mark(endLabel)
return result return result
} }
@@ -245,10 +252,8 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
override fun shouldOptimize() = cases.size > 1 override fun shouldOptimize() = cases.size > 1
override fun genSwitch() { override fun genSwitch() {
with(codegen) { subject.accept(codegen, data).materialize()
subject.accept(codegen, data).materialize() genIntSwitch(cases)
genIntSwitch(cases)
}
} }
} }
@@ -12,7 +12,8 @@ import org.jetbrains.org.objectweb.asm.Label
object AndAnd : IntrinsicMethod() { object AndAnd : IntrinsicMethod() {
private class BooleanConjunction(val arg0: IrExpression, val arg1: IrExpression, codegen: ExpressionCodegen, val data: BlockInfo) : BooleanValue(codegen) { private class BooleanConjunction(val arg0: IrExpression, val arg1: IrExpression, codegen: ExpressionCodegen, val data: BlockInfo) :
BooleanValue(codegen) {
override fun jumpIfFalse(target: Label) { override fun jumpIfFalse(target: Label) {
arg0.accept(codegen, data).coerceToBoolean().jumpIfFalse(target) arg0.accept(codegen, data).coerceToBoolean().jumpIfFalse(target)
@@ -25,6 +26,13 @@ object AndAnd : IntrinsicMethod() {
arg1.accept(codegen, data).coerceToBoolean().jumpIfTrue(target) arg1.accept(codegen, data).coerceToBoolean().jumpIfTrue(target)
mv.visitLabel(stayLabel) mv.visitLabel(stayLabel)
} }
override fun discard() {
val end = Label()
arg0.accept(codegen, data).coerceToBoolean().jumpIfFalse(end)
arg1.accept(codegen, data).discard()
mv.visitLabel(end)
}
} }
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? { override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
@@ -24,11 +24,10 @@ import org.jetbrains.org.objectweb.asm.Type
object ArrayGet : IntrinsicMethod() { object ArrayGet : IntrinsicMethod() {
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? { override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
val dispatchReceiver = expression.dispatchReceiver!! val dispatchReceiver = expression.dispatchReceiver!!
val receiver = dispatchReceiver.accept(codegen, data).coerce(dispatchReceiver.type).materialized val receiver = dispatchReceiver.accept(codegen, data).materializedAt(dispatchReceiver.type)
val elementType = AsmUtil.correctElementType(receiver.type) val elementType = AsmUtil.correctElementType(receiver.type)
expression.getValueArgument(0)!!.accept(codegen, data) expression.getValueArgument(0)!!.accept(codegen, data)
.coerce(Type.INT_TYPE, codegen.context.irBuiltIns.intType) .materializeAt(Type.INT_TYPE, codegen.context.irBuiltIns.intType)
.materialize()
codegen.mv.aload(elementType) codegen.mv.aload(elementType)
return MaterialValue(codegen, elementType, expression.type) return MaterialValue(codegen, elementType, expression.type)
} }
@@ -25,12 +25,12 @@ import org.jetbrains.org.objectweb.asm.Type
object ArraySet : IntrinsicMethod() { object ArraySet : IntrinsicMethod() {
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? { override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
val dispatchReceiver = expression.dispatchReceiver!! val dispatchReceiver = expression.dispatchReceiver!!
val receiver = dispatchReceiver.accept(codegen, data).coerce(dispatchReceiver.type).materialized val receiver = dispatchReceiver.accept(codegen, data).materializedAt(dispatchReceiver.type)
val elementType = AsmUtil.correctElementType(receiver.type) val elementType = AsmUtil.correctElementType(receiver.type)
val elementIrType = receiver.irType.getArrayElementType(codegen.context.irBuiltIns) val elementIrType = receiver.irType.getArrayElementType(codegen.context.irBuiltIns)
expression.getValueArgument(0)!!.accept(codegen, data).coerce(Type.INT_TYPE, codegen.context.irBuiltIns.intType).materialize() expression.getValueArgument(0)!!.accept(codegen, data).materializeAt(Type.INT_TYPE, codegen.context.irBuiltIns.intType)
expression.getValueArgument(1)!!.accept(codegen, data).coerce(elementType, elementIrType).materialize() expression.getValueArgument(1)!!.accept(codegen, data).materializeAt(elementType, elementIrType)
codegen.mv.astore(elementType) codegen.mv.astore(elementType)
return codegen.immaterialUnitValue return codegen.unitValue
} }
} }
@@ -25,7 +25,7 @@ import org.jetbrains.org.objectweb.asm.Opcodes
object Clone : IntrinsicMethod() { object Clone : IntrinsicMethod() {
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo) = with(codegen) { override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo) = with(codegen) {
val result = expression.dispatchReceiver!!.accept(this, data).materialized val result = expression.dispatchReceiver!!.accept(this, data).materialized()
assert(!AsmUtil.isPrimitive(result.type)) { "clone() of primitive type" } assert(!AsmUtil.isPrimitive(result.type)) { "clone() of primitive type" }
val opcode = if (expression is IrCall && expression.superQualifierSymbol != null) Opcodes.INVOKESPECIAL else Opcodes.INVOKEVIRTUAL val opcode = if (expression is IrCall && expression.superQualifierSymbol != null) Opcodes.INVOKESPECIAL else Opcodes.INVOKEVIRTUAL
mv.visitMethodInsn(opcode, "java/lang/Object", "clone", "()Ljava/lang/Object;", false) mv.visitMethodInsn(opcode, "java/lang/Object", "clone", "()Ljava/lang/Object;", false)
@@ -84,6 +84,11 @@ class BooleanComparison(val op: IElementType, val a: MaterialValue, val b: Mater
NumberCompare.patchOpcode(BranchedValue.negatedOperations[NumberCompare.getNumberCompareOpcode(op)]!!, mv, op, a.type) NumberCompare.patchOpcode(BranchedValue.negatedOperations[NumberCompare.getNumberCompareOpcode(op)]!!, mv, op, a.type)
mv.visitJumpInsn(opcode, target) mv.visitJumpInsn(opcode, target)
} }
override fun discard() {
b.discard()
a.discard()
}
} }
@@ -107,6 +112,11 @@ class NonIEEE754FloatComparison(val op: IElementType, val a: MaterialValue, val
invokeStaticComparison(a.type) invokeStaticComparison(a.type)
mv.visitJumpInsn(BranchedValue.negatedOperations[numberCompareOpcode]!!, target) mv.visitJumpInsn(BranchedValue.negatedOperations[numberCompareOpcode]!!, target)
} }
override fun discard() {
b.discard()
a.discard()
}
} }
class PrimitiveComparison( class PrimitiveComparison(
@@ -116,8 +126,8 @@ class PrimitiveComparison(
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? { override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
val parameterType = Type.getType(JvmPrimitiveType.get(KotlinBuiltIns.getPrimitiveType(primitiveNumberType)!!).desc) val parameterType = Type.getType(JvmPrimitiveType.get(KotlinBuiltIns.getPrimitiveType(primitiveNumberType)!!).desc)
val (left, right) = expression.receiverAndArgs() val (left, right) = expression.receiverAndArgs()
val a = left.accept(codegen, data).coerce(parameterType, left.type).materialized val a = left.accept(codegen, data).materializedAt(parameterType, left.type)
val b = right.accept(codegen, data).coerce(parameterType, right.type).materialized val b = right.accept(codegen, data).materializedAt(parameterType, right.type)
val useNonIEEE754Comparison = val useNonIEEE754Comparison =
!codegen.context.state.languageVersionSettings.supportsFeature(LanguageFeature.ProperIeee754Comparisons) !codegen.context.state.languageVersionSettings.supportsFeature(LanguageFeature.ProperIeee754Comparisons)
@@ -18,7 +18,7 @@ import org.jetbrains.org.objectweb.asm.Type
object EnumValueOf : IntrinsicMethod() { object EnumValueOf : IntrinsicMethod() {
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo) = with(codegen) { override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo) = with(codegen) {
val type = expression.getTypeArgument(0)!! val type = expression.getTypeArgument(0)!!
val result = expression.getValueArgument(0)!!.accept(this, data).materialized val result = expression.getValueArgument(0)!!.accept(this, data).materialized()
if (type.isReifiedTypeParameter) { if (type.isReifiedTypeParameter) {
// Note that the inliner expects exactly the following sequence of instructions. // Note that the inliner expects exactly the following sequence of instructions.
// <REIFIED-OPERATIONS-MARKER> // <REIFIED-OPERATIONS-MARKER>
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.codegen.pseudoInsns.fakeAlwaysTrueIfeq
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrNull import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.isNullable import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.ir.types.toKotlinType import org.jetbrains.kotlin.ir.types.toKotlinType
@@ -39,8 +40,8 @@ class ExplicitEquals : IntrinsicMethod() {
val (a, b) = expression.receiverAndArgs() val (a, b) = expression.receiverAndArgs()
// TODO use specialized boxed type - this might require types like 'java.lang.Integer' in IR // TODO use specialized boxed type - this might require types like 'java.lang.Integer' in IR
a.accept(codegen, data).coerce(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType).materialize() a.accept(codegen, data).materializeAt(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType)
b.accept(codegen, data).coerce(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType).materialize() b.accept(codegen, data).materializeAt(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType)
codegen.mv.visitMethodInsn( codegen.mv.visitMethodInsn(
Opcodes.INVOKEVIRTUAL, Opcodes.INVOKEVIRTUAL,
AsmTypes.OBJECT_TYPE.internalName, AsmTypes.OBJECT_TYPE.internalName,
@@ -54,15 +55,13 @@ class ExplicitEquals : IntrinsicMethod() {
} }
class Equals(val operator: IElementType) : IntrinsicMethod() { class Equals(val operator: IElementType) : IntrinsicMethod() {
private class BooleanConstantFalseCheck(val value: PromisedValue) : BooleanValue(value.codegen) {
override fun materialize() = value.discard().let { mv.iconst(0) }
override fun jumpIfFalse(target: Label) = value.discard().let { mv.fakeAlwaysTrueIfeq(target) }
override fun jumpIfTrue(target: Label) = value.discard().let { mv.fakeAlwaysFalseIfeq(target) }
}
private class BooleanNullCheck(val value: PromisedValue) : BooleanValue(value.codegen) { private class BooleanNullCheck(val value: PromisedValue) : BooleanValue(value.codegen) {
override fun jumpIfFalse(target: Label) = value.materialize().also { mv.ifnonnull(target) } override fun jumpIfFalse(target: Label) = value.materialize().also { mv.ifnonnull(target) }
override fun jumpIfTrue(target: Label) = value.materialize().also { mv.ifnull(target) } override fun jumpIfTrue(target: Label) = value.materialize().also { mv.ifnull(target) }
override fun discard() {
value.discard()
}
} }
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? { override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
@@ -72,8 +71,10 @@ class Equals(val operator: IElementType) : IntrinsicMethod() {
val value = irValue.accept(codegen, data) val value = irValue.accept(codegen, data)
return if (!isPrimitive(value.type) && (irValue.type.classOrNull?.owner?.isInline != true || irValue.type.isNullable())) return if (!isPrimitive(value.type) && (irValue.type.classOrNull?.owner?.isInline != true || irValue.type.isNullable()))
BooleanNullCheck(value) BooleanNullCheck(value)
else else {
BooleanConstantFalseCheck(value) value.discard()
BooleanConstant(codegen, false)
}
} }
val leftType = with(codegen) { a.asmType } val leftType = with(codegen) { a.asmType }
@@ -84,14 +85,14 @@ class Equals(val operator: IElementType) : IntrinsicMethod() {
// is that `equals` is a total order (-0 < +0 and NaN == NaN) and `===` is IEEE754-compliant. // is that `equals` is a total order (-0 < +0 and NaN == NaN) and `===` is IEEE754-compliant.
(!isPrimitive(leftType) || leftType != rightType || leftType == Type.FLOAT_TYPE || leftType == Type.DOUBLE_TYPE) (!isPrimitive(leftType) || leftType != rightType || leftType == Type.FLOAT_TYPE || leftType == Type.DOUBLE_TYPE)
return if (useEquals) { return if (useEquals) {
a.accept(codegen, data).coerce(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType).materialize() a.accept(codegen, data).materializeAt(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType)
b.accept(codegen, data).coerce(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType).materialize() b.accept(codegen, data).materializeAt(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType)
genAreEqualCall(codegen.mv) genAreEqualCall(codegen.mv)
MaterialValue(codegen, Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType) MaterialValue(codegen, Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType)
} else { } else {
val operandType = if (!isPrimitive(leftType)) AsmTypes.OBJECT_TYPE else leftType val operandType = if (!isPrimitive(leftType)) AsmTypes.OBJECT_TYPE else leftType
val aValue = a.accept(codegen, data).coerce(operandType, a.type).materialized val aValue = a.accept(codegen, data).materializedAt(operandType, a.type)
val bValue = b.accept(codegen, data).coerce(operandType, b.type).materialized val bValue = b.accept(codegen, data).materializedAt(operandType, b.type)
BooleanComparison(operator, aValue, bValue) BooleanComparison(operator, aValue, bValue)
} }
} }
@@ -30,13 +30,13 @@ import org.jetbrains.org.objectweb.asm.Type
object HashCode : IntrinsicMethod() { object HashCode : IntrinsicMethod() {
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo) = with(codegen) { override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo) = with(codegen) {
val receiver = expression.dispatchReceiver ?: error("No receiver for hashCode: ${expression.render()}") val receiver = expression.dispatchReceiver ?: error("No receiver for hashCode: ${expression.render()}")
val result = receiver.accept(this, data).materialized val result = receiver.accept(this, data).materialized()
val target = context.state.target val target = context.state.target
when { when {
irFunction.origin == JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD || irFunction.origin == IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER -> irFunction.origin == JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD || irFunction.origin == IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER ->
AsmUtil.genHashCode(mv, mv, result.type, target) AsmUtil.genHashCode(mv, mv, result.type, target)
target == JvmTarget.JVM_1_6 -> { target == JvmTarget.JVM_1_6 -> {
result.coerceToBoxed(receiver.type).materialize() result.materializeAtBoxed(receiver.type)
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "hashCode", "()I", false) mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "hashCode", "()I", false)
} }
else -> { else -> {
@@ -6,12 +6,12 @@
package org.jetbrains.kotlin.backend.jvm.intrinsics package org.jetbrains.kotlin.backend.jvm.intrinsics
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo import org.jetbrains.kotlin.backend.jvm.codegen.*
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.backend.jvm.codegen.PromisedValue
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.Method import org.jetbrains.org.objectweb.asm.commons.Method
@@ -27,11 +27,11 @@ abstract class IntrinsicMethod {
with(codegen) { with(codegen) {
val descriptor = methodSignatureMapper.mapSignatureSkipGeneric(expression.symbol.owner) val descriptor = methodSignatureMapper.mapSignatureSkipGeneric(expression.symbol.owner)
val stackValue = toCallable(expression, descriptor, context).invoke(mv, codegen, data) val stackValue = toCallable(expression, descriptor, context).invoke(mv, codegen, data)
return object : PromisedValue(this, stackValue.type, expression.type) { stackValue.put(mv)
override fun materialize() = stackValue.put(mv) return MaterialValue(this, stackValue.type, expression.type)
}
} }
companion object { companion object {
fun calcReceiverType(call: IrMemberAccessExpression, context: JvmBackendContext): Type { fun calcReceiverType(call: IrMemberAccessExpression, context: JvmBackendContext): Type {
return context.typeMapper.mapType(call.dispatchReceiver?.type ?: call.extensionReceiver!!.type) return context.typeMapper.mapType(call.dispatchReceiver?.type ?: call.extensionReceiver!!.type)
@@ -26,22 +26,21 @@ import org.jetbrains.org.objectweb.asm.Type
object JavaClassProperty : IntrinsicMethod() { object JavaClassProperty : IntrinsicMethod() {
private fun invokeGetClass(value: PromisedValue) { private fun invokeGetClass(value: PromisedValue) {
value.materialize()
value.mv.invokevirtual("java/lang/Object", "getClass", "()Ljava/lang/Class;", false) value.mv.invokevirtual("java/lang/Object", "getClass", "()Ljava/lang/Class;", false)
} }
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, value.codegen.context.irBuiltIns.unitType)) invokeGetClass(value.materializedAt(AsmTypes.UNIT_TYPE, value.codegen.context.irBuiltIns.unitType))
value.irType.classOrNull?.owner?.isInline == true -> value.irType.classOrNull?.owner?.isInline == true ->
invokeGetClass(value.coerceToBoxed(value.irType)) invokeGetClass(value.materializedAtBoxed(value.irType))
isPrimitive(value.type) -> { isPrimitive(value.type) -> {
value.discard() value.discard()
value.mv.getstatic(boxType(value.type).internalName, "TYPE", "Ljava/lang/Class;") value.mv.getstatic(boxType(value.type).internalName, "TYPE", "Ljava/lang/Class;")
} }
else -> else ->
invokeGetClass(value) invokeGetClass(value.materialized())
} }
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? { override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
@@ -27,6 +27,9 @@ object Not : IntrinsicMethod() {
class BooleanNegation(val value: BooleanValue) : BooleanValue(value.codegen) { class BooleanNegation(val value: BooleanValue) : BooleanValue(value.codegen) {
override fun jumpIfFalse(target: Label) = value.jumpIfTrue(target) override fun jumpIfFalse(target: Label) = value.jumpIfTrue(target)
override fun jumpIfTrue(target: Label) = value.jumpIfFalse(target) override fun jumpIfTrue(target: Label) = value.jumpIfFalse(target)
override fun discard() {
value.discard()
}
} }
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo) = override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo) =
@@ -13,7 +13,8 @@ import org.jetbrains.org.objectweb.asm.Label
object OrOr : IntrinsicMethod() { object OrOr : IntrinsicMethod() {
private class BooleanDisjunction( private class BooleanDisjunction(
val arg0: IrExpression, val arg1: IrExpression, codegen: ExpressionCodegen, val data: BlockInfo) : BooleanValue(codegen) { val arg0: IrExpression, val arg1: IrExpression, codegen: ExpressionCodegen, val data: BlockInfo
) : BooleanValue(codegen) {
override fun jumpIfFalse(target: Label) { override fun jumpIfFalse(target: Label) {
val stayLabel = Label() val stayLabel = Label()
@@ -26,6 +27,13 @@ object OrOr : IntrinsicMethod() {
arg0.accept(codegen, data).coerceToBoolean().jumpIfTrue(target) arg0.accept(codegen, data).coerceToBoolean().jumpIfTrue(target)
arg1.accept(codegen, data).coerceToBoolean().jumpIfTrue(target) arg1.accept(codegen, data).coerceToBoolean().jumpIfTrue(target)
} }
override fun discard() {
val end = Label()
arg0.accept(codegen, data).coerceToBoolean().jumpIfTrue(end)
arg1.accept(codegen, data).discard()
mv.visitLabel(end)
}
} }
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? { override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
@@ -28,6 +28,6 @@ object ReassignParameter : IntrinsicMethod() {
val parameter = parameterGet?.symbol as? IrValueParameterSymbol val parameter = parameterGet?.symbol as? IrValueParameterSymbol
?: throw AssertionError("${expression.getValueArgument(0)} is not a get of a parameter") ?: throw AssertionError("${expression.getValueArgument(0)} is not a get of a parameter")
codegen.setVariable(parameter, expression.getValueArgument(1)!!, data) codegen.setVariable(parameter, expression.getValueArgument(1)!!, data)
return codegen.immaterialUnitValue return codegen.unitValue
} }
} }
@@ -7,20 +7,23 @@ package org.jetbrains.kotlin.backend.jvm.intrinsics
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
import org.jetbrains.kotlin.backend.jvm.codegen.MaterialValue
import org.jetbrains.kotlin.backend.jvm.codegen.PromisedValue import org.jetbrains.kotlin.backend.jvm.codegen.PromisedValue
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.collectRealOverrides import org.jetbrains.kotlin.ir.util.collectRealOverrides
import org.jetbrains.kotlin.ir.util.isSuspend import org.jetbrains.kotlin.ir.util.isSuspend
import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.org.objectweb.asm.Type
/** /**
* Computes the JVM signature of a given IrFunction. The function is passed as an IrFunctionReference * Computes the JVM signature of a given IrFunction. The function is passed as an IrFunctionReference
* to the single argument of the intrinsic. * to the single argument of the intrinsic.
*/ */
object SignatureString : IntrinsicMethod() { object SignatureString : IntrinsicMethod() {
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? { override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue {
val function = (expression.getValueArgument(0) as IrFunctionReference).symbol.owner val function = (expression.getValueArgument(0) as IrFunctionReference).symbol.owner
var resolved = if (function is IrSimpleFunction) function.collectRealOverrides().first() else function var resolved = if (function is IrSimpleFunction) function.collectRealOverrides().first() else function
if (resolved.isSuspend) { if (resolved.isSuspend) {
@@ -28,10 +31,7 @@ object SignatureString : IntrinsicMethod() {
} }
val method = codegen.context.methodSignatureMapper.mapAsmMethod(resolved) val method = codegen.context.methodSignatureMapper.mapAsmMethod(resolved)
val descriptor = method.name + method.descriptor val descriptor = method.name + method.descriptor
return object : PromisedValue(codegen, AsmTypes.JAVA_STRING_TYPE, codegen.context.irBuiltIns.stringType) { codegen.mv.aconst(descriptor)
override fun materialize() { return MaterialValue(codegen, AsmTypes.JAVA_STRING_TYPE, codegen.context.irBuiltIns.stringType)
codegen.mv.aconst(descriptor)
}
}
} }
} }
@@ -5,11 +5,10 @@
package org.jetbrains.kotlin.backend.jvm.intrinsics package org.jetbrains.kotlin.backend.jvm.intrinsics
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo import org.jetbrains.kotlin.backend.jvm.codegen.*
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
import org.jetbrains.kotlin.backend.jvm.codegen.PromisedValue
import org.jetbrains.kotlin.backend.jvm.codegen.coerce
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.org.objectweb.asm.Type
/** /**
* Implicit coercion between IrTypes with the same underlying representation. * Implicit coercion between IrTypes with the same underlying representation.
@@ -19,7 +18,7 @@ import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
* addition to the underlying asmType. * addition to the underlying asmType.
*/ */
object UnsafeCoerce : IntrinsicMethod() { object UnsafeCoerce : IntrinsicMethod() {
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? { override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue {
val from = expression.getTypeArgument(0)!! val from = expression.getTypeArgument(0)!!
val to = expression.getTypeArgument(1)!! val to = expression.getTypeArgument(1)!!
val fromType = codegen.typeMapper.mapType(from) val fromType = codegen.typeMapper.mapType(from)
@@ -30,7 +29,14 @@ object UnsafeCoerce : IntrinsicMethod() {
val arg = expression.getValueArgument(0)!! val arg = expression.getValueArgument(0)!!
val result = arg.accept(codegen, data) val result = arg.accept(codegen, data)
return object : PromisedValue(codegen, toType, to) { return object : PromisedValue(codegen, toType, to) {
override fun materialize() = result.coerce(from).materialize() override fun materializeAt(target: Type, irTarget: IrType) {
result.materializeAt(fromType, from)
super.materializeAt(target, irTarget)
}
override fun discard() {
result.discard()
}
} }
} }
} }