[JS IR BE] add basic support for some binary and equality operations; add some intrinsic infrastructure

This commit is contained in:
Zalim Bashorov
2018-04-12 01:53:48 +03:00
parent 1f7d818307
commit 89aefb2e3f
8 changed files with 224 additions and 13 deletions

View File

@@ -4,6 +4,7 @@
<w>ctor</w>
<w>inlining</w>
<w>interner</w>
<w>intrinsicify</w>
<w>kclass</w>
<w>lookups</w>
<w>minification</w>

View File

@@ -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))

View File

@@ -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)

View File

@@ -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)
}

View File

@@ -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<SimpleMemberKey, IrSimpleFunction>
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 <V> MutableMap<SimpleMemberKey, V>.binOp(type: KotlinType, name: Name, v: V) {
put(SimpleMemberKey(type, name), v)
}
private data class SimpleMemberKey(val klass: KotlinType, val name: Name)

View File

@@ -83,14 +83,6 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
}
override fun visitCall(expression: IrCall, context: JsGenerationContext): JsExpression {
// TODO rewrite more accurately, right now it just copy-pasted and adopted from old version
// TODO support:
// * ir intrinsics
// * js be intrinsics
// * js function
// * getters and setters
// * binary and unary operations
if (expression.symbol == context.staticContext.backendContext.objectCreate.symbol) {
// TODO: temporary workaround until there is no an intrinsic infrastructure
assert(expression.typeArgumentsCount == 1 && expression.valueArgumentsCount == 0)
@@ -104,9 +96,12 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
val dispatchReceiver = expression.dispatchReceiver?.accept(this, context)
val extensionReceiver = expression.extensionReceiver?.accept(this, context)
val arguments = translateCallArguments(expression, context)
context.staticContext.intrinsics[symbol]?.let {
return it(expression, arguments)
}
return if (symbol is IrConstructorSymbol) {
JsNew(JsNameRef((symbol.owner.parent as IrClass).name.asString()), arguments)
} else {
@@ -121,4 +116,4 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
// TODO check when w/o else branch and empty when
return expression.toJsNode(this, context, ::JsConditional)!!
}
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.transformers.irToJs
import org.jetbrains.kotlin.ir.backend.js.JsIntrinsics
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.js.backend.ast.*
typealias IrCallTransformer = (IrCall, List<JsExpression>) -> JsExpression
class JsIntrinsicTransformers(intrinsics: JsIntrinsics) {
private val transformers: Map<IrSymbol, IrCallTransformer>
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<IrSymbol, IrCallTransformer>.binOp(function: IrFunction, op: JsBinaryOperator) {
put(function.symbol, { _, args -> JsBinaryOperation(op, args[0], args[1]) })
}
private fun MutableMap<IrSymbol, IrCallTransformer>.prefixOp(function: IrFunction, op: JsUnaryOperator) {
put(function.symbol, { _, args -> JsPrefixOperation(op, args[0]) })
}

View File

@@ -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)