Return a stripped version of StackValue from ExpressionCodegen
This commit is contained in:
+245
-236
@@ -7,6 +7,8 @@ package org.jetbrains.kotlin.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.descriptors.propertyIfAccessor
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.intrinsics.JavaClassProperty
|
||||
import org.jetbrains.kotlin.backend.jvm.intrinsics.Not
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.CrIrType
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
@@ -15,9 +17,7 @@ import org.jetbrains.kotlin.codegen.ExpressionCodegen.putReifiedOperationMarkerI
|
||||
import org.jetbrains.kotlin.codegen.inline.*
|
||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.OperationKind.AS
|
||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.OperationKind.SAFE_AS
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.JavaClassProperty
|
||||
import org.jetbrains.kotlin.codegen.pseudoInsns.fakeAlwaysFalseIfeq
|
||||
import org.jetbrains.kotlin.codegen.pseudoInsns.fakeAlwaysTrueIfeq
|
||||
import org.jetbrains.kotlin.codegen.pseudoInsns.fixStackAndJump
|
||||
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.isUnit
|
||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||
@@ -42,7 +43,6 @@ import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||
import org.jetbrains.kotlin.types.typesApproximation.approximateCapturedTypes
|
||||
import org.jetbrains.kotlin.types.upperIfFlexible
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
@@ -100,13 +100,80 @@ class BlockInfo private constructor(val parent: BlockInfo?) {
|
||||
|
||||
class VariableInfo(val declaration: IrVariable, val index: Int, val type: Type, val startLabel: Label)
|
||||
|
||||
// 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) {
|
||||
// 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()
|
||||
}
|
||||
|
||||
// A value that *has* been fully constructed.
|
||||
class MaterialValue(codegen: ExpressionCodegen, type: Type) : PromisedValue(codegen, type) {
|
||||
override fun materialize() {}
|
||||
}
|
||||
|
||||
// A value that can be branched on. JVM has certain branching instructions which can be used
|
||||
// to optimize these.
|
||||
abstract class BooleanValue(codegen: ExpressionCodegen) : PromisedValue(codegen, Type.BOOLEAN_TYPE) {
|
||||
abstract fun jumpIfFalse(target: Label)
|
||||
abstract fun jumpIfTrue(target: Label)
|
||||
|
||||
override fun materialize() {
|
||||
val const0 = Label()
|
||||
val end = Label()
|
||||
jumpIfFalse(const0)
|
||||
codegen.mv.iconst(1)
|
||||
codegen.mv.goTo(end)
|
||||
codegen.mv.mark(const0)
|
||||
codegen.mv.iconst(0)
|
||||
codegen.mv.mark(end)
|
||||
}
|
||||
}
|
||||
|
||||
// Same as materialize(), but return a representation of the result.
|
||||
val PromisedValue.materialized: MaterialValue
|
||||
get() {
|
||||
materialize()
|
||||
return MaterialValue(codegen, type)
|
||||
}
|
||||
|
||||
// Materialize and disregard this value. Materialization is forced because, presumably,
|
||||
// we only wanted the side effects anyway.
|
||||
fun PromisedValue.discard(): MaterialValue {
|
||||
materialize()
|
||||
if (type !== Type.VOID_TYPE)
|
||||
AsmUtil.pop(codegen.mv, type)
|
||||
return MaterialValue(codegen, Type.VOID_TYPE)
|
||||
}
|
||||
|
||||
// On materialization, cast the value to a different type.
|
||||
fun PromisedValue.coerce(target: Type) = when (target) {
|
||||
Type.VOID_TYPE -> discard()
|
||||
type -> this
|
||||
else -> object : PromisedValue(codegen, target) {
|
||||
// TODO remove dependency
|
||||
override fun materialize() = StackValue.coerce(this@coerce.materialized.type, type, codegen.mv)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 { codegen.mv.ifeq(target) }
|
||||
override fun jumpIfTrue(target: Label) = coerced.materialize().also { codegen.mv.ifne(target) }
|
||||
override fun materialize() = coerced.materialize()
|
||||
}
|
||||
}
|
||||
|
||||
class ExpressionCodegen(
|
||||
val irFunction: IrFunction,
|
||||
val frame: IrFrameMap,
|
||||
val mv: InstructionAdapter,
|
||||
val classCodegen: ClassCodegen,
|
||||
val isInlineLambda: Boolean = false
|
||||
) : IrElementVisitor<StackValue, BlockInfo>, BaseExpressionCodegen {
|
||||
) : IrElementVisitor<PromisedValue, BlockInfo>, BaseExpressionCodegen {
|
||||
|
||||
var finallyDepth = 0
|
||||
|
||||
@@ -137,47 +204,16 @@ class ExpressionCodegen(
|
||||
private val CallableDescriptor.asmType: Type
|
||||
get() = typeMapper.mapType(this)
|
||||
|
||||
private val IrExpression.asmType: Type
|
||||
val IrExpression.asmType: Type
|
||||
get() = type.asmType
|
||||
|
||||
// Assume this expression's result has already been materialized on the stack
|
||||
// with the correct type.
|
||||
val IrExpression.onStack: StackValue
|
||||
get() = StackValue.onStack(asmType, type.toKotlinType())
|
||||
val IrExpression.onStack: MaterialValue
|
||||
get() = MaterialValue(this@ExpressionCodegen, asmType)
|
||||
|
||||
// If this value is immaterial, construct an object on the top of the stack. This operation is
|
||||
// idempotent and must always be done before generating other values or emitting raw bytecode.
|
||||
internal fun StackValue.materialize(): StackValue {
|
||||
put(type, kotlinType, mv)
|
||||
return StackValue.onStack(type, kotlinType)
|
||||
}
|
||||
|
||||
// Same as above, but if the type is Unit, do not materialize the actual push of a constant value.
|
||||
// This must be done before fan-in jumping to normalize the stack heights.
|
||||
internal fun StackValue.materializeNonUnit(): StackValue =
|
||||
if (kotlinType != null && kotlinType!!.isUnit())
|
||||
discard().coerce(kotlinType!!)
|
||||
else
|
||||
materialize()
|
||||
|
||||
// Materialize and disregard this value. Materialization is forced because, presumably,
|
||||
// we only wanted the side effects anyway.
|
||||
internal fun StackValue.discard(): StackValue =
|
||||
coerce(Type.VOID_TYPE).materialize()
|
||||
|
||||
// On materialization, cast the value to a different type.
|
||||
private fun StackValue.coerce(targetType: Type): StackValue =
|
||||
CoercionValue(this, targetType, null, null)
|
||||
|
||||
private fun StackValue.coerce(toKotlinType: KotlinType): StackValue {
|
||||
// Avoid the code in `StackValue` that attempts to CHECKCAST java.lang.Objects into Unit.
|
||||
if (type != Type.VOID_TYPE && toKotlinType.isUnit())
|
||||
return coerce(Type.VOID_TYPE).coerce(toKotlinType)
|
||||
return CoercionValue(this, toKotlinType.asmType, toKotlinType, null)
|
||||
}
|
||||
|
||||
internal fun StackValue.coerce(toIrType: IrType): StackValue =
|
||||
coerce(toIrType.toKotlinType())
|
||||
val voidValue: MaterialValue
|
||||
get() = MaterialValue(this@ExpressionCodegen, Type.VOID_TYPE)
|
||||
|
||||
private fun markNewLabel() = Label().apply { mv.visitLabel(this) }
|
||||
|
||||
@@ -196,12 +232,8 @@ class ExpressionCodegen(
|
||||
|
||||
// TODO remove
|
||||
fun gen(expression: IrElement, type: Type, data: BlockInfo): StackValue {
|
||||
return expression.accept(this, data).coerce(type).materialize()
|
||||
}
|
||||
|
||||
// TODO remove
|
||||
fun gen(expression: IrElement, data: BlockInfo): StackValue {
|
||||
return expression.accept(this, data).materialize()
|
||||
expression.accept(this, data).coerce(type).materialize()
|
||||
return StackValue.onStack(type)
|
||||
}
|
||||
|
||||
fun generate() {
|
||||
@@ -257,16 +289,12 @@ class ExpressionCodegen(
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitBlock(expression: IrBlock, data: BlockInfo): StackValue {
|
||||
override fun visitBlock(expression: IrBlock, data: BlockInfo): PromisedValue {
|
||||
if (expression.isTransparentScope)
|
||||
return super.visitBlock(expression, data)
|
||||
val info = data.create()
|
||||
return super.visitBlock(expression, info).apply {
|
||||
if (!expression.isTransparentScope) {
|
||||
writeLocalVariablesInTable(info)
|
||||
} else {
|
||||
info.variables.forEach {
|
||||
data.variables.add(it)
|
||||
}
|
||||
}
|
||||
writeLocalVariablesInTable(info)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,26 +320,25 @@ class ExpressionCodegen(
|
||||
}
|
||||
}
|
||||
|
||||
private fun visitStatementContainer(container: IrStatementContainer, data: BlockInfo): StackValue =
|
||||
container.statements.fold(StackValue.none()) { prev, exp ->
|
||||
private fun visitStatementContainer(container: IrStatementContainer, data: BlockInfo) =
|
||||
container.statements.fold(voidValue as PromisedValue) { prev, exp ->
|
||||
prev.discard()
|
||||
exp.accept(this, data).also { (exp as? IrExpression)?.markEndOfStatementIfNeeded() }
|
||||
}
|
||||
|
||||
override fun visitBlockBody(body: IrBlockBody, data: BlockInfo): StackValue {
|
||||
return visitStatementContainer(body, data).discard()
|
||||
}
|
||||
override fun visitBlockBody(body: IrBlockBody, data: BlockInfo) =
|
||||
visitStatementContainer(body, data).discard()
|
||||
|
||||
override fun visitContainerExpression(expression: IrContainerExpression, data: BlockInfo): StackValue {
|
||||
return visitStatementContainer(expression, data).coerce(expression.type)
|
||||
}
|
||||
override fun visitContainerExpression(expression: IrContainerExpression, data: BlockInfo) =
|
||||
visitStatementContainer(expression, data).coerce(expression.asmType)
|
||||
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression, data: BlockInfo): StackValue {
|
||||
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, data: BlockInfo): PromisedValue {
|
||||
expression.markLineNumber(startOffset = true)
|
||||
mv.load(0, OBJECT_TYPE) // HACK
|
||||
return generateCall(expression, null, data)
|
||||
}
|
||||
|
||||
override fun visitCall(expression: IrCall, data: BlockInfo): StackValue {
|
||||
override fun visitCall(expression: IrCall, data: BlockInfo): PromisedValue {
|
||||
expression.markLineNumber(startOffset = true)
|
||||
if (expression.descriptor is ConstructorDescriptor) {
|
||||
return generateNewCall(expression, data)
|
||||
@@ -319,7 +346,7 @@ class ExpressionCodegen(
|
||||
return generateCall(expression, expression.superQualifier, data)
|
||||
}
|
||||
|
||||
private fun generateNewCall(expression: IrCall, data: BlockInfo): StackValue {
|
||||
private fun generateNewCall(expression: IrCall, data: BlockInfo): PromisedValue {
|
||||
val type = expression.asmType
|
||||
if (type.sort == Type.ARRAY) {
|
||||
return generateNewArray(expression, data)
|
||||
@@ -331,7 +358,7 @@ class ExpressionCodegen(
|
||||
return expression.onStack
|
||||
}
|
||||
|
||||
private fun generateNewArray(expression: IrCall, data: BlockInfo): StackValue {
|
||||
private fun generateNewArray(expression: IrCall, data: BlockInfo): PromisedValue {
|
||||
val args = expression.descriptor.valueParameters
|
||||
assert(args.size == 1 || args.size == 2) { "Unknown constructor called: " + args.size + " arguments" }
|
||||
|
||||
@@ -345,15 +372,20 @@ class ExpressionCodegen(
|
||||
return generateCall(expression, expression.superQualifier, data)
|
||||
}
|
||||
|
||||
private fun generateCall(expression: IrFunctionAccessExpression, superQualifier: ClassDescriptor?, data: BlockInfo): StackValue {
|
||||
private fun generateCall(expression: IrFunctionAccessExpression, superQualifier: ClassDescriptor?, data: BlockInfo): PromisedValue {
|
||||
classCodegen.context.irIntrinsics.getIntrinsic(expression.descriptor.original as CallableMemberDescriptor)
|
||||
?.invoke(expression, this, data)?.let { return it }
|
||||
?.invoke(expression, this, data)?.let { return it.coerce(expression.asmType) }
|
||||
val isSuperCall = superQualifier != null
|
||||
val callable = resolveToCallable(expression, isSuperCall)
|
||||
return generateCall(expression, callable, data, isSuperCall)
|
||||
}
|
||||
|
||||
fun generateCall(expression: IrMemberAccessExpression, callable: Callable, data: BlockInfo, isSuperCall: Boolean = false): StackValue {
|
||||
fun generateCall(
|
||||
expression: IrMemberAccessExpression,
|
||||
callable: Callable,
|
||||
data: BlockInfo,
|
||||
isSuperCall: Boolean = false
|
||||
): PromisedValue {
|
||||
val callGenerator = getOrCreateCallGenerator(expression, expression.descriptor, data)
|
||||
|
||||
val receiver = expression.dispatchReceiver
|
||||
@@ -425,64 +457,66 @@ class ExpressionCodegen(
|
||||
if (returnType != null && returnType.isNothing()) {
|
||||
mv.aconst(null)
|
||||
mv.athrow()
|
||||
return expression.onStack
|
||||
return voidValue
|
||||
} else if (expression.descriptor is ConstructorDescriptor) {
|
||||
return StackValue.none()
|
||||
return voidValue
|
||||
} else if (expression.type.isUnit()) {
|
||||
// NewInference allows casting `() -> T` to `() -> Unit`. A CHECKCAST here will fail.
|
||||
return MaterialValue(this, callable.returnType).discard().coerce(expression.asmType)
|
||||
}
|
||||
|
||||
return StackValue.onStack(callable.returnType, returnType).coerce(expression.type)
|
||||
return MaterialValue(this, callable.returnType).coerce(expression.asmType)
|
||||
}
|
||||
|
||||
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, data: BlockInfo): StackValue {
|
||||
//HACK
|
||||
StackValue.local(0, OBJECT_TYPE).put(OBJECT_TYPE, mv)
|
||||
return super.visitDelegatingConstructorCall(expression, data)
|
||||
}
|
||||
|
||||
override fun visitVariable(declaration: IrVariable, data: BlockInfo): StackValue {
|
||||
override fun visitVariable(declaration: IrVariable, data: BlockInfo): PromisedValue {
|
||||
val varType = typeMapper.mapType(declaration.descriptor)
|
||||
val index = frame.enter(declaration.symbol, varType)
|
||||
|
||||
declaration.markLineNumber(startOffset = true)
|
||||
|
||||
declaration.initializer?.let {
|
||||
StackValue.local(index, varType).store(it.accept(this, data).coerce(varType), mv)
|
||||
it.accept(this, data).coerce(varType).materialize()
|
||||
mv.store(index, varType)
|
||||
it.markLineNumber(startOffset = true)
|
||||
}
|
||||
|
||||
data.variables.add(VariableInfo(declaration, index, varType, markNewLabel()))
|
||||
return StackValue.none()
|
||||
return voidValue
|
||||
}
|
||||
|
||||
override fun visitGetValue(expression: IrGetValue, data: BlockInfo): StackValue {
|
||||
override fun visitGetValue(expression: IrGetValue, data: BlockInfo): PromisedValue {
|
||||
// Do not generate line number information for loads from compiler-generated
|
||||
// temporary variables. They do not correspond to variable loads in user code.
|
||||
if (expression.symbol.owner.origin != IrDeclarationOrigin.IR_TEMPORARY_VARIABLE)
|
||||
expression.markLineNumber(startOffset = true)
|
||||
return StackValue.local(findLocalIndex(expression.symbol), expression.asmType).coerce(expression.type)
|
||||
mv.load(findLocalIndex(expression.symbol), expression.asmType)
|
||||
return expression.onStack
|
||||
}
|
||||
|
||||
private fun generateFieldValue(expression: IrFieldAccessExpression, data: BlockInfo, materialize: Boolean): StackValue {
|
||||
val receiverValue = expression.receiver?.accept(this, data) ?: StackValue.none()
|
||||
val materialReceiver = if (materialize) receiverValue.materialize() else receiverValue
|
||||
val propertyDescriptor = expression.descriptor
|
||||
|
||||
val realDescriptor = DescriptorUtils.unwrapFakeOverride(propertyDescriptor)
|
||||
val fieldKotlinType = realDescriptor.original.type
|
||||
val fieldType = typeMapper.mapType(fieldKotlinType)
|
||||
val ownerType = typeMapper.mapImplementationOwner(propertyDescriptor)
|
||||
val fieldName = propertyDescriptor.name.asString()
|
||||
val isStatic = expression.receiver == null // TODO
|
||||
|
||||
return StackValue.field(fieldType, fieldKotlinType, ownerType, fieldName, isStatic, materialReceiver, realDescriptor)
|
||||
}
|
||||
|
||||
override fun visitGetField(expression: IrGetField, data: BlockInfo): StackValue {
|
||||
override fun visitFieldAccess(expression: IrFieldAccessExpression, data: BlockInfo): PromisedValue {
|
||||
val realDescriptor = DescriptorUtils.unwrapFakeOverride(expression.descriptor)
|
||||
val fieldType = typeMapper.mapType(realDescriptor.original.type)
|
||||
val ownerType = typeMapper.mapImplementationOwner(expression.descriptor).internalName
|
||||
val fieldName = expression.descriptor.name.asString()
|
||||
val isStatic = expression.receiver == null
|
||||
expression.markLineNumber(startOffset = true)
|
||||
return generateFieldValue(expression, data, false).coerce(expression.type)
|
||||
expression.receiver?.accept(this, data)?.materialize()
|
||||
return if (expression is IrSetField) {
|
||||
expression.value.accept(this, data).coerce(fieldType).materialize()
|
||||
when {
|
||||
isStatic -> mv.putstatic(ownerType, fieldName, fieldType.descriptor)
|
||||
else -> mv.putfield(ownerType, fieldName, fieldType.descriptor)
|
||||
}
|
||||
voidValue.coerce(expression.asmType)
|
||||
} else {
|
||||
when {
|
||||
isStatic -> mv.getstatic(ownerType, fieldName, fieldType.descriptor)
|
||||
else -> mv.getfield(ownerType, fieldName, fieldType.descriptor)
|
||||
}
|
||||
MaterialValue(this, fieldType).coerce(expression.asmType)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitSetField(expression: IrSetField, data: BlockInfo): StackValue {
|
||||
override fun visitSetField(expression: IrSetField, data: BlockInfo): PromisedValue {
|
||||
val expressionValue = expression.value
|
||||
// Do not add redundant field initializers that initialize to default values.
|
||||
// "expression.origin == null" means that the field is initialized when it is declared,
|
||||
@@ -490,11 +524,7 @@ class ExpressionCodegen(
|
||||
val skip = irFunction is IrConstructor && irFunction.isPrimary &&
|
||||
expression.origin == null && expressionValue is IrConst<*> &&
|
||||
isDefaultValueForType(expression.symbol.owner.type.asmType, expressionValue.value)
|
||||
if (!skip) {
|
||||
expression.markLineNumber(startOffset = true)
|
||||
generateFieldValue(expression, data, true).store(expressionValue.accept(this, data), mv)
|
||||
}
|
||||
return StackValue.none().coerce(expression.type)
|
||||
return if (skip) voidValue.coerce(expression.asmType) else super.visitSetField(expression, data)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -524,37 +554,48 @@ class ExpressionCodegen(
|
||||
throw AssertionError("Non-mapped local declaration: $dump\n in ${irFunction.dump()}")
|
||||
}
|
||||
|
||||
override fun visitSetVariable(expression: IrSetVariable, data: BlockInfo): StackValue {
|
||||
override fun visitSetVariable(expression: IrSetVariable, data: BlockInfo): PromisedValue {
|
||||
expression.markLineNumber(startOffset = true)
|
||||
expression.value.markLineNumber(startOffset = true)
|
||||
val value = expression.value.accept(this, data)
|
||||
StackValue.local(findLocalIndex(expression.symbol), expression.descriptor.asmType).store(value, mv)
|
||||
return StackValue.none().coerce(expression.type)
|
||||
expression.value.accept(this, data).coerce(expression.descriptor.asmType).materialize()
|
||||
mv.store(findLocalIndex(expression.symbol), expression.descriptor.asmType)
|
||||
return voidValue.coerce(expression.asmType)
|
||||
}
|
||||
|
||||
override fun <T> visitConst(expression: IrConst<T>, data: BlockInfo): StackValue {
|
||||
override fun <T> visitConst(expression: IrConst<T>, data: BlockInfo): PromisedValue {
|
||||
expression.markLineNumber(startOffset = true)
|
||||
return StackValue.constant(expression.value, expression.asmType).coerce(expression.type)
|
||||
when (val value = expression.value) {
|
||||
is Boolean -> return object : BooleanValue(this) {
|
||||
override fun jumpIfFalse(target: Label) = if (value) Unit else codegen.mv.goTo(target)
|
||||
override fun jumpIfTrue(target: Label) = if (value) codegen.mv.goTo(target) else Unit
|
||||
override fun materialize() = codegen.mv.iconst(if (value) 1 else 0)
|
||||
}
|
||||
is Char -> mv.iconst(value.toInt())
|
||||
is Long -> mv.lconst(value)
|
||||
is Float -> mv.fconst(value)
|
||||
is Double -> mv.dconst(value)
|
||||
is Number -> mv.iconst(value.toInt())
|
||||
else -> mv.aconst(value)
|
||||
}
|
||||
return expression.onStack
|
||||
}
|
||||
|
||||
override fun visitExpressionBody(body: IrExpressionBody, data: BlockInfo): StackValue {
|
||||
return body.expression.accept(this, data)
|
||||
}
|
||||
override fun visitExpressionBody(body: IrExpressionBody, data: BlockInfo) =
|
||||
body.expression.accept(this, data)
|
||||
|
||||
override fun visitElement(element: IrElement, data: BlockInfo): StackValue {
|
||||
override fun visitElement(element: IrElement, data: BlockInfo) =
|
||||
throw AssertionError(
|
||||
"Unexpected IR element found during code generation. Either code generation for it " +
|
||||
"is not implemented, or it should have been lowered: ${element.render()}"
|
||||
)
|
||||
}
|
||||
|
||||
// TODO maybe remove?
|
||||
override fun visitClass(declaration: IrClass, data: BlockInfo): StackValue {
|
||||
override fun visitClass(declaration: IrClass, data: BlockInfo): PromisedValue {
|
||||
classCodegen.generateLocalClass(declaration)
|
||||
return StackValue.none()
|
||||
return voidValue
|
||||
}
|
||||
|
||||
override fun visitVararg(expression: IrVararg, data: BlockInfo): StackValue {
|
||||
override fun visitVararg(expression: IrVararg, data: BlockInfo): PromisedValue {
|
||||
expression.markLineNumber(startOffset = true)
|
||||
val outType = expression.type
|
||||
val type = expression.asmType
|
||||
@@ -624,13 +665,11 @@ class ExpressionCodegen(
|
||||
} else {
|
||||
mv.iconst(size)
|
||||
newArrayInstruction(expression.type.toKotlinType())
|
||||
val elementKotlinType = classCodegen.context.builtIns.getArrayElementType(outType.toKotlinType())
|
||||
for ((i, element) in expression.elements.withIndex()) {
|
||||
mv.dup()
|
||||
val array = StackValue.onStack(elementType, outType.toKotlinType())
|
||||
val index = StackValue.constant(i).materialize()
|
||||
val value = element.accept(this, data).coerce(elementType).materialize()
|
||||
StackValue.arrayElement(elementType, elementKotlinType, array, index).store(value, mv)
|
||||
mv.iconst(i)
|
||||
element.accept(this, data).coerce(elementType).materialize()
|
||||
mv.astore(elementType)
|
||||
}
|
||||
}
|
||||
return expression.onStack
|
||||
@@ -652,7 +691,7 @@ class ExpressionCodegen(
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitReturn(expression: IrReturn, data: BlockInfo): StackValue {
|
||||
override fun visitReturn(expression: IrReturn, data: BlockInfo): PromisedValue {
|
||||
val owner = expression.returnTargetSymbol.owner
|
||||
//TODO: should be owner != irFunction
|
||||
val isNonLocalReturn = state.typeMapper.mapFunctionName(
|
||||
@@ -665,7 +704,7 @@ class ExpressionCodegen(
|
||||
mv, "java/lang/UnsupportedOperationException",
|
||||
"Non-local returns are not allowed with inlining disabled"
|
||||
)
|
||||
return StackValue.none()
|
||||
return voidValue
|
||||
}
|
||||
|
||||
val returnType = typeMapper.mapReturnType(owner.descriptor)
|
||||
@@ -679,10 +718,10 @@ class ExpressionCodegen(
|
||||
mv.areturn(returnType)
|
||||
mv.mark(afterReturnLabel)
|
||||
mv.nop()/*TODO check RESTORE_STACK_IN_TRY_CATCH processor*/
|
||||
return StackValue.none()
|
||||
return voidValue
|
||||
}
|
||||
|
||||
override fun visitWhen(expression: IrWhen, data: BlockInfo): StackValue {
|
||||
override fun visitWhen(expression: IrWhen, data: BlockInfo): PromisedValue {
|
||||
expression.markLineNumber(startOffset = true)
|
||||
SwitchGenerator(expression, data, this).generate()?.let { return it }
|
||||
|
||||
@@ -699,9 +738,9 @@ class ExpressionCodegen(
|
||||
if (branch.condition.isFalseConst())
|
||||
continue // The branch body is dead code.
|
||||
} else {
|
||||
BranchedValue.condJump(branch.condition.accept(this, data), elseLabel, true, mv)
|
||||
branch.condition.accept(this, data).coerceToBoolean().jumpIfFalse(elseLabel)
|
||||
}
|
||||
val result = branch.result.accept(this, data).coerce(expression.type).materializeNonUnit()
|
||||
val result = branch.result.accept(this, data).coerce(expression.asmType).materialized
|
||||
if (branch.condition.isTrueConst()) {
|
||||
// The rest of the expression is dead code.
|
||||
mv.mark(endLabel)
|
||||
@@ -712,23 +751,23 @@ 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 = StackValue.none().coerce(expression.type).materializeNonUnit()
|
||||
val result = voidValue.coerce(expression.asmType).materialized
|
||||
mv.mark(endLabel)
|
||||
return result
|
||||
}
|
||||
|
||||
override fun visitTypeOperator(expression: IrTypeOperatorCall, data: BlockInfo): StackValue {
|
||||
override fun visitTypeOperator(expression: IrTypeOperatorCall, data: BlockInfo): PromisedValue {
|
||||
val kotlinType = expression.typeOperand.toKotlinType()
|
||||
val asmType = kotlinType.asmType
|
||||
return when (expression.operator) {
|
||||
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> {
|
||||
val result = expression.argument.accept(this, data)
|
||||
expression.argument.accept(this, data).discard()
|
||||
expression.argument.markEndOfStatementIfNeeded()
|
||||
result.coerce(expression.type)
|
||||
voidValue.coerce(expression.asmType)
|
||||
}
|
||||
|
||||
IrTypeOperator.IMPLICIT_CAST -> {
|
||||
expression.argument.accept(this, data).coerce(expression.type)
|
||||
IrTypeOperator.IMPLICIT_CAST, IrTypeOperator.IMPLICIT_INTEGER_COERCION -> {
|
||||
expression.argument.accept(this, data).coerce(expression.asmType)
|
||||
}
|
||||
|
||||
IrTypeOperator.CAST, IrTypeOperator.SAFE_CAST -> {
|
||||
@@ -746,7 +785,7 @@ class ExpressionCodegen(
|
||||
state.languageVersionSettings.isReleaseCoroutines()
|
||||
)
|
||||
}
|
||||
StackValue.onStack(boxedType).coerce(expression.type)
|
||||
MaterialValue(this, boxedType).coerce(expression.asmType)
|
||||
}
|
||||
|
||||
IrTypeOperator.INSTANCEOF, IrTypeOperator.NOT_INSTANCEOF -> {
|
||||
@@ -758,14 +797,15 @@ class ExpressionCodegen(
|
||||
} else {
|
||||
generateIsCheck(mv, kotlinType, type, state.languageVersionSettings.isReleaseCoroutines())
|
||||
}
|
||||
if (IrTypeOperator.NOT_INSTANCEOF == expression.operator) {
|
||||
return StackValue.not(expression.onStack)
|
||||
}
|
||||
expression.onStack
|
||||
// TODO remove this type operator, generate an intrinsic call around INSTANCEOF instead
|
||||
if (IrTypeOperator.NOT_INSTANCEOF == expression.operator)
|
||||
Not.BooleanNegation(expression.onStack.coerceToBoolean())
|
||||
else
|
||||
expression.onStack
|
||||
}
|
||||
|
||||
IrTypeOperator.IMPLICIT_NOTNULL -> {
|
||||
val value = expression.argument.accept(this, data).materialize()
|
||||
val value = expression.argument.accept(this, data).materialized
|
||||
mv.dup()
|
||||
mv.visitLdcInsn("TODO provide message for IMPLICIT_NOTNULL") /*TODO*/
|
||||
mv.invokestatic(
|
||||
@@ -773,12 +813,7 @@ class ExpressionCodegen(
|
||||
"(Ljava/lang/Object;Ljava/lang/String;)V", false
|
||||
)
|
||||
// Unbox primitives.
|
||||
value.coerce(expression.type)
|
||||
}
|
||||
|
||||
IrTypeOperator.IMPLICIT_INTEGER_COERCION -> {
|
||||
// Force-materialize the intermediate int to ensure that it is in fact an int.
|
||||
expression.argument.accept(this, data).coerce(Type.INT_TYPE).materialize().coerce(expression.type)
|
||||
value.coerce(expression.asmType)
|
||||
}
|
||||
|
||||
else -> throw AssertionError("type operator ${expression.operator} should have been lowered")
|
||||
@@ -798,64 +833,57 @@ class ExpressionCodegen(
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitStringConcatenation(expression: IrStringConcatenation, data: BlockInfo): StackValue {
|
||||
override fun visitStringConcatenation(expression: IrStringConcatenation, data: BlockInfo): PromisedValue {
|
||||
expression.markLineNumber(startOffset = true)
|
||||
return when (expression.arguments.size) {
|
||||
0 -> StackValue.constant("", expression.asmType)
|
||||
when (expression.arguments.size) {
|
||||
0 -> mv.aconst("")
|
||||
1 -> {
|
||||
// Convert single arg to string.
|
||||
val argStackValue = expression.arguments[0].accept(this, data).materialize()
|
||||
AsmUtil.genToString(argStackValue, argStackValue.type, argStackValue.kotlinType, typeMapper).put(expression.asmType, mv)
|
||||
expression.onStack
|
||||
val type = expression.arguments[0].accept(this, data).materialized.type
|
||||
AsmUtil.genToString(StackValue.onStack(type), type, null, typeMapper).put(expression.asmType, mv)
|
||||
}
|
||||
else -> {
|
||||
// Use StringBuilder to concatenate.
|
||||
AsmUtil.genStringBuilderConstructor(mv)
|
||||
expression.arguments.forEach {
|
||||
val stackValue = it.accept(this, data).materialize()
|
||||
AsmUtil.genInvokeAppendMethod(mv, stackValue.type, stackValue.kotlinType)
|
||||
AsmUtil.genInvokeAppendMethod(mv, it.accept(this, data).materialized.type, null)
|
||||
}
|
||||
mv.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false)
|
||||
expression.onStack
|
||||
}
|
||||
}
|
||||
return expression.onStack
|
||||
}
|
||||
|
||||
// Avoid true condition generation for tailrec
|
||||
// ASM and the Java verifier assume that L1 is reachable which causes several verifications to fail.
|
||||
// To avoid them, trivial jump elimination is required.
|
||||
// L0
|
||||
// ICONST_1 //could be eliminated
|
||||
// IFEQ L1 //could be eliminated
|
||||
// .... // no jumps
|
||||
// GOTO L0
|
||||
// L1
|
||||
//TODO: write elimination lower
|
||||
private fun generateLoopJump(condition: IrExpression, data: BlockInfo, label: Label, jumpIfFalse: Boolean) {
|
||||
condition.markLineNumber(true)
|
||||
if (condition is IrConst<*> && condition.value == true) {
|
||||
if (jumpIfFalse) {
|
||||
mv.fakeAlwaysFalseIfeq(label)
|
||||
} else {
|
||||
mv.fakeAlwaysTrueIfeq(label)
|
||||
}
|
||||
} else {
|
||||
BranchedValue.condJump(condition.accept(this, data), label, jumpIfFalse, mv)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitWhileLoop(loop: IrWhileLoop, data: BlockInfo): StackValue {
|
||||
override fun visitWhileLoop(loop: IrWhileLoop, data: BlockInfo): PromisedValue {
|
||||
val continueLabel = markNewLabel()
|
||||
val endLabel = Label()
|
||||
generateLoopJump(loop.condition, data, endLabel, true)
|
||||
|
||||
// Mark stack depth for break
|
||||
mv.fakeAlwaysFalseIfeq(endLabel)
|
||||
loop.condition.markLineNumber(true)
|
||||
loop.condition.accept(this, data).coerceToBoolean().jumpIfFalse(endLabel)
|
||||
data.withBlock(LoopInfo(loop, continueLabel, endLabel)) {
|
||||
loop.body?.accept(this, data)?.discard()
|
||||
}
|
||||
mv.goTo(continueLabel)
|
||||
mv.mark(endLabel)
|
||||
return voidValue
|
||||
}
|
||||
|
||||
return StackValue.none()
|
||||
override fun visitDoWhileLoop(loop: IrDoWhileLoop, data: BlockInfo): PromisedValue {
|
||||
val entry = markNewLabel()
|
||||
val endLabel = Label()
|
||||
val continueLabel = Label()
|
||||
// Mark stack depth for break/continue
|
||||
mv.fakeAlwaysFalseIfeq(continueLabel)
|
||||
mv.fakeAlwaysFalseIfeq(endLabel)
|
||||
data.withBlock(LoopInfo(loop, continueLabel, endLabel)) {
|
||||
loop.body?.accept(this, data)?.discard()
|
||||
}
|
||||
mv.visitLabel(continueLabel)
|
||||
loop.condition.markLineNumber(true)
|
||||
loop.condition.accept(this, data).coerceToBoolean().jumpIfTrue(entry)
|
||||
mv.mark(endLabel)
|
||||
return voidValue
|
||||
}
|
||||
|
||||
private fun unwindBlockStack(endLabel: Label, data: BlockInfo, loop: IrLoop? = null): LoopInfo? {
|
||||
@@ -868,36 +896,17 @@ class ExpressionCodegen(
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitBreakContinue(jump: IrBreakContinue, data: BlockInfo): StackValue {
|
||||
override fun visitBreakContinue(jump: IrBreakContinue, data: BlockInfo): PromisedValue {
|
||||
jump.markLineNumber(startOffset = true)
|
||||
val endLabel = Label()
|
||||
val stackElement = unwindBlockStack(endLabel, data, jump.loop)
|
||||
?: throw AssertionError("Target label for break/continue not found")
|
||||
mv.fixStackAndJump(if (jump is IrBreak) stackElement.breakLabel else stackElement.continueLabel)
|
||||
mv.mark(endLabel)
|
||||
return StackValue.none()
|
||||
return voidValue
|
||||
}
|
||||
|
||||
override fun visitDoWhileLoop(loop: IrDoWhileLoop, data: BlockInfo): StackValue {
|
||||
val entry = markNewLabel()
|
||||
val endLabel = Label()
|
||||
val continueLabel = Label()
|
||||
|
||||
mv.fakeAlwaysFalseIfeq(continueLabel)
|
||||
mv.fakeAlwaysFalseIfeq(endLabel)
|
||||
|
||||
data.withBlock(LoopInfo(loop, continueLabel, endLabel)) {
|
||||
loop.body?.accept(this, data)?.discard()
|
||||
}
|
||||
|
||||
mv.visitLabel(continueLabel)
|
||||
generateLoopJump(loop.condition, data, entry, false)
|
||||
mv.mark(endLabel)
|
||||
|
||||
return StackValue.none()
|
||||
}
|
||||
|
||||
override fun visitTry(aTry: IrTry, data: BlockInfo): StackValue {
|
||||
override fun visitTry(aTry: IrTry, data: BlockInfo): PromisedValue {
|
||||
aTry.markLineNumber(startOffset = true)
|
||||
return if (aTry.finallyExpression != null)
|
||||
data.withBlock(TryInfo(aTry.finallyExpression!!)) { visitTryWithInfo(aTry, data, it) }
|
||||
@@ -905,15 +914,17 @@ class ExpressionCodegen(
|
||||
visitTryWithInfo(aTry, data, null)
|
||||
}
|
||||
|
||||
private fun visitTryWithInfo(aTry: IrTry, data: BlockInfo, tryInfo: TryInfo?): StackValue {
|
||||
private fun visitTryWithInfo(aTry: IrTry, data: BlockInfo, tryInfo: TryInfo?): PromisedValue {
|
||||
val tryBlockStart = markNewLabel()
|
||||
mv.nop()
|
||||
val tryAsmType = aTry.asmType
|
||||
val tryResult = aTry.tryResult.accept(this, data)
|
||||
val isExpression = true //TODO: more wise check is required
|
||||
var savedValue: StackValue.Local? = null
|
||||
var savedValue: Int? = null
|
||||
if (isExpression) {
|
||||
savedValue = StackValue.local(frame.enterTemp(aTry.asmType), aTry.asmType)
|
||||
savedValue.store(tryResult.coerce(aTry.type), mv)
|
||||
tryResult.coerce(tryAsmType).materialize()
|
||||
savedValue = frame.enterTemp(tryAsmType)
|
||||
mv.store(savedValue, tryAsmType)
|
||||
} else {
|
||||
tryResult.discard()
|
||||
}
|
||||
@@ -939,7 +950,8 @@ class ExpressionCodegen(
|
||||
catchBody.markLineNumber(true)
|
||||
val catchResult = catchBody.accept(this, data)
|
||||
if (savedValue != null) {
|
||||
savedValue.store(catchResult.coerce(aTry.type), mv)
|
||||
catchResult.coerce(tryAsmType).materialize()
|
||||
mv.store(savedValue, tryAsmType)
|
||||
} else {
|
||||
catchResult.discard()
|
||||
}
|
||||
@@ -986,11 +998,11 @@ class ExpressionCodegen(
|
||||
|
||||
// TODO: generate a common `finally` for try & catch blocks here? Right now this breaks the inliner.
|
||||
if (savedValue != null) {
|
||||
savedValue.put(mv)
|
||||
frame.leaveTemp(aTry.asmType)
|
||||
return StackValue.onStack(aTry.asmType)
|
||||
mv.load(savedValue, tryAsmType)
|
||||
frame.leaveTemp(tryAsmType)
|
||||
return aTry.onStack
|
||||
}
|
||||
return StackValue.none()
|
||||
return voidValue
|
||||
}
|
||||
|
||||
private fun genTryCatchCover(catchStart: Label, tryStart: Label, tryEnd: Label, tryGaps: List<Pair<Label, Label>>, type: String?) {
|
||||
@@ -1007,7 +1019,7 @@ class ExpressionCodegen(
|
||||
if (isFinallyMarkerRequired()) {
|
||||
generateFinallyMarker(mv, finallyDepth, true)
|
||||
}
|
||||
gen(tryInfo.onExit, data).discard()
|
||||
tryInfo.onExit.accept(this, data).discard()
|
||||
if (isFinallyMarkerRequired()) {
|
||||
generateFinallyMarker(mv, finallyDepth, false)
|
||||
}
|
||||
@@ -1023,10 +1035,9 @@ class ExpressionCodegen(
|
||||
if (data.hasFinallyBlocks()) {
|
||||
if (Type.VOID_TYPE != returnType) {
|
||||
val returnValIndex = frame.enterTemp(returnType)
|
||||
val localForReturnValue = StackValue.local(returnValIndex, returnType)
|
||||
localForReturnValue.store(StackValue.onStack(returnType), mv)
|
||||
mv.store(returnValIndex, returnType)
|
||||
unwindBlockStack(afterReturnLabel, data, null)
|
||||
localForReturnValue.put(returnType, mv)
|
||||
mv.load(returnValIndex, returnType)
|
||||
frame.leaveTemp(returnType)
|
||||
} else {
|
||||
unwindBlockStack(afterReturnLabel, data, null)
|
||||
@@ -1034,36 +1045,32 @@ class ExpressionCodegen(
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitThrow(expression: IrThrow, data: BlockInfo): StackValue {
|
||||
override fun visitThrow(expression: IrThrow, data: BlockInfo): PromisedValue {
|
||||
expression.markLineNumber(startOffset = true)
|
||||
expression.value.accept(this, data).coerce(AsmTypes.JAVA_THROWABLE_TYPE).materialize()
|
||||
mv.athrow()
|
||||
return StackValue.none()
|
||||
return voidValue
|
||||
}
|
||||
|
||||
override fun visitGetClass(expression: IrGetClass, data: BlockInfo): StackValue {
|
||||
override fun visitGetClass(expression: IrGetClass, data: BlockInfo) =
|
||||
generateClassLiteralReference(expression, true, data)
|
||||
return expression.onStack
|
||||
}
|
||||
|
||||
override fun visitClassReference(expression: IrClassReference, data: BlockInfo): StackValue {
|
||||
override fun visitClassReference(expression: IrClassReference, data: BlockInfo) =
|
||||
generateClassLiteralReference(expression, true, data)
|
||||
return expression.onStack
|
||||
}
|
||||
|
||||
fun generateClassLiteralReference(
|
||||
classReference: IrExpression,
|
||||
wrapIntoKClass: Boolean,
|
||||
data: BlockInfo
|
||||
) {
|
||||
if (classReference !is IrClassReference /* && DescriptorUtils.isObjectQualifier(classReference.descriptor)*/) {
|
||||
assert(classReference is IrGetClass)
|
||||
JavaClassProperty.generateImpl(mv, (classReference as IrGetClass).argument.accept(this, data))
|
||||
} else {
|
||||
): PromisedValue {
|
||||
if (classReference is IrGetClass) {
|
||||
// TODO transform one sort of access into the other?
|
||||
JavaClassProperty.invokeWith(classReference.argument.accept(this, data))
|
||||
} else if (classReference is IrClassReference) {
|
||||
val classType = classReference.classType
|
||||
if (classType is CrIrType) {
|
||||
putJavaLangClassInstance(mv, classType.type, null, typeMapper)
|
||||
return
|
||||
return classReference.onStack
|
||||
} else {
|
||||
val kotlinType = classType.toKotlinType()
|
||||
if (TypeUtils.isTypeParameter(kotlinType)) {
|
||||
@@ -1073,12 +1080,14 @@ class ExpressionCodegen(
|
||||
|
||||
putJavaLangClassInstance(mv, typeMapper.mapType(kotlinType), kotlinType, typeMapper)
|
||||
}
|
||||
} else {
|
||||
throw AssertionError("not an IrGetClass or IrClassReference: ${classReference.dump()}")
|
||||
}
|
||||
|
||||
if (wrapIntoKClass) {
|
||||
wrapJavaClassIntoKClass(mv)
|
||||
}
|
||||
|
||||
return classReference.onStack
|
||||
}
|
||||
|
||||
private fun resolveToCallable(irCall: IrMemberAccessExpression, isSuper: Boolean): Callable {
|
||||
|
||||
+6
-8
@@ -5,9 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.`when`.SwitchCodegen.Companion.preferLookupOverSwitch
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.dump
|
||||
@@ -22,7 +20,7 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
|
||||
data class ValueToLabel(val value: Any?, val label: Label)
|
||||
|
||||
// @return null if the IrWhen cannot be emitted as lookupswitch or tableswitch.
|
||||
fun generate(): StackValue? {
|
||||
fun generate(): PromisedValue? {
|
||||
val expressionToLabels = ArrayList<ExpressionToLabel>()
|
||||
var elseExpression: IrExpression? = null
|
||||
val callToLabels = ArrayList<CallToLabel>()
|
||||
@@ -187,7 +185,7 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
|
||||
|
||||
open fun shouldOptimize() = false
|
||||
|
||||
open fun genOptimizedIfEnoughCases(): StackValue? {
|
||||
open fun genOptimizedIfEnoughCases(): PromisedValue? {
|
||||
if (!shouldOptimize())
|
||||
return null
|
||||
|
||||
@@ -219,17 +217,17 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
|
||||
}
|
||||
}
|
||||
|
||||
protected fun genBranchTargets(): StackValue {
|
||||
protected fun genBranchTargets(): PromisedValue {
|
||||
with(codegen) {
|
||||
val endLabel = Label()
|
||||
for ((thenExpression, label) in expressionToLabels) {
|
||||
mv.visitLabel(label)
|
||||
thenExpression.accept(codegen, data).coerce(expression.type).materializeNonUnit()
|
||||
thenExpression.accept(codegen, data).coerce(expression.asmType).materialized
|
||||
mv.goTo(endLabel)
|
||||
}
|
||||
mv.visitLabel(defaultLabel)
|
||||
val stackValue = elseExpression?.accept(codegen, data) ?: StackValue.none()
|
||||
val result = stackValue.coerce(expression.type).materializeNonUnit()
|
||||
val stackValue = elseExpression?.accept(codegen, data) ?: voidValue
|
||||
val result = stackValue.coerce(expression.asmType).materialized
|
||||
mv.mark(endLabel)
|
||||
return result
|
||||
}
|
||||
|
||||
+1
-5
@@ -35,11 +35,7 @@ object ArrayConstructor : IntrinsicMethod() {
|
||||
return object : IrIntrinsicFunction(expression, signature, context, expression.argTypes(context)) {
|
||||
|
||||
override fun invoke(v: InstructionAdapter, codegen: ExpressionCodegen, data: BlockInfo): StackValue {
|
||||
// TODO fix this hack
|
||||
val result = codegen.generateCall(expression, this, data)
|
||||
with(codegen) {
|
||||
result.materialize()
|
||||
}
|
||||
codegen.generateCall(expression, this, data).materialize()
|
||||
return StackValue.onStack(Type.getObjectType("[" + AsmTypes.OBJECT_TYPE.internalName))
|
||||
}
|
||||
}
|
||||
|
||||
+28
-10
@@ -16,16 +16,19 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import com.intellij.psi.tree.IElementType
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.comparisonOperandType
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.BranchedValue
|
||||
import org.jetbrains.kotlin.codegen.NumberCompare
|
||||
import org.jetbrains.kotlin.codegen.ObjectCompare
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||
import org.jetbrains.kotlin.lexer.KtSingleValueToken
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
import java.lang.UnsupportedOperationException
|
||||
@@ -62,19 +65,34 @@ class CompareTo : IntrinsicMethod() {
|
||||
}
|
||||
}
|
||||
|
||||
class BooleanComparison(val op: IElementType, val a: MaterialValue, val b: MaterialValue) : BooleanValue(a.codegen) {
|
||||
override fun jumpIfFalse(target: Label) {
|
||||
// TODO 1. get rid of the dependency; 2. take `b.type` into account.
|
||||
val opcode = if (a.type.sort == Type.OBJECT)
|
||||
ObjectCompare.getObjectCompareOpcode(op)
|
||||
else
|
||||
NumberCompare.patchOpcode(NumberCompare.getNumberCompareOpcode(op), codegen.mv, op, a.type)
|
||||
codegen.mv.visitJumpInsn(opcode, target)
|
||||
}
|
||||
|
||||
override fun jumpIfTrue(target: Label) {
|
||||
val opcode = if (a.type.sort == Type.OBJECT)
|
||||
BranchedValue.negatedOperations[ObjectCompare.getObjectCompareOpcode(op)]!!
|
||||
else
|
||||
NumberCompare.patchOpcode(BranchedValue.negatedOperations[NumberCompare.getNumberCompareOpcode(op)]!!, codegen.mv, op, a.type)
|
||||
codegen.mv.visitJumpInsn(opcode, target)
|
||||
}
|
||||
}
|
||||
|
||||
class PrimitiveComparison(
|
||||
private val primitiveNumberType: KotlinType,
|
||||
private val operatorToken: KtSingleValueToken
|
||||
) : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): StackValue? {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||
val parameterType = codegen.typeMapper.mapType(primitiveNumberType)
|
||||
val (left, right) = expression.receiverAndArgs()
|
||||
return StackValue.cmp(
|
||||
operatorToken,
|
||||
parameterType,
|
||||
codegen.gen(left, parameterType, data),
|
||||
codegen.gen(right, parameterType, data)
|
||||
)
|
||||
val a = left.accept(codegen, data).coerce(parameterType).materialized
|
||||
val b = right.accept(codegen, data).coerce(parameterType).materialized
|
||||
return BooleanComparison(operatorToken, a, b)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,7 @@ package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import com.intellij.psi.tree.IElementType
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.*
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
@@ -19,46 +18,46 @@ import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
||||
import org.jetbrains.kotlin.ir.util.isNullConst
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberOrNullableType
|
||||
import org.jetbrains.kotlin.types.typeUtil.upperBoundedByPrimitiveNumberOrNullableType
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
class Equals(val operator: IElementType) : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): StackValue? {
|
||||
val (leftArg, rightArg) = expression.receiverAndArgs()
|
||||
var leftType = codegen.typeMapper.mapType(leftArg.type.toKotlinType())
|
||||
var rightType = codegen.typeMapper.mapType(rightArg.type.toKotlinType())
|
||||
if (isPrimitive(leftType) != isPrimitive(rightType)) {
|
||||
leftType = boxType(leftType)
|
||||
rightType = boxType(rightType)
|
||||
}
|
||||
|
||||
val opToken = expression.origin
|
||||
return if (leftArg.isNullConst() || rightArg.isNullConst()) {
|
||||
val other = if (leftArg.isNullConst()) rightArg else leftArg
|
||||
return StackValue.compareWithNull(other.accept(codegen, data), Opcodes.IFNONNULL)
|
||||
} else if (leftType.isFloatingPoint && rightType.isFloatingPoint) {
|
||||
genEqualsBoxedOnStack(operator)
|
||||
} else if (opToken === IrStatementOrigin.EQEQEQ || opToken === IrStatementOrigin.EXCLEQEQ) {
|
||||
// TODO: always casting to the type of the left operand in case of primitives looks wrong
|
||||
val operandType = if (isPrimitive(leftType)) leftType else OBJECT_TYPE
|
||||
StackValue.cmp(operator, operandType, codegen.gen(leftArg, operandType, data), codegen.gen(rightArg, operandType, data))
|
||||
} else {
|
||||
genEqualsForExpressionsOnStack(operator, codegen.gen(leftArg, leftType, data), codegen.gen(rightArg, rightType, data))
|
||||
}
|
||||
private class BooleanNullCheck(val value: PromisedValue) : BooleanValue(value.codegen) {
|
||||
override fun jumpIfFalse(target: Label) = value.materialize().also { codegen.mv.ifnonnull(target) }
|
||||
override fun jumpIfTrue(target: Label) = value.materialize().also { codegen.mv.ifnull(target) }
|
||||
}
|
||||
|
||||
private val Type.isFloatingPoint: Boolean
|
||||
get() = this == Type.DOUBLE_TYPE || this == Type.FLOAT_TYPE
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||
val (a, b) = expression.receiverAndArgs()
|
||||
if (a.isNullConst() || b.isNullConst()) {
|
||||
return BooleanNullCheck(if (a.isNullConst()) b.accept(codegen, data) else a.accept(codegen, data))
|
||||
}
|
||||
|
||||
val leftType = with(codegen) { a.asmType }
|
||||
val rightType = with(codegen) { b.asmType }
|
||||
val opToken = expression.origin
|
||||
val useEquals = opToken !== IrStatementOrigin.EQEQEQ && opToken !== IrStatementOrigin.EXCLEQEQ &&
|
||||
// `==` is `equals` for objects and floating-point numbers. In the latter case, the difference
|
||||
// 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)
|
||||
val operandType = if (!isPrimitive(leftType) || useEquals) AsmTypes.OBJECT_TYPE else leftType
|
||||
val aValue = a.accept(codegen, data).coerce(operandType).materialized
|
||||
val bValue = b.accept(codegen, data).coerce(operandType).materialized
|
||||
if (useEquals) {
|
||||
AsmUtil.genAreEqualCall(codegen.mv)
|
||||
return MaterialValue(codegen, Type.BOOLEAN_TYPE)
|
||||
}
|
||||
return BooleanComparison(operator, aValue, bValue)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Ieee754Equals(val operandType: Type) : IntrinsicMethod() {
|
||||
|
||||
private val boxedOperandType = AsmUtil.boxType(operandType)
|
||||
|
||||
override fun toCallable(
|
||||
@@ -110,4 +109,3 @@ class Ieee754Equals(val operandType: Type) : IntrinsicMethod() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-8
@@ -8,8 +8,7 @@ package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.PromisedValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||
@@ -25,12 +24,14 @@ abstract class IntrinsicMethod {
|
||||
context: JvmBackendContext
|
||||
): IrIntrinsicFunction = TODO("implement toCallable() or invoke() of $this")
|
||||
|
||||
open fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): StackValue? =
|
||||
toCallable(
|
||||
expression,
|
||||
codegen.typeMapper.mapSignatureSkipGeneric(expression.descriptor),
|
||||
codegen.context
|
||||
).invoke(codegen.mv, codegen, data)
|
||||
open fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? =
|
||||
with(codegen) {
|
||||
val descriptor = typeMapper.mapSignatureSkipGeneric(expression.descriptor)
|
||||
val stackValue = toCallable(expression, descriptor, context).invoke(mv, codegen, data)
|
||||
return object : PromisedValue(codegen, stackValue.type) {
|
||||
override fun materialize() = stackValue.put(codegen.mv)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun calcReceiverType(call: IrMemberAccessExpression, context: JvmBackendContext): Type {
|
||||
|
||||
+18
-30
@@ -16,41 +16,29 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.boxType
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
object JavaClassProperty : IntrinsicMethod() {
|
||||
|
||||
override fun toCallable(expression: IrMemberAccessExpression, signature: JvmMethodSignature, context: JvmBackendContext): IrIntrinsicFunction {
|
||||
return object: IrIntrinsicFunction(expression, signature, context) {
|
||||
|
||||
override fun invoke(v: InstructionAdapter, codegen: org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen, data: BlockInfo): StackValue {
|
||||
val value = codegen.gen(expression.extensionReceiver!!, data)
|
||||
val type = value.type
|
||||
when {
|
||||
type == Type.VOID_TYPE -> {
|
||||
StackValue.unit().put(v)
|
||||
v.invokevirtual("java/lang/Object", "getClass", "()Ljava/lang/Class;", false)
|
||||
}
|
||||
isPrimitive(type) -> {
|
||||
AsmUtil.pop(v, type)
|
||||
v.getstatic(boxType(type).internalName, "TYPE", "Ljava/lang/Class;")
|
||||
}
|
||||
else -> v.invokevirtual("java/lang/Object", "getClass", "()Ljava/lang/Class;", false)
|
||||
}
|
||||
|
||||
return with(codegen) {
|
||||
expression.onStack
|
||||
}
|
||||
}
|
||||
fun invokeWith(value: PromisedValue) {
|
||||
if (value.type == Type.VOID_TYPE) {
|
||||
return invokeWith(value.coerce(AsmTypes.UNIT_TYPE))
|
||||
}
|
||||
if (isPrimitive(value.type)) {
|
||||
value.discard()
|
||||
value.codegen.mv.getstatic(boxType(value.type).internalName, "TYPE", "Ljava/lang/Class;")
|
||||
} else {
|
||||
value.materialize()
|
||||
value.codegen.mv.invokevirtual("java/lang/Object", "getClass", "()Ljava/lang/Class;", false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||
invokeWith(expression.extensionReceiver!!.accept(codegen, data))
|
||||
return with(codegen) { expression.onStack }
|
||||
}
|
||||
}
|
||||
|
||||
+4
-6
@@ -18,18 +18,16 @@ package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.PromisedValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrClassReference
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrGetClass
|
||||
|
||||
class KClassJavaProperty : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): StackValue? {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||
val extensionReceiver = expression.extensionReceiver
|
||||
if (extensionReceiver !is IrClassReference && extensionReceiver !is IrGetClass) {
|
||||
if (extensionReceiver !is IrClassReference && extensionReceiver !is IrGetClass)
|
||||
return null
|
||||
}
|
||||
codegen.generateClassLiteralReference(extensionReceiver, false, data)
|
||||
return with(codegen) { expression.onStack }
|
||||
return codegen.generateClassLiteralReference(extensionReceiver, false, data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,11 +17,18 @@
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BooleanValue
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.coerceToBoolean
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
|
||||
class Not : IntrinsicMethod() {
|
||||
class BooleanNegation(val value: BooleanValue) : BooleanValue(value.codegen) {
|
||||
override fun jumpIfFalse(target: Label) = value.jumpIfTrue(target)
|
||||
override fun jumpIfTrue(target: Label) = value.jumpIfFalse(target)
|
||||
}
|
||||
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo) =
|
||||
StackValue.not(expression.dispatchReceiver!!.accept(codegen, data))
|
||||
BooleanNegation(expression.dispatchReceiver!!.accept(codegen, data).coerceToBoolean())
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
fun identity(x: Int): Int {
|
||||
return when {
|
||||
x < 0 -> identity(x + 1) - 1
|
||||
|
||||
Vendored
+1
@@ -1,3 +1,4 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
inline fun inlineFunVoid(f: () -> Unit): Unit {
|
||||
return f()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user