From 1d8b9c5a58b6cc4e2702ec3be79c32a2f48ef0e1 Mon Sep 17 00:00:00 2001 From: Michael Bogdanov Date: Mon, 19 Sep 2016 14:11:18 +0300 Subject: [PATCH] Initial implementation of ExpressionCodegen --- .../kotlin/codegen/FunctionCodegen.java | 2 +- .../kotlin/backend/jvm/ExpressionCodegen.kt | 244 ++++++++++++++++++ .../kotlin/backend/jvm/FunctionCodegen.kt | 36 +++ .../kotlin/backend/jvm/JvmClassCodegen.kt | 22 +- 4 files changed, 293 insertions(+), 11 deletions(-) create mode 100644 compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ExpressionCodegen.kt create mode 100644 compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/FunctionCodegen.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index db65861304d..9662574c300 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java @@ -906,7 +906,7 @@ public class FunctionCodegen { } @NotNull - private static FrameMap createFrameMap( + public static FrameMap createFrameMap( @NotNull GenerationState state, @NotNull FunctionDescriptor function, @NotNull JvmMethodSignature signature, diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ExpressionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ExpressionCodegen.kt new file mode 100644 index 00000000000..d2c97b86118 --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ExpressionCodegen.kt @@ -0,0 +1,244 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.backend.jvm + +import org.jetbrains.kotlin.backend.jvm.intrinsics.IntrinsicMethods +import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicFunction +import org.jetbrains.kotlin.codegen.BranchedValue +import org.jetbrains.kotlin.codegen.Callable +import org.jetbrains.kotlin.codegen.FrameMap +import org.jetbrains.kotlin.codegen.StackValue +import org.jetbrains.kotlin.codegen.StackValue.expression +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrVariable +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.visitors.IrElementVisitor +import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE +import org.jetbrains.org.objectweb.asm.Label +import org.jetbrains.org.objectweb.asm.Type +import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter + +//Combine to StackValue? +class Output +class BlockInfo { + val variables: MutableList = mutableListOf() +} +/*TODO*/ +val intrinsics = IntrinsicMethods() + +class VariableInfo(val name: String, val index: Int, val type: Type, val startLabel: Label) + +class ExpressionCodegen( + val irFunction: IrFunction, + val frame: FrameMap, + val mv: InstructionAdapter, + val classCodegen: JvmClassCodegen +) : IrElementVisitor { + + val typeMapper = classCodegen.typeMapper + + fun generate() { + mv.visitCode() + val info = BlockInfo() + irFunction.body?.accept(this, info) + val returnType = typeMapper.mapReturnType(irFunction.descriptor) + if (returnType == Type.VOID_TYPE) { + //for implicit return + mv.areturn(Type.VOID_TYPE) + } + writeLocalVariablesInTable(info) + mv.visitEnd() + } + + override fun visitBlockBody(body: IrBlockBody, data: BlockInfo): Output? { + body.statements.forEach { it.accept(this, data) } + return null + } + + override fun visitBlock(expression: IrBlock, data: BlockInfo): Output? { + val info = BlockInfo() + return super.visitBlock(expression, info).apply { + if (!expression.isTransparentScope) { + writeLocalVariablesInTable(info) + } + else { + info.variables.forEach { + data.variables.add(it) + } + } + } + } + + private fun writeLocalVariablesInTable(info: BlockInfo) { + val endLabel = markNewLabel() + info.variables.forEach { + mv.visitLocalVariable(it.name, it.type.descriptor, null, it.startLabel, endLabel, it.index) + } + } + + override fun visitCall(expression: IrCall, data: BlockInfo): Output? { + return generateCall(expression, expression.superQualifier, data) + } + + private fun generateCall(expression: IrCall, superQualifier: ClassDescriptor?, data: BlockInfo): Output? { + val callable = resolveToCallable(expression, superQualifier != null) + if (callable is IrIntrinsicFunction) { + callable.invoke(mv, this, data) + } else { + expression.dispatchReceiver?.accept(this, data) + expression.extensionReceiver?.accept(this, data) + expression.descriptor.valueParameters.forEachIndexed { i, valueParameterDescriptor -> + expression.getValueArgument(i)?.accept(this, data) + //coerce? + } + + callable.genInvokeInstruction(mv) + } + return null + } + + + + override fun visitInstanceInitializerCall(expression: IrInstanceInitializerCall, data: BlockInfo): Output? { + return null + } + + override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, data: BlockInfo): Output? { + //HACK + StackValue.local(0, OBJECT_TYPE).put(OBJECT_TYPE, mv) + return super.visitDelegatingConstructorCall(expression, data) + } + + override fun visitVariable(declaration: IrVariable, data: BlockInfo): Output? { + val varType = typeMapper.mapType(declaration.descriptor) + val index = frame.enter(declaration.descriptor, varType) + + declaration.initializer?.apply { + accept(this@ExpressionCodegen, data) + StackValue.local(index, varType).store(StackValue.onStack(this.asmType), mv) + } + + val info = VariableInfo( + declaration.descriptor.name.asString(), + index, + varType, + markNewLabel() + ) + data.variables.add(info) + + return null + } + + fun gen(expression: IrExpression, type: Type, data: BlockInfo) { + expression.accept(this, data) + StackValue.onStack(expression.asmType).put(type, mv) + } + + fun gen(expression: IrExpression, data: BlockInfo) { + gen(expression, expression.asmType, data) + } + + override fun visitGetExtensionReceiver(expression: IrGetExtensionReceiver, data: BlockInfo): Output? { + generateLocal(expression.descriptor, expression.asmType) + return null + } + + override fun visitGetVariable(expression: IrGetVariable, data: BlockInfo): Output? { + generateLocal(expression.descriptor, expression.asmType) + return null + } + + private fun generateLocal(descriptor: CallableDescriptor, type: Type) { + StackValue.local(frame.getIndex(descriptor), type).put(type, mv) + } + + override fun visitSetVariable(expression: IrSetVariable, data: BlockInfo): Output? { + expression.value.accept(this, data) + StackValue.local(frame.getIndex(expression.descriptor), expression.asmType).store(StackValue.onStack(expression.value.asmType), mv) + return null + } + + override fun visitThisReference(expression: IrThisReference, data: BlockInfo): Output? { + StackValue.local(0, expression.asmType).put(expression.asmType, mv) + return null + } + + override fun visitConst(expression: IrConst, data: BlockInfo): Output? { + val value = expression.value + val type = expression.asmType + StackValue.constant(value, type).put(type, mv) + return null + } + + override fun visitElement(element: IrElement, data: BlockInfo): Output? { + TODO("not implemented for $element") //To change body of created functions use File | Settings | File Templates. + } + + fun markNewLabel(): Label { + return Label().apply { mv.visitLabel(this) } + } + + override fun visitReturn(expression: IrReturn, data: BlockInfo): Output? { + expression.value?.accept(this, data) + mv.areturn(expression.asmType) + return null + } + + + override fun visitWhen(expression: IrWhen, data: BlockInfo): Output? { + if (IrStatementOrigin.IF == expression.origin) { + val elseLabel = Label() + val condition = expression.getNthCondition(0)!! + gen(condition, data) + BranchedValue.condJump(StackValue.onStack(condition.asmType), elseLabel, true, mv) + + val end = Label() + + gen(expression.getNthResult(0)!!, data) + + mv.goTo(end) + mv.mark(elseLabel) + + expression.getNthResult(1)?.apply { + gen(this, data) + } + + mv.mark(end) + } else { + super.visitWhen(expression, data) + } + return null + } + + val IrExpression.asmType: Type + get() = typeMapper.mapType(this.type) + + private fun resolveToCallable(irCall: IrCall, isSuper: Boolean): Callable { + val intrinsic = intrinsics.getIntrinsic(irCall.descriptor as CallableMemberDescriptor) + if (intrinsic != null) { + return intrinsic.toCallable(irCall, typeMapper.mapSignatureSkipGeneric(irCall.descriptor as FunctionDescriptor), classCodegen.context) + } + + return typeMapper.mapToCallableMethod(irCall.descriptor as FunctionDescriptor, isSuper) + } +} + diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/FunctionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/FunctionCodegen.kt new file mode 100644 index 00000000000..be771d1db2f --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/FunctionCodegen.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.backend.jvm + +import org.jetbrains.kotlin.codegen.AsmUtil.isStaticMethod +import org.jetbrains.kotlin.codegen.FunctionCodegen.createFrameMap +import org.jetbrains.kotlin.codegen.OwnerKind +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter + +class FunctionCodegen(val irFunction: IrFunction, val classCodegen: JvmClassCodegen) { + + fun generate() { + val signature = classCodegen.typeMapper.mapSignatureWithGeneric(irFunction.descriptor, OwnerKind.IMPLEMENTATION) + val frameMap = createFrameMap(classCodegen.state, irFunction.descriptor, signature, isStaticMethod(OwnerKind.IMPLEMENTATION, + irFunction.descriptor)) + val methodVisitor = classCodegen.visitor.newMethod(irFunction.OtherOrigin, irFunction.descriptor.calculateCommonFlags(), irFunction.descriptor.name.asString(), signature.asmMethod.descriptor, + signature.genericsSignature, null/*TODO support exception*/) + + ExpressionCodegen(irFunction, frameMap, InstructionAdapter(methodVisitor), classCodegen).generate() + } +} diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmClassCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmClassCodegen.kt index d85435a3d21..69fb91dc95c 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmClassCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmClassCodegen.kt @@ -21,10 +21,7 @@ import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen import org.jetbrains.kotlin.codegen.MemberCodegen.badDescriptor import org.jetbrains.kotlin.codegen.OwnerKind import org.jetbrains.kotlin.codegen.SuperClassInfo -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.MemberDescriptor -import org.jetbrains.kotlin.descriptors.Modality -import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.resolve.DescriptorUtils @@ -33,8 +30,9 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin import org.jetbrains.kotlin.resolve.source.PsiSourceElement import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.org.objectweb.asm.Opcodes +import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter -class JvmClassCodegen private constructor(val irClass: IrClass, context: JvmBackendContext) { +class JvmClassCodegen private constructor(val irClass: IrClass, val context: JvmBackendContext) { val state = context.state @@ -119,9 +117,7 @@ class JvmClassCodegen private constructor(val irClass: IrClass, context: JvmBack } fun generateMethod(method: IrFunction) { - val signature = typeMapper.mapSignatureWithGeneric(method.descriptor, OwnerKind.IMPLEMENTATION) - visitor.newMethod(method.OtherOrigin, method.descriptor.calculateCommonFlags(), method.descriptor.name.asString(), signature.asmMethod.descriptor, - signature.genericsSignature, null/*TODO support exception*/) + FunctionCodegen(method, this).generate() } } @@ -146,8 +142,14 @@ fun MemberDescriptor.calculateCommonFlags(): Int { } when (modality) { - Modality.ABSTRACT -> {flags = flags.or(Opcodes.ACC_ABSTRACT)} - Modality.FINAL -> {flags = flags.or(Opcodes.ACC_FINAL)} + Modality.ABSTRACT -> { + flags = flags.or(Opcodes.ACC_ABSTRACT) + } + Modality.FINAL -> { + if (this !is ConstructorDescriptor) { + flags = flags.or(Opcodes.ACC_FINAL) + } + } Modality.OPEN -> { assert(!Visibilities.isPrivate(visibility)) }