mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-03-10 08:31:29 +00:00
[JS IR BE] add basic support for some binary and equality operations; add some intrinsic infrastructure
This commit is contained in:
1
.idea/dictionaries/bashor.xml
generated
1
.idea/dictionaries/bashor.xml
generated
@@ -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>
|
||||
|
||||
@@ -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))
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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)!!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]) })
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user