[atomicfu-K/N] Introduction of K/N transformation
diff --git a/libraries/tools/atomicfu/src/common/kotlin/org/jetbrains/kotlinx/atomicfu/gradle/AtomicfuKotlinGradleSubplugin.kt b/libraries/tools/atomicfu/src/common/kotlin/org/jetbrains/kotlinx/atomicfu/gradle/AtomicfuKotlinGradleSubplugin.kt
index afcdc9a..e1a66db 100644
--- a/libraries/tools/atomicfu/src/common/kotlin/org/jetbrains/kotlinx/atomicfu/gradle/AtomicfuKotlinGradleSubplugin.kt
+++ b/libraries/tools/atomicfu/src/common/kotlin/org/jetbrains/kotlinx/atomicfu/gradle/AtomicfuKotlinGradleSubplugin.kt
@@ -37,7 +37,8 @@
         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 @@
     open class AtomicfuKotlinGradleExtension {
         var isJsIrTransformationEnabled = false
         var isJvmIrTransformationEnabled = false
+        var isNativeIrTransformationEnabled = false
     }
 
     override fun getPluginArtifact(): SubpluginArtifact =
@@ -58,4 +60,6 @@
     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?
 }
diff --git a/native/native.tests/build.gradle.kts b/native/native.tests/build.gradle.kts
index 215c0ea..95f12a7 100644
--- a/native/native.tests/build.gradle.kts
+++ b/native/native.tests/build.gradle.kts
@@ -38,6 +38,8 @@
     }
 }
 
+testsJar {}
+
 if (kotlinBuildProperties.isInJpsBuildIdeaSync) {
     apply(plugin = "idea")
     idea {
diff --git a/native/native.tests/tests/org/jetbrains/kotlin/generators/tests/GenerateNativeTests.kt b/native/native.tests/tests/org/jetbrains/kotlin/generators/tests/GenerateNativeTests.kt
index cefd90c..0056786 100644
--- a/native/native.tests/tests/org/jetbrains/kotlin/generators/tests/GenerateNativeTests.kt
+++ b/native/native.tests/tests/org/jetbrains/kotlin/generators/tests/GenerateNativeTests.kt
@@ -241,6 +241,16 @@
                 model("diagnostics/nativeTests", excludedPattern = CUSTOM_TEST_DATA_EXTENSION_PATTERN)
             }
         }
+
+        // Atomicfu compiler plugin native tests. // TODO: to delete
+        testGroup("plugins/atomicfu/atomicfu-compiler/test", "plugins/atomicfu/atomicfu-compiler/testData") {
+            testClass<AbstractNativeBlackBoxTest>(
+                suiteTestClassName = "AtomicfuNativeTestGenerated",
+                annotations = listOf(atomicfu(), provider<UseStandardTestCaseGroupProvider>())
+            ) {
+                model("nativeBox")
+            }
+        }
     }
 }
 
@@ -280,3 +290,6 @@
 
 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")
diff --git a/plugins/atomicfu/atomicfu-compiler/build.gradle.kts b/plugins/atomicfu/atomicfu-compiler/build.gradle.kts
index 90c6cbe..db31341 100644
--- a/plugins/atomicfu/atomicfu-compiler/build.gradle.kts
+++ b/plugins/atomicfu/atomicfu-compiler/build.gradle.kts
@@ -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 @@
     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 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(org.jetbrains.kotlin.gradle.plugin.mpp.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,8 @@
 }
 
 dependencies {
+    testImplementation(project(mapOf("path" to ":native:native.tests")))
+    testImplementation(project(mapOf("path" to ":native:native.tests")))
     compileOnly(intellijCore())
     compileOnly(commonDependency("org.jetbrains.intellij.deps:asm-all"))
 
@@ -68,14 +90,31 @@
     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.20.2") { isTransitive = false }
+    atomicfuNativeKlib("org.jetbrains.kotlinx:atomicfu:0.20.2") { isTransitive = false }
 
     embedded(project(":kotlinx-atomicfu-runtime")) {
         attributes {
@@ -125,3 +164,13 @@
 
 publish()
 standardPublicJars()
+
+// val myInfrastructureTest = nativeTest("myInfrastructureTest", "infrastructure")
+
+val nativeBoxTest = nativeTest(
+    taskName = "nativeBoxTest",
+    tag = "atomicfu",
+    requirePlatformLibs = true,
+    customDependencies = listOf(atomicfuJvmClasspath),
+    customKlibDependencies = listOf(atomicfuNativeKlib)
+)
diff --git a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/native/AtomicfuNativeIrTransformer.kt b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/native/AtomicfuNativeIrTransformer.kt
new file mode 100644
index 0000000..b80104f
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/native/AtomicfuNativeIrTransformer.kt
@@ -0,0 +1,434 @@
+/*
+ * 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.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.AbstractAtomicfuTransformer
+import org.jetbrains.kotlinx.atomicfu.compiler.backend.js.buildSimpleType
+
+
+private const val ATOMICFU = "atomicfu"
+private const val LOOP = "loop"
+private const val UPDATE = "update"
+private const val ACTION = "action\$$ATOMICFU"
+private const val REF_GETTER = "refGetter\$$ATOMICFU"
+
+class AtomicfuNativeIrTransformer(
+    pluginContext: IrPluginContext,
+    override val atomicSymbols: NativeAtomicSymbols
+) : AbstractAtomicfuTransformer(pluginContext) {
+
+    override val atomicPropertiesTransformer: AtomicPropertiesTransformer
+        get() = NativeAtomicPropertiesTransformer()
+
+    override val atomicExtensionsTransformer: AtomicExtensionTransformer
+        get() = NativeAtomicExtensionTransformer()
+
+    override val atomicFunctionsTransformer: AtomicFunctionCallTransformer
+        get() = NativeAtomicFunctionCallTransformer()
+
+    private inner class NativeAtomicPropertiesTransformer : AtomicPropertiesTransformer() {
+
+        override fun IrClass.addTransformedInClassAtomic(atomicProperty: IrProperty): IrProperty =
+            addVolatileProperty(atomicProperty)
+
+        override fun IrDeclarationContainer.addTransformedStaticAtomic(atomicProperty: IrProperty): IrProperty =
+            addVolatileProperty(atomicProperty)
+
+        override fun IrDeclarationContainer.addTransformedAtomicArray(atomicProperty: IrProperty): IrProperty? {
+            // todo: just skip them (as a box) and do not transform any subsequent calls on this array
+            // todo: design API for atomic array intrinsics
+            return null
+        }
+
+        private fun IrDeclarationContainer.addVolatileProperty(from: IrProperty): IrProperty {
+            val parentContainer = this
+            with(atomicSymbols.createBuilder(from.symbol)) {
+                val volatileField = buildVolatileBackingField(from, parentContainer, false)
+                return parentContainer.addProperty(volatileField, from.visibility, isVar = true, isStatic = from.getter!!.dispatchReceiverParameter == null).also {
+                    atomicPropertyToVolatile[from] = it
+                }
+            }
+        }
+    }
+
+    private inner class NativeAtomicExtensionTransformer : AtomicExtensionTransformer() {
+
+        override fun IrDeclarationContainer.transformAllAtomicExtensions() {
+            declarations.filter { it is IrSimpleFunction && it.isAtomicExtension() }.forEach {
+                declarations.add((it as IrSimpleFunction).deepCopyWithSymbols(this).transformAtomicExtension())
+                // TODO: while arrays are not supported, non-transformed atomic extensions are not removed
+                //declarations.remove(it)
+            }
+        }
+
+        // inline fun AtomicInt.foo(arg: Int) -> foo(crossinline refGetter: () -> KMutableProperty0<Int>, arg: Int)
+        private fun IrSimpleFunction.transformAtomicExtension(): IrFunction {
+            val mangledName = mangleAtomicExtensionName(this.name.asString(), false)
+            val valueType = extensionReceiverParameter!!.type.atomicToPrimitiveType()
+            this.name = Name.identifier(mangledName)
+            val refGetterLambda = buildValueParameter(this) {
+                name = Name.identifier("refGetter\$atomicfu")
+                index = 0
+                type = atomicSymbols.kMutableProperty0GetterType(valueType)
+                isCrossInline = true
+            }
+            valueParameters = listOf(refGetterLambda) + valueParameters
+            extensionReceiverParameter = null
+            return this
+        }
+    }
+
+    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)) {
+                // Transformation of the original atomic extension body should be skipped,
+                // because invocations on atomic array elements are left untransformed.
+                val containingFunction =
+                    (if (parentFunction?.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA) parentFunction.parent else parentFunction) as? IrSimpleFunction
+                if (containingFunction != null && containingFunction.isAtomicExtension()) {
+                    return expression
+                }
+                /**
+                 * 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:
+                 *
+                 * val a = atomic(0)                    @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)
+                 * }                                      }
+                 */
+                requireNotNull(parentFunction) { "Parent function of the call ${expression.render()} is null" }
+                val getPropertyReference = buildVolatilePropertyReference(getPropertyReceiver, parentFunction)
+                return irCallAtomicNativeIntrinsic(
+                    functionName = functionName,
+                    propertyRef = getPropertyReference,
+                    valueType = valueType,
+                    valueArguments = expression.valueArguments
+                )
+            }
+
+        override fun transformAtomicUpdateCallOnArrayElement(
+            expression: IrCall,
+            functionName: String,
+            valueType: IrType,
+            getPropertyReceiver: IrExpression,
+            parentFunction: IrFunction?
+        ): IrExpression {
+            // invocations with array element receiver are left untransformed
+            return expression
+        }
+
+        override fun transformAtomicExtensionCall(
+            expression: IrCall,
+            originalAtomicExtension: IrSimpleFunction,
+            getPropertyReceiver: IrExpression,
+            isArrayReceiver: Boolean,
+            parentFunction: IrFunction?
+        ): IrCall =
+            with(atomicSymbols.createBuilder(expression.symbol)) {
+                // Transformation of the original atomic extension body should be skipped,
+                // because invocations on atomic array elements are left untransformed.
+                val containingFunction =
+                    (if (parentFunction?.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA) parentFunction.parent else parentFunction) as? IrSimpleFunction
+                if (isArrayReceiver || (containingFunction != null && containingFunction.isAtomicExtension())) {
+                    return expression
+                }
+                /**
+                 * Call of the atomic extension on the atomic property is replaced
+                 * with the transformed atomic extension call, passing getter of the corresponding volatile property as an argument:
+                 *
+                 *
+                 * 1. Receiver of the atomic extension call is atomic property getter call.
+                 *
+                 * Atomic extension call is replaced with the transformed extension invocation and
+                 * inline getter of the corresponding volatile property is passed as an argument:
+                 *
+                 * val a = atomic(0)                              @Volatile var a$volatile = 0
+                 * fun AtomicInt.foo(new: Int) {...}              fun foo$atomicfu(crossinline refGetter: () -> KMutableProperty0<Int>, new: Int)
+                 *
+                 * <get-a>().foo(5)                     --->      foo$atomicfu({_ -> this::a$volatile}, 5)
+                 *
+                 *
+                 * 2. Atomic extension is invoked in the body of the transformed atomic extension,
+                 * the call receiver is the old <this> receiver of the extension.
+                 *
+                 * This call is replaced with the transformed extension call
+                 * and takes the refGetter value parameter of the parent function as an argument.
+                 *
+                 * 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)
+                 * }                                      }
+                 */
+                requireNotNull(parentFunction) { "Parent function of the call ${expression.render()} is null" }
+                val parent = originalAtomicExtension.parent as IrDeclarationContainer
+                val transformedAtomicExtension = parent.getTransformedAtomicExtension(originalAtomicExtension, isArrayReceiver)
+                val volatilePropertyGetter = buildVolatilePropertyGetter(getPropertyReceiver, parentFunction) ?: return expression // 13107 // 13215
+                return irCallWithArgs(
+                    symbol = transformedAtomicExtension.symbol,
+                    dispatchReceiver = expression.dispatchReceiver,
+                    extensionReceiver = null,
+                    valueArguments = listOf(volatilePropertyGetter) + expression.valueArguments
+                )
+            }
+
+        override fun transformedAtomicfuInlineFunctionCall(
+            expression: IrCall,
+            functionName: String,
+            valueType: IrType,
+            getPropertyReceiver: IrExpression,
+            isArrayReceiver: Boolean,
+            parentFunction: IrFunction?
+        ): IrCall {
+            with(atomicSymbols.createBuilder(expression.symbol)) {
+                // Transformation of the original atomic extension body should be skipped,
+                // because invocations on atomic array elements are left untransformed.
+                val containingFunction =
+                    (if (parentFunction?.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA) parentFunction.parent else parentFunction) as? IrSimpleFunction
+                if (isArrayReceiver || (containingFunction != null && containingFunction.isAtomicExtension())) {
+                    return expression
+                }
+                requireNotNull(parentFunction) { "Parent function of the call ${expression.render()} is null" }
+                val loopFunc = parentFunction.parentDeclarationContainer.getOrBuildInlineLoopFunction(
+                    functionName = functionName,
+                    valueType = if (valueType.isBoolean()) irBuiltIns.intType else valueType,
+                    isArrayReceiver = isArrayReceiver
+                )
+                val action = (expression.getValueArgument(0) as IrFunctionExpression).apply {
+                    function.body?.transform(this@NativeAtomicFunctionCallTransformer, parentFunction)
+                    // todo check for type in extension receiver here
+                    // todo: we don't have to do this here, that's only for JVM
+                    if (function.valueParameters[0].type.isBoolean()) {
+                        function.valueParameters[0].type = irBuiltIns.intType
+                        function.returnType = irBuiltIns.intType
+                    }
+                }
+                val volatilePropertyGetter = buildVolatilePropertyGetter(getPropertyReceiver, parentFunction) ?: return expression
+                return irCallWithArgs(
+                    symbol = loopFunc.symbol,
+                    dispatchReceiver = parentFunction.containingFunction.dispatchReceiverParameter?.capture(),
+                    extensionReceiver = null,
+                    valueArguments = listOf(volatilePropertyGetter, action)
+                )
+            }
+        }
+
+        override fun IrDeclarationContainer.getTransformedAtomicExtension(
+            declaration: IrSimpleFunction,
+            isArrayReceiver: Boolean
+        ): IrSimpleFunction = findDeclaration {
+            it.name.asString() == mangleAtomicExtensionName(declaration.name.asString(), isArrayReceiver) &&
+                    it.isTransformedAtomicExtension()
+        }
+            ?: error("Could not find corresponding transformed declaration for the atomic extension ${declaration.render()} ${if (isArrayReceiver) "for array element receiver" else ""}")
+
+        override fun IrValueParameter.remapValueParameter(transformedExtension: IrFunction): IrValueParameter? {
+            if (index < 0 && !type.isAtomicValueType()) {
+                // index == -1 for `this` parameter
+                return transformedExtension.dispatchReceiverParameter
+                    ?: error { "Dispatch receiver of ${transformedExtension.render()} is null" }
+            }
+            if (index >= 0) {
+                return transformedExtension.valueParameters[index]
+            }
+            return null
+        }
+
+        private fun IrDeclarationContainer.getOrBuildInlineLoopFunction(
+            functionName: String,
+            valueType: IrType,
+            isArrayReceiver: Boolean
+        ): IrSimpleFunction {
+            val parent = this
+            val mangledName = mangleAtomicExtensionName(functionName, isArrayReceiver)
+            findDeclaration<IrSimpleFunction> {
+                it.name.asString() == mangledName
+                // todo put back all the checks
+//                        it.valueParameters.isNotEmpty() &&
+//                        it.valueParameters[0].type == irBuiltIns.functionN(0) &&
+//                        (it.valueParameters[0].type as IrSimpleType).arguments.firstOrNull() == irBuiltIns.kMutableProperty0Class
+            }?.let { return it }
+            println(valueType)
+            /**
+             * 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
+             *   }
+             * }
+             */
+            return pluginContext.irFactory.buildFun {
+                name = Name.identifier(mangledName)
+                isInline = true
+                visibility = DescriptorVisibilities.PRIVATE
+            }.apply {
+                with(atomicSymbols.createBuilder(symbol)) {
+                    dispatchReceiverParameter = (parent as? IrClass)?.thisReceiver?.deepCopyWithSymbols(this@apply)
+                    addTypeParameter("T", irBuiltIns.anyNType)
+                    val type = buildSimpleType(typeParameters[0].symbol, emptyList())
+                    addValueParameter(REF_GETTER, atomicSymbols.kMutableProperty0GetterType(type))
+                    if (functionName == LOOP) {
+                        addValueParameter(ACTION, atomicSymbols.function1Type(type, irBuiltIns.unitType))
+                        body = atomicfuLoopBody(valueParameters[0], valueParameters[1])
+                        returnType = irBuiltIns.unitType
+                    } else {
+                        addValueParameter(ACTION, atomicSymbols.function1Type(type, type))
+                        body = atomicfuUpdateBody(functionName, type, valueParameters[0], valueParameters[1])
+                        returnType = if (functionName == UPDATE) irBuiltIns.unitType else type
+                    }
+                }
+                this.parent = parent
+                parent.declarations.add(this)
+            }
+        }
+
+        private fun IrFunction.isAtomicExtension(): Boolean =
+            extensionReceiverParameter != null && isInline && extensionReceiverParameter!!.type.isAtomicValueType()
+
+        override fun IrFunction.isTransformedAtomicExtension(): Boolean =
+            extensionReceiverParameter == null && valueParameters.isNotEmpty() && valueParameters[0].name.asString() == "refGetter\$atomicfu"
+
+        private fun NativeAtomicfuIrBuilder.buildVolatilePropertyReference(
+            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 = atomicPropertyToVolatile[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.isTransformedAtomicExtension())
+                val refGetter = parentFunction.valueParameters[0].capture()
+                irCall(atomicSymbols.invoke0Symbol).apply { dispatchReceiver = refGetter }
+            }
+            else -> error("Unsupported type of atomic receiver expression: ${getPropertyReceiver.render()}")
+        }
+
+        private fun NativeAtomicfuIrBuilder.buildVolatilePropertyGetter(
+            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 isArrayReceiver = getPropertyReceiver.isArrayElementGetter()
+                    // leave invocations on array elements untransformed for now
+                    if (isArrayReceiver) return null
+                    val atomicProperty = getPropertyReceiver.getCorrespondingProperty()
+                    val volatileProperty = atomicPropertyToVolatile[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>")
+                            origin = IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA
+                            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())
+                    return parentFunction.valueParameters[0].capture()
+                }
+                else -> error("Unsupported type of atomic receiver expression: ${getPropertyReceiver.render()}")
+            }
+        }
+
+        override fun IrExpression.isArrayElementReceiver(parentFunction: IrFunction?): Boolean {
+            return if (this is IrCall) this.isArrayElementGetter() else false
+        }
+    }
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/native/NativeAtomicSymbols.kt b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/native/NativeAtomicSymbols.kt
new file mode 100644
index 0000000..2c04e80
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/native/NativeAtomicSymbols.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.descriptors.ClassKind
+import org.jetbrains.kotlin.ir.declarations.IrClass
+import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
+import org.jetbrains.kotlin.ir.declarations.IrPackageFragment
+import org.jetbrains.kotlin.ir.symbols.IrSymbol
+import org.jetbrains.kotlin.ir.types.*
+import org.jetbrains.kotlin.ir.util.getPropertyGetter
+import org.jetbrains.kotlin.name.*
+import org.jetbrains.kotlinx.atomicfu.compiler.backend.common.AbstractAtomicSymbols
+
+class NativeAtomicSymbols(
+    context: IrPluginContext,
+    moduleFragment: IrModuleFragment
+) : AbstractAtomicSymbols(context, moduleFragment) {
+    override val volatileAnnotationClass: IrClass
+        get() = context.referenceClass(ClassId(FqName("kotlin.concurrent"), Name.identifier("Volatile")))?.owner
+            ?: error("kotlin.concurrent.Volatile class is not found")
+
+    // AtomicInt class
+
+    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 kMutableProperty0Get = irBuiltIns.kMutableProperty0Class.functionByName("get")
+
+    val kMutableProperty0Set = irBuiltIns.kMutableProperty0Class.functionByName("set")
+
+    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 =
+        irSimpleType(irBuiltIns.kMutableProperty0Class, listOf(typeArg))
+
+    // () -> KMutableProperty0<T>
+    fun kMutableProperty0GetterType(typeArg: IrType): IrType = function0Type(kMutableProperty0Type(typeArg))
+
+    override fun createBuilder(symbol: IrSymbol, startOffset: Int, endOffset: Int) =
+        NativeAtomicfuIrBuilder(this, symbol, startOffset, endOffset)
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/native/NativeAtomicfuIrBuilder.kt b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/native/NativeAtomicfuIrBuilder.kt
new file mode 100644
index 0000000..31e248a
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/native/NativeAtomicfuIrBuilder.kt
@@ -0,0 +1,242 @@
+/*
+ * 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.ir.UNDEFINED_OFFSET
+import org.jetbrains.kotlin.ir.builders.*
+import org.jetbrains.kotlin.ir.declarations.IrProperty
+import org.jetbrains.kotlin.ir.declarations.IrValueParameter
+import org.jetbrains.kotlin.ir.expressions.IrCall
+import org.jetbrains.kotlin.ir.expressions.IrExpression
+import org.jetbrains.kotlin.ir.expressions.impl.IrPropertyReferenceImpl
+import org.jetbrains.kotlin.ir.expressions.implicitCastTo
+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.dump
+import org.jetbrains.kotlin.ir.util.dumpKotlinLike
+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,
+        valueArguments: List<IrExpression?>
+    ): IrCall = when (functionName) {
+        "<get-value>" -> callGetter(atomicSymbols.kMutableProperty0Get, propertyRef)
+        "<set-value>", "lazySet" -> callSetter(atomicSymbols.kMutableProperty0Set, propertyRef, 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 (valueType.isBoolean() && it.type.isInt()) it.toBoolean() else it
+    }
+
+    private fun callGetter(getterSymbol: IrSimpleFunctionSymbol, receiver: IrExpression?): IrCall =
+        irCall(getterSymbol).apply {
+            dispatchReceiver = receiver
+        }
+//            .let {
+//            if (valueType.isBoolean() && it.type.isInt()) it.toBoolean() else it
+//        }
+
+    private fun callSetter(setterSymbol: IrSimpleFunctionSymbol, receiver: IrExpression?, value: IrExpression?): IrCall =
+        irCall(setterSymbol).apply {
+            dispatchReceiver = receiver
+            putValueArgument(0, value)
+        }
+
+    private fun invokeRefGetter(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)
+        }
+    }
+    */
+    fun atomicfuLoopBody(refGetter: IrValueParameter, action: IrValueParameter) =
+        irBlockBody {
+            +irWhile().apply {
+                condition = irTrue()
+                body = irBlock {
+                    // todo: replace with the get intrinsic call
+                    val cur = createTmpVariable(
+                        callGetter(atomicSymbols.kMutableProperty0Get, invokeRefGetter(irGet(refGetter))),
+                        "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
+        }
+    }
+    */
+    fun atomicfuUpdateBody(
+        functionName: String,
+        valueType: IrType,
+        refGetter: IrValueParameter,
+        action: IrValueParameter
+    ) =
+        irBlockBody {
+            +irWhile().apply {
+                condition = irTrue()
+                body = irBlock {
+                    val cur = createTmpVariable(
+                        callGetter(atomicSymbols.kMutableProperty0Get, invokeRefGetter(irGet(refGetter))),
+                        "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 = invokeRefGetter(irGet(refGetter)),
+                            valueType = valueType,
+                            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")
+                        }
+                    )
+                }
+            }
+        }
+
+    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 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.irSimpleType(context.irBuiltIns.kMutableProperty0Class, listOf(backingField.type)),
+            symbol = property.symbol,
+            typeArgumentsCount = 0,
+            field = backingField.symbol,
+            getter = property.getter?.symbol,
+            setter = property.setter?.symbol
+        ).apply {
+            dispatchReceiver = classReceiver
+        }
+    }
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/extensions/AtomicfuLoweringExtension.kt b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/extensions/AtomicfuLoweringExtension.kt
index cdb0a158..725f55e 100644
--- a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/extensions/AtomicfuLoweringExtension.kt
+++ b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/extensions/AtomicfuLoweringExtension.kt
@@ -17,22 +17,32 @@
 import org.jetbrains.kotlin.ir.visitors.acceptVoid
 import org.jetbrains.kotlin.platform.isJs
 import org.jetbrains.kotlin.platform.jvm.isJvm
-import org.jetbrains.kotlinx.atomicfu.compiler.backend.jvm.AtomicSymbols
+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 = AtomicSymbols(pluginContext.irBuiltIns, 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)
+                }
             }
         }
     }
@@ -62,4 +72,4 @@
             declaration.acceptChildrenVoid(this)
         }
     })
-}
\ No newline at end of file
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlin/konan/blackboxtest/AtomicfuNativeTestGenerated.java b/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlin/konan/blackboxtest/AtomicfuNativeTestGenerated.java
new file mode 100644
index 0000000..4a5c764
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlin/konan/blackboxtest/AtomicfuNativeTestGenerated.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2010-2023 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.kotlin.konan.blackboxtest;
+
+import com.intellij.testFramework.TestDataPath;
+import org.jetbrains.kotlin.konan.blackboxtest.support.group.UseStandardTestCaseGroupProvider;
+import org.jetbrains.kotlin.test.util.KtTestUtil;
+import org.junit.jupiter.api.Tag;
+import org.jetbrains.kotlin.test.TestMetadata;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.util.regex.Pattern;
+
+/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.GenerateNativeTestsKt}. DO NOT MODIFY MANUALLY */
+@SuppressWarnings("all")
+@TestMetadata("plugins/atomicfu/atomicfu-compiler/testData/nativeBox")
+@TestDataPath("$PROJECT_ROOT")
+@Tag("atomicfu")
+@UseStandardTestCaseGroupProvider()
+public class AtomicfuNativeTestGenerated extends AbstractNativeBlackBoxTest {
+    @Test
+    public void testAllFilesPresentInNativeBox() throws Exception {
+        KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/atomicfu/atomicfu-compiler/testData/nativeBox"), Pattern.compile("^(.+)\\.kt$"), null, true);
+    }
+
+    @Test
+    @TestMetadata("ArithmeticTest.kt")
+    public void testArithmeticTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ArithmeticTest.kt");
+    }
+
+    @Test
+    @TestMetadata("ComplexLoopTest.kt")
+    public void testComplexLoopTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ComplexLoopTest.kt");
+    }
+
+    @Test
+    @TestMetadata("EasyAtomicExtensionTest.kt")
+    public void testEasyAtomicExtensionTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/EasyAtomicExtensionTest.kt");
+    }
+
+    @Test
+    @TestMetadata("ExtensionLoopTest.kt")
+    public void testExtensionLoopTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ExtensionLoopTest.kt");
+    }
+
+    @Test
+    @TestMetadata("InitBlockInitializationTest.kt")
+    public void testInitBlockInitializationTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/InitBlockInitializationTest.kt");
+    }
+
+    @Test
+    @TestMetadata("ParameterizedInlineFunExtensionTest.kt")
+    public void testParameterizedInlineFunExtensionTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ParameterizedInlineFunExtensionTest.kt");
+    }
+
+    @Test
+    @TestMetadata("TopLevelTest.kt")
+    public void testTopLevelTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/TopLevelTest.kt");
+    }
+
+    @Test
+    @TestMetadata("ArrayInlineExtensionTest.kt")
+    public void testArrayInlineExtensionTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ArrayInlineExtensionTest.kt");
+    }
+
+    @Test
+    @TestMetadata("FieldInObjectTest.kt")
+    public void testFieldInObjectTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/FieldInObjectTest.kt");
+    }
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ArithmeticTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ArithmeticTest.kt
new file mode 100644
index 0000000..7c531cb
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ArithmeticTest.kt
@@ -0,0 +1,147 @@
+// FREE_COMPILER_ARGS: -Xplugin=/Users/Maria.Sokolova/IdeaProjects/kotlin/plugins/atomicfu/atomicfu-compiler/build/libs/kotlin-atomicfu-compiler-plugin-1.9.255-SNAPSHOT-atomicfu-1.jar
+
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class IntArithmetic {
+    val _x = atomic(0)
+    val x get() = _x.value
+}
+
+class LongArithmetic {
+    val _x = atomic(4294967296)
+    val x get() = _x.value
+    val y = atomic(5000000000)
+    val z = atomic(2424920024888888848)
+    val max = atomic(9223372036854775807)
+}
+
+class BooleanArithmetic {
+    val _x = atomic(false)
+    val x get() = _x.value
+}
+
+class ReferenceArithmetic {
+    val _x = atomic<String?>(null)
+
+    private val prev = "djvndkjvnkdjvndr"
+
+    private val _next = atomic<Any?>(null)
+    private val _prev = atomic(prev)
+}
+
+class VisibilitiesTest {
+    val a = atomic(0)
+    public val b = atomic(1)
+    private val c = atomic(2)
+    internal val d = atomic(3)
+
+    fun test() {
+        a.lazySet(45)
+        b.lazySet(56)
+        c.lazySet(46)
+        d.lazySet(67)
+    }
+}
+
+@Test
+fun testGetValue() {
+    val a = IntArithmetic()
+    a._x.value = 5
+    assertEquals(5, a._x.value)
+    var aValue = a._x.value
+    assertEquals(5, aValue)
+    assertEquals(5, a.x)
+}
+
+@Test
+fun testAtomicCallPlaces() {
+    val a = IntArithmetic()
+    a._x.value = 5
+    a._x.compareAndSet(5, 42)
+    val res = a._x.compareAndSet(42, 45)
+    assertTrue(res)
+    assertTrue(a._x.compareAndSet(45, 77))
+    assertFalse(a._x.compareAndSet(95, 77))
+    a._x.compareAndSet(77, 88)
+    assertEquals(88, a._x.value)
+}
+
+@Test
+fun testInt() {
+    val a = IntArithmetic()
+    assertEquals(0, a.x)
+    val update = 3
+    assertEquals(0, a._x.getAndSet(update))
+    assertTrue(a._x.compareAndSet(update, 8))
+    a._x.lazySet(1)
+    assertEquals(1, a.x)
+    assertEquals(1, a._x.getAndSet(2))
+    assertEquals(2, a.x)
+    assertEquals(2, a._x.getAndIncrement())
+    assertEquals(3, a.x)
+    assertEquals(3, a._x.getAndDecrement())
+    assertEquals(2, a.x)
+    assertEquals(2, a._x.getAndAdd(2))
+    assertEquals(4, a.x)
+    assertEquals(7, a._x.addAndGet(3))
+    assertEquals(7, a.x)
+    assertEquals(8, a._x.incrementAndGet())
+    assertEquals(8, a.x)
+    assertEquals(7, a._x.decrementAndGet())
+    assertEquals(7, a.x)
+    assertTrue(a._x.compareAndSet(7, 10))
+}
+
+
+@Test
+fun testLong() {
+    val a = LongArithmetic()
+    assertEquals(2424920024888888848, a.z.value)
+    a.z.lazySet(8424920024888888848)
+    assertEquals(8424920024888888848, a.z.value)
+    assertEquals(8424920024888888848, a.z.getAndSet(8924920024888888848))
+    assertEquals(8924920024888888848, a.z.value)
+    assertEquals(8924920024888888849, a.z.incrementAndGet())
+    assertEquals(8924920024888888849, a.z.value)
+    assertEquals(8924920024888888849, a.z.getAndDecrement())
+    assertEquals(8924920024888888848, a.z.value)
+    assertEquals(8924920024888888848, a.z.getAndAdd(100000000000000000))
+    assertEquals(9024920024888888848, a.z.value)
+    assertEquals(-198452011965886959, a.z.addAndGet(-9223372036854775807))
+    assertEquals(-198452011965886959, a.z.value)
+    assertEquals(-198452011965886958, a.z.incrementAndGet())
+    assertEquals(-198452011965886958, a.z.value)
+    assertEquals(-198452011965886959, a.z.decrementAndGet())
+    assertEquals(-198452011965886959, a.z.value)
+}
+
+@Test
+fun testBoolean() {
+    val a = BooleanArithmetic()
+    a._x.value
+    assertEquals(false, a._x.value)
+    assertFalse(a.x)
+    a._x.lazySet(true)
+    assertTrue(a.x)
+    assertTrue(a._x.getAndSet(true))
+    assertTrue(a._x.compareAndSet(true, false))
+    assertFalse(a.x)
+}
+
+@Test
+fun testReference() {
+    val a = ReferenceArithmetic()
+    a._x.value = "aaa"
+    assertEquals("aaa", a._x.value)
+    a._x.lazySet("bb")
+    assertEquals("bb", a._x.value)
+    assertEquals("bb", a._x.getAndSet("ccc"))
+    assertEquals("ccc", a._x.value)
+}
+
+@Test
+fun visibiltiesTest() {
+    val a = VisibilitiesTest()
+    a.test()
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ArrayInlineExtensionTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ArrayInlineExtensionTest.kt
new file mode 100644
index 0000000..a9bb2ff
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ArrayInlineExtensionTest.kt
@@ -0,0 +1,83 @@
+// FREE_COMPILER_ARGS: -Xplugin=/Users/Maria.Sokolova/IdeaProjects/kotlin/plugins/atomicfu/atomicfu-compiler/build/libs/kotlin-atomicfu-compiler-plugin-1.9.255-SNAPSHOT-atomicfu-1.jar
+
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class ArrayInlineExtensionTest {
+    val intArr = AtomicIntArray(10)
+    val a = atomic(100)
+    val longArr = AtomicLongArray(10)
+    val refArr = atomicArrayOfNulls<Any?>(5)
+
+    class A(val s: String)
+
+    private inline fun casLoop(to: Int): Int {
+        intArr[0].loop { cur ->
+            if (intArr[0].compareAndSet(cur, to)) return intArr[0].value
+            return 777
+        }
+    }
+
+    private inline fun casLoopExpression(to: Long): Long = longArr[3].loop { cur ->
+        if (longArr[3].compareAndSet(cur, to)) return longArr[3].value
+        return 777
+    }
+
+    private inline fun AtomicInt.extensionLoop(to: Int): Int {
+        loop { cur ->
+            if (compareAndSet(cur, to)) return value
+            return 777
+        }
+    }
+
+    private inline fun AtomicInt.extensionLoopExpression(to: Int): Int = loop { cur ->
+        lazySet(cur + 10)
+        return if (compareAndSet(cur, to)) value else incrementAndGet()
+    }
+
+    private inline fun AtomicInt.extensionLoopMixedReceivers(first: Int, second: Int, index: Int): Int {
+        loop { cur ->
+            compareAndSet(cur, first)
+            intArr[index].compareAndSet(first, second)
+            return value
+        }
+    }
+
+    private inline fun AtomicInt.extensionLoopRecursive(to: Int): Int {
+        loop { cur ->
+            compareAndSet(cur, to)
+            // TODO: revert this comment when atomic arrays are supported and original atomic extensions are removed completely.
+            // a.extensionLoop(5)
+            extensionLoop(to + 100)
+            return value
+        }
+    }
+
+    private inline fun AtomicInt.foo(to: Int): Int {
+        loop { cur ->
+            if (compareAndSet(cur, to)) return 777
+            else return value
+        }
+    }
+
+    private inline fun AtomicInt.bar(delta: Int): Int {
+        return foo(value + delta)
+    }
+
+    fun testIntExtensionLoops() {
+        assertEquals(5, casLoop(5))
+        assertEquals(6, casLoopExpression(6))
+        assertEquals(66, intArr[1].extensionLoop(66))
+        assertEquals(66, intArr[2].extensionLoop(66))
+        assertEquals(77, intArr[1].extensionLoopExpression(777))
+        assertEquals(99, intArr[1].extensionLoopMixedReceivers(88, 99, 1))
+        assertEquals(200, intArr[1].extensionLoopRecursive(100))
+        assertEquals(777, intArr[1].bar(100))
+    }
+}
+
+@Test
+fun testIntExtensionLoops() {
+    val testClass = ArrayInlineExtensionTest()
+    testClass.testIntExtensionLoops()
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ComplexLoopTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ComplexLoopTest.kt
new file mode 100644
index 0000000..28f3009
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ComplexLoopTest.kt
@@ -0,0 +1,77 @@
+// FREE_COMPILER_ARGS: -Xplugin=/Users/Maria.Sokolova/IdeaProjects/kotlin/plugins/atomicfu/atomicfu-compiler/build/libs/kotlin-atomicfu-compiler-plugin-1.9.255-SNAPSHOT-atomicfu-1.jar
+
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+private val topLevelA = atomic(0)
+
+class ComplexLoopTest {
+    val a = atomic(10)
+    val long = atomic(6757L)
+    val b = atomic(11)
+    val c = atomic(12)
+    val r = atomic<String>("aaa")
+    val intArr = AtomicIntArray(10)
+
+    private inline fun AtomicInt.fooInt() {
+        loop { cur ->
+            if (compareAndSet(cur, 67)) return
+        }
+    }
+
+    private inline fun AtomicLong.fooLong() {
+        loop { cur ->
+            if (compareAndSet(cur, 67L)) return
+        }
+    }
+
+    private inline fun embeddedLoops(to: Int): Int =
+        a.loop { aValue ->
+            if (!a.compareAndSet(aValue, to)) return 666
+            b.loop { bValue ->
+                return if (b.compareAndSet(bValue, to)) a.value + b.value else 777
+            }
+        }
+
+    private inline fun AtomicInt.extensionEmbeddedLoops(to: Int): Int =
+        loop { cur1 ->
+            compareAndSet(cur1, to)
+            loop { cur2 ->
+                return cur2
+            }
+        }
+
+    private inline fun embeddedUpdate(to: Int): Int =
+        a.loop { aValue ->
+            a.compareAndSet(aValue, to)
+            return a.updateAndGet { cur -> cur + 100 }
+        }
+
+    private inline fun AtomicRef<String>.extesntionEmbeddedRefUpdate(to: String): String =
+        loop { value ->
+            compareAndSet(value, to)
+            return updateAndGet { cur -> "${cur}AAA" }
+        }
+
+    fun test() {
+        a.fooInt()
+        assertEquals(67, a.value)
+        b.fooInt()
+        assertEquals(67, b.value)
+        c.fooInt()
+        assertEquals(67, c.value)
+        long.fooLong()
+        assertEquals(67L, long.value)
+        assertEquals(24, embeddedLoops(12))
+        assertEquals(77, c.extensionEmbeddedLoops(77))
+        assertEquals(66, intArr[0].extensionEmbeddedLoops(66))
+        assertEquals(166, embeddedUpdate(66))
+        assertEquals("bbbAAA", r.extesntionEmbeddedRefUpdate("bbb"))
+    }
+}
+
+@Test
+fun testComplexLoopTest() {
+    val testClass = ComplexLoopTest()
+    testClass.test()
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/EasyAtomicExtensionTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/EasyAtomicExtensionTest.kt
new file mode 100644
index 0000000..e9285e4
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/EasyAtomicExtensionTest.kt
@@ -0,0 +1,30 @@
+// FREE_COMPILER_ARGS: -Xplugin=/Users/Maria.Sokolova/IdeaProjects/kotlin/plugins/atomicfu/atomicfu-compiler/build/libs/kotlin-atomicfu-compiler-plugin-1.9.255-SNAPSHOT-atomicfu-1.jar
+
+import kotlinx.atomicfu.*
+import kotlin.test.*
+import kotlin.reflect.*
+
+private class AAA {
+    val _a = atomic(67)
+    var intVal: Int = 77
+}
+
+inline fun AtomicInt.foo() {
+    val intialValue = value
+    assertEquals(intialValue, value)
+    value = 56
+    assertEquals(56, getAndSet(77))
+    assertEquals(77, value)
+    innerFoo(value)
+    assertEquals(1000, value)
+}
+
+inline fun AtomicInt.innerFoo(currentValue: Int) {
+    compareAndSet(currentValue, 1000)
+}
+
+@Test
+fun testAtomicExtension() {
+    val aClass = AAA()
+    aClass._a.foo()
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ExtensionLoopTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ExtensionLoopTest.kt
new file mode 100644
index 0000000..cda1411
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ExtensionLoopTest.kt
@@ -0,0 +1,109 @@
+// FREE_COMPILER_ARGS: -Xplugin=/Users/Maria.Sokolova/IdeaProjects/kotlin/plugins/atomicfu/atomicfu-compiler/build/libs/kotlin-atomicfu-compiler-plugin-1.9.255-SNAPSHOT-atomicfu-1.jar
+
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class ExtensionLoopTest {
+    val a = atomic(0)
+    val a1 = atomic(1)
+    val b = atomic(true)
+    val l = atomic(5000000000)
+    val r = atomic<A>(A("aaaa"))
+    val rs = atomic<String>("bbbb")
+
+    class A(val s: String)
+
+    private inline fun casLoop(to: Int): Int {
+        a.loop { cur ->
+            if (a.compareAndSet(cur, to)) return a.value
+            return 777
+        }
+    }
+
+    private inline fun casLoopExpression(to: Int): Int = a.loop { cur ->
+        if (a.compareAndSet(cur, to)) return a.value
+        return 777
+    }
+
+    private inline fun AtomicInt.extensionLoop(to: Int): Int {
+        loop { cur ->
+            if (compareAndSet(cur, to)) return value
+            return 777
+        }
+    }
+
+    private inline fun AtomicInt.extensionLoopExpression(to: Int): Int = loop { cur ->
+        lazySet(cur + 10)
+        return if (compareAndSet(cur, to)) value + 1 else incrementAndGet()
+    }
+
+    // TODO: revert this when atomic arrays are supported.
+    // Atomic extensions should not contain invocations on receivers other than <this> extension receiver.
+    // Atomic arrays and invocations on atomic array elements are not supported for now
+    // -> original untransformed atomic extensions are not removed
+    // (they should not contain invocations on atomic properties that are already replaced with volatile properties)
+//    private inline fun AtomicInt.extensionLoopMixedReceivers(first: Int, second: Int): Int {
+//        loop { cur ->
+//            compareAndSet(cur, first)
+//            a.compareAndSet(first, second)
+//            return value
+//        }
+    //}
+
+    private inline fun AtomicInt.extensionLoopRecursive(to: Int): Int {
+        loop { cur ->
+            compareAndSet(cur, to)
+            extensionLoop(5)
+            return value
+        }
+    }
+
+    private inline fun AtomicInt.foo(to: Int): Int {
+        loop { cur ->
+            if (compareAndSet(cur, to)) return 777
+            else return value
+        }
+    }
+
+    private inline fun AtomicInt.bar(delta: Int): Int {
+        return foo(value + delta)
+    }
+
+    inline fun AtomicInt.extensionEmbeddedLoops(to: Int): Int =
+        loop { cur1 ->
+            compareAndSet(value, to)
+            loop { cur2 ->
+                return cur2
+            }
+        }
+
+    fun testIntExtensionLoops() {
+        assertEquals(5, casLoop(5))
+        assertEquals(45, a.extensionEmbeddedLoops(45))
+        assertEquals(6, casLoopExpression(6))
+        assertEquals(17, a.extensionLoopExpression(777))
+        assertEquals(66, a.extensionLoop(66))
+        //assertEquals(99, a.extensionLoopMixedReceivers(88, 99))
+        assertEquals(5, a.extensionLoopRecursive(100))
+        assertEquals(777, a.bar(100))
+    }
+}
+
+
+private val ref = atomic<String>("aaa")
+
+private inline fun AtomicRef<String>.topLevelExtensionLoop(to: String): String = loop { cur ->
+    lazySet(cur + to)
+    return value
+}
+
+fun testTopLevelExtensionLoop() {
+    assertEquals("aaattt", ref.topLevelExtensionLoop("ttt"))
+}
+
+@Test
+fun testExtensionLoop() {
+    val testClass = ExtensionLoopTest()
+    testClass.testIntExtensionLoops()
+    testTopLevelExtensionLoop()
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/FieldInObjectTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/FieldInObjectTest.kt
new file mode 100644
index 0000000..dd75936
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/FieldInObjectTest.kt
@@ -0,0 +1,65 @@
+// FREE_COMPILER_ARGS: -Xplugin=/Users/Maria.Sokolova/IdeaProjects/kotlin/plugins/atomicfu/atomicfu-compiler/build/libs/kotlin-atomicfu-compiler-plugin-1.9.255-SNAPSHOT-atomicfu-1.jar
+
+import kotlinx.atomicfu.*
+import kotlin.test.*
+import kotlin.random.*
+
+object Provider {
+    private val port = atomic(Random.nextInt(20, 90) * 100)
+    fun next(): Int = port.incrementAndGet()
+
+    private val _l = atomic(2424920024888888848)
+    fun getL() = _l.incrementAndGet()
+
+    val _ref = atomic<String?>(null)
+
+    val _x = atomic(false)
+
+    val intArr = AtomicIntArray(10)
+    val longArr = AtomicLongArray(10)
+    val refArr = atomicArrayOfNulls<Any?>(5)
+}
+
+//object DelegatedProvider {
+//    val _a = atomic(42)
+//    var a: Int by _a
+//
+//    var vInt by atomic(77)
+//}
+
+@Test
+fun testFieldInObject() {
+    val port = Provider.next()
+    assertEquals(port + 1, Provider.next())
+
+    assertEquals(2424920024888888849, Provider.getL())
+
+    Provider._ref.compareAndSet(null, "abc")
+    assertEquals("abc", Provider._ref.value)
+
+    assertFalse(Provider._x.value)
+
+    Provider.intArr[8].value = 454
+    assertEquals(455, Provider.intArr[8].incrementAndGet())
+
+    Provider.longArr[8].value = 4544096409680468
+    assertEquals(4544096409680470, Provider.longArr[8].addAndGet(2))
+
+    Provider.refArr[1].value = Provider._ref.value
+    assertEquals("abc", Provider.refArr[1].value)
+}
+
+//@Test
+//fun testDelegatedPropertiesInObject() {
+//    assertEquals(42, DelegatedProvider.a)
+//    DelegatedProvider._a.compareAndSet(42, 56)
+//    assertEquals(56, DelegatedProvider.a)
+//    DelegatedProvider.a = 77
+//    DelegatedProvider._a.compareAndSet(77,  66)
+//    assertEquals(66, DelegatedProvider._a.value)
+//    assertEquals(66, DelegatedProvider.a)
+//
+//    assertEquals(77, DelegatedProvider.vInt)
+//    DelegatedProvider.vInt = 55
+//    assertEquals(110, DelegatedProvider.vInt * 2)
+//}
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/InitBlockInitializationTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/InitBlockInitializationTest.kt
new file mode 100644
index 0000000..98a15fc
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/InitBlockInitializationTest.kt
@@ -0,0 +1,115 @@
+// FREE_COMPILER_ARGS: -Xplugin=/Users/Maria.Sokolova/IdeaProjects/kotlin/plugins/atomicfu/atomicfu-compiler/build/libs/kotlin-atomicfu-compiler-plugin-1.9.255-SNAPSHOT-atomicfu-1.jar
+
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class InitBlockInitializationTest {
+    val a: AtomicInt
+    val a1: AtomicInt
+    val b: AtomicBoolean
+    val l: AtomicLong
+    val r: AtomicRef<A>
+    val rs: AtomicRef<String>
+
+    init {
+        a = atomic(0)
+        a1 = atomic(1)
+        b = atomic(true)
+        l = atomic(5000000000)
+        r = atomic<A>(A("aaaa"))
+        rs = atomic<String>("bbbb")
+    }
+
+    class A(val s: String)
+
+    private inline fun casLoop(to: Int): Int {
+        a.loop { cur ->
+            if (a.compareAndSet(cur, to)) return a.value
+            return 777
+        }
+    }
+
+    private inline fun casLoopExpression(to: Int): Int = a.loop { cur ->
+        if (a.compareAndSet(cur, to)) return a.value
+        return 777
+    }
+
+    private inline fun AtomicInt.extensionLoop(to: Int): Int {
+        loop { cur ->
+            if (compareAndSet(cur, to)) return value
+            return 777
+        }
+    }
+
+    private inline fun AtomicInt.extensionLoopExpression(to: Int): Int = loop { cur ->
+        lazySet(cur + 10)
+        return if (compareAndSet(cur, to)) value + 1 else incrementAndGet()
+    }
+
+    private inline fun AtomicInt.extensionLoopMixedReceivers(first: Int, second: Int): Int {
+        loop { cur ->
+            compareAndSet(cur, first)
+            compareAndSet(first, second)
+            return value
+        }
+    }
+
+    private inline fun AtomicInt.extensionLoopRecursive(to: Int): Int {
+        loop { cur ->
+            compareAndSet(cur, to)
+            extensionLoop(5)
+            return value
+        }
+    }
+
+    private inline fun AtomicInt.foo(to: Int): Int {
+        loop { cur ->
+            if (compareAndSet(cur, to)) return 777
+            else return value
+        }
+    }
+
+    private inline fun AtomicInt.bar(delta: Int): Int {
+        return foo(value + delta)
+    }
+
+    inline fun AtomicInt.extensionEmbeddedLoops(to: Int): Int =
+        loop { cur1 ->
+            compareAndSet(value, to)
+            loop { cur2 ->
+                return cur2
+            }
+        }
+
+    fun testIntExtensionLoops() {
+        a.compareAndSet(0, 56)
+        assertEquals(56, a.value)
+        assertEquals(5, casLoop(5))
+        assertEquals(45, a.extensionEmbeddedLoops(45))
+        assertEquals(6, casLoopExpression(6))
+        assertEquals(17, a.extensionLoopExpression(777))
+        assertEquals(66, a.extensionLoop(66))
+        assertEquals(99, a.extensionLoopMixedReceivers(88, 99))
+        assertEquals(5, a.extensionLoopRecursive(100))
+        assertEquals(777, a.bar(100))
+    }
+}
+
+
+private val ref = atomic<String>("aaa")
+
+private inline fun AtomicRef<String>.topLevelExtensionLoop(to: String): String = loop { cur ->
+    lazySet(cur + to)
+    return value
+}
+
+fun testTopLevelExtensionLoop() {
+    assertEquals("aaattt", ref.topLevelExtensionLoop("ttt"))
+}
+
+@Test
+fun testInitBlockInitialization() {
+    val testClass = InitBlockInitializationTest()
+    testClass.testIntExtensionLoops()
+    testTopLevelExtensionLoop()
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ParameterizedInlineFunExtensionTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ParameterizedInlineFunExtensionTest.kt
new file mode 100644
index 0000000..30fdb28
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ParameterizedInlineFunExtensionTest.kt
@@ -0,0 +1,29 @@
+// FREE_COMPILER_ARGS: -Xplugin=/Users/Maria.Sokolova/IdeaProjects/kotlin/plugins/atomicfu/atomicfu-compiler/build/libs/kotlin-atomicfu-compiler-plugin-1.9.255-SNAPSHOT-atomicfu-1.jar
+
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class ParameterizedInlineFunExtensionTest {
+
+    private inline fun <S> AtomicRef<S>.foo(res1: S, res2: S, foo: (S) -> S): S {
+        val res = bar(res1, res2)
+        return res
+    }
+
+    private inline fun <S> AtomicRef<S>.bar(res1: S, res2: S): S {
+        return res2
+    }
+
+    private val tail = atomic("aaa")
+
+    fun testClose() {
+        val res = tail.foo("bbb", "ccc") { s -> s }
+        assertEquals("ccc", res)
+    }
+}
+
+@Test
+fun testParameterizedInlineFunExtensionTest() {
+    val testClass = ParameterizedInlineFunExtensionTest()
+    testClass.testClose()
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/TopLevelTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/TopLevelTest.kt
new file mode 100644
index 0000000..ca156ca
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/TopLevelTest.kt
@@ -0,0 +1,161 @@
+// FREE_COMPILER_ARGS: -Xplugin=/Users/Maria.Sokolova/IdeaProjects/kotlin/plugins/atomicfu/atomicfu-compiler/build/libs/kotlin-atomicfu-compiler-plugin-1.9.255-SNAPSHOT-atomicfu-1.jar
+
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+private val a = atomic(0)
+private val b = atomic(2424920024888888848)
+private val c = atomic(true)
+private val abcNode = atomic(ANode(BNode(CNode(8))))
+private val any = atomic<Any?>(null)
+
+private val intArr = AtomicIntArray(3)
+private val longArr = AtomicLongArray(5)
+private val booleanArr = AtomicBooleanArray(4)
+private val refArr = atomicArrayOfNulls<ANode<BNode<CNode>>>(5)
+private val anyRefArr = atomicArrayOfNulls<Any>(10)
+
+private val stringAtomicNullArr = atomicArrayOfNulls<String>(10)
+
+data class ANode<T>(val b: T)
+data class BNode<T>(val c: T)
+data class CNode(val d: Int)
+
+@Test
+fun testTopLevelInt() {
+    assertEquals(0, a.value)
+    assertEquals(0, a.getAndSet(3))
+    assertTrue(a.compareAndSet(3, 8))
+    a.lazySet(1)
+    assertEquals(1, a.value)
+    assertEquals(1, a.getAndSet(2))
+    assertEquals(2, a.value)
+    assertEquals(2, a.getAndIncrement())
+    assertEquals(3, a.value)
+    assertEquals(3, a.getAndDecrement())
+    assertEquals(2, a.value)
+    assertEquals(2, a.getAndAdd(2))
+    assertEquals(4, a.value)
+    assertEquals(7, a.addAndGet(3))
+    assertEquals(7, a.value)
+    assertEquals(8, a.incrementAndGet())
+    assertEquals(8, a.value)
+    assertEquals(7, a.decrementAndGet())
+    assertEquals(7, a.value)
+    assertTrue(a.compareAndSet(7, 10))
+}
+
+@Test
+fun testTopLevelLong() {
+    assertEquals(2424920024888888848, b.value)
+    b.lazySet(8424920024888888848)
+    assertEquals(8424920024888888848, b.value)
+    assertEquals(8424920024888888848, b.getAndSet(8924920024888888848))
+    assertEquals(8924920024888888848, b.value)
+    assertEquals(8924920024888888849, b.incrementAndGet())
+    assertEquals(8924920024888888849, b.value)
+    assertEquals(8924920024888888849, b.getAndDecrement())
+    assertEquals(8924920024888888848, b.value)
+    assertEquals(8924920024888888848, b.getAndAdd(100000000000000000))
+    assertEquals(9024920024888888848, b.value)
+    assertEquals(-198452011965886959, b.addAndGet(-9223372036854775807))
+    assertEquals(-198452011965886959, b.value)
+    assertEquals(-198452011965886958, b.incrementAndGet())
+    assertEquals(-198452011965886958, b.value)
+    assertEquals(-198452011965886959, b.decrementAndGet())
+    assertEquals(-198452011965886959, b.value)
+}
+
+@Test
+fun testTopLevelBoolean() {
+    assertTrue(c.value)
+    c.lazySet(false)
+    assertFalse(c.value)
+    assertTrue(!c.getAndSet(true))
+    assertTrue(c.compareAndSet(true, false))
+    assertFalse(c.value)
+}
+
+@Test
+fun testTopLevelRef() {
+    assertEquals(8, abcNode.value.b.c.d)
+    val newNode = ANode(BNode(CNode(76)))
+    assertEquals(8, abcNode.getAndSet(newNode).b.c.d)
+    assertEquals(76, abcNode.value.b.c.d)
+    val l = IntArray(4){i -> i}
+    any.lazySet(l)
+    assertEquals(2, (any.value as IntArray)[2])
+}
+
+@Test
+fun testTopLevelArrayOfNulls() {
+    assertEquals(null, stringAtomicNullArr[0].value)
+    assertTrue(stringAtomicNullArr[0].compareAndSet(null, "aa"))
+    stringAtomicNullArr[1].lazySet("aa")
+    assertTrue(stringAtomicNullArr[0].value == stringAtomicNullArr[1].value)
+}
+
+fun testIntArray() {
+    assertTrue(intArr[0].compareAndSet(0, 3))
+    assertEquals(0, intArr[1].value)
+    intArr[0].lazySet(5)
+    assertEquals(5, intArr[0].value + intArr[1].value + intArr[2].value)
+    assertTrue(intArr[0].compareAndSet(5, 10))
+    assertEquals(10, intArr[0].getAndDecrement())
+    assertEquals(9, intArr[0].value)
+    intArr[2].value = 2
+    assertEquals(2, intArr[2].value)
+    assertTrue(intArr[2].compareAndSet(2, 34))
+    assertEquals(34, intArr[2].value)
+}
+
+fun testLongArray() {
+    longArr[0].value = 2424920024888888848
+    assertEquals(2424920024888888848, longArr[0].value)
+    longArr[0].lazySet(8424920024888888848)
+    assertEquals(8424920024888888848, longArr[0].value)
+    val ac = longArr[0].value
+    longArr[3].value = ac
+    assertEquals(8424920024888888848, longArr[3].getAndSet(8924920024888888848))
+    assertEquals(8924920024888888848, longArr[3].value)
+    val ac1 = longArr[3].value
+    longArr[4].value = ac1
+    assertEquals(8924920024888888849, longArr[4].incrementAndGet())
+    assertEquals(8924920024888888849, longArr[4].value)
+    assertEquals(8924920024888888849, longArr[4].getAndDecrement())
+    assertEquals(8924920024888888848, longArr[4].value)
+    longArr[4].value = 8924920024888888848
+    assertEquals(8924920024888888848, longArr[4].getAndAdd(100000000000000000))
+    val ac2 = longArr[4].value
+    longArr[1].value = ac2
+    assertEquals(9024920024888888848, longArr[1].value)
+    assertEquals(-198452011965886959, longArr[1].addAndGet(-9223372036854775807))
+    assertEquals(-198452011965886959, longArr[1].value)
+    assertEquals(-198452011965886958, longArr[1].incrementAndGet())
+    assertEquals(-198452011965886958, longArr[1].value)
+    assertEquals(-198452011965886959, longArr[1].decrementAndGet())
+    assertEquals(-198452011965886959, longArr[1].value)
+}
+
+fun testBooleanArray() {
+    assertFalse(booleanArr[1].value)
+    booleanArr[1].compareAndSet(false, true)
+    booleanArr[0].lazySet(true)
+    assertFalse(booleanArr[2].getAndSet(true))
+    assertTrue(booleanArr[0].value && booleanArr[1].value && booleanArr[2].value)
+}
+
+@Suppress("UNCHECKED_CAST")
+fun testRefArray() {
+    val a2 = ANode(BNode(CNode(2)))
+    val a3 = ANode(BNode(CNode(3)))
+    refArr[0].value = a2
+    assertEquals(2, refArr[0].value!!.b.c.d)
+    assertTrue(refArr[0].compareAndSet(a2, a3))
+    assertEquals(3, refArr[0].value!!.b.c.d)
+    val r0 = refArr[0].value
+    refArr[3].value = r0
+    assertEquals(3, refArr[3].value!!.b.c.d)
+    val a = abcNode.value
+    assertTrue(refArr[3].compareAndSet(a3, a))
+}