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)