From 89aefb2e3f8799701a89aba859947d100f0f9c36 Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Thu, 12 Apr 2018 01:53:48 +0300 Subject: [PATCH] [JS IR BE] add basic support for some binary and equality operations; add some intrinsic infrastructure --- .idea/dictionaries/bashor.xml | 1 + .../kotlin/ir/backend/js/JsIntrinsics.kt | 31 +++++ .../ir/backend/js/JsIrBackendContext.kt | 8 +- .../kotlin/ir/backend/js/compiler.kt | 3 + .../IntrinsicifyBuiltinOperationsLowering.kt | 125 ++++++++++++++++++ .../IrElementToJsExpressionTransformer.kt | 15 +-- .../irToJs/JsIntrinsicTransformers.kt | 47 +++++++ .../ir/backend/js/utils/JsStaticContext.kt | 7 +- 8 files changed, 224 insertions(+), 13 deletions(-) create mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt create mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/IntrinsicifyBuiltinOperationsLowering.kt create mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt diff --git a/.idea/dictionaries/bashor.xml b/.idea/dictionaries/bashor.xml index 7c16dc2d5d4..74461b8e7d1 100644 --- a/.idea/dictionaries/bashor.xml +++ b/.idea/dictionaries/bashor.xml @@ -4,6 +4,7 @@ ctor inlining interner + intrinsicify kclass lookups minification diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt new file mode 100644 index 00000000000..edfb344e8c1 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.ir.backend.js + +import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns + +class JsIntrinsics(irBuiltIns: IrBuiltIns) { + // Equality + val jsEqeq = irBuiltIns.binOpBool("jsEqeq") + val jsNotEq = irBuiltIns.binOpBool("jsNotEq") + val jsEqeqeq = irBuiltIns.binOpBool("jsEqeqeq") + val jsNotEqeq = irBuiltIns.binOpBool("jsNotEqeq") + + // Unary operations + val jsNot = irBuiltIns.unOpBool("jsNot") + + // Binary operations + val jsPlus = irBuiltIns.binOp("jsPlus") + val jsMinus = irBuiltIns.binOp("jsMinus") + val jsMult = irBuiltIns.binOp("jsMult") + val jsDiv = irBuiltIns.binOp("jsDiv") + val jsMod = irBuiltIns.binOp("jsMod") +} + +private fun IrBuiltIns.unOpBool(name: String) = defineOperator(name, bool, listOf(anyN, anyN)) +private fun IrBuiltIns.binOpBool(name: String) = defineOperator(name, bool, listOf(anyN, anyN)) +private fun IrBuiltIns.unOp(name: String) = defineOperator(name, anyN, listOf(anyN, anyN)) +private fun IrBuiltIns.binOp(name: String) = defineOperator(name, anyN, listOf(anyN, anyN)) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt index bc8ea8bc15b..23ab367eb59 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt @@ -15,7 +15,10 @@ import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.ir.IrElement -import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol @@ -34,6 +37,9 @@ class JsIrBackendContext( irModuleFragment: IrModuleFragment, symbolTable: SymbolTable ) : CommonBackendContext { + + val intrinsics = JsIntrinsics(irBuiltIns) + override val builtIns = module.builtIns override val sharedVariablesManager = JsSharedVariablesManager(builtIns) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt index 7a502f84ad4..e2455bbc88a 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt @@ -9,6 +9,7 @@ import com.intellij.openapi.project.Project import org.jetbrains.kotlin.backend.common.lower.* import org.jetbrains.kotlin.backend.common.runOnFilePostfix import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.ir.backend.js.lower.IntrinsicifyBuiltinOperationsLowering import org.jetbrains.kotlin.ir.backend.js.lower.SecondaryCtorLowering import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer import org.jetbrains.kotlin.ir.declarations.IrFile @@ -60,4 +61,6 @@ fun JsIrBackendContext.lower(file: IrFile) { PropertiesLowering().lower(file) InitializersLowering(this, JsLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, false).runOnFilePostfix(file) SecondaryCtorLowering(this).lower(file) + + IntrinsicifyBuiltinOperationsLowering(this).lower(file) } \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/IntrinsicifyBuiltinOperationsLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/IntrinsicifyBuiltinOperationsLowering.kt new file mode 100644 index 00000000000..aa7b96802c4 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/IntrinsicifyBuiltinOperationsLowering.kt @@ -0,0 +1,125 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.ir.backend.js.lower + +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext +import org.jetbrains.kotlin.ir.backend.js.utils.name +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction +import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.copyTypeArgumentsFrom +import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl +import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.util.OperatorNameConventions + +class IntrinsicifyBuiltinOperationsLowering(private val context: JsIrBackendContext) : FileLoweringPass { + + private val primitiveNumberIntrinsics: Map + + init { + primitiveNumberIntrinsics = mutableMapOf() + + primitiveNumberIntrinsics.run { + val primitiveNumbers = context.irBuiltIns.run { listOf(int, short, byte, float, double) } + + for (type in primitiveNumbers) { + binOp(type, OperatorNameConventions.PLUS, context.intrinsics.jsPlus) + binOp(type, OperatorNameConventions.MINUS, context.intrinsics.jsMinus) + binOp(type, OperatorNameConventions.TIMES, context.intrinsics.jsMult) + binOp(type, OperatorNameConventions.DIV, context.intrinsics.jsDiv) + binOp(type, OperatorNameConventions.MOD, context.intrinsics.jsMod) + binOp(type, OperatorNameConventions.REM, context.intrinsics.jsMod) + } + } + } + + override fun lower(irFile: IrFile) { + irFile.transform(object : IrElementTransformerVoid() { + override fun visitCall(expression: IrCall): IrExpression { + val call = super.visitCall(expression) + + if (call is IrCall) { + val symbol = call.symbol + + when (symbol) { + context.irBuiltIns.eqeqeqSymbol -> { + context.intrinsics.jsEqeqeq.symbol + } + context.irBuiltIns.eqeqSymbol -> { + // TODO implement right way + context.intrinsics.jsEqeq.symbol + } + context.irBuiltIns.booleanNotSymbol -> { + context.intrinsics.jsNot.symbol + } + else -> null + }?.let { + return irCall(call, it) + } + + (symbol.owner as? IrFunction)?.dispatchReceiverParameter?.let { + val key = SimpleMemberKey(it.type, symbol.name) + + primitiveNumberIntrinsics[key]?.let { + // TODO: don't apply intrinsics when type of receiver or argument is Long + return irCall(call, it.symbol, dispatchReceiverAsFirstArgument = true) + } + } + } + + return call + } + }, null) + } +} + +// TODO extract to common place? +private fun irCall(call: IrCall, newSymbol: IrFunctionSymbol, dispatchReceiverAsFirstArgument: Boolean = false): IrCall = + call.run { + IrCallImpl( + startOffset, + endOffset, + type, + newSymbol, + newSymbol.descriptor, + typeArgumentsCount, + origin, + superQualifierSymbol + ).apply { + copyTypeAndValueArgumentsFrom(call, dispatchReceiverAsFirstArgument) + } + } + +// TODO extract to common place? +private fun IrCall.copyTypeAndValueArgumentsFrom(call: IrCall, dispatchReceiverAsFirstArgument: Boolean = false) { + copyTypeArgumentsFrom(call) + + var j = 0 + + if (!dispatchReceiverAsFirstArgument) { + dispatchReceiver = call.dispatchReceiver + } else { + putValueArgument(j++, call.dispatchReceiver) + } + + extensionReceiver = call.extensionReceiver + + for (i in 0 until call.valueArgumentsCount) { + putValueArgument(j++, call.getValueArgument(i)) + } +} + +private fun MutableMap.binOp(type: KotlinType, name: Name, v: V) { + put(SimpleMemberKey(type, name), v) +} + +private data class SimpleMemberKey(val klass: KotlinType, val name: Name) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt index b47563ec0c0..7a57070e9b6 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt @@ -83,14 +83,6 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer) -> JsExpression + +class JsIntrinsicTransformers(intrinsics: JsIntrinsics) { + private val transformers: Map + + init { + transformers = mutableMapOf() + + transformers.apply { + binOp(intrinsics.jsEqeqeq, JsBinaryOperator.REF_EQ) + binOp(intrinsics.jsNotEqeq, JsBinaryOperator.REF_NEQ) + binOp(intrinsics.jsEqeq, JsBinaryOperator.EQ) + binOp(intrinsics.jsNotEq, JsBinaryOperator.NEQ) + + prefixOp(intrinsics.jsNot, JsUnaryOperator.NOT) + + binOp(intrinsics.jsPlus, JsBinaryOperator.ADD) + binOp(intrinsics.jsMinus, JsBinaryOperator.SUB) + binOp(intrinsics.jsMult, JsBinaryOperator.MUL) + binOp(intrinsics.jsDiv, JsBinaryOperator.DIV) + binOp(intrinsics.jsMod, JsBinaryOperator.MOD) + } + } + + operator fun get(symbol: IrSymbol): IrCallTransformer? = transformers[symbol] +} + +private fun MutableMap.binOp(function: IrFunction, op: JsBinaryOperator) { + put(function.symbol, { _, args -> JsBinaryOperation(op, args[0], args[1]) }) +} + +private fun MutableMap.prefixOp(function: IrFunction, op: JsUnaryOperator) { + put(function.symbol, { _, args -> JsPrefixOperation(op, args[0]) }) +} diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt index a9308201d16..5706f1d8b39 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt @@ -5,10 +5,12 @@ package org.jetbrains.kotlin.ir.backend.js.utils -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext +import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIntrinsicTransformers import org.jetbrains.kotlin.ir.symbols.IrSymbol -import org.jetbrains.kotlin.js.backend.ast.* +import org.jetbrains.kotlin.js.backend.ast.JsExpression +import org.jetbrains.kotlin.js.backend.ast.JsGlobalBlock +import org.jetbrains.kotlin.js.backend.ast.JsRootScope import org.jetbrains.kotlin.name.Name @@ -18,6 +20,7 @@ class JsStaticContext( private val nameGenerator: NameGenerator, val backendContext: JsIrBackendContext ) { + val intrinsics = JsIntrinsicTransformers(backendContext.intrinsics) fun getNameForSymbol(irSymbol: IrSymbol) = nameGenerator.getNameForSymbol(irSymbol, rootScope) fun getSpecialRefForName(name: Name): JsExpression = nameGenerator.getSpecialRefForName(name)