[atomicfu-K/N]: Support Native backend in atomicfu compiler plugin
Atomicfu compiler plugin supported transformations for K/N: * atomic properties are replaced with volatile properties and all the operations are delegated to native atomic intrinsics * atomic arrays are replaced with kotlin.concurrent.Atomic*Arrays * all other features available on JVM are covered as well (custom atomic extensions, delegated properties, debug tracing) See (KT-58358, https://github.com/Kotlin/kotlinx-atomicfu/issues/261) Merge-request: KT-MR-11253 Merged-by: Maria Sokolova <maria.sokolova@jetbrains.com>
This commit is contained in:
+5
-1
@@ -37,7 +37,8 @@ class AtomicfuKotlinGradleSubplugin :
|
||||
val project = kotlinCompilation.target.project
|
||||
val config = project.extensions.getByType(AtomicfuKotlinGradleExtension::class.java)
|
||||
return (config.isJsIrTransformationEnabled && kotlinCompilation.target.isJs()) ||
|
||||
(config.isJvmIrTransformationEnabled && kotlinCompilation.target.isJvm())
|
||||
(config.isJvmIrTransformationEnabled && kotlinCompilation.target.isJvm()) ||
|
||||
(config.isNativeIrTransformationEnabled && kotlinCompilation.target.isNative())
|
||||
}
|
||||
|
||||
override fun applyToCompilation(
|
||||
@@ -48,6 +49,7 @@ class AtomicfuKotlinGradleSubplugin :
|
||||
open class AtomicfuKotlinGradleExtension {
|
||||
var isJsIrTransformationEnabled = false
|
||||
var isJvmIrTransformationEnabled = false
|
||||
var isNativeIrTransformationEnabled = false
|
||||
}
|
||||
|
||||
override fun getPluginArtifact(): SubpluginArtifact =
|
||||
@@ -58,4 +60,6 @@ class AtomicfuKotlinGradleSubplugin :
|
||||
private fun KotlinTarget.isJs() = platformType == KotlinPlatformType.js
|
||||
|
||||
private fun KotlinTarget.isJvm() = platformType == KotlinPlatformType.jvm || platformType == KotlinPlatformType.androidJvm
|
||||
|
||||
private fun KotlinTarget.isNative() = platformType == KotlinPlatformType.native // todo wasm?
|
||||
}
|
||||
|
||||
@@ -36,6 +36,8 @@ sourceSets {
|
||||
}
|
||||
}
|
||||
|
||||
testsJar {}
|
||||
|
||||
// Tasks that run different sorts of tests. Most frequent use case: running specific tests at TeamCity.
|
||||
val infrastructureTest = nativeTest("infrastructureTest", "infrastructure")
|
||||
val codegenBoxTest = nativeTest("codegenBoxTest", "codegen & !frontend-fir")
|
||||
|
||||
@@ -305,3 +305,6 @@ private fun frontendFir() = arrayOf(
|
||||
|
||||
private fun debugger() = annotation(Tag::class.java, "debugger")
|
||||
private fun infrastructure() = annotation(Tag::class.java, "infrastructure")
|
||||
private fun k1libContents() = annotation(Tag::class.java, "k1libContents")
|
||||
private fun k2libContents() = annotation(Tag::class.java, "k2libContents")
|
||||
private fun atomicfu() = annotation(Tag::class.java, "atomicfu")
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsCompilerAttribute
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
|
||||
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsCompilerAttribute
|
||||
import org.jetbrains.kotlin.gradle.targets.js.d8.D8RootPlugin
|
||||
import org.jetbrains.kotlin.konan.target.HostManager
|
||||
|
||||
description = "Atomicfu Compiler Plugin"
|
||||
|
||||
@@ -9,6 +12,11 @@ plugins {
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
project.configureJvmToolchain(JdkMajorVersion.JDK_11_0)
|
||||
|
||||
// WARNING: Native target is host-dependent. Re-running the same build on another host OS may bring to a different result.
|
||||
val nativeTargetName = HostManager.host.name
|
||||
|
||||
val antLauncherJar by configurations.creating
|
||||
val testJsRuntime by configurations.creating {
|
||||
attributes {
|
||||
@@ -26,6 +34,18 @@ val atomicfuJsClasspath by configurations.creating {
|
||||
|
||||
val atomicfuJvmClasspath by configurations.creating
|
||||
|
||||
val atomicfuNativeKlib by configurations.creating {
|
||||
attributes {
|
||||
attribute(KotlinPlatformType.attribute, KotlinPlatformType.native)
|
||||
// WARNING: Native target is host-dependent. Re-running the same build on another host OS may bring to a different result.
|
||||
attribute(KotlinNativeTarget.konanTargetAttribute, nativeTargetName)
|
||||
attribute(Usage.USAGE_ATTRIBUTE, objects.named(KotlinUsages.KOTLIN_API))
|
||||
attribute(KotlinPlatformType.attribute, KotlinPlatformType.native)
|
||||
// todo: don't add platform specific attribute
|
||||
attribute(KotlinNativeTarget.konanTargetAttribute, org.jetbrains.kotlin.konan.target.KonanTarget.MACOS_X64.toString())
|
||||
}
|
||||
}
|
||||
|
||||
val atomicfuJsIrRuntimeForTests by configurations.creating {
|
||||
attributes {
|
||||
attribute(KotlinPlatformType.attribute, KotlinPlatformType.js)
|
||||
@@ -39,6 +59,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation(project(mapOf("path" to ":native:native.tests")))
|
||||
compileOnly(intellijCore())
|
||||
compileOnly(commonDependency("org.jetbrains.intellij.deps:asm-all"))
|
||||
|
||||
@@ -68,14 +89,31 @@ dependencies {
|
||||
testApi(commonDependency("junit:junit"))
|
||||
testApi(project(":kotlin-test:kotlin-test-jvm"))
|
||||
|
||||
// Dependencies for Kotlin/Native test infra:
|
||||
testImplementation(projectTests(":native:native.tests"))
|
||||
testImplementation(project(":native:kotlin-native-utils"))
|
||||
testImplementation(commonDependency("org.jetbrains.teamcity:serviceMessages"))
|
||||
|
||||
// todo: remove unnecessary dependencies
|
||||
testImplementation(project(":kotlin-compiler-runner-unshaded"))
|
||||
|
||||
testImplementation(commonDependency("commons-lang:commons-lang"))
|
||||
testImplementation(projectTests(":compiler:tests-common"))
|
||||
testImplementation(projectTests(":compiler:tests-common-new"))
|
||||
testImplementation(projectTests(":compiler:test-infrastructure"))
|
||||
testCompileOnly("org.jetbrains.kotlinx:atomicfu:0.17.1") // todo: do not hardcode atomicfu version
|
||||
|
||||
testApiJUnit5()
|
||||
|
||||
testRuntimeOnly(kotlinStdlib())
|
||||
testRuntimeOnly(project(":kotlin-preloader")) // it's required for ant tests
|
||||
testRuntimeOnly(project(":compiler:backend-common"))
|
||||
testRuntimeOnly(commonDependency("org.fusesource.jansi", "jansi"))
|
||||
|
||||
atomicfuJsClasspath("org.jetbrains.kotlinx:atomicfu-js:0.17.1") { isTransitive = false }
|
||||
atomicfuJvmClasspath("org.jetbrains.kotlinx:atomicfu:0.17.1") { isTransitive = false }
|
||||
atomicfuJsIrRuntimeForTests(project(":kotlinx-atomicfu-runtime")) { isTransitive = false }
|
||||
atomicfuJvmClasspath("org.jetbrains.kotlinx:atomicfu:0.17.1") { isTransitive = false }
|
||||
atomicfuNativeKlib("org.jetbrains.kotlinx:atomicfu:0.17.1") { isTransitive = false }
|
||||
|
||||
embedded(project(":kotlinx-atomicfu-runtime")) {
|
||||
attributes {
|
||||
@@ -125,3 +163,11 @@ projectTest(jUnitMode = JUnitMode.JUnit5) {
|
||||
|
||||
publish()
|
||||
standardPublicJars()
|
||||
|
||||
val nativeBoxTest = nativeTest(
|
||||
taskName = "nativeBoxTest",
|
||||
tag = "atomicfu",
|
||||
requirePlatformLibs = true,
|
||||
customDependencies = listOf(atomicfuJvmClasspath),
|
||||
customKlibDependencies = listOf(atomicfuNativeKlib)
|
||||
)
|
||||
|
||||
+7
-4
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.defaultType
|
||||
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
|
||||
import org.jetbrains.kotlin.ir.types.isBoolean
|
||||
import org.jetbrains.kotlin.ir.types.isInt
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -41,11 +42,13 @@ abstract class AbstractAtomicfuIrBuilder(
|
||||
}
|
||||
|
||||
// atomicArr.get(index)
|
||||
fun atomicGetArrayElement(atomicArrayClass: IrClassSymbol, receiver: IrExpression, index: IrExpression) =
|
||||
irCall(atomicSymbols.getAtomicHandlerFunctionSymbol(atomicArrayClass, "get")).apply {
|
||||
fun atomicGetArrayElement(atomicArrayClass: IrClassSymbol, returnType: IrType, receiver: IrExpression, index: IrExpression): IrCall {
|
||||
val arrayElement = irCall(atomicSymbols.getAtomicHandlerFunctionSymbol(atomicArrayClass, "get")).apply {
|
||||
dispatchReceiver = receiver
|
||||
putValueArgument(0, index)
|
||||
}
|
||||
return if (returnType.isBoolean() && arrayElement.type.isInt()) arrayElement.toBoolean() else arrayElement
|
||||
}
|
||||
|
||||
fun irCallWithArgs(symbol: IrSimpleFunctionSymbol, dispatchReceiver: IrExpression?, extensionReceiver: IrExpression?, valueArguments: List<IrExpression?>) =
|
||||
irCall(symbol).apply {
|
||||
@@ -157,7 +160,7 @@ abstract class AbstractAtomicfuIrBuilder(
|
||||
}
|
||||
|
||||
fun IrExpression.toBoolean() = irNotEquals(this, irInt(0)) as IrCall
|
||||
fun IrExpression.toInt() = irIfThenElse(irBuiltIns.intType, irEquals(this, irBoolean(true)), irInt(1), irInt(0))
|
||||
fun IrExpression.toInt() = irIfThenElse(irBuiltIns.intType, this, irInt(1), irInt(0))
|
||||
|
||||
fun irClassWithPrivateConstructor(
|
||||
name: String,
|
||||
@@ -293,7 +296,7 @@ abstract class AbstractAtomicfuIrBuilder(
|
||||
}
|
||||
|
||||
abstract fun atomicfuLoopBody(valueType: IrType, valueParameters: List<IrValueParameter>): IrBlockBody
|
||||
abstract fun atomicfuArrayLoopBody(atomicArrayClass: IrClassSymbol, valueParameters: List<IrValueParameter>): IrBlockBody
|
||||
abstract fun atomicfuArrayLoopBody(atomicArrayClass: IrClassSymbol, valueType: IrType, valueParameters: List<IrValueParameter>): IrBlockBody
|
||||
abstract fun atomicfuUpdateBody(functionName: String, valueType: IrType, valueParameters: List<IrValueParameter>): IrBlockBody
|
||||
abstract fun atomicfuArrayUpdateBody(functionName: String, valueType: IrType, atomicArrayClass: IrClassSymbol, valueParameters: List<IrValueParameter>): IrBlockBody
|
||||
}
|
||||
|
||||
+42
-32
@@ -13,12 +13,9 @@ import org.jetbrains.kotlin.descriptors.DescriptorVisibility
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.valueArguments
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||
import org.jetbrains.kotlin.ir.builders.irGetField
|
||||
import org.jetbrains.kotlin.ir.builders.irSetField
|
||||
import org.jetbrains.kotlin.ir.builders.irBlockBody
|
||||
import org.jetbrains.kotlin.ir.builders.irReturn
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
@@ -51,27 +48,32 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
|
||||
|
||||
companion object {
|
||||
const val CONSTRAINTS_MESSAGE =
|
||||
"\n\nPlease make sure that you follow these constraints for using atomic properties:\n" +
|
||||
" * To ensure that atomic properties are not accessed out of the current Kotlin module, it is necessary to declare atomic properties as private or internal,\n" +
|
||||
" or make the containing class private or internal.\n" +
|
||||
" To expose the atomic property value to the public, use a delegated property declared in the same scope, e.g: \n" +
|
||||
"\nPlease make sure that you follow these constraints for using atomic properties:\n" +
|
||||
" * To ensure that atomic properties are not accessed out of the current Kotlin module, it is necessary to declare atomic properties as private or internal.\n" +
|
||||
" Alternatively, you can make the containing class private or internal.\n" +
|
||||
" If you need to expose the atomic property value to the public, consider using a delegated property declared within the same scope:\n" +
|
||||
" ```\n" +
|
||||
" private val _a = atomic<T>(initial) \n" +
|
||||
" public var a: T by _a \n" +
|
||||
" ```\n" +
|
||||
" * Only perform operations directly on atomic values:\n" +
|
||||
" * Avoid storing references to atomic values in local variables, e.g.\n" +
|
||||
" ```\n" +
|
||||
" val top = atomic<Node?>(null)\n" +
|
||||
" top.compareAndSet(null, Node(1)) // OK: direct invocation on the atomic property is allowed \n" +
|
||||
" ```\n" +
|
||||
" ```\n" +
|
||||
" val tmp = top\n" +
|
||||
" tmp.compareAndSet(null, Node(1)) // DON'T DO THIS: invocation on a local variable is NOT allowed \n" +
|
||||
" ```\n" +
|
||||
" * Do not leak references to atomic values in other way (return, pass as params, etc).\n" +
|
||||
" * Avoid introducing complex data flow within parameters of atomic operations:\n" +
|
||||
" Instead of top.compareAndSet(cur, <complex_expression>) use \n" +
|
||||
" * Directly invoke operations on atomic properties, like this:\n" +
|
||||
" ```\n" +
|
||||
" val top = atomic<Node?>(null)\n" +
|
||||
" top.compareAndSet(null, Node(1)) // OK\n" +
|
||||
" ```\n" +
|
||||
" * Refrain from invoking atomic operations on local variables:\n" +
|
||||
" ```\n" +
|
||||
" val top = atomic<Node?>(null)\n" +
|
||||
" val tmp = top\n" +
|
||||
" tmp.compareAndSet(null, Node(1)) // DON'T DO THIS\n" +
|
||||
" ```\n" +
|
||||
" * Avoid leaking references to atomic values in other ways, such as returning them or passing them as parameters.\n" +
|
||||
" * Be cautious with the complexity of data flow within parameters of atomic operations.\n" +
|
||||
" For instance, instead of using intricate expression directly as an argument, e.g.:\n" +
|
||||
" ```\n" +
|
||||
" top.compareAndSet(cur, <complex_expression>)\n" +
|
||||
" ```\n" +
|
||||
" create a separate variable to hold the complex expression's value and then perform the operation:\n" +
|
||||
" ```\n" +
|
||||
" val newValue = <complex_expression>\n" +
|
||||
" top.compareAndSet(cur, newValue) \n" +
|
||||
@@ -223,7 +225,7 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
|
||||
*/
|
||||
private fun IrDeclarationContainer.transformDelegatedAtomic(atomicProperty: IrProperty) {
|
||||
val getDelegate = atomicProperty.backingField?.initializer?.expression
|
||||
require(getDelegate is IrCall) { "Unexpected initializer of the delegated property ${atomicProperty.render()}: " +
|
||||
require(getDelegate is IrCall) { "Unexpected initializer of the delegated property ${atomicProperty.atomicfuRender()}: " +
|
||||
"expected invocation of the delegate atomic property getter, but found ${getDelegate?.render()}." + CONSTRAINTS_MESSAGE }
|
||||
val delegateVolatileField = when {
|
||||
getDelegate.isAtomicFactoryCall() -> {
|
||||
@@ -341,7 +343,7 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
|
||||
parentContainer: IrDeclarationContainer
|
||||
): IrField {
|
||||
val atomicArrayField =
|
||||
requireNotNull(atomicProperty.backingField) { "The backing field of the atomic array ${atomicProperty.render()} should not be null." + CONSTRAINTS_MESSAGE }
|
||||
requireNotNull(atomicProperty.backingField) { "The backing field of the atomic array [${atomicProperty.atomicfuRender()}] should not be null." + CONSTRAINTS_MESSAGE }
|
||||
val initializer = atomicArrayField.initializer?.expression
|
||||
if (initializer == null) {
|
||||
// replace field initialization in the init block
|
||||
@@ -362,7 +364,7 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
|
||||
parentContainer
|
||||
).also {
|
||||
val initExpr = it.initializer?.expression
|
||||
?: error("The generated atomic array field ${it.render()} should've already be initialized." + CONSTRAINTS_MESSAGE)
|
||||
?: error("The generated atomic array field [${it.render()}] should've already be initialized." + CONSTRAINTS_MESSAGE)
|
||||
it.initializer = null
|
||||
initBlock.updateFieldInitialization(atomicArrayField.symbol, it.symbol, initExpr, initExprIndex)
|
||||
}
|
||||
@@ -451,8 +453,8 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
|
||||
(parent is IrClass &&
|
||||
(parentAsClass.visibility == DescriptorVisibilities.PRIVATE || parentAsClass.visibility == DescriptorVisibilities.INTERNAL))) {
|
||||
"To ensure that atomic properties are not accessed out of the current Kotlin module, it is necessary to declare atomic properties as private or internal.\n" +
|
||||
"Please consider declaring [${this.atomicfuRender()}] from [${this.parent.render()}] as a private or internal property." +
|
||||
if (parent is IrClass) ",\nYou may also make the containing class [${parentAsClass.render()}] private or internal.\n" else "\n" +
|
||||
"Please consider declaring [${this.atomicfuRender()}] from [${this.parent.render()}] as a private or internal property.\n" +
|
||||
(if (parent is IrClass) "You may also make the containing class [${parentAsClass.render()}] private or internal.\n" else "") +
|
||||
"Alternatively, if you need to expose the atomic property value to the public, you can use a delegated property declared within the same scope, e.g:\n" +
|
||||
"```\n" +
|
||||
"private val _a = atomic<T>(initial) \n" +
|
||||
@@ -554,7 +556,8 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
|
||||
val isArrayReceiver = when {
|
||||
propertyGetterCall is IrCall -> propertyGetterCall.isArrayElementReceiver(data)
|
||||
propertyGetterCall.isThisReceiver() -> data != null && data.name.asString().isMangledAtomicArrayExtension()
|
||||
else -> error("Unsupported atomic array receiver ${propertyGetterCall.render()}" + CONSTRAINTS_MESSAGE)
|
||||
else -> error("Could not define the type of receiver of the function call ${expression.render()}: " +
|
||||
"found function call receiver ${propertyGetterCall.render()}, parentFunction = ${data?.render()}." + CONSTRAINTS_MESSAGE)
|
||||
}
|
||||
if (expression.symbol.owner.isFromKotlinxAtomicfuPackage()) {
|
||||
/**
|
||||
@@ -692,14 +695,17 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
|
||||
requireNotNull(parentFunction) { "Expected containing function of the call ${expression.render()}, but found null." + CONSTRAINTS_MESSAGE }
|
||||
val loopFunc = parentFunction.parentDeclarationContainer.getOrBuildInlineLoopFunction(
|
||||
functionName = functionName,
|
||||
valueType = if (valueType.isBoolean()) irBuiltIns.intType else valueType,
|
||||
valueType = if (valueType.isBoolean() && castBooleanFieldsToInt) irBuiltIns.intType else valueType,
|
||||
isArrayReceiver = isArrayReceiver
|
||||
)
|
||||
// We have to copy this lambda, because it is passed to both foo$atomicfu and foo$atomicfu$array transformations,
|
||||
// and otherwise causes "lambda is already bound" error in K/N.
|
||||
val action = (expression.getValueArgument(0) as IrFunctionExpression).apply {
|
||||
function.body?.transform(this@AtomicFunctionCallTransformer, parentFunction)
|
||||
if (function.valueParameters[0].type.isBoolean()) {
|
||||
// In case of AtomicBoolean receiver on JVM the argument of the lambda is cast to Int, because the current value of the receiver is retrieved with the AtomicIntegerUpdater.
|
||||
// AtomicBoolean.loop(action: (Boolean)-> Unit) -> action: (Int) -> Unit
|
||||
// For native this is not necessary and the type is left unchanged.
|
||||
if (function.valueParameters[0].type.isBoolean() && castBooleanFieldsToInt) {
|
||||
function.valueParameters[0].type = irBuiltIns.intType
|
||||
function.returnType = irBuiltIns.intType
|
||||
}
|
||||
@@ -835,7 +841,7 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
|
||||
val isArrayReceiver = parentFunction.name.asString().isMangledAtomicArrayExtension()
|
||||
if (isArrayReceiver) parentFunction.valueParameters[0].capture() else parentFunction.valueParameters[1].capture()
|
||||
}
|
||||
else -> error("Unexpected type of atomic function call receiver: ${atomicCallReceiver.render()} \n" + CONSTRAINTS_MESSAGE)
|
||||
else -> error("Unexpected type of atomic function call receiver: ${atomicCallReceiver.render()}, parentFunction = ${parentFunction?.render()}." + CONSTRAINTS_MESSAGE)
|
||||
}
|
||||
|
||||
override fun visitGetValue(expression: IrGetValue, data: IrFunction?): IrExpression {
|
||||
@@ -923,7 +929,7 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
|
||||
addSyntheticValueParametersToTransformedAtomicExtension(true, valueType)
|
||||
addValueParameter(ACTION, atomicSymbols.function1Type(valueType, irBuiltIns.unitType))
|
||||
body = with(atomicSymbols.createBuilder(symbol)) {
|
||||
atomicfuArrayLoopBody(atomicfuArrayClass, valueParameters)
|
||||
atomicfuArrayLoopBody(atomicfuArrayClass, valueType, valueParameters)
|
||||
}
|
||||
returnType = irBuiltIns.unitType
|
||||
}
|
||||
@@ -977,6 +983,10 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
|
||||
}
|
||||
}
|
||||
|
||||
// If true, transforms AtomicBoolean properties to Int volatile property + updater.
|
||||
// Tweaks types of value arguments where this property is used accordingly.
|
||||
abstract val castBooleanFieldsToInt: Boolean
|
||||
|
||||
// Builds the signature of the transformed atomic extension.
|
||||
abstract fun buildTransformedAtomicExtensionSignature(atomicExtension: IrFunction, isArrayReceiver: Boolean): IrSimpleFunction
|
||||
// Adds value parameters for transformed inline extension functions.
|
||||
@@ -1110,7 +1120,7 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
|
||||
require(index.name.asString() == INDEX && index.type == irBuiltIns.intType)
|
||||
index.capture()
|
||||
}
|
||||
else -> error("Unexpected type of atomic receiver expression: ${this.render()} \n" + CONSTRAINTS_MESSAGE)
|
||||
else -> error("Unexpected type of atomic array receiver: ${this.render()}, parentFunction = ${parentFunction?.render()}\n" + CONSTRAINTS_MESSAGE)
|
||||
}
|
||||
|
||||
// A.kt -> A$VolatileWrapper$atomicfu
|
||||
|
||||
+7
-1
@@ -98,7 +98,7 @@ class AtomicfuJvmIrTransformer(
|
||||
* }
|
||||
* } }
|
||||
*/
|
||||
val volatileField = buildVolatileBackingField(from, parentClass, true)
|
||||
val volatileField = buildVolatileBackingField(from, parentClass, castBooleanFieldsToInt)
|
||||
val volatileProperty = if (volatileField.parent == from.parent) {
|
||||
// The index is relevant only if the property belongs to the same class as the original atomic property (not the generated wrapper).
|
||||
parentClass.replacePropertyAtIndex(volatileField, DescriptorVisibilities.PRIVATE, isVar = true, isStatic = false, index)
|
||||
@@ -271,6 +271,12 @@ class AtomicfuJvmIrTransformer(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* On JVM all Boolean fields are cast to Int, as they only can be updated via AtomicIntegerFieldUpdaters
|
||||
*/
|
||||
override val castBooleanFieldsToInt: Boolean
|
||||
get() = true
|
||||
|
||||
/**
|
||||
* Builds the signature of the transformed atomic extension:
|
||||
*
|
||||
|
||||
+3
-4
@@ -112,7 +112,6 @@ class JvmAtomicfuIrBuilder internal constructor(
|
||||
}
|
||||
*/
|
||||
// dispatchReceiver: IrValueParameter, atomicHandler: IrValueParameter, action: IrValueParameter
|
||||
// todo this should also be abstracted out
|
||||
override fun atomicfuLoopBody(valueType: IrType, valueParameters: List<IrValueParameter>) =
|
||||
irBlockBody {
|
||||
val dispatchReceiver = valueParameters[0]
|
||||
@@ -141,7 +140,7 @@ class JvmAtomicfuIrBuilder internal constructor(
|
||||
}
|
||||
}
|
||||
*/
|
||||
override fun atomicfuArrayLoopBody(atomicArrayClass: IrClassSymbol, valueParameters: List<IrValueParameter>) =
|
||||
override fun atomicfuArrayLoopBody(atomicArrayClass: IrClassSymbol, valueType: IrType, valueParameters: List<IrValueParameter>) =
|
||||
irBlockBody {
|
||||
val atomicHandler = valueParameters[0]
|
||||
val index = valueParameters[1]
|
||||
@@ -150,7 +149,7 @@ class JvmAtomicfuIrBuilder internal constructor(
|
||||
condition = irTrue()
|
||||
body = irBlock {
|
||||
val cur = createTmpVariable(
|
||||
atomicGetArrayElement(atomicArrayClass, irGet(atomicHandler), irGet(index)),
|
||||
atomicGetArrayElement(atomicArrayClass, valueType, irGet(atomicHandler), irGet(index)),
|
||||
"atomicfu\$cur", false
|
||||
)
|
||||
+irCall(atomicSymbols.invoke1Symbol).apply {
|
||||
@@ -270,7 +269,7 @@ class JvmAtomicfuIrBuilder internal constructor(
|
||||
condition = irTrue()
|
||||
body = irBlock {
|
||||
val cur = createTmpVariable(
|
||||
atomicGetArrayElement(atomicArrayClass, irGet(atomicHandler), irGet(index)),
|
||||
atomicGetArrayElement(atomicArrayClass, valueType, irGet(atomicHandler), irGet(index)),
|
||||
"atomicfu\$cur", false
|
||||
)
|
||||
val upd = createTmpVariable(
|
||||
|
||||
+338
@@ -0,0 +1,338 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.kotlinx.atomicfu.compiler.backend.native
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.representativeUpperBound
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.valueArguments
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.*
|
||||
import org.jetbrains.kotlin.ir.builders.irBlockBody
|
||||
import org.jetbrains.kotlin.ir.builders.irCall
|
||||
import org.jetbrains.kotlin.ir.builders.irReturn
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionExpressionImpl
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlinx.atomicfu.compiler.backend.common.AbstractAtomicSymbols
|
||||
import org.jetbrains.kotlinx.atomicfu.compiler.backend.common.AbstractAtomicfuTransformer
|
||||
|
||||
private const val ATOMICFU = "atomicfu"
|
||||
private const val REF_GETTER = "refGetter\$$ATOMICFU"
|
||||
private const val ATOMIC_ARRAY = "atomicArray\$$ATOMICFU"
|
||||
private const val INDEX = "index\$$ATOMICFU"
|
||||
|
||||
class AtomicfuNativeIrTransformer(
|
||||
pluginContext: IrPluginContext,
|
||||
override val atomicSymbols: NativeAtomicSymbols
|
||||
) : AbstractAtomicfuTransformer(pluginContext) {
|
||||
|
||||
override val atomicPropertiesTransformer: AtomicPropertiesTransformer
|
||||
get() = NativeAtomicPropertiesTransformer()
|
||||
|
||||
override val atomicFunctionsTransformer: AtomicFunctionCallTransformer
|
||||
get() = NativeAtomicFunctionCallTransformer()
|
||||
|
||||
private inner class NativeAtomicPropertiesTransformer : AtomicPropertiesTransformer() {
|
||||
|
||||
override fun IrClass.addTransformedInClassAtomic(atomicProperty: IrProperty, index: Int): IrProperty =
|
||||
addVolatileProperty(atomicProperty, index)
|
||||
|
||||
override fun IrDeclarationContainer.addTransformedStaticAtomic(atomicProperty: IrProperty, index: Int): IrProperty =
|
||||
addVolatileProperty(atomicProperty, index)
|
||||
|
||||
private fun IrDeclarationContainer.addVolatileProperty(from: IrProperty, index: Int): IrProperty {
|
||||
/**
|
||||
* Atomic property [from] is replaced with the volatile field of the value type stored in [from]
|
||||
* which is atomically updated via atomic intrinsics (see details about intrinsics in [kotlin.concurrent.AtomicInt]).
|
||||
*
|
||||
* Volatile field has the same visibility as the original atomic property.
|
||||
*
|
||||
* internal val a = atomic(0) --> internal @Volatile var a: Int = 0
|
||||
*/
|
||||
val parentContainer = this
|
||||
with(atomicSymbols.createBuilder(from.symbol)) {
|
||||
val volatileField = buildVolatileBackingField(from, parentContainer, castBooleanFieldsToInt)
|
||||
return parentContainer.replacePropertyAtIndex(volatileField, from.visibility, isVar = true, isStatic = from.parent is IrFile, index).also {
|
||||
atomicfuPropertyToVolatile[from] = it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class NativeAtomicFunctionCallTransformer : AtomicFunctionCallTransformer() {
|
||||
|
||||
override fun transformAtomicUpdateCallOnProperty(
|
||||
expression: IrCall,
|
||||
functionName: String,
|
||||
valueType: IrType,
|
||||
castType: IrType?,
|
||||
getPropertyReceiver: IrExpression,
|
||||
parentFunction: IrFunction?
|
||||
): IrExpression =
|
||||
with(atomicSymbols.createBuilder(expression.symbol)) {
|
||||
/**
|
||||
* Atomic update call on the atomic property is replaced
|
||||
* with the atomic intrinsic call on the corresponding volatile property reference:
|
||||
*
|
||||
* 1. Function call receiver is atomic property getter call.
|
||||
*
|
||||
* The call is replaced with atomic intrinsic call and the new receiver is
|
||||
* the reference to the corresponding volatile property:
|
||||
*
|
||||
* internal val a = atomic(0) internal @Volatile var a$volatile = 0
|
||||
* <get-a>().compareAndSet(0, 5) ---> (this::a$volatile).compareAndSetField(0, 5)
|
||||
*
|
||||
*
|
||||
* 2. Function is called in the body of the transformed atomic extension,
|
||||
* the call receiver is the old <this> receiver of the extension.
|
||||
*
|
||||
* The call is replaced with atomic intrinsic call and the new receiver is
|
||||
* the invoked getter of the property reference that is the first parameter of the parent function:
|
||||
*
|
||||
* fun AtomicInt.foo(new: Int) { fun foo$atomicfu(crossinline refGetter: () -> KMutableProperty0<Int>, new: Int) {
|
||||
* this.compareAndSet(value, new) ---> refGetter().compareAndSetField(refGetter().get(), new)
|
||||
* } }
|
||||
*/
|
||||
val getPropertyReference = getVolatilePropertyReference(getPropertyReceiver, parentFunction)
|
||||
return irCallAtomicNativeIntrinsic(
|
||||
functionName = functionName,
|
||||
propertyRef = getPropertyReference,
|
||||
valueType = valueType,
|
||||
returnType = expression.type,
|
||||
valueArguments = expression.valueArguments
|
||||
)
|
||||
}
|
||||
|
||||
private fun NativeAtomicfuIrBuilder.getVolatilePropertyReference(
|
||||
getPropertyReceiver: IrExpression,
|
||||
parentFunction: IrFunction?
|
||||
): IrExpression = when {
|
||||
getPropertyReceiver is IrCall -> {
|
||||
/**
|
||||
* Function call receiver is atomic property getter call.
|
||||
* The new receiver is the reference to the corresponding volatile property:
|
||||
*
|
||||
* <get-a>().compareAndSet(0, 5) ---> (this::a$volatile).compareAndSetField(0, 5)
|
||||
*/
|
||||
val atomicProperty = getPropertyReceiver.getCorrespondingProperty()
|
||||
val volatileProperty = atomicfuPropertyToVolatile[atomicProperty]
|
||||
?: error("No volatile property was generated for the atomic property ${atomicProperty.render()}")
|
||||
irPropertyReference(
|
||||
property = volatileProperty,
|
||||
classReceiver = getPropertyReceiver.dispatchReceiver
|
||||
)
|
||||
}
|
||||
getPropertyReceiver.isThisReceiver() -> {
|
||||
/**
|
||||
* Function call receiver is the old <this> receiver of the extension.
|
||||
* The new receiver is the invoked getter of the property reference.
|
||||
* that is the first parameter of the parent function:
|
||||
*
|
||||
* fun AtomicInt.foo(new: Int) { fun foo$atomicfu(refGetter: () -> KMutableProperty0<Int>, new: Int) {
|
||||
* this.compareAndSet(value, new) ---> refGetter().compareAndSetField(refGetter().get(), new)
|
||||
* } }
|
||||
*/
|
||||
require(parentFunction != null && parentFunction.isTransformedAtomicExtension())
|
||||
val refGetter = parentFunction.valueParameters[0]
|
||||
require(refGetter.name.asString() == REF_GETTER && refGetter.type.classOrNull == irBuiltIns.functionN(0).symbol)
|
||||
irCall(atomicSymbols.invoke0Symbol).apply { dispatchReceiver = refGetter.capture() }
|
||||
}
|
||||
else -> error("Unsupported type of atomic receiver expression: ${getPropertyReceiver.render()}")
|
||||
}
|
||||
|
||||
override fun buildSyntheticValueArgsForTransformedAtomicExtensionCall(
|
||||
expression: IrCall,
|
||||
getPropertyReceiver: IrExpression,
|
||||
isArrayReceiver: Boolean,
|
||||
parentFunction: IrFunction
|
||||
): List<IrExpression?> {
|
||||
/**
|
||||
* 1. Receiver of the atomic extension call is atomic property getter call.
|
||||
* For atomic property receiver: generate getter lambda for the property reference corresponding to the call receiver
|
||||
*
|
||||
* inline fun AtomicInt.foo(arg: Int) {..} inline foo$atomicfu(refGetter: () -> KMutableProperty0<Int>, arg: Int)
|
||||
* aClass._a.foo(arg) --> foo$atomicfu({_ -> aClass::_a}, arg)
|
||||
*
|
||||
* For array property receiver: pass atomic array and the index of the element as an argument
|
||||
*
|
||||
* inline fun AtomicInt.foo(arg: Int) {..} inline foo$atomicfu$array(array: AtomicIntArray, index: Int, arg: Int)
|
||||
* aClass.intArr[7].foo(arg) --> foo$atomicfu$array(intArr, 7, arg)
|
||||
*
|
||||
* 2. Atomic extension is invoked in the body of the transformed atomic extension,
|
||||
* the call receiver is the old <this> receiver of the extension:
|
||||
* get the corresponding argument from the parent function.
|
||||
*
|
||||
* fun AtomicInt.bar() {..} fun bar$atomicfu(refGetter: () -> KMutableProperty0<Int>) { .. }
|
||||
*
|
||||
* fun AtomicInt.foo(new: Int) { fun foo$atomicfu(refGetter: () -> KMutableProperty0<Int>, new: Int) {
|
||||
* this.bar() ---> bar$atomicfu(refGetter)
|
||||
* } }
|
||||
*/
|
||||
with(atomicSymbols.createBuilder(expression.symbol)) {
|
||||
return if (isArrayReceiver) {
|
||||
val index = getPropertyReceiver.getArrayElementIndex(parentFunction)
|
||||
val atomicArray = getAtomicHandler(getPropertyReceiver, parentFunction)
|
||||
listOf(atomicArray, index)
|
||||
} else {
|
||||
listOf(buildVolatilePropertyRefGetter(getPropertyReceiver, parentFunction))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This counter is used to ensure uniqueness of functions for refGetter lambdas,
|
||||
// as several functions with the same name may be created in the same scope
|
||||
private var refGetterCounter: Int = 0
|
||||
|
||||
private fun NativeAtomicfuIrBuilder.buildVolatilePropertyRefGetter(
|
||||
getPropertyReceiver: IrExpression,
|
||||
parentFunction: IrFunction
|
||||
): IrExpression {
|
||||
when {
|
||||
getPropertyReceiver is IrCall -> {
|
||||
/**
|
||||
* Receiver of the atomic extension call is atomic property getter call.
|
||||
* Generate inline getter lambda of the corresponding volatile property
|
||||
* to pass as an argument to the transformed extension call:
|
||||
*
|
||||
* <get-a>().foo(5) ---> foo$atomicfu({_ -> this::a$volatile}, 5)
|
||||
*/
|
||||
val atomicProperty = getPropertyReceiver.getCorrespondingProperty()
|
||||
val volatileProperty = atomicfuPropertyToVolatile[atomicProperty]
|
||||
?: error("No volatile property was generated for the atomic property ${atomicProperty.render()}")
|
||||
val valueType = volatileProperty.backingField!!.type
|
||||
return IrFunctionExpressionImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||
type = atomicSymbols.kMutableProperty0GetterType(valueType),
|
||||
function = irBuiltIns.irFactory.buildFun {
|
||||
name = Name.identifier("<${volatileProperty.name.asString()}-getter-${refGetterCounter++}>")
|
||||
origin = AbstractAtomicSymbols.ATOMICFU_GENERATED_FUNCTION
|
||||
returnType = atomicSymbols.kMutableProperty0Type(valueType)
|
||||
isInline = true
|
||||
visibility = DescriptorVisibilities.LOCAL
|
||||
}.apply {
|
||||
val lambda = this
|
||||
body = irBlockBody {
|
||||
+irReturn(
|
||||
irPropertyReference(volatileProperty, getPropertyReceiver.dispatchReceiver)
|
||||
).apply {
|
||||
type = irBuiltIns.nothingType
|
||||
returnTargetSymbol = lambda.symbol
|
||||
}
|
||||
}
|
||||
parent = parentFunction
|
||||
},
|
||||
origin = IrStatementOrigin.LAMBDA
|
||||
)
|
||||
}
|
||||
getPropertyReceiver.isThisReceiver() -> {
|
||||
/**
|
||||
* Atomic extension is invoked in the body of the transformed atomic extension,
|
||||
* the call receiver is the old <this> receiver of the extension.
|
||||
* Pass the parameter of parent extension as an argument:
|
||||
*
|
||||
* fun AtomicInt.foo(new: Int) { fun foo$atomicfu(refGetter: () -> KMutableProperty0<Int>, new: Int) {
|
||||
* this.bar() ---> bar$atomicfu(refGetter)
|
||||
* } }
|
||||
*/
|
||||
require(parentFunction.isTransformedAtomicExtension())
|
||||
val refGetter = parentFunction.valueParameters[0]
|
||||
require(refGetter.name.asString() == REF_GETTER && refGetter.type.classOrNull == irBuiltIns.functionN(0).symbol)
|
||||
return refGetter.capture()
|
||||
}
|
||||
else -> error("Unsupported type of atomic receiver expression: ${getPropertyReceiver.render()}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun IrValueParameter.remapValueParameter(transformedExtension: IrFunction): IrValueParameter? {
|
||||
if (index < 0 && !type.isAtomicValueType()) {
|
||||
// data is a transformed function
|
||||
// index == -1 for `this` parameter
|
||||
return transformedExtension.dispatchReceiverParameter
|
||||
?: error("Dispatch receiver of ${transformedExtension.render()} is null" + CONSTRAINTS_MESSAGE)
|
||||
}
|
||||
if (index >= 0) {
|
||||
val shift = if (transformedExtension.name.asString().isMangledAtomicArrayExtension()) 2 else 1
|
||||
return transformedExtension.valueParameters[index + shift]
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun IrExpression.isArrayElementReceiver(parentFunction: IrFunction?): Boolean {
|
||||
return if (this is IrCall) this.isArrayElementGetter() else false
|
||||
}
|
||||
|
||||
override fun IrFunction.checkSyntheticArrayElementExtensionParameter(): Boolean {
|
||||
if (valueParameters.size < 2) return false
|
||||
return valueParameters[0].name.asString() == ATOMIC_ARRAY && atomicSymbols.isAtomicArrayHandlerType(valueParameters[0].type) &&
|
||||
valueParameters[1].name.asString() == INDEX && valueParameters[1].type == irBuiltIns.intType
|
||||
}
|
||||
|
||||
override fun IrFunction.checkSyntheticAtomicExtensionParameters(): Boolean {
|
||||
if (valueParameters.isEmpty()) return false
|
||||
return valueParameters[0].name.asString() == REF_GETTER && valueParameters[0].type.classOrNull == irBuiltIns.functionN(0).symbol
|
||||
}
|
||||
|
||||
override fun IrFunction.checkSyntheticParameterTypes(isArrayReceiver: Boolean, receiverValueType: IrType): Boolean {
|
||||
if (isArrayReceiver) {
|
||||
if (valueParameters.size < 2) return false
|
||||
val atomicArrayClassSymbol = atomicSymbols.getAtomicArrayClassByValueType(receiverValueType)
|
||||
return valueParameters[0].name.asString() == ATOMIC_ARRAY && valueParameters[0].type.classOrNull == atomicArrayClassSymbol &&
|
||||
valueParameters[1].name.asString() == INDEX && valueParameters[1].type == irBuiltIns.intType
|
||||
} else {
|
||||
if (valueParameters.size < 1) return false
|
||||
return valueParameters[0].name.asString() == REF_GETTER && valueParameters[0].type.classOrNull == irBuiltIns.functionN(0).symbol
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* On Native AtomicBoolean fields are transformed into Boolean volatile properties and updated via intrinsics.
|
||||
*/
|
||||
override val castBooleanFieldsToInt: Boolean
|
||||
get() = false
|
||||
|
||||
/**
|
||||
* Builds the signature of the transformed atomic extension:
|
||||
*
|
||||
* inline fun AtomicInt.foo(arg: Int) --> inline fun foo$atomicfu(refGetter: () -> KMutableProperty0<Int>, arg': Int)
|
||||
* inline fun foo$atomicfu$array(atomicArray: AtomicIntegerArray, index: Int, arg': Int)
|
||||
*/
|
||||
override fun buildTransformedAtomicExtensionSignature(atomicExtension: IrFunction, isArrayReceiver: Boolean): IrSimpleFunction {
|
||||
val mangledName = mangleAtomicExtensionName(atomicExtension.name.asString(), isArrayReceiver)
|
||||
val atomicReceiverType = atomicExtension.extensionReceiverParameter!!.type
|
||||
val valueType = (atomicReceiverType as IrSimpleType).atomicToPrimitiveType()
|
||||
return pluginContext.irFactory.buildFun {
|
||||
name = Name.identifier(mangledName)
|
||||
isInline = true
|
||||
visibility = atomicExtension.visibility
|
||||
origin = AbstractAtomicSymbols.ATOMICFU_GENERATED_FUNCTION
|
||||
}.apply {
|
||||
extensionReceiverParameter = null
|
||||
dispatchReceiverParameter = atomicExtension.dispatchReceiverParameter?.deepCopyWithSymbols(this)
|
||||
atomicExtension.typeParameters.forEach { addTypeParameter(it.name.asString(), it.representativeUpperBound) }
|
||||
addSyntheticValueParametersToTransformedAtomicExtension(isArrayReceiver, if (valueType == irBuiltIns.anyNType) atomicReceiverType.arguments.first().typeOrNull!! else valueType)
|
||||
atomicExtension.valueParameters.forEach { addValueParameter(it.name, it.type) }
|
||||
returnType = atomicExtension.returnType
|
||||
this.parent = atomicExtension.parent
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds synthetic value parameters to the transformed atomic extension (custom atomic extension or atomicfu inline update functions).
|
||||
*/
|
||||
override fun IrFunction.addSyntheticValueParametersToTransformedAtomicExtension(isArrayReceiver: Boolean, valueType: IrType) {
|
||||
if (!isArrayReceiver) {
|
||||
addValueParameter(REF_GETTER, atomicSymbols.kMutableProperty0GetterType(valueType)).apply { isCrossinline = true }
|
||||
} else {
|
||||
addValueParameter(ATOMIC_ARRAY, atomicSymbols.getParameterizedAtomicArrayType(valueType))
|
||||
addValueParameter(INDEX, irBuiltIns.intType)
|
||||
}
|
||||
}
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.kotlinx.atomicfu.compiler.backend.native
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.backend.jvm.functionByName
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.constructors
|
||||
import org.jetbrains.kotlin.ir.util.render
|
||||
import org.jetbrains.kotlin.name.*
|
||||
import org.jetbrains.kotlinx.atomicfu.compiler.backend.common.AbstractAtomicSymbols
|
||||
import org.jetbrains.kotlinx.atomicfu.compiler.backend.js.referenceFunction
|
||||
|
||||
class NativeAtomicSymbols(
|
||||
context: IrPluginContext,
|
||||
moduleFragment: IrModuleFragment
|
||||
) : AbstractAtomicSymbols(context, moduleFragment) {
|
||||
// kotlin.concurrent.Volatile annotation class
|
||||
override val volatileAnnotationClass: IrClass
|
||||
get() = context.referenceClass(ClassId(FqName("kotlin.concurrent"), Name.identifier("Volatile")))?.owner
|
||||
?: error("kotlin.concurrent.Volatile class is not found")
|
||||
|
||||
// kotlin.concurrent.AtomicIntArray
|
||||
override val atomicIntArrayClassSymbol: IrClassSymbol
|
||||
get() = context.referenceClass(ClassId(FqName("kotlin.concurrent"), Name.identifier("AtomicIntArray")))
|
||||
?: error("kotlin.concurrent.AtomicIntArray is not found")
|
||||
|
||||
// kotlin.concurrent.AtomicLongArray
|
||||
override val atomicLongArrayClassSymbol: IrClassSymbol
|
||||
get() = context.referenceClass(ClassId(FqName("kotlin.concurrent"), Name.identifier("AtomicLongArray")))
|
||||
?: error("kotlin.concurrent.AtomicLongArray is not found")
|
||||
|
||||
// kotlin.concurrent.AtomicArray
|
||||
override val atomicRefArrayClassSymbol: IrClassSymbol
|
||||
get() = context.referenceClass(ClassId(FqName("kotlin.concurrent"), Name.identifier("AtomicArray")))
|
||||
?: error("kotlin.concurrent.AtomicArray is not found")
|
||||
|
||||
override fun getAtomicArrayConstructor(atomicArrayClassSymbol: IrClassSymbol): IrFunctionSymbol =
|
||||
when (atomicArrayClassSymbol) {
|
||||
atomicIntArrayClassSymbol, atomicLongArrayClassSymbol -> {
|
||||
atomicArrayClassSymbol.owner.constructors.singleOrNull {
|
||||
it.valueParameters.size == 1 && it.valueParameters[0].type == irBuiltIns.intType
|
||||
}?.symbol ?: error("No `public constructor(size: Int) {}` was found for ${atomicArrayClassSymbol.owner.render()}")
|
||||
}
|
||||
atomicRefArrayClassSymbol -> {
|
||||
context.referenceFunctions(CallableId(FqName("kotlin.concurrent"), Name.identifier("AtomicArray"))).singleOrNull()
|
||||
?: error("No factory function `public inline fun <reified T> AtomicArray(size: Int, noinline init: (Int) -> T)` was found for ${atomicArrayClassSymbol.owner.render()}")
|
||||
}
|
||||
else -> error("Unsupported atomic array class found: ${atomicArrayClassSymbol.owner.render()}")
|
||||
}
|
||||
|
||||
// Intrinsics for atomic update of volatile properties
|
||||
|
||||
val atomicGetFieldIntrinsic =
|
||||
context.referenceFunctions(CallableId(FqName("kotlin.concurrent"), Name.identifier("atomicGetField"))).single()
|
||||
|
||||
val atomicSetFieldIntrinsic =
|
||||
context.referenceFunctions(CallableId(FqName("kotlin.concurrent"), Name.identifier("atomicSetField"))).single()
|
||||
|
||||
val compareAndSetFieldIntrinsic =
|
||||
context.referenceFunctions(CallableId(FqName("kotlin.concurrent"), Name.identifier("compareAndSetField"))).single()
|
||||
|
||||
val getAndSetFieldIntrinsic =
|
||||
context.referenceFunctions(CallableId(FqName("kotlin.concurrent"), Name.identifier("getAndSetField"))).single()
|
||||
|
||||
val getAndAddIntFieldIntrinsic =
|
||||
context.referenceFunctions(CallableId(FqName("kotlin.concurrent"), Name.identifier("getAndAddField")))
|
||||
.single { it.owner.returnType.isInt() }
|
||||
|
||||
val getAndAddLongFieldIntrinsic =
|
||||
context.referenceFunctions(CallableId(FqName("kotlin.concurrent"), Name.identifier("getAndAddField")))
|
||||
.single { it.owner.returnType.isLong() }
|
||||
|
||||
val intPlusOperator = context.referenceFunctions(CallableId(StandardClassIds.Int, Name.identifier("plus")))
|
||||
.single { it.owner.valueParameters[0].type.isInt() }
|
||||
|
||||
val longPlusOperator = context.referenceFunctions(CallableId(StandardClassIds.Long, Name.identifier("plus")))
|
||||
.single { it.owner.valueParameters[0].type.isLong() }
|
||||
|
||||
// KMutableProperty0<T>
|
||||
fun kMutableProperty0Type(typeArg: IrType): IrType =
|
||||
buildSimpleType(irBuiltIns.kMutableProperty0Class, listOf(typeArg))
|
||||
|
||||
// () -> KMutableProperty0<T>
|
||||
fun kMutableProperty0GetterType(typeArg: IrType): IrType = function0Type(kMutableProperty0Type(typeArg))
|
||||
|
||||
fun getParameterizedAtomicArrayType(elementType: IrType): IrType {
|
||||
val atomicArrayClassSymbol = getAtomicArrayClassByValueType(elementType)
|
||||
return if (atomicArrayClassSymbol == atomicRefArrayClassSymbol) {
|
||||
buildSimpleType(atomicArrayClassSymbol, listOf(elementType))
|
||||
} else {
|
||||
atomicArrayClassSymbol.defaultType
|
||||
}
|
||||
}
|
||||
|
||||
override fun createBuilder(symbol: IrSymbol, startOffset: Int, endOffset: Int) =
|
||||
NativeAtomicfuIrBuilder(this, symbol, startOffset, endOffset)
|
||||
}
|
||||
+395
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.kotlinx.atomicfu.compiler.backend.native
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlinx.atomicfu.compiler.backend.common.AbstractAtomicSymbols
|
||||
import org.jetbrains.kotlinx.atomicfu.compiler.backend.common.AbstractAtomicfuIrBuilder
|
||||
|
||||
class NativeAtomicfuIrBuilder(
|
||||
override val atomicSymbols: NativeAtomicSymbols,
|
||||
symbol: IrSymbol,
|
||||
startOffset: Int,
|
||||
endOffset: Int
|
||||
): AbstractAtomicfuIrBuilder(atomicSymbols.irBuiltIns, symbol, startOffset, endOffset) {
|
||||
|
||||
internal fun irCallAtomicNativeIntrinsic(
|
||||
functionName: String,
|
||||
propertyRef: IrExpression,
|
||||
valueType: IrType,
|
||||
returnType: IrType,
|
||||
valueArguments: List<IrExpression?>
|
||||
): IrExpression = when (functionName) {
|
||||
"<get-value>" -> atomicGetField(propertyRef, valueType)
|
||||
"<set-value>", "lazySet" -> atomicSetField(propertyRef, valueType, valueArguments[0])
|
||||
"compareAndSet" -> compareAndSetField(propertyRef, valueType, valueArguments[0], valueArguments[1])
|
||||
"getAndSet" -> getAndSetField(propertyRef, valueType, valueArguments[0])
|
||||
"getAndAdd" -> getAndAddField(propertyRef, valueType, valueArguments[0])
|
||||
"getAndIncrement" -> getAndIncrementField(propertyRef, valueType)
|
||||
"getAndDecrement" -> getAndDecrementField(propertyRef, valueType)
|
||||
"addAndGet" -> addAndGetField(propertyRef, valueType, valueArguments[0])
|
||||
"incrementAndGet" -> incrementAndGetField(propertyRef, valueType)
|
||||
"decrementAndGet" -> decrementAndGetField(propertyRef, valueType)
|
||||
else -> error("Unsupported atomic function name $functionName")
|
||||
}.let {
|
||||
if (returnType.isBoolean()) irImplicitCast(it, atomicSymbols.irBuiltIns.booleanType) else it
|
||||
}
|
||||
|
||||
private fun atomicGetField(receiver: IrExpression?, valueType: IrType): IrCall =
|
||||
irCall(atomicSymbols.atomicGetFieldIntrinsic).apply {
|
||||
extensionReceiver = receiver
|
||||
putTypeArgument(0, valueType)
|
||||
}
|
||||
|
||||
private fun atomicSetField(receiver: IrExpression?, valueType: IrType, newValue: IrExpression?): IrCall =
|
||||
irCall(atomicSymbols.atomicSetFieldIntrinsic).apply {
|
||||
extensionReceiver = receiver
|
||||
putTypeArgument(0, valueType)
|
||||
putValueArgument(0, newValue)
|
||||
}
|
||||
|
||||
private fun compareAndSetField(propertyRef: IrExpression, valueType: IrType, expected: IrExpression?, updated: IrExpression?) =
|
||||
callNativeAtomicIntrinsic(propertyRef, atomicSymbols.compareAndSetFieldIntrinsic, valueType, expected, updated)
|
||||
|
||||
private fun getAndSetField(propertyRef: IrExpression, valueType: IrType, value: IrExpression?) =
|
||||
callNativeAtomicIntrinsic(propertyRef, atomicSymbols.getAndSetFieldIntrinsic, valueType, value)
|
||||
|
||||
private fun getAndAddField(propertyRef: IrExpression, valueType: IrType, delta: IrExpression?): IrCall =
|
||||
when {
|
||||
valueType.isInt() ->
|
||||
callNativeAtomicIntrinsic(propertyRef, atomicSymbols.getAndAddIntFieldIntrinsic, null, delta)
|
||||
valueType.isLong() ->
|
||||
callNativeAtomicIntrinsic(
|
||||
propertyRef,
|
||||
atomicSymbols.getAndAddLongFieldIntrinsic,
|
||||
null,
|
||||
delta?.implicitCastTo(context.irBuiltIns.longType)
|
||||
)
|
||||
else -> error("kotlin.native.internal/getAndAddField intrinsic is not supported for values of type ${valueType.dumpKotlinLike()}")
|
||||
}
|
||||
|
||||
private fun addAndGetField(propertyRef: IrExpression, valueType: IrType, delta: IrExpression?): IrCall =
|
||||
getAndAddField(propertyRef, valueType, delta).plus(delta)
|
||||
|
||||
private fun getAndIncrementField(propertyRef: IrExpression, valueType: IrType): IrCall {
|
||||
val delta = if (valueType.isInt()) irInt(1) else irLong(1)
|
||||
return getAndAddField(propertyRef, valueType, delta)
|
||||
}
|
||||
|
||||
private fun getAndDecrementField(propertyRef: IrExpression, valueType: IrType): IrCall {
|
||||
val delta = if (valueType.isInt()) irInt(-1) else irLong(-1)
|
||||
return getAndAddField(propertyRef, valueType, delta)
|
||||
}
|
||||
|
||||
private fun incrementAndGetField(propertyRef: IrExpression, valueType: IrType): IrCall {
|
||||
val delta = if (valueType.isInt()) irInt(1) else irLong(1)
|
||||
return addAndGetField(propertyRef, valueType, delta)
|
||||
}
|
||||
|
||||
private fun decrementAndGetField(propertyRef: IrExpression, valueType: IrType): IrCall {
|
||||
val delta = if (valueType.isInt()) irInt(-1) else irLong(-1)
|
||||
return addAndGetField(propertyRef, valueType, delta)
|
||||
}
|
||||
|
||||
private fun callNativeAtomicIntrinsic(
|
||||
propertyRef: IrExpression,
|
||||
symbol: IrSimpleFunctionSymbol,
|
||||
typeArgument: IrType?,
|
||||
vararg valueArguments: IrExpression?
|
||||
): IrCall =
|
||||
irCall(symbol).apply {
|
||||
extensionReceiver = propertyRef
|
||||
typeArgument?.let { putTypeArgument(0, it) }
|
||||
valueArguments.forEachIndexed { index, arg ->
|
||||
putValueArgument(index, arg)
|
||||
}
|
||||
}
|
||||
|
||||
private fun invokePropertyGetter(refGetter: IrExpression) = irCall(atomicSymbols.invoke0Symbol).apply { dispatchReceiver = refGetter }
|
||||
|
||||
/*
|
||||
inline fun <T> loop$atomicfu(refGetter: () -> KMutableProperty0<T>, action: (T) -> Unit) {
|
||||
while (true) {
|
||||
val cur = refGetter().get()
|
||||
action(cur)
|
||||
}
|
||||
}
|
||||
*/
|
||||
override fun atomicfuLoopBody(valueType: IrType, valueParameters: List<IrValueParameter>): IrBlockBody =
|
||||
irBlockBody {
|
||||
val refGetter = valueParameters[0]
|
||||
val action = valueParameters[1]
|
||||
+irWhile().apply {
|
||||
condition = irTrue()
|
||||
body = irBlock {
|
||||
val cur = createTmpVariable(
|
||||
atomicGetField(invokePropertyGetter(irGet(refGetter)), valueType),
|
||||
"atomicfu\$cur", false
|
||||
)
|
||||
+irCall(atomicSymbols.invoke1Symbol).apply {
|
||||
dispatchReceiver = irGet(action)
|
||||
putValueArgument(0, irGet(cur))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
inline fun <T> loop$atomicfu$array(atomicArray: AtomicArray<T>, index: Int, action: (T) -> Unit) {
|
||||
while (true) {
|
||||
val cur = atomicArray.get(index)
|
||||
action(cur)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
override fun atomicfuArrayLoopBody(atomicArrayClass: IrClassSymbol, valueType: IrType, valueParameters: List<IrValueParameter>): IrBlockBody =
|
||||
irBlockBody {
|
||||
val array = valueParameters[0]
|
||||
val index = valueParameters[1]
|
||||
val action = valueParameters[2]
|
||||
+irWhile().apply {
|
||||
condition = irTrue()
|
||||
body = irBlock {
|
||||
val atomicArrayClassSymbol = (array.type as IrSimpleType).classOrNull ?: error("Failed to obtain the class corresponding to the array type ${array.render()}")
|
||||
val cur = createTmpVariable(
|
||||
atomicGetArrayElement(atomicArrayClassSymbol, valueType, irGet(array), irGet(index)),
|
||||
"atomicfu\$cur", false
|
||||
)
|
||||
+irCall(atomicSymbols.invoke1Symbol).apply {
|
||||
dispatchReceiver = irGet(action)
|
||||
putValueArgument(0, irGet(cur))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
inline fun update$atomicfu(refGetter: () -> KMutableProperty0<Int>, action: (Int) -> Int) {
|
||||
while (true) {
|
||||
val cur = refGetter().get()
|
||||
val upd = action(cur)
|
||||
if (refGetter().compareAndSetField(cur, upd)) return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline fun getAndUpdate$atomicfu(refGetter: () -> KMutableProperty0<Int>, action: (Int) -> Int): Int {
|
||||
while (true) {
|
||||
val cur = refGetter().get()
|
||||
val upd = action(cur)
|
||||
if (refGetter().compareAndSetField(cur, upd)) return cur
|
||||
}
|
||||
}
|
||||
|
||||
inline fun getAndUpdate$atomicfu(refGetter: () -> KMutableProperty0<Int>, action: (Int) -> Int): Int {
|
||||
while (true) {
|
||||
val cur = refGetter().get()
|
||||
val upd = action(cur)
|
||||
if (refGetter().compareAndSetField(cur, upd)) return upd
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
override fun atomicfuUpdateBody(functionName: String, valueType: IrType, valueParameters: List<IrValueParameter>): IrBlockBody =
|
||||
irBlockBody {
|
||||
val refGetter = valueParameters[0]
|
||||
val action = valueParameters[1]
|
||||
+irWhile().apply {
|
||||
condition = irTrue()
|
||||
body = irBlock {
|
||||
val cur = createTmpVariable(
|
||||
atomicGetField(invokePropertyGetter(irGet(refGetter)), valueType),
|
||||
"atomicfu\$cur", false
|
||||
)
|
||||
val upd = createTmpVariable(
|
||||
irCall(atomicSymbols.invoke1Symbol).apply {
|
||||
dispatchReceiver = irGet(action)
|
||||
putValueArgument(0, irGet(cur))
|
||||
}, "atomicfu\$upd", false
|
||||
)
|
||||
+irIfThen(
|
||||
type = atomicSymbols.irBuiltIns.unitType,
|
||||
condition = irCallAtomicNativeIntrinsic(
|
||||
functionName = "compareAndSet",
|
||||
propertyRef = invokePropertyGetter(irGet(refGetter)),
|
||||
valueType = valueType,
|
||||
returnType = atomicSymbols.irBuiltIns.booleanType,
|
||||
valueArguments = listOf(irGet(cur), irGet(upd))
|
||||
),
|
||||
thenPart = when (functionName) {
|
||||
"update" -> irReturnUnit()
|
||||
"getAndUpdate" -> irReturn(irGet(cur))
|
||||
"updateAndGet" -> irReturn(irGet(upd))
|
||||
else -> error("Unsupported atomicfu inline loop function name: $functionName")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
inline fun update$atomicfu$array(atomicArray: AtomicArray<T>, index: Int, action: (Int) -> Int) {
|
||||
while (true) {
|
||||
val cur = atomicArray[index]
|
||||
val upd = action(cur)
|
||||
if (atomicArray.compareAndSet(index, cur, upd)) return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline fun getAndUpdate$atomicfu$array(atomicArray: AtomicArray<T>, index: Int, action: (Int) -> Int): Int {
|
||||
while (true) {
|
||||
val cur = atomicArray[index]
|
||||
val upd = action(cur)
|
||||
if (atomicArray.compareAndSet(index, cur, upd)) return cur
|
||||
}
|
||||
}
|
||||
|
||||
inline fun getAndUpdate$atomicfu$array(atomicArray: AtomicArray<T>, index: Int, action: (Int) -> Int): Int {
|
||||
while (true) {
|
||||
val cur = atomicArray[index]
|
||||
val upd = action(cur)
|
||||
if (atomicArray.compareAndSet(index, cur, upd)) return upd
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
override fun atomicfuArrayUpdateBody(
|
||||
functionName: String,
|
||||
valueType: IrType,
|
||||
atomicArrayClass: IrClassSymbol,
|
||||
valueParameters: List<IrValueParameter>
|
||||
): IrBlockBody =
|
||||
irBlockBody {
|
||||
val array = valueParameters[0]
|
||||
val index = valueParameters[1]
|
||||
val action = valueParameters[2]
|
||||
+irWhile().apply {
|
||||
condition = irTrue()
|
||||
body = irBlock {
|
||||
val atomicArrayClassSymbol = (array.type as IrSimpleType).classOrNull ?: error("Failed to obtain the class corresponding to the array type ${array.render()}")
|
||||
val cur = createTmpVariable(
|
||||
atomicGetArrayElement(atomicArrayClassSymbol, valueType, irGet(array), irGet(index)),
|
||||
"atomicfu\$cur", false
|
||||
)
|
||||
val upd = createTmpVariable(
|
||||
irCall(atomicSymbols.invoke1Symbol).apply {
|
||||
dispatchReceiver = irGet(action)
|
||||
putValueArgument(0, irGet(cur))
|
||||
}, "atomicfu\$upd", false
|
||||
)
|
||||
+irIfThen(
|
||||
type = atomicSymbols.irBuiltIns.unitType,
|
||||
condition = callAtomicArray(
|
||||
arrayClassSymbol = atomicArrayClassSymbol,
|
||||
functionName = "compareAndSet",
|
||||
dispatchReceiver = irGet(array),
|
||||
index = irGet(index),
|
||||
valueArguments = listOf(irGet(cur), irGet(upd)),
|
||||
isBooleanReceiver = valueType.isBoolean()
|
||||
),
|
||||
thenPart = when (functionName) {
|
||||
"update" -> irReturnUnit()
|
||||
"getAndUpdate" -> irReturn(if (valueType.isBoolean() && irGet(cur).type.isInt()) irGet(cur).toBoolean() else irGet(cur))
|
||||
"updateAndGet" -> irReturn(if (valueType.isBoolean() && irGet(cur).type.isInt()) irGet(upd).toBoolean() else irGet(upd))
|
||||
else -> error("Unsupported atomicfu inline loop function name: $functionName")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrCall.plus(other: IrExpression?): IrCall {
|
||||
val returnType = this.symbol.owner.returnType
|
||||
val plusOperatorSymbol = when {
|
||||
returnType.isInt() -> atomicSymbols.intPlusOperator
|
||||
returnType.isLong() -> atomicSymbols.longPlusOperator
|
||||
else -> error("Return type of the function ${this.symbol.owner.dump()} is expected to be Int or Long, but found $returnType")
|
||||
}
|
||||
return irCall(plusOperatorSymbol).apply {
|
||||
dispatchReceiver = this@plus
|
||||
putValueArgument(0, other)
|
||||
}
|
||||
}
|
||||
|
||||
fun irPropertyReference(property: IrProperty, classReceiver: IrExpression?): IrPropertyReferenceImpl {
|
||||
val backingField = requireNotNull(property.backingField) { "Backing field of the property $property should not be null" }
|
||||
return IrPropertyReferenceImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||
type = atomicSymbols.buildSimpleType(context.irBuiltIns.kMutableProperty0Class, listOf(backingField.type)),
|
||||
symbol = property.symbol,
|
||||
typeArgumentsCount = 1,
|
||||
field = backingField.symbol,
|
||||
getter = property.getter?.symbol,
|
||||
setter = property.setter?.symbol
|
||||
).apply {
|
||||
putTypeArgument(0, backingField.type)
|
||||
dispatchReceiver = classReceiver
|
||||
}
|
||||
}
|
||||
|
||||
override fun newAtomicArray(
|
||||
atomicArrayClass: IrClassSymbol,
|
||||
size: IrExpression,
|
||||
dispatchReceiver: IrExpression?
|
||||
): IrFunctionAccessExpression =
|
||||
when (atomicArrayClass) {
|
||||
atomicSymbols.atomicIntArrayClassSymbol, atomicSymbols.atomicLongArrayClassSymbol -> {
|
||||
irCall(atomicSymbols.getAtomicArrayConstructor(atomicArrayClass)).apply {
|
||||
putValueArgument(0, size) // size
|
||||
this.dispatchReceiver = dispatchReceiver
|
||||
}
|
||||
}
|
||||
atomicSymbols.atomicRefArrayClassSymbol -> {
|
||||
val factoryFunction = atomicSymbols.getAtomicArrayConstructor(atomicArrayClass).owner
|
||||
irCall(factoryFunction).apply {
|
||||
val initType = atomicSymbols.function1Type(atomicSymbols.irBuiltIns.intType, atomicSymbols.irBuiltIns.anyNType)
|
||||
val nullLambda = IrFunctionExpressionImpl(
|
||||
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET,
|
||||
type = initType,
|
||||
function = atomicSymbols.irBuiltIns.irFactory.buildFun {
|
||||
name = Name.identifier("<anonymous>")
|
||||
origin = AbstractAtomicSymbols.ATOMICFU_GENERATED_FUNCTION
|
||||
returnType = atomicSymbols.irBuiltIns.anyNType
|
||||
visibility = DescriptorVisibilities.LOCAL
|
||||
}.apply {
|
||||
val lambda = this
|
||||
addValueParameter("it", atomicSymbols.irBuiltIns.intType)
|
||||
body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, listOf(
|
||||
IrReturnImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||
type = atomicSymbols.irBuiltIns.nothingType,
|
||||
returnTargetSymbol = lambda.symbol,
|
||||
value = IrConstImpl.constNull(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, atomicSymbols.irBuiltIns.nothingNType)
|
||||
)
|
||||
))
|
||||
parent = factoryFunction
|
||||
},
|
||||
origin = IrStatementOrigin.LAMBDA
|
||||
)
|
||||
putValueArgument(0, size) // size
|
||||
putValueArgument(1, nullLambda)
|
||||
this.dispatchReceiver = dispatchReceiver
|
||||
}
|
||||
}
|
||||
else -> error("Unsupported atomic array class found: ${atomicArrayClass.owner.render()}")
|
||||
}
|
||||
}
|
||||
+17
-7
@@ -17,22 +17,32 @@ import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
import org.jetbrains.kotlin.platform.isJs
|
||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||
import org.jetbrains.kotlin.platform.konan.isNative
|
||||
import org.jetbrains.kotlinx.atomicfu.compiler.backend.jvm.JvmAtomicSymbols
|
||||
import org.jetbrains.kotlinx.atomicfu.compiler.backend.js.AtomicfuJsIrTransformer
|
||||
import org.jetbrains.kotlinx.atomicfu.compiler.backend.jvm.AtomicfuJvmIrTransformer
|
||||
import org.jetbrains.kotlinx.atomicfu.compiler.backend.native.AtomicfuNativeIrTransformer
|
||||
import org.jetbrains.kotlinx.atomicfu.compiler.backend.native.NativeAtomicSymbols
|
||||
|
||||
public open class AtomicfuLoweringExtension : IrGenerationExtension {
|
||||
override fun generate(
|
||||
moduleFragment: IrModuleFragment,
|
||||
pluginContext: IrPluginContext
|
||||
) {
|
||||
if (pluginContext.platform.isJvm()) {
|
||||
val atomicSymbols = JvmAtomicSymbols(pluginContext, moduleFragment)
|
||||
AtomicfuJvmIrTransformer(pluginContext, atomicSymbols).transform(moduleFragment)
|
||||
}
|
||||
if (pluginContext.platform.isJs()) {
|
||||
for (file in moduleFragment.files) {
|
||||
AtomicfuClassLowering(pluginContext).runOnFileInOrder(file)
|
||||
val platform = pluginContext.platform
|
||||
when {
|
||||
platform.isJvm() -> {
|
||||
val atomicSymbols = JvmAtomicSymbols(pluginContext, moduleFragment)
|
||||
AtomicfuJvmIrTransformer(pluginContext, atomicSymbols).transform(moduleFragment)
|
||||
}
|
||||
platform.isNative() -> {
|
||||
val atomicSymbols = NativeAtomicSymbols(pluginContext, moduleFragment)
|
||||
AtomicfuNativeIrTransformer(pluginContext, atomicSymbols).transform(moduleFragment)
|
||||
}
|
||||
platform.isJs() -> {
|
||||
for (file in moduleFragment.files) {
|
||||
AtomicfuClassLowering(pluginContext).runOnFileInOrder(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+160
-11
@@ -1,13 +1,9 @@
|
||||
import kotlinx.atomicfu.*
|
||||
import kotlin.test.*
|
||||
|
||||
class ArrayInlineExtensionTest {
|
||||
class AtomicIntArrayInlineExtensionTest {
|
||||
private val intArr = AtomicIntArray(10)
|
||||
private val a = atomic(100)
|
||||
private val longArr = AtomicLongArray(10)
|
||||
private val refArr = atomicArrayOfNulls<Any?>(5)
|
||||
|
||||
class A(val s: String)
|
||||
private val a = atomic(0)
|
||||
|
||||
private inline fun casLoop(to: Int): Int {
|
||||
intArr[0].loop { cur ->
|
||||
@@ -16,8 +12,8 @@ class ArrayInlineExtensionTest {
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun casLoopExpression(to: Long): Long = longArr[3].loop { cur ->
|
||||
if (longArr[3].compareAndSet(cur, to)) return longArr[3].value
|
||||
private inline fun casLoopExpression(to: Int): Int = intArr[3].loop { cur ->
|
||||
if (intArr[3].compareAndSet(cur, to)) return intArr[3].value
|
||||
return 777
|
||||
}
|
||||
|
||||
@@ -60,7 +56,7 @@ class ArrayInlineExtensionTest {
|
||||
return foo(value + delta)
|
||||
}
|
||||
|
||||
fun testIntExtensionLoops() {
|
||||
fun test() {
|
||||
assertEquals(5, casLoop(5))
|
||||
assertEquals(6, casLoopExpression(6))
|
||||
assertEquals(66, intArr[1].extensionLoop(66))
|
||||
@@ -72,8 +68,161 @@ class ArrayInlineExtensionTest {
|
||||
}
|
||||
}
|
||||
|
||||
class AtomicLongArrayInlineExtensionTest {
|
||||
private val longArr = AtomicLongArray(10)
|
||||
private val a = atomic(0L)
|
||||
|
||||
private inline fun casLoop(to: Long): Long {
|
||||
longArr[0].loop { cur ->
|
||||
if (longArr[0].compareAndSet(cur, to)) return longArr[0].value
|
||||
return 777L
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun casLoopExpression(to: Long): Long = longArr[3].loop { cur ->
|
||||
if (longArr[3].compareAndSet(cur, to)) return longArr[3].value
|
||||
return 777L
|
||||
}
|
||||
|
||||
private inline fun AtomicLong.extensionLoop(to: Long): Long {
|
||||
loop { cur ->
|
||||
if (compareAndSet(cur, to)) return value
|
||||
return 777L
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun AtomicLong.extensionLoopExpression(to: Long): Long = loop { cur ->
|
||||
lazySet(cur + 10L)
|
||||
return if (compareAndSet(cur, to)) value else incrementAndGet()
|
||||
}
|
||||
|
||||
private inline fun AtomicLong.extensionLoopMixedReceivers(first: Long, second: Long, index: Int): Long {
|
||||
loop { cur ->
|
||||
compareAndSet(cur, first)
|
||||
longArr[index].compareAndSet(first, second)
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun AtomicLong.extensionLoopRecursive(to: Long): Long {
|
||||
loop { cur ->
|
||||
compareAndSet(cur, to)
|
||||
a.extensionLoop(5L)
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun AtomicLong.foo(to: Long): Long {
|
||||
loop { cur ->
|
||||
if (compareAndSet(cur, to)) return 777L
|
||||
else return value
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun AtomicLong.bar(delta: Long): Long {
|
||||
return foo(value + delta)
|
||||
}
|
||||
|
||||
fun test() {
|
||||
assertEquals(5L, casLoop(5L))
|
||||
assertEquals(6L, casLoopExpression(6L))
|
||||
assertEquals(66L, longArr[1].extensionLoop(66L))
|
||||
assertEquals(66L, longArr[2].extensionLoop(66L))
|
||||
assertEquals(77L, longArr[1].extensionLoopExpression(777L))
|
||||
assertEquals(99L, longArr[1].extensionLoopMixedReceivers(88L, 99L, 1))
|
||||
assertEquals(100L, longArr[1].extensionLoopRecursive(100L))
|
||||
assertEquals(777L, longArr[1].bar(100L))
|
||||
}
|
||||
}
|
||||
|
||||
class AtomicBooleanArrayInlineExtensionTest {
|
||||
private val booleanArr = AtomicBooleanArray(10)
|
||||
|
||||
private inline fun casLoop(to: Boolean): Boolean {
|
||||
booleanArr[0].loop { cur ->
|
||||
if (booleanArr[0].compareAndSet(cur, to)) return booleanArr[0].value
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun casLoopExpression(to: Boolean): Boolean = booleanArr[3].loop { cur ->
|
||||
if (booleanArr[3].compareAndSet(cur, to)) return booleanArr[3].value
|
||||
}
|
||||
|
||||
private inline fun AtomicBoolean.extensionLoop(to: Boolean): Boolean {
|
||||
loop { cur ->
|
||||
if (compareAndSet(cur, to)) return value
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun AtomicBoolean.extensionLoopExpression(to: Boolean): Boolean = loop { cur ->
|
||||
lazySet(false)
|
||||
return if (compareAndSet(cur, to)) value else !value
|
||||
}
|
||||
|
||||
private inline fun AtomicBoolean.extensionLoopMixedReceivers(first: Boolean, second: Boolean, index: Int): Boolean {
|
||||
loop { cur ->
|
||||
compareAndSet(cur, first)
|
||||
booleanArr[index].compareAndSet(first, second)
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
fun test() {
|
||||
assertEquals(true, casLoop(true))
|
||||
assertEquals(true, casLoopExpression(true))
|
||||
assertEquals(true, booleanArr[1].extensionLoop(true))
|
||||
assertEquals(true, booleanArr[1].extensionLoopExpression(true))
|
||||
assertEquals(false, booleanArr[7].extensionLoopMixedReceivers(true, false, 7))
|
||||
}
|
||||
}
|
||||
|
||||
class AtomicRefArrayInlineExtensionTest {
|
||||
private val refArr = atomicArrayOfNulls<String?>(10)
|
||||
private val a = atomic(0L)
|
||||
|
||||
private inline fun casLoop(to: String): String? {
|
||||
refArr[0].loop { cur ->
|
||||
if (refArr[0].compareAndSet(cur, to)) return refArr[0].value
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun casLoopExpression(to: String): String? = refArr[3].loop { cur ->
|
||||
if (refArr[3].compareAndSet(cur, to)) return refArr[3].value
|
||||
}
|
||||
|
||||
private inline fun AtomicRef<String?>.extensionLoop(to: String): String? {
|
||||
loop { cur ->
|
||||
if (compareAndSet(cur, to)) return value
|
||||
else "incorrect"
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun AtomicRef<String?>.extensionLoopExpression(to: String): String? = loop { cur ->
|
||||
lazySet("aaa")
|
||||
return if (compareAndSet(cur, to)) value else "CAS_failed"
|
||||
}
|
||||
|
||||
private inline fun AtomicRef<String?>.extensionLoopMixedReceivers(first: String, second: String, index: Int): String? {
|
||||
loop { cur ->
|
||||
compareAndSet(cur, first)
|
||||
refArr[index].compareAndSet(first, second)
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
fun test() {
|
||||
assertEquals("aaa", casLoop("aaa"))
|
||||
assertEquals("bbb", casLoopExpression("bbb"))
|
||||
assertEquals("ccc", refArr[1].extensionLoop("ccc"))
|
||||
assertEquals("CAS_failed", refArr[1].extensionLoopExpression("ccc"))
|
||||
assertEquals("bbb", refArr[7].extensionLoopMixedReceivers("aaa", "bbb", 7))
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val testClass = ArrayInlineExtensionTest()
|
||||
testClass.testIntExtensionLoops()
|
||||
AtomicIntArrayInlineExtensionTest().test()
|
||||
AtomicLongArrayInlineExtensionTest().test()
|
||||
AtomicBooleanArrayInlineExtensionTest().test()
|
||||
AtomicRefArrayInlineExtensionTest().test()
|
||||
return "OK"
|
||||
}
|
||||
|
||||
Vendored
+76
-16
@@ -1,26 +1,40 @@
|
||||
@kotlin.Metadata
|
||||
public final class ArrayInlineExtensionTest$A {
|
||||
public final class ArrayInlineExtensionTestKt {
|
||||
// source: 'ArrayInlineExtensionTest.kt'
|
||||
private final @org.jetbrains.annotations.NotNull field s: java.lang.String
|
||||
public method <init>(@org.jetbrains.annotations.NotNull p0: java.lang.String): void
|
||||
public final @org.jetbrains.annotations.NotNull method getS(): java.lang.String
|
||||
public final inner class ArrayInlineExtensionTest$A
|
||||
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
|
||||
}
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class ArrayInlineExtensionTest {
|
||||
public final class AtomicBooleanArrayInlineExtensionTest {
|
||||
// source: 'ArrayInlineExtensionTest.kt'
|
||||
private synthetic final field booleanArr: java.util.concurrent.atomic.AtomicIntegerArray
|
||||
public method <init>(): void
|
||||
private final method casLoop(p0: boolean): boolean
|
||||
private final method casLoopExpression(p0: boolean): boolean
|
||||
private synthetic final method extensionLoop$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, p2: boolean): boolean
|
||||
private synthetic final method extensionLoop$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: boolean): boolean
|
||||
private synthetic final method extensionLoopExpression$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, p2: boolean): boolean
|
||||
private synthetic final method extensionLoopExpression$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: boolean): boolean
|
||||
private synthetic final method extensionLoopMixedReceivers$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, p2: boolean, p3: boolean, p4: int): boolean
|
||||
private synthetic final method extensionLoopMixedReceivers$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: boolean, p3: boolean, p4: int): boolean
|
||||
private synthetic final method getBooleanArr(): java.util.concurrent.atomic.AtomicIntegerArray
|
||||
private synthetic final method loop$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, p2: kotlin.jvm.functions.Function1): void
|
||||
private synthetic final method loop$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: kotlin.jvm.functions.Function1): void
|
||||
public final method test(): void
|
||||
}
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class AtomicIntArrayInlineExtensionTest {
|
||||
// source: 'ArrayInlineExtensionTest.kt'
|
||||
private synthetic final static field a$volatile$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater
|
||||
private synthetic volatile field a$volatile: int
|
||||
private synthetic final field intArr: java.util.concurrent.atomic.AtomicIntegerArray
|
||||
private synthetic final field longArr: java.util.concurrent.atomic.AtomicLongArray
|
||||
private synthetic final field refArr: java.util.concurrent.atomic.AtomicReferenceArray
|
||||
static method <clinit>(): void
|
||||
public method <init>(): void
|
||||
private synthetic final method bar$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, p2: int): int
|
||||
private synthetic final method bar$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int): int
|
||||
private final method casLoop(p0: int): int
|
||||
private final method casLoopExpression(p0: long): long
|
||||
private final method casLoopExpression(p0: int): int
|
||||
private synthetic final method extensionLoop$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, p2: int): int
|
||||
private synthetic final method extensionLoop$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int): int
|
||||
private synthetic final method extensionLoopExpression$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, p2: int): int
|
||||
@@ -34,18 +48,64 @@ public final class ArrayInlineExtensionTest {
|
||||
private synthetic final static method getA$volatile$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater
|
||||
private synthetic final method getA$volatile(): int
|
||||
private synthetic final method getIntArr(): java.util.concurrent.atomic.AtomicIntegerArray
|
||||
private synthetic final method getLongArr(): java.util.concurrent.atomic.AtomicLongArray
|
||||
private synthetic final method getRefArr(): java.util.concurrent.atomic.AtomicReferenceArray
|
||||
private synthetic final method loop$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, p2: kotlin.jvm.functions.Function1): void
|
||||
private synthetic final method loop$atomicfu$array(p0: java.util.concurrent.atomic.AtomicLongArray, p1: int, p2: kotlin.jvm.functions.Function1): void
|
||||
private synthetic final method loop$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: kotlin.jvm.functions.Function1): void
|
||||
private synthetic final method setA$volatile(p0: int): void
|
||||
public final method testIntExtensionLoops(): void
|
||||
public final inner class ArrayInlineExtensionTest$A
|
||||
public final method test(): void
|
||||
}
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class ArrayInlineExtensionTestKt {
|
||||
public final class AtomicLongArrayInlineExtensionTest {
|
||||
// source: 'ArrayInlineExtensionTest.kt'
|
||||
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
|
||||
private synthetic final static field a$volatile$FU: java.util.concurrent.atomic.AtomicLongFieldUpdater
|
||||
private synthetic volatile field a$volatile: long
|
||||
private synthetic final field longArr: java.util.concurrent.atomic.AtomicLongArray
|
||||
static method <clinit>(): void
|
||||
public method <init>(): void
|
||||
private synthetic final method bar$atomicfu$array(p0: java.util.concurrent.atomic.AtomicLongArray, p1: int, p2: long): long
|
||||
private synthetic final method bar$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicLongFieldUpdater, p2: long): long
|
||||
private final method casLoop(p0: long): long
|
||||
private final method casLoopExpression(p0: long): long
|
||||
private synthetic final method extensionLoop$atomicfu$array(p0: java.util.concurrent.atomic.AtomicLongArray, p1: int, p2: long): long
|
||||
private synthetic final method extensionLoop$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicLongFieldUpdater, p2: long): long
|
||||
private synthetic final method extensionLoopExpression$atomicfu$array(p0: java.util.concurrent.atomic.AtomicLongArray, p1: int, p2: long): long
|
||||
private synthetic final method extensionLoopExpression$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicLongFieldUpdater, p2: long): long
|
||||
private synthetic final method extensionLoopMixedReceivers$atomicfu$array(p0: java.util.concurrent.atomic.AtomicLongArray, p1: int, p2: long, p3: long, p4: int): long
|
||||
private synthetic final method extensionLoopMixedReceivers$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicLongFieldUpdater, p2: long, p3: long, p4: int): long
|
||||
private synthetic final method extensionLoopRecursive$atomicfu$array(p0: java.util.concurrent.atomic.AtomicLongArray, p1: int, p2: long): long
|
||||
private synthetic final method extensionLoopRecursive$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicLongFieldUpdater, p2: long): long
|
||||
private synthetic final method foo$atomicfu$array(p0: java.util.concurrent.atomic.AtomicLongArray, p1: int, p2: long): long
|
||||
private synthetic final method foo$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicLongFieldUpdater, p2: long): long
|
||||
private synthetic final static method getA$volatile$FU(): java.util.concurrent.atomic.AtomicLongFieldUpdater
|
||||
private synthetic final method getA$volatile(): long
|
||||
private synthetic final method getLongArr(): java.util.concurrent.atomic.AtomicLongArray
|
||||
private synthetic final method loop$atomicfu$array(p0: java.util.concurrent.atomic.AtomicLongArray, p1: int, p2: kotlin.jvm.functions.Function1): void
|
||||
private synthetic final method loop$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicLongFieldUpdater, p2: kotlin.jvm.functions.Function1): void
|
||||
private synthetic final method setA$volatile(p0: long): void
|
||||
public final method test(): void
|
||||
}
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class AtomicRefArrayInlineExtensionTest {
|
||||
// source: 'ArrayInlineExtensionTest.kt'
|
||||
private synthetic final static field a$volatile$FU: java.util.concurrent.atomic.AtomicLongFieldUpdater
|
||||
private synthetic volatile field a$volatile: long
|
||||
private synthetic final field refArr: java.util.concurrent.atomic.AtomicReferenceArray
|
||||
static method <clinit>(): void
|
||||
public method <init>(): void
|
||||
private final method casLoop(p0: java.lang.String): java.lang.String
|
||||
private final method casLoopExpression(p0: java.lang.String): java.lang.String
|
||||
private synthetic final method extensionLoop$atomicfu$array(p0: java.util.concurrent.atomic.AtomicReferenceArray, p1: int, p2: java.lang.String): java.lang.String
|
||||
private synthetic final method extensionLoop$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p2: java.lang.String): java.lang.String
|
||||
private synthetic final method extensionLoopExpression$atomicfu$array(p0: java.util.concurrent.atomic.AtomicReferenceArray, p1: int, p2: java.lang.String): java.lang.String
|
||||
private synthetic final method extensionLoopExpression$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p2: java.lang.String): java.lang.String
|
||||
private synthetic final method extensionLoopMixedReceivers$atomicfu$array(p0: java.util.concurrent.atomic.AtomicReferenceArray, p1: int, p2: java.lang.String, p3: java.lang.String, p4: int): java.lang.String
|
||||
private synthetic final method extensionLoopMixedReceivers$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p2: java.lang.String, p3: java.lang.String, p4: int): java.lang.String
|
||||
private synthetic final static method getA$volatile$FU(): java.util.concurrent.atomic.AtomicLongFieldUpdater
|
||||
private synthetic final method getA$volatile(): long
|
||||
private synthetic final method getRefArr(): java.util.concurrent.atomic.AtomicReferenceArray
|
||||
private synthetic final method loop$atomicfu$array(p0: java.util.concurrent.atomic.AtomicReferenceArray, p1: int, p2: kotlin.jvm.functions.Function1): void
|
||||
private synthetic final method loop$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p2: kotlin.jvm.functions.Function1): void
|
||||
private synthetic final method setA$volatile(p0: long): void
|
||||
public final method test(): void
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user