[JS IR] Move isPure check to backend.common and add platform-specific check on calls with side effects
This commit is contained in:
+5
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
|
||||
import org.jetbrains.kotlin.ir.builders.irCall
|
||||
import org.jetbrains.kotlin.ir.builders.irString
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFileSymbol
|
||||
@@ -45,4 +46,8 @@ interface CommonBackendContext : BackendContext, LoggingContext {
|
||||
classSymbolMap: MutableMap<IrClassSymbol, IrClassSymbol>,
|
||||
functionSymbolMap: MutableMap<IrSimpleFunctionSymbol, IrSimpleFunctionSymbol>
|
||||
) {}
|
||||
|
||||
fun isSideEffectFree(call: IrCall): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -644,3 +644,42 @@ private fun computeAllOverridden(function: IrSimpleFunction, result: MutableSet<
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: support more cases like built-in operator call and so on
|
||||
fun IrExpression?.isPure(
|
||||
anyVariable: Boolean,
|
||||
checkFields: Boolean = true,
|
||||
context: CommonBackendContext? = null
|
||||
): Boolean {
|
||||
if (this == null) return true
|
||||
|
||||
fun IrExpression.isPureImpl(): Boolean {
|
||||
return when (this) {
|
||||
is IrConst<*> -> true
|
||||
is IrGetValue -> {
|
||||
if (anyVariable) return true
|
||||
val valueDeclaration = symbol.owner
|
||||
if (valueDeclaration is IrVariable) !valueDeclaration.isVar
|
||||
else true
|
||||
}
|
||||
is IrCall -> context?.isSideEffectFree(this) ?: false
|
||||
is IrGetObjectValue -> type.isUnit()
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
if (isPureImpl()) return true
|
||||
|
||||
if (!checkFields) return false
|
||||
|
||||
if (this is IrGetField) {
|
||||
if (!symbol.owner.isFinal) {
|
||||
if (!anyVariable) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return receiver.isPure(anyVariable)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
+1
-1
@@ -19,7 +19,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
|
||||
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
|
||||
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
||||
import org.jetbrains.kotlin.ir.util.isPure
|
||||
import org.jetbrains.kotlin.backend.common.ir.isPure
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
|
||||
|
||||
+26
-5
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.DescriptorlessExternalPackageFragmentSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
@@ -58,6 +59,9 @@ class JsIrBackendContext(
|
||||
|
||||
override var inVerbosePhase: Boolean = false
|
||||
|
||||
override fun isSideEffectFree(call: IrCall): Boolean =
|
||||
call.symbol in intrinsics.primitiveToLiteralConstructor.values
|
||||
|
||||
val devMode = configuration[JSConfigurationKeys.DEVELOPER_MODE] ?: false
|
||||
val errorPolicy = configuration[JSConfigurationKeys.ERROR_TOLERANCE_POLICY] ?: ErrorTolerancePolicy.DEFAULT
|
||||
|
||||
@@ -131,6 +135,7 @@ class JsIrBackendContext(
|
||||
private val COROUTINE_CONTEXT_NAME = Name.identifier("coroutineContext")
|
||||
private val COROUTINE_IMPL_NAME = Name.identifier("CoroutineImpl")
|
||||
private val CONTINUATION_NAME = Name.identifier("Continuation")
|
||||
|
||||
// TODO: what is more clear way reference this getter?
|
||||
private val CONTINUATION_CONTEXT_GETTER_NAME = Name.special("<get-context>")
|
||||
|
||||
@@ -262,15 +267,29 @@ class JsIrBackendContext(
|
||||
val coroutineGetContext: IrSimpleFunctionSymbol
|
||||
get() {
|
||||
val contextGetter =
|
||||
continuationClass.owner.declarations.filterIsInstance<IrSimpleFunction>().atMostOne { it.name == CONTINUATION_CONTEXT_GETTER_NAME }
|
||||
?: continuationClass.owner.declarations.filterIsInstance<IrProperty>().atMostOne { it.name == CONTINUATION_CONTEXT_PROPERTY_NAME }?.getter!!
|
||||
continuationClass.owner.declarations.filterIsInstance<IrSimpleFunction>()
|
||||
.atMostOne { it.name == CONTINUATION_CONTEXT_GETTER_NAME }
|
||||
?: continuationClass.owner.declarations.filterIsInstance<IrProperty>()
|
||||
.atMostOne { it.name == CONTINUATION_CONTEXT_PROPERTY_NAME }?.getter!!
|
||||
return contextGetter.symbol
|
||||
}
|
||||
|
||||
val coroutineGetContextJs
|
||||
get() = ir.symbols.coroutineGetContext
|
||||
|
||||
val coroutineEmptyContinuation = symbolTable.referenceProperty(getProperty(FqName.fromSegments(listOf("kotlin", "coroutines", "js", "internal", "EmptyContinuation"))))
|
||||
val coroutineEmptyContinuation = symbolTable.referenceProperty(
|
||||
getProperty(
|
||||
FqName.fromSegments(
|
||||
listOf(
|
||||
"kotlin",
|
||||
"coroutines",
|
||||
"js",
|
||||
"internal",
|
||||
"EmptyContinuation"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val coroutineContextProperty: PropertyDescriptor
|
||||
get() {
|
||||
@@ -283,7 +302,8 @@ class JsIrBackendContext(
|
||||
|
||||
val newThrowableSymbol = symbolTable.referenceSimpleFunction(getJsInternalFunction("newThrowable"))
|
||||
val extendThrowableSymbol = symbolTable.referenceSimpleFunction(getJsInternalFunction("extendThrowable"))
|
||||
val setPropertiesToThrowableInstanceSymbol = symbolTable.referenceSimpleFunction(getJsInternalFunction("setPropertiesToThrowableInstance"))
|
||||
val setPropertiesToThrowableInstanceSymbol =
|
||||
symbolTable.referenceSimpleFunction(getJsInternalFunction("setPropertiesToThrowableInstance"))
|
||||
|
||||
val throwISEsymbol = symbolTable.referenceSimpleFunction(getFunctions(kotlinPackageFqn.child(Name.identifier("THROW_ISE"))).single())
|
||||
val throwIAEsymbol = symbolTable.referenceSimpleFunction(getFunctions(kotlinPackageFqn.child(Name.identifier("THROW_IAE"))).single())
|
||||
@@ -314,7 +334,8 @@ class JsIrBackendContext(
|
||||
val defaultThrowableCtor by lazy2 { throwableConstructors.single { !it.owner.isPrimary && it.owner.valueParameters.size == 0 } }
|
||||
|
||||
val kpropertyBuilder = getFunctions(FqName("kotlin.js.getPropertyCallableRef")).single().let { symbolTable.referenceSimpleFunction(it) }
|
||||
val klocalDelegateBuilder = getFunctions(FqName("kotlin.js.getLocalDelegateReference")).single().let { symbolTable.referenceSimpleFunction(it) }
|
||||
val klocalDelegateBuilder =
|
||||
getFunctions(FqName("kotlin.js.getLocalDelegateReference")).single().let { symbolTable.referenceSimpleFunction(it) }
|
||||
|
||||
private fun referenceOperators(): Map<Name, MutableMap<IrClassifierSymbol, IrSimpleFunctionSymbol>> {
|
||||
val primitiveIrSymbols = irBuiltIns.primitiveIrTypes.map { it.classifierOrFail as IrClassSymbol }
|
||||
|
||||
+2
-2
@@ -14,7 +14,7 @@ import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||
import org.jetbrains.kotlin.ir.util.isPure
|
||||
import org.jetbrains.kotlin.backend.common.ir.isPure
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
@@ -519,7 +519,7 @@ class BlockDecomposerTransformer(
|
||||
compositesLeft == 0 -> value
|
||||
index == 0 && dontDetachFirstArgument -> value
|
||||
value == null -> value
|
||||
value.isPure(false) -> value
|
||||
value.isPure(anyVariable = false, context = context) -> value
|
||||
else -> {
|
||||
// TODO: do not wrap if value is pure (const, variable, etc)
|
||||
val irVar = makeTempVar(value.type, value)
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrArithBuilder
|
||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.prependFunctionCall
|
||||
import org.jetbrains.kotlin.ir.util.isPure
|
||||
import org.jetbrains.kotlin.backend.common.ir.isPure
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildField
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrArithBuilder
|
||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||
import org.jetbrains.kotlin.ir.util.isPure
|
||||
import org.jetbrains.kotlin.backend.common.ir.isPure
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationParent
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.ir.backend.js.lower.cleanup
|
||||
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.util.isPure
|
||||
import org.jetbrains.kotlin.backend.common.ir.isPure
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.types.isNothing
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||
import org.jetbrains.kotlin.ir.util.isPure
|
||||
import org.jetbrains.kotlin.backend.common.ir.isPure
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
|
||||
+1
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.wasm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
||||
import org.jetbrains.kotlin.backend.common.ir.isPure
|
||||
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.at
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
|
||||
@@ -558,39 +558,4 @@ val IrFunction.originalFunction: IrFunction
|
||||
get() = (this as? IrAttributeContainer)?.attributeOwnerId as? IrFunction ?: this
|
||||
|
||||
val IrProperty.originalProperty: IrProperty
|
||||
get() = attributeOwnerId as? IrProperty ?: this
|
||||
|
||||
// TODO: support more cases like built-in operator call and so on
|
||||
|
||||
fun IrExpression?.isPure(anyVariable: Boolean, checkFields: Boolean = true): Boolean {
|
||||
if (this == null) return true
|
||||
|
||||
fun IrExpression.isPureImpl(): Boolean {
|
||||
return when (this) {
|
||||
is IrConst<*> -> true
|
||||
is IrGetValue -> {
|
||||
if (anyVariable) return true
|
||||
val valueDeclaration = symbol.owner
|
||||
if (valueDeclaration is IrVariable) !valueDeclaration.isVar
|
||||
else true
|
||||
}
|
||||
is IrGetObjectValue -> type.isUnit()
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
if (isPureImpl()) return true
|
||||
|
||||
if (!checkFields) return false
|
||||
|
||||
if (this is IrGetField) {
|
||||
if (!symbol.owner.isFinal) {
|
||||
if (!anyVariable) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return receiver.isPure(anyVariable)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
get() = attributeOwnerId as? IrProperty ?: this
|
||||
Reference in New Issue
Block a user