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.common.descriptors.propertyIfAccessor
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
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.backend.jvm.lower.CrIrType
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.codegen.*
|
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.*
|
||||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.OperationKind.AS
|
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.OperationKind.AS
|
||||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.OperationKind.SAFE_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.fakeAlwaysFalseIfeq
|
||||||
import org.jetbrains.kotlin.codegen.pseudoInsns.fakeAlwaysTrueIfeq
|
|
||||||
import org.jetbrains.kotlin.codegen.pseudoInsns.fixStackAndJump
|
import org.jetbrains.kotlin.codegen.pseudoInsns.fixStackAndJump
|
||||||
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter
|
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
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.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
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.types.toKotlinType
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||||
@@ -42,7 +43,6 @@ import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
|
|||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
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.typesApproximation.approximateCapturedTypes
|
||||||
import org.jetbrains.kotlin.types.upperIfFlexible
|
import org.jetbrains.kotlin.types.upperIfFlexible
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
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)
|
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(
|
class ExpressionCodegen(
|
||||||
val irFunction: IrFunction,
|
val irFunction: IrFunction,
|
||||||
val frame: IrFrameMap,
|
val frame: IrFrameMap,
|
||||||
val mv: InstructionAdapter,
|
val mv: InstructionAdapter,
|
||||||
val classCodegen: ClassCodegen,
|
val classCodegen: ClassCodegen,
|
||||||
val isInlineLambda: Boolean = false
|
val isInlineLambda: Boolean = false
|
||||||
) : IrElementVisitor<StackValue, BlockInfo>, BaseExpressionCodegen {
|
) : IrElementVisitor<PromisedValue, BlockInfo>, BaseExpressionCodegen {
|
||||||
|
|
||||||
var finallyDepth = 0
|
var finallyDepth = 0
|
||||||
|
|
||||||
@@ -137,47 +204,16 @@ class ExpressionCodegen(
|
|||||||
private val CallableDescriptor.asmType: Type
|
private val CallableDescriptor.asmType: Type
|
||||||
get() = typeMapper.mapType(this)
|
get() = typeMapper.mapType(this)
|
||||||
|
|
||||||
private val IrExpression.asmType: Type
|
val IrExpression.asmType: Type
|
||||||
get() = type.asmType
|
get() = type.asmType
|
||||||
|
|
||||||
// Assume this expression's result has already been materialized on the stack
|
// Assume this expression's result has already been materialized on the stack
|
||||||
// with the correct type.
|
// with the correct type.
|
||||||
val IrExpression.onStack: StackValue
|
val IrExpression.onStack: MaterialValue
|
||||||
get() = StackValue.onStack(asmType, type.toKotlinType())
|
get() = MaterialValue(this@ExpressionCodegen, asmType)
|
||||||
|
|
||||||
// If this value is immaterial, construct an object on the top of the stack. This operation is
|
val voidValue: MaterialValue
|
||||||
// idempotent and must always be done before generating other values or emitting raw bytecode.
|
get() = MaterialValue(this@ExpressionCodegen, Type.VOID_TYPE)
|
||||||
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())
|
|
||||||
|
|
||||||
private fun markNewLabel() = Label().apply { mv.visitLabel(this) }
|
private fun markNewLabel() = Label().apply { mv.visitLabel(this) }
|
||||||
|
|
||||||
@@ -196,12 +232,8 @@ class ExpressionCodegen(
|
|||||||
|
|
||||||
// TODO remove
|
// TODO remove
|
||||||
fun gen(expression: IrElement, type: Type, data: BlockInfo): StackValue {
|
fun gen(expression: IrElement, type: Type, data: BlockInfo): StackValue {
|
||||||
return expression.accept(this, data).coerce(type).materialize()
|
expression.accept(this, data).coerce(type).materialize()
|
||||||
}
|
return StackValue.onStack(type)
|
||||||
|
|
||||||
// TODO remove
|
|
||||||
fun gen(expression: IrElement, data: BlockInfo): StackValue {
|
|
||||||
return expression.accept(this, data).materialize()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generate() {
|
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()
|
val info = data.create()
|
||||||
return super.visitBlock(expression, info).apply {
|
return super.visitBlock(expression, info).apply {
|
||||||
if (!expression.isTransparentScope) {
|
writeLocalVariablesInTable(info)
|
||||||
writeLocalVariablesInTable(info)
|
|
||||||
} else {
|
|
||||||
info.variables.forEach {
|
|
||||||
data.variables.add(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,26 +320,25 @@ class ExpressionCodegen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun visitStatementContainer(container: IrStatementContainer, data: BlockInfo): StackValue =
|
private fun visitStatementContainer(container: IrStatementContainer, data: BlockInfo) =
|
||||||
container.statements.fold(StackValue.none()) { prev, exp ->
|
container.statements.fold(voidValue as PromisedValue) { prev, exp ->
|
||||||
prev.discard()
|
prev.discard()
|
||||||
exp.accept(this, data).also { (exp as? IrExpression)?.markEndOfStatementIfNeeded() }
|
exp.accept(this, data).also { (exp as? IrExpression)?.markEndOfStatementIfNeeded() }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitBlockBody(body: IrBlockBody, data: BlockInfo): StackValue {
|
override fun visitBlockBody(body: IrBlockBody, data: BlockInfo) =
|
||||||
return visitStatementContainer(body, data).discard()
|
visitStatementContainer(body, data).discard()
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitContainerExpression(expression: IrContainerExpression, data: BlockInfo): StackValue {
|
override fun visitContainerExpression(expression: IrContainerExpression, data: BlockInfo) =
|
||||||
return visitStatementContainer(expression, data).coerce(expression.type)
|
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)
|
expression.markLineNumber(startOffset = true)
|
||||||
|
mv.load(0, OBJECT_TYPE) // HACK
|
||||||
return generateCall(expression, null, data)
|
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)
|
expression.markLineNumber(startOffset = true)
|
||||||
if (expression.descriptor is ConstructorDescriptor) {
|
if (expression.descriptor is ConstructorDescriptor) {
|
||||||
return generateNewCall(expression, data)
|
return generateNewCall(expression, data)
|
||||||
@@ -319,7 +346,7 @@ class ExpressionCodegen(
|
|||||||
return generateCall(expression, expression.superQualifier, data)
|
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
|
val type = expression.asmType
|
||||||
if (type.sort == Type.ARRAY) {
|
if (type.sort == Type.ARRAY) {
|
||||||
return generateNewArray(expression, data)
|
return generateNewArray(expression, data)
|
||||||
@@ -331,7 +358,7 @@ class ExpressionCodegen(
|
|||||||
return expression.onStack
|
return expression.onStack
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateNewArray(expression: IrCall, data: BlockInfo): StackValue {
|
private fun generateNewArray(expression: IrCall, data: BlockInfo): PromisedValue {
|
||||||
val args = expression.descriptor.valueParameters
|
val args = expression.descriptor.valueParameters
|
||||||
assert(args.size == 1 || args.size == 2) { "Unknown constructor called: " + args.size + " arguments" }
|
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)
|
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)
|
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 isSuperCall = superQualifier != null
|
||||||
val callable = resolveToCallable(expression, isSuperCall)
|
val callable = resolveToCallable(expression, isSuperCall)
|
||||||
return generateCall(expression, callable, data, 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 callGenerator = getOrCreateCallGenerator(expression, expression.descriptor, data)
|
||||||
|
|
||||||
val receiver = expression.dispatchReceiver
|
val receiver = expression.dispatchReceiver
|
||||||
@@ -425,64 +457,66 @@ class ExpressionCodegen(
|
|||||||
if (returnType != null && returnType.isNothing()) {
|
if (returnType != null && returnType.isNothing()) {
|
||||||
mv.aconst(null)
|
mv.aconst(null)
|
||||||
mv.athrow()
|
mv.athrow()
|
||||||
return expression.onStack
|
return voidValue
|
||||||
} else if (expression.descriptor is ConstructorDescriptor) {
|
} 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 MaterialValue(this, callable.returnType).coerce(expression.asmType)
|
||||||
return StackValue.onStack(callable.returnType, returnType).coerce(expression.type)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, data: BlockInfo): StackValue {
|
override fun visitVariable(declaration: IrVariable, data: BlockInfo): PromisedValue {
|
||||||
//HACK
|
|
||||||
StackValue.local(0, OBJECT_TYPE).put(OBJECT_TYPE, mv)
|
|
||||||
return super.visitDelegatingConstructorCall(expression, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitVariable(declaration: IrVariable, data: BlockInfo): StackValue {
|
|
||||||
val varType = typeMapper.mapType(declaration.descriptor)
|
val varType = typeMapper.mapType(declaration.descriptor)
|
||||||
val index = frame.enter(declaration.symbol, varType)
|
val index = frame.enter(declaration.symbol, varType)
|
||||||
|
|
||||||
declaration.markLineNumber(startOffset = true)
|
declaration.markLineNumber(startOffset = true)
|
||||||
|
|
||||||
declaration.initializer?.let {
|
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)
|
it.markLineNumber(startOffset = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
data.variables.add(VariableInfo(declaration, index, varType, markNewLabel()))
|
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
|
// Do not generate line number information for loads from compiler-generated
|
||||||
// temporary variables. They do not correspond to variable loads in user code.
|
// temporary variables. They do not correspond to variable loads in user code.
|
||||||
if (expression.symbol.owner.origin != IrDeclarationOrigin.IR_TEMPORARY_VARIABLE)
|
if (expression.symbol.owner.origin != IrDeclarationOrigin.IR_TEMPORARY_VARIABLE)
|
||||||
expression.markLineNumber(startOffset = true)
|
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 {
|
override fun visitFieldAccess(expression: IrFieldAccessExpression, data: BlockInfo): PromisedValue {
|
||||||
val receiverValue = expression.receiver?.accept(this, data) ?: StackValue.none()
|
val realDescriptor = DescriptorUtils.unwrapFakeOverride(expression.descriptor)
|
||||||
val materialReceiver = if (materialize) receiverValue.materialize() else receiverValue
|
val fieldType = typeMapper.mapType(realDescriptor.original.type)
|
||||||
val propertyDescriptor = expression.descriptor
|
val ownerType = typeMapper.mapImplementationOwner(expression.descriptor).internalName
|
||||||
|
val fieldName = expression.descriptor.name.asString()
|
||||||
val realDescriptor = DescriptorUtils.unwrapFakeOverride(propertyDescriptor)
|
val isStatic = expression.receiver == null
|
||||||
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 {
|
|
||||||
expression.markLineNumber(startOffset = true)
|
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
|
val expressionValue = expression.value
|
||||||
// Do not add redundant field initializers that initialize to default values.
|
// Do not add redundant field initializers that initialize to default values.
|
||||||
// "expression.origin == null" means that the field is initialized when it is declared,
|
// "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 &&
|
val skip = irFunction is IrConstructor && irFunction.isPrimary &&
|
||||||
expression.origin == null && expressionValue is IrConst<*> &&
|
expression.origin == null && expressionValue is IrConst<*> &&
|
||||||
isDefaultValueForType(expression.symbol.owner.type.asmType, expressionValue.value)
|
isDefaultValueForType(expression.symbol.owner.type.asmType, expressionValue.value)
|
||||||
if (!skip) {
|
return if (skip) voidValue.coerce(expression.asmType) else super.visitSetField(expression, data)
|
||||||
expression.markLineNumber(startOffset = true)
|
|
||||||
generateFieldValue(expression, data, true).store(expressionValue.accept(this, data), mv)
|
|
||||||
}
|
|
||||||
return StackValue.none().coerce(expression.type)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -524,37 +554,48 @@ class ExpressionCodegen(
|
|||||||
throw AssertionError("Non-mapped local declaration: $dump\n in ${irFunction.dump()}")
|
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.markLineNumber(startOffset = true)
|
||||||
expression.value.markLineNumber(startOffset = true)
|
expression.value.markLineNumber(startOffset = true)
|
||||||
val value = expression.value.accept(this, data)
|
expression.value.accept(this, data).coerce(expression.descriptor.asmType).materialize()
|
||||||
StackValue.local(findLocalIndex(expression.symbol), expression.descriptor.asmType).store(value, mv)
|
mv.store(findLocalIndex(expression.symbol), expression.descriptor.asmType)
|
||||||
return StackValue.none().coerce(expression.type)
|
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)
|
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 {
|
override fun visitExpressionBody(body: IrExpressionBody, data: BlockInfo) =
|
||||||
return body.expression.accept(this, data)
|
body.expression.accept(this, data)
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitElement(element: IrElement, data: BlockInfo): StackValue {
|
override fun visitElement(element: IrElement, data: BlockInfo) =
|
||||||
throw AssertionError(
|
throw AssertionError(
|
||||||
"Unexpected IR element found during code generation. Either code generation for it " +
|
"Unexpected IR element found during code generation. Either code generation for it " +
|
||||||
"is not implemented, or it should have been lowered: ${element.render()}"
|
"is not implemented, or it should have been lowered: ${element.render()}"
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
// TODO maybe remove?
|
// TODO maybe remove?
|
||||||
override fun visitClass(declaration: IrClass, data: BlockInfo): StackValue {
|
override fun visitClass(declaration: IrClass, data: BlockInfo): PromisedValue {
|
||||||
classCodegen.generateLocalClass(declaration)
|
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)
|
expression.markLineNumber(startOffset = true)
|
||||||
val outType = expression.type
|
val outType = expression.type
|
||||||
val type = expression.asmType
|
val type = expression.asmType
|
||||||
@@ -624,13 +665,11 @@ class ExpressionCodegen(
|
|||||||
} else {
|
} else {
|
||||||
mv.iconst(size)
|
mv.iconst(size)
|
||||||
newArrayInstruction(expression.type.toKotlinType())
|
newArrayInstruction(expression.type.toKotlinType())
|
||||||
val elementKotlinType = classCodegen.context.builtIns.getArrayElementType(outType.toKotlinType())
|
|
||||||
for ((i, element) in expression.elements.withIndex()) {
|
for ((i, element) in expression.elements.withIndex()) {
|
||||||
mv.dup()
|
mv.dup()
|
||||||
val array = StackValue.onStack(elementType, outType.toKotlinType())
|
mv.iconst(i)
|
||||||
val index = StackValue.constant(i).materialize()
|
element.accept(this, data).coerce(elementType).materialize()
|
||||||
val value = element.accept(this, data).coerce(elementType).materialize()
|
mv.astore(elementType)
|
||||||
StackValue.arrayElement(elementType, elementKotlinType, array, index).store(value, mv)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return expression.onStack
|
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
|
val owner = expression.returnTargetSymbol.owner
|
||||||
//TODO: should be owner != irFunction
|
//TODO: should be owner != irFunction
|
||||||
val isNonLocalReturn = state.typeMapper.mapFunctionName(
|
val isNonLocalReturn = state.typeMapper.mapFunctionName(
|
||||||
@@ -665,7 +704,7 @@ class ExpressionCodegen(
|
|||||||
mv, "java/lang/UnsupportedOperationException",
|
mv, "java/lang/UnsupportedOperationException",
|
||||||
"Non-local returns are not allowed with inlining disabled"
|
"Non-local returns are not allowed with inlining disabled"
|
||||||
)
|
)
|
||||||
return StackValue.none()
|
return voidValue
|
||||||
}
|
}
|
||||||
|
|
||||||
val returnType = typeMapper.mapReturnType(owner.descriptor)
|
val returnType = typeMapper.mapReturnType(owner.descriptor)
|
||||||
@@ -679,10 +718,10 @@ 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 StackValue.none()
|
return voidValue
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitWhen(expression: IrWhen, data: BlockInfo): StackValue {
|
override fun visitWhen(expression: IrWhen, data: BlockInfo): PromisedValue {
|
||||||
expression.markLineNumber(startOffset = true)
|
expression.markLineNumber(startOffset = true)
|
||||||
SwitchGenerator(expression, data, this).generate()?.let { return it }
|
SwitchGenerator(expression, data, this).generate()?.let { return it }
|
||||||
|
|
||||||
@@ -699,9 +738,9 @@ class ExpressionCodegen(
|
|||||||
if (branch.condition.isFalseConst())
|
if (branch.condition.isFalseConst())
|
||||||
continue // The branch body is dead code.
|
continue // The branch body is dead code.
|
||||||
} else {
|
} 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()) {
|
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)
|
||||||
@@ -712,23 +751,23 @@ class ExpressionCodegen(
|
|||||||
}
|
}
|
||||||
// Produce the default value for the type. Doesn't really matter right now, as non-exhaustive
|
// Produce the default value for the type. Doesn't really matter right now, as non-exhaustive
|
||||||
// conditionals cannot be used as expressions.
|
// conditionals cannot be used as expressions.
|
||||||
val result = StackValue.none().coerce(expression.type).materializeNonUnit()
|
val result = voidValue.coerce(expression.asmType).materialized
|
||||||
mv.mark(endLabel)
|
mv.mark(endLabel)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitTypeOperator(expression: IrTypeOperatorCall, data: BlockInfo): StackValue {
|
override fun visitTypeOperator(expression: IrTypeOperatorCall, data: BlockInfo): PromisedValue {
|
||||||
val kotlinType = expression.typeOperand.toKotlinType()
|
val kotlinType = expression.typeOperand.toKotlinType()
|
||||||
val asmType = kotlinType.asmType
|
val asmType = kotlinType.asmType
|
||||||
return when (expression.operator) {
|
return when (expression.operator) {
|
||||||
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> {
|
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> {
|
||||||
val result = expression.argument.accept(this, data)
|
expression.argument.accept(this, data).discard()
|
||||||
expression.argument.markEndOfStatementIfNeeded()
|
expression.argument.markEndOfStatementIfNeeded()
|
||||||
result.coerce(expression.type)
|
voidValue.coerce(expression.asmType)
|
||||||
}
|
}
|
||||||
|
|
||||||
IrTypeOperator.IMPLICIT_CAST -> {
|
IrTypeOperator.IMPLICIT_CAST, IrTypeOperator.IMPLICIT_INTEGER_COERCION -> {
|
||||||
expression.argument.accept(this, data).coerce(expression.type)
|
expression.argument.accept(this, data).coerce(expression.asmType)
|
||||||
}
|
}
|
||||||
|
|
||||||
IrTypeOperator.CAST, IrTypeOperator.SAFE_CAST -> {
|
IrTypeOperator.CAST, IrTypeOperator.SAFE_CAST -> {
|
||||||
@@ -746,7 +785,7 @@ class ExpressionCodegen(
|
|||||||
state.languageVersionSettings.isReleaseCoroutines()
|
state.languageVersionSettings.isReleaseCoroutines()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
StackValue.onStack(boxedType).coerce(expression.type)
|
MaterialValue(this, boxedType).coerce(expression.asmType)
|
||||||
}
|
}
|
||||||
|
|
||||||
IrTypeOperator.INSTANCEOF, IrTypeOperator.NOT_INSTANCEOF -> {
|
IrTypeOperator.INSTANCEOF, IrTypeOperator.NOT_INSTANCEOF -> {
|
||||||
@@ -758,14 +797,15 @@ class ExpressionCodegen(
|
|||||||
} else {
|
} else {
|
||||||
generateIsCheck(mv, kotlinType, type, state.languageVersionSettings.isReleaseCoroutines())
|
generateIsCheck(mv, kotlinType, type, state.languageVersionSettings.isReleaseCoroutines())
|
||||||
}
|
}
|
||||||
if (IrTypeOperator.NOT_INSTANCEOF == expression.operator) {
|
// TODO remove this type operator, generate an intrinsic call around INSTANCEOF instead
|
||||||
return StackValue.not(expression.onStack)
|
if (IrTypeOperator.NOT_INSTANCEOF == expression.operator)
|
||||||
}
|
Not.BooleanNegation(expression.onStack.coerceToBoolean())
|
||||||
expression.onStack
|
else
|
||||||
|
expression.onStack
|
||||||
}
|
}
|
||||||
|
|
||||||
IrTypeOperator.IMPLICIT_NOTNULL -> {
|
IrTypeOperator.IMPLICIT_NOTNULL -> {
|
||||||
val value = expression.argument.accept(this, data).materialize()
|
val value = expression.argument.accept(this, data).materialized
|
||||||
mv.dup()
|
mv.dup()
|
||||||
mv.visitLdcInsn("TODO provide message for IMPLICIT_NOTNULL") /*TODO*/
|
mv.visitLdcInsn("TODO provide message for IMPLICIT_NOTNULL") /*TODO*/
|
||||||
mv.invokestatic(
|
mv.invokestatic(
|
||||||
@@ -773,12 +813,7 @@ class ExpressionCodegen(
|
|||||||
"(Ljava/lang/Object;Ljava/lang/String;)V", false
|
"(Ljava/lang/Object;Ljava/lang/String;)V", false
|
||||||
)
|
)
|
||||||
// Unbox primitives.
|
// Unbox primitives.
|
||||||
value.coerce(expression.type)
|
value.coerce(expression.asmType)
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> throw AssertionError("type operator ${expression.operator} should have been lowered")
|
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)
|
expression.markLineNumber(startOffset = true)
|
||||||
return when (expression.arguments.size) {
|
when (expression.arguments.size) {
|
||||||
0 -> StackValue.constant("", expression.asmType)
|
0 -> mv.aconst("")
|
||||||
1 -> {
|
1 -> {
|
||||||
// Convert single arg to string.
|
// Convert single arg to string.
|
||||||
val argStackValue = expression.arguments[0].accept(this, data).materialize()
|
val type = expression.arguments[0].accept(this, data).materialized.type
|
||||||
AsmUtil.genToString(argStackValue, argStackValue.type, argStackValue.kotlinType, typeMapper).put(expression.asmType, mv)
|
AsmUtil.genToString(StackValue.onStack(type), type, null, typeMapper).put(expression.asmType, mv)
|
||||||
expression.onStack
|
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// Use StringBuilder to concatenate.
|
// Use StringBuilder to concatenate.
|
||||||
AsmUtil.genStringBuilderConstructor(mv)
|
AsmUtil.genStringBuilderConstructor(mv)
|
||||||
expression.arguments.forEach {
|
expression.arguments.forEach {
|
||||||
val stackValue = it.accept(this, data).materialize()
|
AsmUtil.genInvokeAppendMethod(mv, it.accept(this, data).materialized.type, null)
|
||||||
AsmUtil.genInvokeAppendMethod(mv, stackValue.type, stackValue.kotlinType)
|
|
||||||
}
|
}
|
||||||
mv.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false)
|
mv.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false)
|
||||||
expression.onStack
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return expression.onStack
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avoid true condition generation for tailrec
|
override fun visitWhileLoop(loop: IrWhileLoop, data: BlockInfo): PromisedValue {
|
||||||
// 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 {
|
|
||||||
val continueLabel = markNewLabel()
|
val continueLabel = markNewLabel()
|
||||||
val endLabel = Label()
|
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)) {
|
data.withBlock(LoopInfo(loop, continueLabel, endLabel)) {
|
||||||
loop.body?.accept(this, data)?.discard()
|
loop.body?.accept(this, data)?.discard()
|
||||||
}
|
}
|
||||||
mv.goTo(continueLabel)
|
mv.goTo(continueLabel)
|
||||||
mv.mark(endLabel)
|
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? {
|
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)
|
jump.markLineNumber(startOffset = true)
|
||||||
val endLabel = Label()
|
val endLabel = Label()
|
||||||
val stackElement = unwindBlockStack(endLabel, data, jump.loop)
|
val stackElement = unwindBlockStack(endLabel, data, jump.loop)
|
||||||
?: 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 StackValue.none()
|
return voidValue
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitDoWhileLoop(loop: IrDoWhileLoop, data: BlockInfo): StackValue {
|
override fun visitTry(aTry: IrTry, data: BlockInfo): PromisedValue {
|
||||||
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 {
|
|
||||||
aTry.markLineNumber(startOffset = true)
|
aTry.markLineNumber(startOffset = true)
|
||||||
return if (aTry.finallyExpression != null)
|
return if (aTry.finallyExpression != null)
|
||||||
data.withBlock(TryInfo(aTry.finallyExpression!!)) { visitTryWithInfo(aTry, data, it) }
|
data.withBlock(TryInfo(aTry.finallyExpression!!)) { visitTryWithInfo(aTry, data, it) }
|
||||||
@@ -905,15 +914,17 @@ class ExpressionCodegen(
|
|||||||
visitTryWithInfo(aTry, data, null)
|
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()
|
val tryBlockStart = markNewLabel()
|
||||||
mv.nop()
|
mv.nop()
|
||||||
|
val tryAsmType = aTry.asmType
|
||||||
val tryResult = aTry.tryResult.accept(this, data)
|
val tryResult = aTry.tryResult.accept(this, data)
|
||||||
val isExpression = true //TODO: more wise check is required
|
val isExpression = true //TODO: more wise check is required
|
||||||
var savedValue: StackValue.Local? = null
|
var savedValue: Int? = null
|
||||||
if (isExpression) {
|
if (isExpression) {
|
||||||
savedValue = StackValue.local(frame.enterTemp(aTry.asmType), aTry.asmType)
|
tryResult.coerce(tryAsmType).materialize()
|
||||||
savedValue.store(tryResult.coerce(aTry.type), mv)
|
savedValue = frame.enterTemp(tryAsmType)
|
||||||
|
mv.store(savedValue, tryAsmType)
|
||||||
} else {
|
} else {
|
||||||
tryResult.discard()
|
tryResult.discard()
|
||||||
}
|
}
|
||||||
@@ -939,7 +950,8 @@ class ExpressionCodegen(
|
|||||||
catchBody.markLineNumber(true)
|
catchBody.markLineNumber(true)
|
||||||
val catchResult = catchBody.accept(this, data)
|
val catchResult = catchBody.accept(this, data)
|
||||||
if (savedValue != null) {
|
if (savedValue != null) {
|
||||||
savedValue.store(catchResult.coerce(aTry.type), mv)
|
catchResult.coerce(tryAsmType).materialize()
|
||||||
|
mv.store(savedValue, tryAsmType)
|
||||||
} else {
|
} else {
|
||||||
catchResult.discard()
|
catchResult.discard()
|
||||||
}
|
}
|
||||||
@@ -986,11 +998,11 @@ class ExpressionCodegen(
|
|||||||
|
|
||||||
// TODO: generate a common `finally` for try & catch blocks here? Right now this breaks the inliner.
|
// TODO: generate a common `finally` for try & catch blocks here? Right now this breaks the inliner.
|
||||||
if (savedValue != null) {
|
if (savedValue != null) {
|
||||||
savedValue.put(mv)
|
mv.load(savedValue, tryAsmType)
|
||||||
frame.leaveTemp(aTry.asmType)
|
frame.leaveTemp(tryAsmType)
|
||||||
return StackValue.onStack(aTry.asmType)
|
return aTry.onStack
|
||||||
}
|
}
|
||||||
return StackValue.none()
|
return voidValue
|
||||||
}
|
}
|
||||||
|
|
||||||
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?) {
|
||||||
@@ -1007,7 +1019,7 @@ class ExpressionCodegen(
|
|||||||
if (isFinallyMarkerRequired()) {
|
if (isFinallyMarkerRequired()) {
|
||||||
generateFinallyMarker(mv, finallyDepth, true)
|
generateFinallyMarker(mv, finallyDepth, true)
|
||||||
}
|
}
|
||||||
gen(tryInfo.onExit, data).discard()
|
tryInfo.onExit.accept(this, data).discard()
|
||||||
if (isFinallyMarkerRequired()) {
|
if (isFinallyMarkerRequired()) {
|
||||||
generateFinallyMarker(mv, finallyDepth, false)
|
generateFinallyMarker(mv, finallyDepth, false)
|
||||||
}
|
}
|
||||||
@@ -1023,10 +1035,9 @@ class ExpressionCodegen(
|
|||||||
if (data.hasFinallyBlocks()) {
|
if (data.hasFinallyBlocks()) {
|
||||||
if (Type.VOID_TYPE != returnType) {
|
if (Type.VOID_TYPE != returnType) {
|
||||||
val returnValIndex = frame.enterTemp(returnType)
|
val returnValIndex = frame.enterTemp(returnType)
|
||||||
val localForReturnValue = StackValue.local(returnValIndex, returnType)
|
mv.store(returnValIndex, returnType)
|
||||||
localForReturnValue.store(StackValue.onStack(returnType), mv)
|
|
||||||
unwindBlockStack(afterReturnLabel, data, null)
|
unwindBlockStack(afterReturnLabel, data, null)
|
||||||
localForReturnValue.put(returnType, mv)
|
mv.load(returnValIndex, returnType)
|
||||||
frame.leaveTemp(returnType)
|
frame.leaveTemp(returnType)
|
||||||
} else {
|
} else {
|
||||||
unwindBlockStack(afterReturnLabel, data, null)
|
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.markLineNumber(startOffset = true)
|
||||||
expression.value.accept(this, data).coerce(AsmTypes.JAVA_THROWABLE_TYPE).materialize()
|
expression.value.accept(this, data).coerce(AsmTypes.JAVA_THROWABLE_TYPE).materialize()
|
||||||
mv.athrow()
|
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)
|
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)
|
generateClassLiteralReference(expression, true, data)
|
||||||
return expression.onStack
|
|
||||||
}
|
|
||||||
|
|
||||||
fun generateClassLiteralReference(
|
fun generateClassLiteralReference(
|
||||||
classReference: IrExpression,
|
classReference: IrExpression,
|
||||||
wrapIntoKClass: Boolean,
|
wrapIntoKClass: Boolean,
|
||||||
data: BlockInfo
|
data: BlockInfo
|
||||||
) {
|
): PromisedValue {
|
||||||
if (classReference !is IrClassReference /* && DescriptorUtils.isObjectQualifier(classReference.descriptor)*/) {
|
if (classReference is IrGetClass) {
|
||||||
assert(classReference is IrGetClass)
|
// TODO transform one sort of access into the other?
|
||||||
JavaClassProperty.generateImpl(mv, (classReference as IrGetClass).argument.accept(this, data))
|
JavaClassProperty.invokeWith(classReference.argument.accept(this, data))
|
||||||
} else {
|
} else if (classReference is IrClassReference) {
|
||||||
val classType = classReference.classType
|
val classType = classReference.classType
|
||||||
if (classType is CrIrType) {
|
if (classType is CrIrType) {
|
||||||
putJavaLangClassInstance(mv, classType.type, null, typeMapper)
|
putJavaLangClassInstance(mv, classType.type, null, typeMapper)
|
||||||
return
|
return classReference.onStack
|
||||||
} else {
|
} else {
|
||||||
val kotlinType = classType.toKotlinType()
|
val kotlinType = classType.toKotlinType()
|
||||||
if (TypeUtils.isTypeParameter(kotlinType)) {
|
if (TypeUtils.isTypeParameter(kotlinType)) {
|
||||||
@@ -1073,12 +1080,14 @@ class ExpressionCodegen(
|
|||||||
|
|
||||||
putJavaLangClassInstance(mv, typeMapper.mapType(kotlinType), kotlinType, typeMapper)
|
putJavaLangClassInstance(mv, typeMapper.mapType(kotlinType), kotlinType, typeMapper)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
throw AssertionError("not an IrGetClass or IrClassReference: ${classReference.dump()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wrapIntoKClass) {
|
if (wrapIntoKClass) {
|
||||||
wrapJavaClassIntoKClass(mv)
|
wrapJavaClassIntoKClass(mv)
|
||||||
}
|
}
|
||||||
|
return classReference.onStack
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resolveToCallable(irCall: IrMemberAccessExpression, isSuper: Boolean): Callable {
|
private fun resolveToCallable(irCall: IrMemberAccessExpression, isSuper: Boolean): Callable {
|
||||||
|
|||||||
+6
-8
@@ -5,9 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.backend.jvm.codegen
|
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.codegen.`when`.SwitchCodegen.Companion.preferLookupOverSwitch
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.types.*
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
import org.jetbrains.kotlin.ir.util.dump
|
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)
|
data class ValueToLabel(val value: Any?, val label: Label)
|
||||||
|
|
||||||
// @return null if the IrWhen cannot be emitted as lookupswitch or tableswitch.
|
// @return null if the IrWhen cannot be emitted as lookupswitch or tableswitch.
|
||||||
fun generate(): StackValue? {
|
fun generate(): PromisedValue? {
|
||||||
val expressionToLabels = ArrayList<ExpressionToLabel>()
|
val expressionToLabels = ArrayList<ExpressionToLabel>()
|
||||||
var elseExpression: IrExpression? = null
|
var elseExpression: IrExpression? = null
|
||||||
val callToLabels = ArrayList<CallToLabel>()
|
val callToLabels = ArrayList<CallToLabel>()
|
||||||
@@ -187,7 +185,7 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf
|
|||||||
|
|
||||||
open fun shouldOptimize() = false
|
open fun shouldOptimize() = false
|
||||||
|
|
||||||
open fun genOptimizedIfEnoughCases(): StackValue? {
|
open fun genOptimizedIfEnoughCases(): PromisedValue? {
|
||||||
if (!shouldOptimize())
|
if (!shouldOptimize())
|
||||||
return null
|
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) {
|
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).materializeNonUnit()
|
thenExpression.accept(codegen, data).coerce(expression.asmType).materialized
|
||||||
mv.goTo(endLabel)
|
mv.goTo(endLabel)
|
||||||
}
|
}
|
||||||
mv.visitLabel(defaultLabel)
|
mv.visitLabel(defaultLabel)
|
||||||
val stackValue = elseExpression?.accept(codegen, data) ?: StackValue.none()
|
val stackValue = elseExpression?.accept(codegen, data) ?: voidValue
|
||||||
val result = stackValue.coerce(expression.type).materializeNonUnit()
|
val result = stackValue.coerce(expression.asmType).materialized
|
||||||
mv.mark(endLabel)
|
mv.mark(endLabel)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-5
@@ -35,11 +35,7 @@ object ArrayConstructor : IntrinsicMethod() {
|
|||||||
return object : IrIntrinsicFunction(expression, signature, context, expression.argTypes(context)) {
|
return object : IrIntrinsicFunction(expression, signature, context, expression.argTypes(context)) {
|
||||||
|
|
||||||
override fun invoke(v: InstructionAdapter, codegen: ExpressionCodegen, data: BlockInfo): StackValue {
|
override fun invoke(v: InstructionAdapter, codegen: ExpressionCodegen, data: BlockInfo): StackValue {
|
||||||
// TODO fix this hack
|
codegen.generateCall(expression, this, data).materialize()
|
||||||
val result = codegen.generateCall(expression, this, data)
|
|
||||||
with(codegen) {
|
|
||||||
result.materialize()
|
|
||||||
}
|
|
||||||
return StackValue.onStack(Type.getObjectType("[" + AsmTypes.OBJECT_TYPE.internalName))
|
return StackValue.onStack(Type.getObjectType("[" + AsmTypes.OBJECT_TYPE.internalName))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-10
@@ -16,16 +16,19 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
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.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.comparisonOperandType
|
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.IrFunctionAccessExpression
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||||
import org.jetbrains.kotlin.lexer.KtSingleValueToken
|
import org.jetbrains.kotlin.lexer.KtSingleValueToken
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||||
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.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||||
import java.lang.UnsupportedOperationException
|
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(
|
class PrimitiveComparison(
|
||||||
private val primitiveNumberType: KotlinType,
|
private val primitiveNumberType: KotlinType,
|
||||||
private val operatorToken: KtSingleValueToken
|
private val operatorToken: KtSingleValueToken
|
||||||
) : IntrinsicMethod() {
|
) : 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 parameterType = codegen.typeMapper.mapType(primitiveNumberType)
|
||||||
val (left, right) = expression.receiverAndArgs()
|
val (left, right) = expression.receiverAndArgs()
|
||||||
return StackValue.cmp(
|
val a = left.accept(codegen, data).coerce(parameterType).materialized
|
||||||
operatorToken,
|
val b = right.accept(codegen, data).coerce(parameterType).materialized
|
||||||
parameterType,
|
return BooleanComparison(operatorToken, a, b)
|
||||||
codegen.gen(left, parameterType, data),
|
|
||||||
codegen.gen(right, parameterType, data)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ package org.jetbrains.kotlin.backend.jvm.intrinsics
|
|||||||
|
|
||||||
import com.intellij.psi.tree.IElementType
|
import com.intellij.psi.tree.IElementType
|
||||||
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.codegen.AsmUtil
|
||||||
import org.jetbrains.kotlin.codegen.AsmUtil.*
|
import org.jetbrains.kotlin.codegen.AsmUtil.*
|
||||||
import org.jetbrains.kotlin.codegen.StackValue
|
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.types.toKotlinType
|
||||||
import org.jetbrains.kotlin.ir.util.isNullConst
|
import org.jetbrains.kotlin.ir.util.isNullConst
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
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.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberOrNullableType
|
import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberOrNullableType
|
||||||
import org.jetbrains.kotlin.types.typeUtil.upperBoundedByPrimitiveNumberOrNullableType
|
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.Type
|
||||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||||
|
|
||||||
class Equals(val operator: IElementType) : IntrinsicMethod() {
|
class Equals(val operator: IElementType) : IntrinsicMethod() {
|
||||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): StackValue? {
|
private class BooleanNullCheck(val value: PromisedValue) : BooleanValue(value.codegen) {
|
||||||
val (leftArg, rightArg) = expression.receiverAndArgs()
|
override fun jumpIfFalse(target: Label) = value.materialize().also { codegen.mv.ifnonnull(target) }
|
||||||
var leftType = codegen.typeMapper.mapType(leftArg.type.toKotlinType())
|
override fun jumpIfTrue(target: Label) = value.materialize().also { codegen.mv.ifnull(target) }
|
||||||
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 val Type.isFloatingPoint: Boolean
|
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||||
get() = this == Type.DOUBLE_TYPE || this == Type.FLOAT_TYPE
|
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() {
|
class Ieee754Equals(val operandType: Type) : IntrinsicMethod() {
|
||||||
|
|
||||||
private val boxedOperandType = AsmUtil.boxType(operandType)
|
private val boxedOperandType = AsmUtil.boxType(operandType)
|
||||||
|
|
||||||
override fun toCallable(
|
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.JvmBackendContext
|
||||||
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.codegen.StackValue
|
import org.jetbrains.kotlin.backend.jvm.codegen.PromisedValue
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
|
||||||
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
|
||||||
@@ -25,12 +24,14 @@ abstract class IntrinsicMethod {
|
|||||||
context: JvmBackendContext
|
context: JvmBackendContext
|
||||||
): IrIntrinsicFunction = TODO("implement toCallable() or invoke() of $this")
|
): IrIntrinsicFunction = TODO("implement toCallable() or invoke() of $this")
|
||||||
|
|
||||||
open fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): StackValue? =
|
open fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? =
|
||||||
toCallable(
|
with(codegen) {
|
||||||
expression,
|
val descriptor = typeMapper.mapSignatureSkipGeneric(expression.descriptor)
|
||||||
codegen.typeMapper.mapSignatureSkipGeneric(expression.descriptor),
|
val stackValue = toCallable(expression, descriptor, context).invoke(mv, codegen, data)
|
||||||
codegen.context
|
return object : PromisedValue(codegen, stackValue.type) {
|
||||||
).invoke(codegen.mv, codegen, data)
|
override fun materialize() = stackValue.put(codegen.mv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun calcReceiverType(call: IrMemberAccessExpression, context: JvmBackendContext): Type {
|
fun calcReceiverType(call: IrMemberAccessExpression, context: JvmBackendContext): Type {
|
||||||
|
|||||||
+18
-30
@@ -16,41 +16,29 @@
|
|||||||
|
|
||||||
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.codegen.*
|
||||||
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
|
|
||||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
|
||||||
import org.jetbrains.kotlin.codegen.AsmUtil.boxType
|
import org.jetbrains.kotlin.codegen.AsmUtil.boxType
|
||||||
import org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive
|
import org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive
|
||||||
import org.jetbrains.kotlin.codegen.StackValue
|
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||||
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.InstructionAdapter
|
|
||||||
|
|
||||||
object JavaClassProperty : IntrinsicMethod() {
|
object JavaClassProperty : IntrinsicMethod() {
|
||||||
|
fun invokeWith(value: PromisedValue) {
|
||||||
override fun toCallable(expression: IrMemberAccessExpression, signature: JvmMethodSignature, context: JvmBackendContext): IrIntrinsicFunction {
|
if (value.type == Type.VOID_TYPE) {
|
||||||
return object: IrIntrinsicFunction(expression, signature, context) {
|
return invokeWith(value.coerce(AsmTypes.UNIT_TYPE))
|
||||||
|
}
|
||||||
override fun invoke(v: InstructionAdapter, codegen: org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen, data: BlockInfo): StackValue {
|
if (isPrimitive(value.type)) {
|
||||||
val value = codegen.gen(expression.extensionReceiver!!, data)
|
value.discard()
|
||||||
val type = value.type
|
value.codegen.mv.getstatic(boxType(value.type).internalName, "TYPE", "Ljava/lang/Class;")
|
||||||
when {
|
} else {
|
||||||
type == Type.VOID_TYPE -> {
|
value.materialize()
|
||||||
StackValue.unit().put(v)
|
value.codegen.mv.invokevirtual("java/lang/Object", "getClass", "()Ljava/lang/Class;", false)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.BlockInfo
|
||||||
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
|
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.IrClassReference
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrGetClass
|
import org.jetbrains.kotlin.ir.expressions.IrGetClass
|
||||||
|
|
||||||
class KClassJavaProperty : IntrinsicMethod() {
|
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
|
val extensionReceiver = expression.extensionReceiver
|
||||||
if (extensionReceiver !is IrClassReference && extensionReceiver !is IrGetClass) {
|
if (extensionReceiver !is IrClassReference && extensionReceiver !is IrGetClass)
|
||||||
return null
|
return null
|
||||||
}
|
return codegen.generateClassLiteralReference(extensionReceiver, false, data)
|
||||||
codegen.generateClassLiteralReference(extensionReceiver, false, data)
|
|
||||||
return with(codegen) { expression.onStack }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,11 +17,18 @@
|
|||||||
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.BlockInfo
|
||||||
|
import org.jetbrains.kotlin.backend.jvm.codegen.BooleanValue
|
||||||
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
|
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.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||||
|
import org.jetbrains.org.objectweb.asm.Label
|
||||||
|
|
||||||
class Not : IntrinsicMethod() {
|
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) =
|
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 {
|
fun identity(x: Int): Int {
|
||||||
return when {
|
return when {
|
||||||
x < 0 -> identity(x + 1) - 1
|
x < 0 -> identity(x + 1) - 1
|
||||||
|
|||||||
Vendored
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// IGNORE_BACKEND: JVM_IR
|
||||||
inline fun inlineFunVoid(f: () -> Unit): Unit {
|
inline fun inlineFunVoid(f: () -> Unit): Unit {
|
||||||
return f()
|
return f()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user