Atomicfu compiler plugin for JS/IR backend
diff --git a/generators/build.gradle.kts b/generators/build.gradle.kts
index 496b815..fad5447 100644
--- a/generators/build.gradle.kts
+++ b/generators/build.gradle.kts
@@ -71,6 +71,7 @@
     testCompile(projectTests(":kotlin-sam-with-receiver-compiler-plugin"))
     testCompile(projectTests(":kotlinx-serialization-compiler-plugin"))
     testCompile(projectTests(":plugins:fir:fir-plugin-prototype"))
+    testCompile(projectTests(":kotlinx-atomicfu-compiler-plugin"))
     testCompile(projectTests(":idea:jvm-debugger:jvm-debugger-test"))
     testCompile(projectTests(":generators:test-generator"))
     testCompile(projectTests(":idea"))
diff --git a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt
index 2f93f03..1a40c95 100644
--- a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt
+++ b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt
@@ -187,6 +187,9 @@
 import org.jetbrains.kotlin.shortenRefs.AbstractFirShortenRefsTest
 import org.jetbrains.kotlin.shortenRefs.AbstractShortenRefsTest
 import org.jetbrains.kotlin.test.TargetBackend
+import org.jetbrains.kotlin.tools.projectWizard.cli.AbstractBuildFileGenerationTest
+import org.jetbrains.kotlinx.atomicfu.AbstractBasicAtomicfuTest
+import org.jetbrains.kotlinx.atomicfu.AbstractLocksAtomicfuTest
 import org.jetbrains.kotlin.tools.projectWizard.cli.AbstractProjectTemplateBuildFileGenerationTest
 import org.jetbrains.kotlin.tools.projectWizard.cli.AbstractYamlBuildFileGenerationTest
 import org.jetbrains.kotlin.tools.projectWizard.wizard.AbstractProjectTemplateNewWizardProjectImportTest
@@ -1787,6 +1790,18 @@
             }
         }
 
+        testGroup(
+            "plugins/atomicfu/atomicfu-compiler/test",
+            "plugins/atomicfu/atomicfu-compiler/testData"
+        ) {
+            testClass<AbstractBasicAtomicfuTest> {
+                model("basic")
+            }
+            testClass<AbstractLocksAtomicfuTest> {
+                model("locks")
+            }
+        }
+
         testGroup("plugins/fir/fir-plugin-prototype/tests", "plugins/fir/fir-plugin-prototype/testData") {
             testClass<AbstractFirAllOpenDiagnosticTest> {
                 model("")
diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt
index 4465da5..887aa15 100644
--- a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt
+++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt
@@ -854,7 +854,7 @@
 
         val libraries = when (targetBackend) {
             TargetBackend.JS_IR_ES6 -> dependencies
-            TargetBackend.JS_IR -> dependencies
+            TargetBackend.JS_IR -> dependencies + configuration[JSConfigurationKeys.LIBRARIES]!!
             TargetBackend.JS -> JsConfig.JS_STDLIB + JsConfig.JS_KOTLIN_TEST + dependencies
             else -> error("Unsupported target backend: $targetBackend")
         }
diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt
index 3d72e26..cb35e88 100644
--- a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt
+++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt
@@ -99,11 +99,13 @@
 
         val transitiveLibraries = config.configuration[JSConfigurationKeys.TRANSITIVE_LIBRARIES]!!.map { File(it).name }
 
+        val libraries = config.configuration[JSConfigurationKeys.LIBRARIES] ?: emptyList()
+
         val allKlibPaths = (runtimeKlibs + transitiveLibraries.map {
             compilationCache[it] ?: error("Can't find compiled module for dependency $it")
         }).map { File(it).absolutePath }
 
-        val resolvedLibraries = jsResolveLibraries(allKlibPaths, emptyList(), messageCollectorLogger(MessageCollector.NONE))
+        val resolvedLibraries = jsResolveLibraries(allKlibPaths + libraries, emptyList(), messageCollectorLogger(MessageCollector.NONE))
 
         val actualOutputFile = outputFile.absolutePath.let {
             if (!isMainModule) it.replace("_v5.js", "/") else it
diff --git a/libraries/configureGradleTools.gradle b/libraries/configureGradleTools.gradle
index 35020d1..9015789 100644
--- a/libraries/configureGradleTools.gradle
+++ b/libraries/configureGradleTools.gradle
@@ -1,5 +1,5 @@
 
-configure([project(':kotlin-gradle-plugin'), project(':kotlin-allopen'), project(':kotlin-noarg'), project(':kotlin-serialization')]) { project ->
+configure([project(':kotlin-gradle-plugin'), project(':kotlin-allopen'), project(':kotlin-noarg'), project(':kotlin-serialization'), project(':atomicfu')]) { project ->
     apply plugin: 'com.gradle.plugin-publish'
 
     afterEvaluate {
diff --git a/libraries/tools/atomicfu/build.gradle b/libraries/tools/atomicfu/build.gradle
new file mode 100644
index 0000000..740c4be
--- /dev/null
+++ b/libraries/tools/atomicfu/build.gradle
@@ -0,0 +1,39 @@
+repositories {
+    mavenCentral()
+}
+
+apply plugin: 'kotlin'
+apply plugin: 'jps-compatible'
+
+configurePublishing(project)
+
+pill {
+    variant = 'FULL'
+}
+
+dependencies {
+    compileOnly project(':kotlin-gradle-plugin')
+    compileOnly project(':kotlin-gradle-plugin-api')
+
+    compileOnly kotlinStdlib()
+    compileOnly project(path: ':kotlin-compiler-embeddable', configuration: 'runtimeJar')
+
+    embedded(project(":kotlinx-atomicfu-compiler-plugin")) { transitive = false }
+}
+
+jar {
+    manifestAttributes(manifest, project)
+}
+
+ArtifactsKt.runtimeJar(project, EmbeddableKt.rewriteDefaultJarDepsToShadedCompiler(project, {}), {})
+configureSourcesJar()
+configureJavadocJar()
+
+pluginBundle {
+    plugins {
+        atomicfu {
+            id = 'org.jetbrains.kotlin.plugin.atomicfu'
+            description = displayName = 'Kotlin compiler plugin for kotlinx.atomicfu library'
+        }
+    }
+}
\ No newline at end of file
diff --git a/libraries/tools/atomicfu/src/main/kotlin/org/jetbrains/kotlinx/atomicfu/gradle/AtomicfuSubplugin.kt b/libraries/tools/atomicfu/src/main/kotlin/org/jetbrains/kotlinx/atomicfu/gradle/AtomicfuSubplugin.kt
new file mode 100644
index 0000000..eac1670
--- /dev/null
+++ b/libraries/tools/atomicfu/src/main/kotlin/org/jetbrains/kotlinx/atomicfu/gradle/AtomicfuSubplugin.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2010-2020 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlinx.atomicfu.gradle
+
+import org.gradle.api.NamedDomainObjectContainer
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.plugins.ExtensionAware
+import org.gradle.api.tasks.compile.AbstractCompile
+import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
+import org.jetbrains.kotlin.gradle.plugin.*
+import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget
+import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
+import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
+import org.jetbrains.kotlin.gradle.dsl.*
+
+class AtomicfuGradleSubplugin : Plugin<Project> {
+    companion object {
+        fun isEnabled(project: Project) = project.plugins.findPlugin(AtomicfuGradleSubplugin::class.java) != null
+    }
+
+    override fun apply(project: Project) {
+        // nothing here
+    }
+}
+
+class AtomicfuKotlinGradleSubplugin : KotlinGradleSubplugin<AbstractCompile> {
+    companion object {
+        const val ATOMICFU_ARTIFACT_NAME = "atomicfu"
+    }
+
+    override fun isApplicable(project: Project, task: AbstractCompile) =
+        task is Kotlin2JsCompile && project.hasIrTargets()
+
+
+    private fun Project.hasIrTargets(): Boolean {
+        extensions.findByType(KotlinProjectExtension::class.java)?.let { kotlinExtension ->
+            if (kotlinExtension is KotlinJsProjectExtension) {
+                if (kotlinExtension._target?.isJsIrTarget() == true) return true
+            }
+            val targetsExtension = (kotlinExtension as? ExtensionAware)?.extensions?.findByName("targets")
+            @Suppress("UNCHECKED_CAST")
+            if (targetsExtension != null) {
+                val targets = targetsExtension as NamedDomainObjectContainer<KotlinTarget>
+                if (targets.any { it.isJsIrTarget() }) return true
+            }
+        }
+        return false
+    }
+
+    private fun KotlinTarget.isJsIrTarget(): Boolean =
+        (this is KotlinJsTarget && this.irTarget != null) || this is KotlinJsIrTarget
+
+    override fun apply(
+        project: Project,
+        kotlinCompile: AbstractCompile,
+        javaCompile: AbstractCompile?,
+        variantData: Any?,
+        androidProjectHandler: Any?,
+        kotlinCompilation: KotlinCompilation<*>?
+    ): List<SubpluginOption> {
+        return emptyList()
+    }
+
+    override fun getPluginArtifact(): SubpluginArtifact =
+        JetBrainsSubpluginArtifact(ATOMICFU_ARTIFACT_NAME)
+
+    override fun getCompilerPluginId() = "org.jetbrains.kotlinx.atomicfu"
+}
diff --git a/libraries/tools/atomicfu/src/main/resources/META-INF/gradle-plugins/atomicfu-jsir.properties b/libraries/tools/atomicfu/src/main/resources/META-INF/gradle-plugins/atomicfu-jsir.properties
new file mode 100644
index 0000000..899d3fd
--- /dev/null
+++ b/libraries/tools/atomicfu/src/main/resources/META-INF/gradle-plugins/atomicfu-jsir.properties
@@ -0,0 +1 @@
+implementation-class=org.jetbrains.kotlinx.atomicfu.gradle.AtomicfuGradleSubplugin
\ No newline at end of file
diff --git a/libraries/tools/atomicfu/src/main/resources/META-INF/gradle-plugins/org.jetbrains.kotlin.plugin.atomicfu.properties b/libraries/tools/atomicfu/src/main/resources/META-INF/gradle-plugins/org.jetbrains.kotlin.plugin.atomicfu.properties
new file mode 100644
index 0000000..899d3fd
--- /dev/null
+++ b/libraries/tools/atomicfu/src/main/resources/META-INF/gradle-plugins/org.jetbrains.kotlin.plugin.atomicfu.properties
@@ -0,0 +1 @@
+implementation-class=org.jetbrains.kotlinx.atomicfu.gradle.AtomicfuGradleSubplugin
\ No newline at end of file
diff --git a/libraries/tools/atomicfu/src/main/resources/META-INF/services/org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin b/libraries/tools/atomicfu/src/main/resources/META-INF/services/org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin
new file mode 100644
index 0000000..82e8567
--- /dev/null
+++ b/libraries/tools/atomicfu/src/main/resources/META-INF/services/org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin
@@ -0,0 +1,17 @@
+#
+# Copyright 2010-2020 JetBrains s.r.o.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.jetbrains.kotlinx.atomicfu.gradle.AtomicfuKotlinGradleSubplugin
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/build.gradle.kts b/plugins/atomicfu/atomicfu-compiler/build.gradle.kts
new file mode 100644
index 0000000..37b1568
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/build.gradle.kts
@@ -0,0 +1,157 @@
+import org.jetbrains.kotlin.gradle.targets.js.KotlinJsCompilerAttribute
+import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
+import org.gradle.internal.os.OperatingSystem
+
+
+description = "Atomicfu Compiler Plugin"
+
+plugins {
+    kotlin("jvm")
+    id("jps-compatible")
+    id("com.github.node-gradle.node") version "2.2.0"
+    id("de.undercouch.download")
+    id("com.gradle.enterprise.test-distribution")
+}
+
+node {
+    download = true
+    version = "10.16.2"
+}
+
+val antLauncherJar by configurations.creating
+val testJsRuntime by configurations.creating {
+    attributes {
+        attribute(Usage.USAGE_ATTRIBUTE, objects.named(KotlinUsages.KOTLIN_RUNTIME))
+        attribute(KotlinPlatformType.attribute, KotlinPlatformType.js)
+    }
+}
+
+val atomicfuClasspath by configurations.creating {
+    attributes {
+        attribute(KotlinPlatformType.attribute, KotlinPlatformType.js)
+        attribute(KotlinJsCompilerAttribute.jsCompilerAttribute, KotlinJsCompilerAttribute.ir)
+    }
+}
+
+val atomicfuRuntimeForTests by configurations.creating {
+    attributes {
+        attribute(KotlinPlatformType.attribute, KotlinPlatformType.js)
+        attribute(KotlinJsCompilerAttribute.jsCompilerAttribute, KotlinJsCompilerAttribute.ir)
+        attribute(Usage.USAGE_ATTRIBUTE, objects.named(KotlinUsages.KOTLIN_RUNTIME))
+    }
+}
+
+repositories {
+    mavenLocal()
+    jcenter()
+}
+
+dependencies {
+    compileOnly(intellijCoreDep()) { includeJars("intellij-core", "asm-all", rootProject = rootProject) }
+
+    compileOnly(project(":compiler:plugin-api"))
+    compileOnly(project(":compiler:cli-common"))
+    compileOnly(project(":compiler:frontend"))
+    compileOnly(project(":compiler:backend"))
+    compileOnly(project(":compiler:ir.backend.common"))
+    compileOnly(project(":js:js.frontend"))
+    compileOnly(project(":js:js.translator"))
+    compile(project(":compiler:backend.js"))
+
+    runtimeOnly(kotlinStdlib())
+
+    testCompile(projectTests(":compiler:tests-common"))
+    testCompile(projectTests(":js:js.tests"))
+    testCompile(commonDep("junit:junit"))
+
+    testRuntime(kotlinStdlib())
+    testRuntime(project(":kotlin-reflect"))
+    testRuntime(project(":kotlin-preloader")) // it's required for ant tests
+    testRuntime(project(":compiler:backend-common"))
+    testRuntime(commonDep("org.fusesource.jansi", "jansi"))
+
+    atomicfuClasspath("org.jetbrains.kotlinx:atomicfu-js:0.15.1") {
+        isTransitive = false
+    }
+
+    atomicfuRuntimeForTests(project(":kotlinx-atomicfu-runtime"))  { isTransitive = false }
+
+    testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.6.2")
+}
+
+sourceSets {
+    "main" { projectDefault() }
+    "test" { projectDefault() }
+}
+
+runtimeJar()
+sourcesJar()
+javadocJar()
+testsJar()
+
+projectTest(parallel = true) {
+    workingDir = rootDir
+    dependsOn(atomicfuRuntimeForTests)
+    doFirst {
+        systemProperty("atomicfuRuntimeForTests.classpath", atomicfuRuntimeForTests.asPath)
+    }
+    setUpJsBoxTests(jsEnabled = true, jsIrEnabled = true)
+}
+
+enum class OsName { WINDOWS, MAC, LINUX, UNKNOWN }
+enum class OsArch { X86_32, X86_64, UNKNOWN }
+data class OsType(val name: OsName, val arch: OsArch)
+
+fun Test.setupV8() {
+    dependsOn(":js:js.tests:unzipV8")
+    val currentOsType = run {
+        val gradleOs = OperatingSystem.current()
+        val osName = when {
+            gradleOs.isMacOsX -> OsName.MAC
+            gradleOs.isWindows -> OsName.WINDOWS
+            gradleOs.isLinux -> OsName.LINUX
+            else -> OsName.UNKNOWN
+        }
+
+        val osArch = when (System.getProperty("sun.arch.data.model")) {
+            "32" -> OsArch.X86_32
+            "64" -> OsArch.X86_64
+            else -> OsArch.UNKNOWN
+        }
+
+        OsType(osName, osArch)
+    }
+    val v8osString = when (currentOsType) {
+        OsType(OsName.LINUX, OsArch.X86_32) -> "linux32"
+        OsType(OsName.LINUX, OsArch.X86_64) -> "linux64"
+        OsType(OsName.MAC, OsArch.X86_64) -> "mac64"
+        OsType(OsName.WINDOWS, OsArch.X86_32) -> "win32"
+        OsType(OsName.WINDOWS, OsArch.X86_64) -> "win64"
+        else -> error("unsupported os type $currentOsType")
+    }
+    val v8Path = "${rootDir.absolutePath}/js/js.tests/build/tools/v8-${v8osString}-rel-8.8.104/"
+    val v8ExecutablePath = File(v8Path, "d8")
+    systemProperty("javascript.engine.path.V8", v8ExecutablePath)
+    inputs.dir(v8Path)
+}
+
+fun Test.setUpJsBoxTests(jsEnabled: Boolean, jsIrEnabled: Boolean) {
+    setupV8()
+
+    dependsOn(":dist")
+    if (jsIrEnabled) {
+        dependsOn(":kotlin-stdlib-js-ir:compileKotlinJs")
+        systemProperty("kotlin.js.full.stdlib.path", "libraries/stdlib/js-ir/build/classes/kotlin/js/main")
+        dependsOn(":kotlin-stdlib-js-ir-minimal-for-test:compileKotlinJs")
+        systemProperty("kotlin.js.reduced.stdlib.path", "libraries/stdlib/js-ir-minimal-for-test/build/classes/kotlin/js/main")
+        dependsOn(":kotlin-test:kotlin-test-js-ir:compileKotlinJs")
+        systemProperty("kotlin.js.kotlin.test.path", "libraries/kotlin.test/js-ir/build/classes/kotlin/js/main")
+        systemProperty("kotlin.js.kotlin.test.path", "libraries/kotlin.test/js-ir/build/classes/kotlin/js/main")
+        systemProperty("kotlin.js.test.root.out.dir", "$buildDir/")
+        systemProperty("atomicfu.classpath", atomicfuClasspath.asPath)
+    }
+}
+
+val generateTests by generator("org.jetbrains.kotlin.generators.tests.GenerateJsTestsKt")
+val testDataDir = project(":js:js.translator").projectDir.resolve("testData")
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar b/plugins/atomicfu/atomicfu-compiler/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
new file mode 100644
index 0000000..b3e8636
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
@@ -0,0 +1,17 @@
+#
+# Copyright 2010-2017 JetBrains s.r.o.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.jetbrains.kotlinx.atomicfu.compiler.extensions.AtomicfuComponentRegistrar
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/src/org.jetbrains.kotlinx.atomicfu.compiler/extensions/AtomicFUTransformerJsIr.kt b/plugins/atomicfu/atomicfu-compiler/src/org.jetbrains.kotlinx.atomicfu.compiler/extensions/AtomicFUTransformerJsIr.kt
new file mode 100644
index 0000000..9e715f0
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/src/org.jetbrains.kotlinx.atomicfu.compiler/extensions/AtomicFUTransformerJsIr.kt
@@ -0,0 +1,596 @@
+/*
+ * Copyright 2010-2020 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.extensions
+
+import org.jetbrains.kotlin.backend.common.deepCopyWithVariables
+import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
+import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
+import org.jetbrains.kotlin.ir.*
+import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder.buildValueParameter
+import org.jetbrains.kotlin.ir.builders.declarations.buildTypeParameter
+import org.jetbrains.kotlin.ir.expressions.impl.*
+import org.jetbrains.kotlin.ir.declarations.*
+import org.jetbrains.kotlin.ir.expressions.*
+import org.jetbrains.kotlin.ir.symbols.*
+import org.jetbrains.kotlin.ir.symbols.impl.IrClassSymbolImpl
+import org.jetbrains.kotlin.ir.symbols.impl.IrTypeParameterSymbolImpl
+import org.jetbrains.kotlin.ir.types.*
+import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
+import org.jetbrains.kotlin.ir.util.*
+import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
+import org.jetbrains.kotlin.name.*
+
+private const val KOTLIN = "kotlin"
+private const val AFU_PKG = "kotlinx/atomicfu"
+private const val LOCKS = "locks"
+private const val ATOMIC_CONSTRUCTOR = "atomic"
+private const val ATOMICFU_VALUE_TYPE = """Atomic(Int|Long|Boolean|Ref)"""
+private const val ATOMIC_ARRAY_TYPE = """Atomic(Int|Long|Boolean|)Array"""
+private const val ATOMIC_ARRAY_FACTORY_FUNCTION = "atomicArrayOfNulls"
+private const val ATOMICFU_RUNTIME_FUNCTION_PREDICATE = "atomicfu_"
+private const val REENTRANT_LOCK_TYPE = "ReentrantLock"
+private const val GETTER = "atomicfu\$getter"
+private const val SETTER = "atomicfu\$setter"
+private const val GET = "get"
+private const val SET = "set"
+private const val ATOMICFU_INLINE_FUNCTION = """atomicfu_(loop|update|getAndUpdate|updateAndGet)"""
+
+private fun String.prettyStr() = replace('/', '.')
+
+class AtomicFUTransformer(override val context: IrPluginContext) : IrElementTransformerVoid(), TransformerHelpers {
+
+    private val irBuiltIns = context.irBuiltIns
+
+    private val AFU_CLASSES: Map<String, IrType> = mapOf(
+        "AtomicInt" to irBuiltIns.intType,
+        "AtomicLong" to irBuiltIns.longType,
+        "AtomicRef" to irBuiltIns.anyType,
+        "AtomicBoolean" to irBuiltIns.booleanType
+    )
+
+    private val AFU_ARRAY_CLASSES: Map<String, String> = mapOf(
+        "AtomicIntArray" to "IntArray",
+        "AtomicLongArray" to "LongArray",
+        "AtomicBooleanArray" to "BooleanArray",
+        "AtomicArray" to "Array"
+    )
+
+    override fun visitFile(declaration: IrFile): IrFile {
+        val newDeclarations = mutableListOf<IrDeclaration>()
+        declaration.declarations.forEachIndexed { index, irDeclaration ->
+            irDeclaration.transformAtomicInlineDeclaration()?.let { newDeclarations.add(it) }
+        }
+        declaration.declarations.addAll(newDeclarations)
+        return super.visitFile(declaration)
+    }
+
+    override fun visitClass(declaration: IrClass): IrStatement {
+        val newDeclarations = mutableListOf<IrDeclaration>()
+        declaration.declarations.forEachIndexed { index, irDeclaration ->
+            irDeclaration.transformAtomicInlineDeclaration()?.let { newDeclarations.add(it) }
+        }
+        declaration.declarations.addAll(newDeclarations)
+        return super.visitClass(declaration)
+    }
+
+    override fun visitProperty(declaration: IrProperty): IrStatement {
+        if (declaration.backingField != null) {
+            val backingField = declaration.backingField!!
+            if (backingField.initializer != null) {
+                val initializer = backingField.initializer!!.expression.transformAtomicValueInitializer(backingField)
+                declaration.backingField!!.initializer = context.irFactory.createExpressionBody(initializer)
+            }
+        }
+        return super.visitProperty(declaration)
+    }
+
+    override fun visitFunction(declaration: IrFunction): IrStatement {
+        transformDeclarationBody(declaration.body, declaration)
+        return super.visitFunction(declaration)
+    }
+
+    override fun visitAnonymousInitializer(declaration: IrAnonymousInitializer): IrStatement {
+        transformDeclarationBody(declaration.body, declaration)
+        return super.visitAnonymousInitializer(declaration)
+    }
+
+    private fun transformDeclarationBody(body: IrBody?, parent: IrDeclaration, lambda: IrFunction? = null) {
+        if (body is IrBlockBody) {
+            body.statements.forEachIndexed { i, stmt ->
+                val transformedStmt = stmt.transformStatement(parent, lambda)
+                body.statements[i] = transformedStmt
+                if (transformedStmt is IrCall && transformedStmt.symbol.owner.name.asString().matches(ATOMICFU_INLINE_FUNCTION.toRegex())) {
+                    val lambdaLoop = (transformedStmt.getValueArgument(0) as IrFunctionExpression).function
+                    transformDeclarationBody(lambdaLoop.body, parent, lambdaLoop)
+                }
+            }
+        }
+    }
+
+    private fun IrExpression.transformAtomicValueInitializer(parentDeclaration: IrDeclaration, lambda: IrFunction? = null) =
+        when {
+            type.isAtomicValueType() -> getPureTypeValue().transformAtomicFunctionCall(parentDeclaration, lambda)
+            type.isAtomicArrayType() -> buildPureTypeArrayConstructor()
+            type.isReentrantLockType() -> buildConstNull()
+            else -> this
+        }
+
+    private fun IrDeclaration.transformAtomicInlineDeclaration(): IrDeclaration? {
+        // inline fun Atomic*<T>.foo(...) { ... }
+        if (this is IrFunction &&
+            isInline &&
+            extensionReceiverParameter != null &&
+            extensionReceiverParameter!!.type.isAtomicValueType()
+        ) {
+            val extensionReceiverAtomicType = extensionReceiverParameter!!.type // Atomic*<T>
+            val extensionReceiverValueType = extensionReceiverAtomicType.atomicToValueType() // T
+            val extensionReceiverTypeParameter = extensionReceiverValueType.classifierOrNull?.owner
+            // containing declaration of this type parameter is transformed -> wrap it's type descriptor, so that MangleChecker will skip it
+            val wrappedExtensionReceiverType = extensionReceiverValueType.wrapTypeParameterDescriptor()
+            val getterType = buildGetterType(wrappedExtensionReceiverType)
+            val setterType = buildSetterType(wrappedExtensionReceiverType)
+            val valueParametersCount = valueParameters.size
+            val oldDeclaration = this
+            return buildFunction(parent, origin, name, visibility, isInline, returnType).apply {
+                body = oldDeclaration.body?.deepCopyWithSymbols(this)
+                val oldParameters = oldDeclaration.valueParameters.mapIndexed { index, p ->
+                    val typeParameter = p.type.classifierOrNull?.owner
+                    val wrappedType = if (typeParameter is IrClass) {
+                        val arguments = (p.type as IrSimpleType).arguments.map { typeArg ->
+                            if (typeArg is IrSimpleType && typeArg.classifier.owner === extensionReceiverTypeParameter) {
+                                wrappedExtensionReceiverType as IrTypeArgument
+                            } else typeArg
+                        }
+                        p.type.wrapClassTypeDescriptor(arguments)
+                    } else p.type.wrapTypeParameterDescriptor()
+                    buildValueParameter(this, p.name.identifier, index, wrappedType)
+                }
+                val extendedValueParameters = oldParameters + listOf(
+                    buildValueParameter(this, GETTER, valueParametersCount, getterType),
+                    buildValueParameter(this, SETTER, valueParametersCount + 1, setterType)
+                )
+                valueParameters = extendedValueParameters
+                typeParameters = oldDeclaration.typeParameters.mapIndexed { i, t ->
+                    buildTypeParameter(this) {
+                        origin = t.origin
+                        name = t.name
+                        index = i
+                        isReified = t.isReified
+                        variance = t.variance
+                    }
+                }
+            }
+        }
+        return null
+    }
+
+    private fun IrType.wrapClassTypeDescriptor(typeArguments: List<IrTypeArgument>): IrType {
+        val typeParameter = classifierOrNull?.owner
+        return if (this is IrSimpleType && typeParameter is IrClass) {
+            val classifier = IrClassSymbolImpl().apply { bind(typeParameter) }
+            IrSimpleTypeImpl(classifier, hasQuestionMark, typeArguments, annotations, abbreviation)
+        } else this
+    }
+
+    private fun IrType.wrapTypeParameterDescriptor(): IrType {
+        val typeParameter = classifierOrNull?.owner
+        return if (this is IrSimpleType && typeParameter is IrTypeParameter) {
+            val classifier = IrTypeParameterSymbolImpl().apply { bind(typeParameter) }
+            IrSimpleTypeImpl(classifier, hasQuestionMark, arguments, annotations, abbreviation)
+        } else this
+    }
+
+    private fun IrExpression.getPureTypeValue(): IrExpression {
+        require(this is IrCall && isAtomicFactoryFunction()) { "Illegal initializer for the atomic property $this" }
+        return getValueArgument(0)!!
+    }
+
+    private fun IrExpression.buildPureTypeArrayConstructor() =
+        when (this) {
+            is IrConstructorCall -> {
+                require(isAtomicArrayConstructor())
+                val arrayConstructorSymbol = type.getArrayConstructorSymbol { it.owner.valueParameters.size == 1 }
+                val size = getValueArgument(0)
+                IrConstructorCallImpl(
+                    UNDEFINED_OFFSET, UNDEFINED_OFFSET,
+                    irBuiltIns.unitType, arrayConstructorSymbol,
+                    0, 0, 1
+                ).apply {
+                    putValueArgument(0, size)
+                }
+            }
+            is IrCall -> {
+                require(isAtomicArrayFactoryFunction()) { "Unsupported atomic array factory function $this" }
+                val arrayFactorySymbol = referencePackageFunction("kotlin", "arrayOfNulls")
+                val arrayElementType = getTypeArgument(0)!!
+                val size = getValueArgument(0)
+                buildCall(
+                    target = arrayFactorySymbol,
+                    type = type,
+                    typeArguments = listOf(arrayElementType),
+                    valueArguments = listOf(size)
+                )
+            }
+            else -> error("Illegal type of atomic array initializer")
+        }
+
+    private fun IrCall.runtimeInlineAtomicFunctionCall(atomicType: IrType, accessors: List<IrExpression>): IrCall {
+        val valueArguments = getValueArguments()
+        val functionName = getAtomicFunctionName()
+        val runtimeFunction = getRuntimeFunctionSymbol(functionName, atomicType)
+        return buildCall(
+            target = runtimeFunction,
+            type = type,
+            origin = IrStatementOrigin.INVOKE,
+            typeArguments = if (runtimeFunction.owner.typeParameters.size == 1) listOf(atomicType) else emptyList(),
+            valueArguments = valueArguments + accessors
+        )
+    }
+
+    private fun IrStatement.transformStatement(parentDeclaration: IrDeclaration, lambda: IrFunction? = null) =
+        when (this) {
+            is IrExpression -> transformAtomicFunctionCall(parentDeclaration, lambda)
+            is IrVariable -> {
+                apply { initializer = initializer?.transformAtomicFunctionCall(parentDeclaration, lambda) }
+            }
+            else -> this
+        }
+
+    private fun IrExpression.transformAtomicFunctionCall(parentDeclaration: IrDeclaration, lambda: IrFunction? = null): IrExpression {
+        // erase unchecked cast to the Atomic* type
+        if (this is IrTypeOperatorCall && operator == IrTypeOperator.CAST && typeOperand.isAtomicValueType()) {
+            return argument
+        }
+        if (isAtomicValueInitializerCall()) {
+            return transformAtomicValueInitializer(parentDeclaration, lambda)
+        }
+        when (this) {
+            is IrTypeOperatorCall -> {
+                return apply { argument = argument.transformAtomicFunctionCall(parentDeclaration, lambda) }
+            }
+            is IrStringConcatenationImpl -> {
+                return apply {
+                    arguments.forEachIndexed { i, arg ->
+                        arguments[i] = arg.transformAtomicFunctionCall(parentDeclaration, lambda)
+                    }
+                }
+            }
+            is IrReturn -> {
+                if (parentDeclaration is IrFunction && parentDeclaration.isInline &&
+                    parentDeclaration.hasReceiverAccessorParameters() && returnTargetSymbol !== parentDeclaration.symbol) {
+                    return IrReturnImpl(
+                        startOffset,
+                        endOffset,
+                        type,
+                        parentDeclaration.symbol,
+                        value.transformAtomicFunctionCall(parentDeclaration, lambda)
+                    )
+                }
+                return apply { value = value.transformAtomicFunctionCall(parentDeclaration, lambda) }
+            }
+            is IrSetValue -> {
+                return apply { value = value.transformAtomicFunctionCall(parentDeclaration, lambda) }
+            }
+            is IrSetField -> {
+                return apply { value = value.transformAtomicFunctionCall(parentDeclaration, lambda) }
+            }
+            is IrIfThenElseImpl -> {
+                return apply {
+                    branches.forEachIndexed { i, branch ->
+                        branches[i] = branch.apply {
+                            condition = condition.transformAtomicFunctionCall(parentDeclaration, lambda)
+                            result = result.transformAtomicFunctionCall(parentDeclaration, lambda)
+                        }
+                    }
+                }
+            }
+            is IrWhenImpl -> {
+                return apply {
+                    branches.forEachIndexed { i, branch ->
+                        branches[i] = branch.apply {
+                            condition = condition.transformAtomicFunctionCall(parentDeclaration, lambda)
+                            result = result.transformAtomicFunctionCall(parentDeclaration, lambda)
+                        }
+                    }
+                }
+            }
+            is IrTry -> {
+                return apply {
+                    tryResult = tryResult.transformAtomicFunctionCall(parentDeclaration, lambda)
+                    catches.forEach {
+                        it.result = it.result.transformAtomicFunctionCall(parentDeclaration, lambda)
+                    }
+                    finallyExpression = finallyExpression?.transformAtomicFunctionCall(parentDeclaration, lambda)
+                }
+            }
+            is IrBlock -> {
+                return apply {
+                    statements.forEachIndexed { i, stmt ->
+                        statements[i] = stmt.transformStatement(parentDeclaration, lambda)
+                    }
+                }
+            }
+            is IrGetValue -> {
+                if (lambda != null && symbol.owner.parent == lambda) return this
+                if (symbol is IrValueParameterSymbol && parentDeclaration.isTransformedAtomicExtensionFunction()) {
+                    // replace use site of the value parameter with it's copy from the transformed declaration
+                    val index = (symbol.owner as IrValueParameter).index
+                    if (index >= 0) { // index == -1 for `this` parameter
+                        val transformedValueParameter = (parentDeclaration as IrFunction).valueParameters[index]
+                        return buildGetValue(transformedValueParameter.symbol)
+                    }
+                }
+            }
+            is IrCall -> {
+                dispatchReceiver?.let { dispatchReceiver = it.transformAtomicFunctionCall(parentDeclaration, lambda) }
+                extensionReceiver?.let { extensionReceiver = it.transformAtomicFunctionCall(parentDeclaration, lambda) }
+                getValueArguments().forEachIndexed { i, arg ->
+                    putValueArgument(i, arg?.transformAtomicFunctionCall(parentDeclaration, lambda))
+                }
+                val isInline = symbol.owner.isInline
+                val field = extensionReceiver ?: dispatchReceiver ?: return this
+                if (symbol.isKotlinxAtomicfuPackage() && field.type.isAtomicValueType()) { // invocation of the atomic function
+                    // 1. transform atomic function call on the atomic field
+                    if (field is IrCall) { // property accessor <get-field>
+                        val accessors = field.getPropertyAccessors(lambda ?: parentDeclaration)
+                        return runtimeInlineAtomicFunctionCall(field.type.atomicToValueType(), accessors)
+                    }
+                    // 2. transform atomic function call on the atomic `this` extension receiver
+                    // inline fun Atomic*.foo() { CAS(expect, update) } -> { atomicfu_compareAndSet(expect, update, getter, setter) }
+                    if (field is IrGetValue && parentDeclaration.isTransformedAtomicExtensionFunction()) {
+                        val accessorParameters = (parentDeclaration as IrFunction).valueParameters.takeLast(2).map { it.capture() }
+                        return runtimeInlineAtomicFunctionCall(field.type.atomicToValueType(), accessorParameters)
+                    }
+                } else {
+                    // 3. transform invocation of an inline Atomic* extension function call: a.foo()
+                    if (isInline && field is IrCall && field.type.isAtomicValueType()) {
+                        val accessors = field.getPropertyAccessors(lambda ?: parentDeclaration)
+                        val dispatch = dispatchReceiver
+                        val args = getValueArguments()
+                        val transformedTarget = symbol.owner.getDeclarationWithAccessorParameters()
+                        return buildCall(
+                            target = transformedTarget.symbol,
+                            type = type,
+                            origin = IrStatementOrigin.INVOKE,
+                            valueArguments = args + accessors
+                        ).apply {
+                            dispatchReceiver = dispatch
+                        }
+                    }
+                }
+            }
+            is IrConstructorCall -> {
+                getValueArguments().forEachIndexed { i, arg ->
+                    putValueArgument(i, arg?.transformAtomicFunctionCall(parentDeclaration, lambda))
+                }
+            }
+        }
+        return this
+    }
+
+    private fun IrFunction.hasReceiverAccessorParameters(): Boolean {
+        if (valueParameters.size < 2) return false
+        val params = valueParameters.takeLast(2)
+        return params[0].name.asString() == GETTER && params[1].name.asString() == SETTER
+    }
+
+    private fun IrDeclaration.isTransformedAtomicExtensionFunction(): Boolean =
+        this is IrFunction && this.hasReceiverAccessorParameters()
+
+    private fun IrFunction.getDeclarationWithAccessorParameters(): IrSimpleFunction {
+        val parent = parent as IrDeclarationContainer
+        val params = valueParameters.map { it.type }
+        val extensionType = extensionReceiverParameter?.type?.atomicToValueType()
+        return try {
+            parent.declarations.single {
+                it is IrSimpleFunction &&
+                        it.name == symbol.owner.name &&
+                        it.valueParameters.size == params.size + 2 &&
+                        it.valueParameters.dropLast(2).withIndex().all { p -> p.value.type.classifierOrNull?.owner == params[p.index].classifierOrNull?.owner } &&
+                        it.getGetterReturnType()?.classifierOrNull?.owner == extensionType?.classifierOrNull?.owner
+            } as IrSimpleFunction
+        } catch (e: RuntimeException) {
+            error("Exception while looking for the declaration with accessor parameters: ${e.message}")
+        }
+    }
+
+    private fun IrExpression.isAtomicValueInitializerCall() =
+        (this is IrCall && (this.isAtomicFactoryFunction() || this.isAtomicArrayFactoryFunction())) ||
+                (this is IrConstructorCall && this.isAtomicArrayConstructor()) ||
+                type.isReentrantLockType()
+
+    private fun IrCall.isArrayElementGetter() =
+        dispatchReceiver != null &&
+                dispatchReceiver!!.type.isAtomicArrayType() &&
+                symbol.owner.name.asString() == GET
+
+    private fun IrCall.getBackingField(): IrField {
+        val correspondingPropertySymbol = symbol.owner.correspondingPropertySymbol!!
+        return correspondingPropertySymbol.owner.backingField!!
+    }
+
+    private fun IrCall.buildAccessorLambda(
+        isSetter: Boolean,
+        parentDeclaration: IrDeclaration
+    ): IrFunctionExpression {
+        val isArrayElement = isArrayElementGetter()
+        val getterCall = if (isArrayElement) dispatchReceiver as IrCall else this
+        val valueType = type.atomicToValueType()
+        val type = if (isSetter) buildSetterType(valueType) else buildGetterType(valueType)
+        val name = if (isSetter) setterName(getterCall.symbol.owner.name.getFieldName())
+            else getterName(getterCall.symbol.owner.name.getFieldName())
+        val returnType = if (isSetter) context.irBuiltIns.unitType else valueType
+        val accessorFunction = buildFunction(
+            parent = parentDeclaration as IrDeclarationParent,
+            origin = IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR,
+            name = Name.identifier(name),
+            visibility = DescriptorVisibilities.LOCAL,
+            isInline = true,
+            returnType = returnType
+        ).apply {
+            val valueParameter = buildValueParameter(this, name, 0, valueType)
+            this.valueParameters = if (isSetter) listOf(valueParameter) else emptyList()
+            val body = if (isSetter) {
+                if (isArrayElement) {
+                    val setSymbol = referenceFunction(getterCall.type.referenceClass(), SET)
+                    val elementIndex = getValueArgument(0)!!.deepCopyWithVariables()
+                    buildCall(
+                        target = setSymbol,
+                        type = context.irBuiltIns.unitType,
+                        origin = IrStatementOrigin.LAMBDA,
+                        valueArguments = listOf(elementIndex, valueParameter.capture())
+                    ).apply {
+                        dispatchReceiver = getterCall
+                    }
+                } else {
+                    buildSetField(getterCall.getBackingField(), getterCall.dispatchReceiver, valueParameter.capture())
+                }
+            } else {
+                val getField = buildGetField(getterCall.getBackingField(), getterCall.dispatchReceiver)
+                if (isArrayElement) {
+                    val getSymbol = referenceFunction(getterCall.type.referenceClass(), GET)
+                    val elementIndex = getValueArgument(0)!!.deepCopyWithVariables()
+                    buildCall(
+                        target = getSymbol,
+                        type = valueType,
+                        origin = IrStatementOrigin.LAMBDA,
+                        valueArguments = listOf(elementIndex)
+                    ).apply {
+                        dispatchReceiver = getField.deepCopyWithVariables()
+                    }
+                } else {
+                    getField.deepCopyWithVariables()
+                }
+            }
+            this.body = buildBlockBody(listOf(body))
+            origin = IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR
+        }
+        return IrFunctionExpressionImpl(
+            UNDEFINED_OFFSET, UNDEFINED_OFFSET,
+            type,
+            accessorFunction,
+            IrStatementOrigin.LAMBDA
+        )
+    }
+
+    private fun buildSetField(backingField: IrField, ownerClass: IrExpression?, value: IrGetValue): IrSetField {
+        val receiver = if (ownerClass is IrTypeOperatorCall) ownerClass.argument as IrGetValue else ownerClass
+        val fieldSymbol = backingField.symbol
+        return buildSetField(
+            symbol = fieldSymbol,
+            receiver = receiver,
+            value = value
+        )
+    }
+
+    private fun buildGetField(backingField: IrField, ownerClass: IrExpression?): IrGetField {
+        val receiver = if (ownerClass is IrTypeOperatorCall) ownerClass.argument as IrGetValue else ownerClass
+        return buildGetField(backingField.symbol, receiver)
+    }
+
+    private fun IrCall.getPropertyAccessors(parentDeclaration: IrDeclaration): List<IrExpression> =
+        listOf(buildAccessorLambda(isSetter = false, parentDeclaration = parentDeclaration),
+               buildAccessorLambda(isSetter = true, parentDeclaration = parentDeclaration))
+
+    private fun getRuntimeFunctionSymbol(name: String, type: IrType): IrSimpleFunctionSymbol {
+        val functionName = when (name) {
+            "value.<get-value>" -> "getValue"
+            "value.<set-value>" -> "setValue"
+            else -> name
+        }
+        return referencePackageFunction("kotlinx.atomicfu", "$ATOMICFU_RUNTIME_FUNCTION_PREDICATE$functionName") {
+            val typeArg = it.owner.getGetterReturnType()
+            !(typeArg as IrType).isPrimitiveType() || typeArg == type
+        }
+    }
+
+    private fun IrFunction.getGetterReturnType() = (valueParameters[valueParameters.lastIndex - 1].type as IrSimpleType).arguments.first().typeOrNull
+
+    private fun IrCall.isAtomicFactoryFunction(): Boolean {
+        val name = symbol.owner.name
+        return !name.isSpecial && name.identifier == ATOMIC_CONSTRUCTOR
+    }
+
+    private fun IrCall.isAtomicArrayFactoryFunction(): Boolean {
+        val name = symbol.owner.name
+        return !name.isSpecial && name.identifier == ATOMIC_ARRAY_FACTORY_FUNCTION
+    }
+
+    private fun IrConstructorCall.isAtomicArrayConstructor() = (type as IrSimpleType).isAtomicArrayType()
+
+    private fun IrSymbol.isKotlinxAtomicfuPackage() =
+        this.isPublicApi && signature?.packageFqName()?.asString() == AFU_PKG.prettyStr()
+
+    private fun IrType.isAtomicValueType() = belongsTo(ATOMICFU_VALUE_TYPE)
+    private fun IrType.isAtomicArrayType() = belongsTo(ATOMIC_ARRAY_TYPE)
+    private fun IrType.isReentrantLockType() = belongsTo("$AFU_PKG/$LOCKS", REENTRANT_LOCK_TYPE)
+
+    private fun IrType.belongsTo(typeName: String) = belongsTo(AFU_PKG, typeName)
+
+    private fun IrType.belongsTo(packageName: String, typeName: String): Boolean {
+        if (this !is IrSimpleType || !(classifier.isPublicApi && classifier is IrClassSymbol)) return false
+        val signature = classifier.signature?.asPublic() ?: return false
+        val pckg = signature.packageFqName().asString()
+        val type = signature.declarationFqName
+        return pckg == packageName.prettyStr() && type.matches(typeName.toRegex())
+    }
+
+    private fun IrCall.getAtomicFunctionName(): String {
+        val signature = symbol.signature!!
+        val classFqName = if (signature is IdSignature.AccessorSignature) {
+            signature.accessorSignature.declarationFqName
+        } else (signature.asPublic()!!).declarationFqName
+        val pattern = "$ATOMICFU_VALUE_TYPE\\.(.*)".toRegex()
+        return pattern.findAll(classFqName).firstOrNull()?.let { it.groupValues[2] } ?: classFqName
+    }
+
+    private fun IrType.atomicToValueType(): IrType {
+        require(isAtomicValueType())
+        val classId = ((this as IrSimpleType).classifier.signature as IdSignature.PublicSignature).declarationFqName
+        if (classId == "AtomicRef") {
+            return arguments.first().typeOrNull ?: error("$AFU_PKG/AtomicRef type parameter is not IrTypeProjection")
+        }
+        return AFU_CLASSES[classId] ?: error("IrType ${this.getClass()} does not match any of atomicfu types")
+    }
+
+    private fun buildConstNull() = IrConstImpl.constNull(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.anyType)
+
+    private fun IrType.getArrayConstructorSymbol(predicate: (IrConstructorSymbol) -> Boolean = { true }): IrConstructorSymbol {
+        val afuClassId = ((this as IrSimpleType).classifier.signature as IdSignature.PublicSignature).declarationFqName
+        val classId = FqName("$KOTLIN.${AFU_ARRAY_CLASSES[afuClassId]!!}")
+        return context.referenceConstructors(classId).single(predicate)
+    }
+
+    private fun IrType.referenceClass(): IrClassSymbol {
+        val afuClassId = ((this as IrSimpleType).classifier.signature as IdSignature.PublicSignature).declarationFqName
+        val classId = FqName("$KOTLIN.${AFU_ARRAY_CLASSES[afuClassId]!!}")
+        return context.referenceClass(classId)!!
+    }
+
+    private fun referencePackageFunction(
+        packageName: String,
+        name: String,
+        predicate: (IrFunctionSymbol) -> Boolean = { true }
+    ) = try {
+            context.referenceFunctions(FqName("$packageName.$name")).single(predicate)
+        } catch (e: RuntimeException) {
+            error("Exception while looking for the function `$name` in package `$packageName`: ${e.message}")
+        }
+
+    private fun referenceFunction(classSymbol: IrClassSymbol, functionName: String): IrSimpleFunctionSymbol {
+        val functionId = FqName("$KOTLIN.${classSymbol.owner.name}.$functionName")
+        return try {
+            context.referenceFunctions(functionId).single()
+        } catch (e: RuntimeException) {
+            error("Exception while looking for the function `$functionId`: ${e.message}")
+        }
+    }
+
+    companion object {
+        fun transform(irFile: IrFile, context: IrPluginContext) =
+            irFile.transform(AtomicFUTransformer(context), null)
+    }
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/src/org.jetbrains.kotlinx.atomicfu.compiler/extensions/AtomicfuComponentRegistrar.kt b/plugins/atomicfu/atomicfu-compiler/src/org.jetbrains.kotlinx.atomicfu.compiler/extensions/AtomicfuComponentRegistrar.kt
new file mode 100644
index 0000000..b33555f
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/src/org.jetbrains.kotlinx.atomicfu.compiler/extensions/AtomicfuComponentRegistrar.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2010-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlinx.atomicfu.compiler.extensions
+
+import com.intellij.mock.MockProject
+import com.intellij.openapi.project.Project
+import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
+import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
+import org.jetbrains.kotlin.config.CompilerConfiguration
+
+class AtomicfuComponentRegistrar : ComponentRegistrar {
+    override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
+        registerExtensions(project)
+    }
+
+    companion object {
+        fun registerExtensions(project: Project) {
+            IrGenerationExtension.registerExtension(project, AtomicfuLoweringExtension()) }
+    }
+}
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
new file mode 100644
index 0000000..8191bee
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/src/org.jetbrains.kotlinx.atomicfu.compiler/extensions/AtomicfuLoweringExtension.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2010-2018 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.extensions
+
+import org.jetbrains.kotlin.backend.common.FileLoweringPass
+import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
+import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
+import org.jetbrains.kotlin.backend.common.runOnFilePostfix
+import org.jetbrains.kotlin.ir.IrElement
+import org.jetbrains.kotlin.ir.declarations.*
+import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
+import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
+import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
+import org.jetbrains.kotlin.ir.visitors.acceptVoid
+import java.lang.IllegalStateException
+
+public open class AtomicfuLoweringExtension : IrGenerationExtension {
+    override fun generate(
+        moduleFragment: IrModuleFragment,
+        pluginContext: IrPluginContext
+    ) {
+        val atomicfuClassLowering = AtomicfuClassLowering(pluginContext)
+        for (file in moduleFragment.files) {
+            atomicfuClassLowering.runOnFileInOrder(file)
+        }
+    }
+}
+
+/**
+ * Copy of [runOnFilePostfix], but this implementation first lowers declaration, then its children.
+ */
+fun FileLoweringPass.runOnFileInOrder(irFile: IrFile) {
+    irFile.acceptVoid(object : IrElementVisitorVoid {
+        override fun visitElement(element: IrElement) {
+            element.acceptChildrenVoid(this)
+        }
+
+        override fun visitFile(declaration: IrFile) {
+            lower(declaration)
+            declaration.acceptChildrenVoid(this)
+        }
+    })
+}
+
+private class AtomicfuClassLowering(
+    val context: IrPluginContext
+) : IrElementTransformerVoid(), FileLoweringPass {
+    override fun lower(irFile: IrFile) {
+        AtomicFUTransformer.transform(irFile, context)
+    }
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/src/org.jetbrains.kotlinx.atomicfu.compiler/extensions/TransformerHelpers.kt b/plugins/atomicfu/atomicfu-compiler/src/org.jetbrains.kotlinx.atomicfu.compiler/extensions/TransformerHelpers.kt
new file mode 100644
index 0000000..cf11af1
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/src/org.jetbrains.kotlinx.atomicfu.compiler/extensions/TransformerHelpers.kt
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2010-2020 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.extensions
+
+import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
+import org.jetbrains.kotlin.descriptors.DescriptorVisibility
+import org.jetbrains.kotlin.descriptors.Visibility
+import org.jetbrains.kotlin.ir.IrStatement
+import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
+import org.jetbrains.kotlin.ir.builders.declarations.buildFun
+import org.jetbrains.kotlin.ir.declarations.*
+import org.jetbrains.kotlin.ir.expressions.*
+import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
+import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
+import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
+import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
+import org.jetbrains.kotlin.ir.symbols.*
+import org.jetbrains.kotlin.ir.types.*
+import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
+import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
+import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.types.Variance
+
+interface TransformerHelpers {
+
+    val context: IrPluginContext
+
+    fun buildFunction(
+        parent: IrDeclarationParent,
+        origin: IrDeclarationOrigin,
+        name: Name,
+        visibility: DescriptorVisibility,
+        isInline: Boolean,
+        returnType: IrType
+    ): IrSimpleFunction =
+        context.irFactory.buildFun {
+            startOffset = UNDEFINED_OFFSET
+            endOffset = UNDEFINED_OFFSET
+            this.origin = origin
+            this.name = name
+            this.visibility = visibility
+            this.isInline = isInline
+            this.returnType = returnType
+        }.apply {
+            this.parent = parent
+        }
+
+    fun buildCall(
+        target: IrSimpleFunctionSymbol,
+        type: IrType? = null,
+        origin: IrStatementOrigin? = null,
+        typeArguments: List<IrType> = emptyList(),
+        valueArguments: List<IrExpression?> = emptyList()
+    ): IrCall =
+        IrCallImpl(
+            UNDEFINED_OFFSET,
+            UNDEFINED_OFFSET,
+            type ?: target.owner.returnType,
+            target,
+            typeArguments.size,
+            valueArguments.size,
+            origin
+        ).apply {
+            typeArguments.let {
+                it.withIndex().forEach { (i, t) -> putTypeArgument(i, t) }
+            }
+            valueArguments.let {
+                it.withIndex().forEach { (i, arg) -> putValueArgument(i, arg) }
+            }
+        }
+
+    fun buildBlockBody(statements: List<IrStatement>) =
+        context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET, statements)
+
+    fun buildSetField(
+        symbol: IrFieldSymbol,
+        receiver: IrExpression?,
+        value: IrExpression,
+        superQualifierSymbol: IrClassSymbol? = null
+    ) =
+        IrSetFieldImpl(
+            UNDEFINED_OFFSET,
+            UNDEFINED_OFFSET,
+            symbol,
+            receiver,
+            value,
+            value.type,
+            IrStatementOrigin.GET_PROPERTY,
+            superQualifierSymbol
+        )
+
+    fun buildGetField(symbol: IrFieldSymbol, receiver: IrExpression?, superQualifierSymbol: IrClassSymbol? = null, type: IrType? = null) =
+        IrGetFieldImpl(
+            UNDEFINED_OFFSET,
+            UNDEFINED_OFFSET,
+            symbol,
+            type ?: symbol.owner.type,
+            receiver,
+            IrStatementOrigin.GET_PROPERTY,
+            superQualifierSymbol
+        )
+
+    fun buildFunctionSimpleType(paramsCount: Int, typeParameters: List<IrType>): IrSimpleType {
+        val classSymbol = context.irBuiltIns.function(paramsCount)
+        return IrSimpleTypeImpl(
+            classifier = classSymbol,
+            hasQuestionMark = false,
+            arguments = typeParameters.map { it.toTypeArgument() },
+            annotations = emptyList()
+        )
+    }
+
+    fun buildGetterType(valueType: IrType) = buildFunctionSimpleType(0, listOf(valueType))
+    fun buildSetterType(valueType: IrType) = buildFunctionSimpleType(1, listOf(valueType, context.irBuiltIns.unitType))
+
+    fun buildGetValue(symbol: IrValueSymbol) =
+        IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, symbol.owner.type, symbol)
+
+    fun getterName(name: String) = "<get-$name>"
+    fun setterName(name: String) = "<set-$name>"
+    fun Name.getFieldName() = "<get-(\\w+)>".toRegex().find(asString())?.groupValues?.get(1)
+        ?: error("Getter name ${this.asString()} does not match special name pattern <get-fieldName>")
+
+    private fun IrType.toTypeArgument(): IrTypeArgument {
+        return makeTypeProjection(this, Variance.INVARIANT)
+    }
+
+    fun IrFunctionAccessExpression.getValueArguments() = (0 until valueArgumentsCount).map { i ->
+        getValueArgument(i)
+    }
+
+    fun IrValueParameter.capture() = buildGetValue(symbol)
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/BasicAtomicfuTestGenerated.java b/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/BasicAtomicfuTestGenerated.java
new file mode 100644
index 0000000..526f348
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/BasicAtomicfuTestGenerated.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2010-2020 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;
+
+import com.intellij.testFramework.TestDataPath;
+import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
+import org.jetbrains.kotlin.test.KotlinTestUtils;
+import org.jetbrains.kotlin.test.util.KtTestUtil;
+import org.jetbrains.kotlin.test.TestMetadata;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.regex.Pattern;
+
+/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
+@SuppressWarnings("all")
+@TestMetadata("plugins/atomicfu/atomicfu-compiler/testData/basic")
+@TestDataPath("$PROJECT_ROOT")
+@RunWith(JUnit3RunnerWithInners.class)
+public class BasicAtomicfuTestGenerated extends AbstractBasicAtomicfuTest {
+    private void runTest(String testDataFilePath) throws Exception {
+        KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
+    }
+
+    public void testAllFilesPresentInBasic() throws Exception {
+        KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/atomicfu/atomicfu-compiler/testData/basic"), Pattern.compile("^(.+)\\.kt$"), null, true);
+    }
+
+    @TestMetadata("ArithmeticTest.kt")
+    public void testArithmeticTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/basic/ArithmeticTest.kt");
+    }
+
+    @TestMetadata("AtomicArrayTest.kt")
+    public void testAtomicArrayTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/basic/AtomicArrayTest.kt");
+    }
+
+    @TestMetadata("ArrayInlineFunctionTest.kt")
+    public void testArrayInlineFunctionTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/basic/ArrayInlineFunctionTest.kt");
+    }
+
+    @TestMetadata("ExtensionsTest.kt")
+    public void testExtensionsTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/basic/ExtensionsTest.kt");
+    }
+
+    @TestMetadata("IndexArrayElementGetterTest.kt")
+    public void testIndexArrayElementGetterTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/basic/IndexArrayElementGetterTest.kt");
+    }
+
+    @TestMetadata("LockFreeStackTest.kt")
+    public void testLockFreeStackTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/basic/LockFreeStackTest.kt");
+    }
+
+    @TestMetadata("LockFreeQueueTest.kt")
+    public void testLockFreeQueueTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/basic/LockFreeQueueTest.kt");
+    }
+
+    @TestMetadata("LockFreeLongCounterTest.kt")
+    public void testLockFreeLongCounterTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/basic/LockFreeLongCounterTest.kt");
+    }
+
+    @TestMetadata("LockFreeIntBitsTest.kt")
+    public void testLockFreeIntBitsTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/basic/LockFreeIntBitsTest.kt");
+    }
+
+    @TestMetadata("LoopTest.kt")
+    public void testLoopTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/basic/LoopTest.kt");
+    }
+
+    @TestMetadata("ScopeTest.kt")
+    public void testScopeTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/basic/ScopeTest.kt");
+    }
+
+    @TestMetadata("LockTest.kt")
+    public void testLockTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/basic/LockTest.kt");
+    }
+
+    @TestMetadata("MultiInitTest.kt")
+    public void testMultiInitTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/basic/MultiInitTest.kt");
+    }
+
+    @TestMetadata("SimpleLockTest.kt")
+    public void testSimpleLockTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/basic/SimpleLockTest.kt");
+    }
+
+    @TestMetadata("TopLevelTest.kt")
+    public void testTopLevelTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/basic/TopLevelTest.kt");
+    }
+
+    @TestMetadata("UncheckedCastTest.kt")
+    public void testUncheckedCastTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/basic/UncheckedCastTest.kt");
+    }
+
+    @TestMetadata("PropertyDeclarationTest.kt")
+    public void testPropertyDeclarationTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/basic/PropertyDeclarationTest.kt");
+    }
+
+    @TestMetadata("ParameterizedInlineFunExtensionTest.kt")
+    public void testParameterizedInlineFunExtensionTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/basic/ParameterizedInlineFunExtensionTest.kt");
+    }
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/JsGenerateAndRunTest.kt b/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/JsGenerateAndRunTest.kt
new file mode 100644
index 0000000..f626a25
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/JsGenerateAndRunTest.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2010-2020 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
+
+import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
+import org.jetbrains.kotlin.js.config.JSConfigurationKeys
+import org.jetbrains.kotlin.js.test.BasicBoxTest
+import org.jetbrains.kotlin.js.test.BasicIrBoxTest
+import org.jetbrains.kotlin.serialization.js.ModuleKind
+import org.jetbrains.kotlin.test.KotlinTestUtils
+import org.jetbrains.kotlinx.atomicfu.compiler.extensions.AtomicfuComponentRegistrar
+import org.junit.Test
+import java.io.File
+
+private val atomicfuCompileDependency = System.getProperty("atomicfu.classpath")
+private val atomicfuRuntime = System.getProperty("atomicfuRuntimeForTests.classpath")
+
+abstract class AtomicfuBaseTest(relativePath: String) : BasicIrBoxTest(
+    "plugins/atomicfu/atomicfu-compiler/testData/$relativePath",
+    "plugins/atomicfu/atomicfu-compiler/testData/$relativePath"
+) {
+    override fun createEnvironment(): KotlinCoreEnvironment {
+        return super.createEnvironment().also { environment ->
+            AtomicfuComponentRegistrar.registerExtensions(environment.project)
+            environment.configuration.put(JSConfigurationKeys.LIBRARIES, listOf(atomicfuCompileDependency, atomicfuRuntime))
+            environment.configuration.put(JSConfigurationKeys.MODULE_KIND, ModuleKind.PLAIN)
+        }
+    }
+}
+
+abstract class AbstractBasicAtomicfuTest : AtomicfuBaseTest("basic/")
+abstract class AbstractLocksAtomicfuTest : AtomicfuBaseTest("locks/")
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/LocksAtomicfuTestGenerated.java b/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/LocksAtomicfuTestGenerated.java
new file mode 100644
index 0000000..04016d5
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/LocksAtomicfuTestGenerated.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2010-2020 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;
+
+import com.intellij.testFramework.TestDataPath;
+import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
+import org.jetbrains.kotlin.test.KotlinTestUtils;
+import org.jetbrains.kotlin.test.util.KtTestUtil;
+import org.jetbrains.kotlin.test.TestMetadata;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.regex.Pattern;
+
+/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
+@SuppressWarnings("all")
+@TestMetadata("plugins/atomicfu/atomicfu-compiler/testData/locks")
+@TestDataPath("$PROJECT_ROOT")
+@RunWith(JUnit3RunnerWithInners.class)
+public class LocksAtomicfuTestGenerated extends AbstractLocksAtomicfuTest {
+    private void runTest(String testDataFilePath) throws Exception {
+        KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
+    }
+
+    public void testAllFilesPresentInLocks() throws Exception {
+        KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/atomicfu/atomicfu-compiler/testData/locks"), Pattern.compile("^(.+)\\.kt$"), null, true);
+    }
+
+    @TestMetadata("ReentrantLockTest.kt")
+    public void testReentrantLockTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/locks/ReentrantLockTest.kt");
+    }
+
+    @TestMetadata("SynchronizedObjectTest.kt")
+    public void testSynchronizedObjectTest() throws Exception {
+        runTest("plugins/atomicfu/atomicfu-compiler/testData/locks/SynchronizedObjectTest.kt");
+    }
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/basic/ArithmeticTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/basic/ArithmeticTest.kt
new file mode 100644
index 0000000..c9e3a63
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/basic/ArithmeticTest.kt
@@ -0,0 +1,131 @@
+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)
+}
+
+class ArithmeticTest {
+    val local = atomic(0)
+
+    fun testGetValue() {
+        val a = IntArithmetic()
+        a._x.value = 5
+        check(a._x.value == 5)
+        var aValue = a._x.value
+        check(aValue == 5)
+        check(a.x == 5)
+
+        local.value = 555
+        aValue = local.value
+        check(local.value == aValue)
+    }
+
+    fun testAtomicCallPlaces(): Boolean {
+        val a = IntArithmetic()
+        a._x.value = 5
+        a._x.compareAndSet(5, 42)
+        val res = a._x.compareAndSet(42, 45)
+        check(res)
+        check(a._x.compareAndSet(45, 77))
+        check(!a._x.compareAndSet(95, 77))
+        return a._x.compareAndSet(77, 88)
+    }
+
+    fun testInt() {
+        val a = IntArithmetic()
+        check(a.x == 0)
+        val update = 3
+        check(a._x.getAndSet(update) == 0)
+        check(a._x.compareAndSet(update, 8))
+        a._x.lazySet(1)
+        check(a.x == 1)
+        check(a._x.getAndSet(2) == 1)
+        check(a.x == 2)
+        check(a._x.getAndIncrement() == 2)
+        check(a.x == 3)
+        check(a._x.getAndDecrement() == 3)
+        check(a.x == 2)
+        check(a._x.getAndAdd(2) == 2)
+        check(a.x == 4)
+        check(a._x.addAndGet(3) == 7)
+        check(a.x == 7)
+        check(a._x.incrementAndGet() == 8)
+        check(a.x == 8)
+        check(a._x.decrementAndGet() == 7)
+        check(a.x == 7)
+        check(a._x.compareAndSet(7, 10))
+    }
+
+    fun testLong() {
+        val a = LongArithmetic()
+        check(a.z.value == 2424920024888888848)
+        a.z.lazySet(8424920024888888848)
+        check(a.z.value == 8424920024888888848)
+        check(a.z.getAndSet(8924920024888888848) == 8424920024888888848)
+        check(a.z.value == 8924920024888888848)
+        check(a.z.incrementAndGet() == 8924920024888888849) // fails
+        check(a.z.value == 8924920024888888849)
+        check(a.z.getAndDecrement() == 8924920024888888849)
+        check(a.z.value == 8924920024888888848)
+        check(a.z.getAndAdd(100000000000000000) == 8924920024888888848)
+        check(a.z.value == 9024920024888888848)
+        check(a.z.addAndGet(-9223372036854775807) == -198452011965886959)
+        check(a.z.value == -198452011965886959)
+        check(a.z.incrementAndGet() == -198452011965886958)
+        check(a.z.value == -198452011965886958)
+        check(a.z.decrementAndGet() == -198452011965886959)
+        check(a.z.value == -198452011965886959)
+    }
+
+    fun testBoolean() {
+        val a = BooleanArithmetic()
+        check(!a.x)
+        a._x.lazySet(true)
+        check(a.x)
+        check(a._x.getAndSet(true))
+        check(a._x.compareAndSet(true, false))
+        check(!a.x)
+    }
+
+    fun testReference() {
+        val a = ReferenceArithmetic()
+        a._x.value = "aaa"
+        check(a._x.value == "aaa")
+        a._x.lazySet("bb")
+        check(a._x.value == "bb")
+        check(a._x.getAndSet("ccc") == "bb")
+        check(a._x.value == "ccc")
+    }
+}
+
+fun box(): String {
+    val testClass = ArithmeticTest()
+
+    testClass.testGetValue()
+    if (!testClass.testAtomicCallPlaces()) return "testAtomicCallPlaces: FAILED"
+
+    testClass.testInt()
+    testClass.testLong()
+    testClass.testBoolean()
+    testClass.testReference()
+    return "OK"
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/basic/ArrayInlineFunctionTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/basic/ArrayInlineFunctionTest.kt
new file mode 100644
index 0000000..c9d0b1d
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/basic/ArrayInlineFunctionTest.kt
@@ -0,0 +1,47 @@
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class ArrayInlineFunctionTest {
+    private val anyArr = atomicArrayOfNulls<Any?>(5)
+    private val refArr = atomicArrayOfNulls<Box>(5)
+
+    private data class Box(val n: Int)
+
+    fun testSetArrayElementValueInLoop() {
+        anyArr[0].loop { cur ->
+            assertTrue(anyArr[0].compareAndSet(cur, IntArray(5)))
+            return
+        }
+    }
+
+    private fun action(cur: Box?) = cur?.let { Box(cur.n * 10) }
+
+
+    fun testArrayElementUpdate() {
+        refArr[0].lazySet(Box(5))
+        refArr[0].update { cur -> cur?.let { Box(cur.n * 10) } }
+        assertEquals(refArr[0].value!!.n, 50)
+    }
+
+
+    fun testArrayElementGetAndUpdate() {
+        refArr[0].lazySet(Box(5))
+        assertEquals(refArr[0].getAndUpdate { cur -> action(cur) }!!.n, 5)
+        assertEquals(refArr[0].value!!.n, 50)
+    }
+
+
+    fun testArrayElementUpdateAndGet() {
+        refArr[0].lazySet(Box(5))
+        assertEquals(refArr[0].updateAndGet { cur -> action(cur) }!!.n, 50)
+    }
+}
+
+fun box(): String {
+    val testClass = ArrayInlineFunctionTest()
+    testClass.testSetArrayElementValueInLoop()
+    testClass.testArrayElementGetAndUpdate()
+    testClass.testArrayElementUpdate()
+    testClass.testArrayElementUpdateAndGet()
+    return "OK"
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/basic/AtomicArrayTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/basic/AtomicArrayTest.kt
new file mode 100644
index 0000000..70e23e4
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/basic/AtomicArrayTest.kt
@@ -0,0 +1,97 @@
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class AtomicArrayTest {
+
+    fun testIntArray() {
+        val A = AtomicArrayClass()
+        check(A.intArr[0].compareAndSet(0, 3))
+        check(A.intArr[1].value == 0)
+        A.intArr[0].lazySet(5)
+        check(A.intArr[0].value + A.intArr[1].value + A.intArr[2].value == 5)
+        check(A.intArr[0].compareAndSet(5, 10))
+        check(A.intArr[0].getAndDecrement() == 10)
+        check(A.intArr[0].value == 9)
+        A.intArr[2].value = 2
+        check(A.intArr[2].value == 2)
+        check(A.intArr[2].compareAndSet(2, 34))
+        check(A.intArr[2].value == 34)
+    }
+
+
+    fun testLongArray() {
+        val A = AtomicArrayClass()
+        A.longArr[0].value = 2424920024888888848
+        check(A.longArr[0].value == 2424920024888888848)
+        A.longArr[0].lazySet(8424920024888888848)
+        check(A.longArr[0].value == 8424920024888888848)
+        val ac = A.longArr[0].value
+        A.longArr[3].value = ac
+        check(A.longArr[3].getAndSet(8924920024888888848) == 8424920024888888848)
+        check(A.longArr[3].value == 8924920024888888848)
+        val ac1 = A.longArr[3].value
+        A.longArr[4].value = ac1
+        check(A.longArr[4].incrementAndGet() == 8924920024888888849)
+        check(A.longArr[4].value == 8924920024888888849)
+        check(A.longArr[4].getAndDecrement() == 8924920024888888849)
+        check(A.longArr[4].value == 8924920024888888848)
+        A.longArr[4].value = 8924920024888888848
+        check(A.longArr[4].getAndAdd(100000000000000000) == 8924920024888888848)
+        val ac2 = A.longArr[4].value
+        A.longArr[1].value = ac2
+        check(A.longArr[1].value == 9024920024888888848)
+        check(A.longArr[1].addAndGet(-9223372036854775807) == -198452011965886959)
+        check(A.longArr[1].value == -198452011965886959)
+        check(A.longArr[1].incrementAndGet() == -198452011965886958)
+        check(A.longArr[1].value == -198452011965886958)
+        check(A.longArr[1].decrementAndGet() == -198452011965886959)
+        check(A.longArr[1].value == -198452011965886959)
+    }
+
+
+    fun testBooleanArray() {
+        val A = AtomicArrayClass()
+        check(!A.booleanArr[1].value)
+        A.booleanArr[1].compareAndSet(false, true)
+        A.booleanArr[0].lazySet(true)
+        check(!A.booleanArr[2].getAndSet(true))
+        check(A.booleanArr[0].value && A.booleanArr[1].value && A.booleanArr[2].value)
+        A.booleanArr[0].value = false
+        check(!A.booleanArr[0].value)
+    }
+
+    fun testRefArray() {
+        val A = AtomicArrayClass()
+        val a2 = ARef(2)
+        val a3 = ARef(3)
+        A.refArr[0].value = a2
+        check(A.refArr[0].value!!.n == 2)
+        check(A.refArr[0].compareAndSet(a2, a3))
+        check(A.refArr[0].value!!.n == 3)
+        val r0 = A.refArr[0].value
+        A.refArr[3].value = r0
+        check(A.refArr[3].value!!.n == 3)
+        val a = A.a.value
+        check(A.refArr[3].compareAndSet(a3, a))
+    }
+}
+
+class AtomicArrayClass {
+    val intArr = AtomicIntArray(10)
+    val longArr = AtomicLongArray(10)
+    val booleanArr = AtomicBooleanArray(10)
+    val refArr = atomicArrayOfNulls<ARef>(10)
+    val anyArr = atomicArrayOfNulls<Any?>(10)
+    val a = atomic(ARef(8))
+}
+
+data class ARef(val n: Int)
+
+fun box(): String {
+    val testClass = AtomicArrayTest()
+    testClass.testIntArray()
+    testClass.testLongArray()
+    testClass.testBooleanArray()
+    testClass.testRefArray()
+    return "OK"
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/basic/ExtensionsTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/basic/ExtensionsTest.kt
new file mode 100644
index 0000000..510f90d
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/basic/ExtensionsTest.kt
@@ -0,0 +1,115 @@
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class ExtensionsTest {
+    val a = atomic(0)
+    val l = atomic(0L)
+    val s = atomic<String?>(null)
+    val b = atomic(true)
+
+    fun testScopedFieldGetters() {
+        check(a.value == 0)
+        val update = 3
+        a.lazySet(update)
+        check(a.compareAndSet(update, 8))
+        a.lazySet(1)
+        check(a.value == 1)
+        check(a.getAndSet(2) == 1)
+        check(a.value == 2)
+        check(a.getAndIncrement() == 2)
+        check(a.value == 3)
+        check(a.getAndDecrement() == 3)
+        check(a.value == 2)
+        check(a.getAndAdd(2) == 2)
+        check(a.value == 4)
+        check(a.addAndGet(3) == 7)
+        check(a.value == 7)
+        check(a.incrementAndGet() == 8)
+        check(a.value == 8)
+        check(a.decrementAndGet() == 7)
+        check(a.value == 7)
+        check(a.compareAndSet(7, 10))
+    }
+
+    inline fun AtomicInt.intExtensionArithmetic() {
+        value = 0
+        check(value == 0)
+        val update = 3
+        lazySet(update)
+        check(compareAndSet(update, 8))
+        lazySet(1)
+        check(value == 1)
+        check(getAndSet(2) == 1)
+        check(value == 2)
+        check(getAndIncrement() == 2)
+        check(value == 3)
+        check(getAndDecrement() == 3)
+        check(value == 2)
+        check(getAndAdd(2) == 2)
+        check(value == 4)
+        check(addAndGet(3) == 7)
+        check(value == 7)
+        check(incrementAndGet() == 8)
+        check(value == 8)
+        check(decrementAndGet() == 7)
+        check(value == 7)
+        check(compareAndSet(7, 10))
+        check(compareAndSet(value, 55))
+        check(value == 55)
+    }
+
+    inline fun AtomicLong.longExtensionArithmetic() {
+        value = 2424920024888888848
+        check(value == 2424920024888888848)
+        lazySet(8424920024888888848)
+        check(value == 8424920024888888848)
+        check(getAndSet(8924920024888888848) == 8424920024888888848)
+        check(value == 8924920024888888848)
+        check(incrementAndGet() == 8924920024888888849) // fails
+        check(value == 8924920024888888849)
+        check(getAndDecrement() == 8924920024888888849)
+        check(value == 8924920024888888848)
+        check(getAndAdd(100000000000000000) == 8924920024888888848)
+        check(value == 9024920024888888848)
+        check(addAndGet(-9223372036854775807) == -198452011965886959)
+        check(value == -198452011965886959)
+        check(incrementAndGet() == -198452011965886958)
+        check(value == -198452011965886958)
+        check(decrementAndGet() == -198452011965886959)
+        check(value == -198452011965886959)
+    }
+
+    inline fun AtomicRef<String?>.refExtension() {
+        value = "aaa"
+        check(value == "aaa")
+        lazySet("bb")
+        check(value == "bb")
+        check(getAndSet("ccc") == "bb")
+        check(value == "ccc")
+    }
+
+    inline fun AtomicBoolean.booleanExtensionArithmetic() {
+        value = false
+        check(!value)
+        lazySet(true)
+        check(value)
+        check(getAndSet(true))
+        check(compareAndSet(value, false))
+        check(!value)
+    }
+
+    fun testExtension() {
+        a.intExtensionArithmetic()
+        l.longExtensionArithmetic()
+        s.refExtension()
+        b.booleanExtensionArithmetic()
+    }
+}
+
+
+fun box(): String {
+    val testClass = ExtensionsTest()
+    testClass.testScopedFieldGetters()
+    testClass.testExtension()
+    return "OK"
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/basic/IndexArrayElementGetterTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/basic/IndexArrayElementGetterTest.kt
new file mode 100644
index 0000000..69254e0
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/basic/IndexArrayElementGetterTest.kt
@@ -0,0 +1,32 @@
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class IndexArrayElementGetterTest {
+    private val clazz = AtomicArrayClass()
+
+    fun fib(a: Int): Int = if (a == 0 || a == 1) a else fib(a - 1) + fib(a - 2)
+
+    fun testIndexArrayElementGetting() {
+        clazz.intArr[8].value = 3
+        val i = fib(4)
+        val j = fib(5)
+        assertEquals(clazz.intArr[i + j].value, 3)
+        assertEquals(clazz.intArr[fib(4) + fib(5)].value, 3)
+        clazz.longArr[3].value = 100
+        assertEquals(clazz.longArr[fib(6) - fib(5)].value, 100)
+        assertEquals(clazz.longArr[(fib(6) + fib(4)) % 8].value, 100)
+        assertEquals(clazz.longArr[(fib(6) + fib(4)) % 8].value, 100)
+        assertEquals(clazz.longArr[(fib(4) + fib(5)) % fib(5)].value, 100)
+    }
+
+    class AtomicArrayClass {
+        val intArr = AtomicIntArray(10)
+        val longArr = AtomicLongArray(10)
+    }
+}
+
+fun box(): String {
+    val testClass = IndexArrayElementGetterTest()
+    testClass.testIndexArrayElementGetting()
+    return "OK"
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/basic/LockFreeIntBitsTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/basic/LockFreeIntBitsTest.kt
new file mode 100644
index 0000000..78d84a9
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/basic/LockFreeIntBitsTest.kt
@@ -0,0 +1,57 @@
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class LockFreeIntBitsTest {
+    fun testBasic() {
+        val bs = LockFreeIntBits()
+        check(!bs[0])
+        check(bs.bitSet(0))
+        check(bs[0])
+        check(!bs.bitSet(0))
+
+        check(!bs[1])
+        check(bs.bitSet(1))
+        check(bs[1])
+        check(!bs.bitSet(1))
+        check(!bs.bitSet(0))
+
+        check(bs[0])
+        check(bs.bitClear(0))
+        check(!bs.bitClear(0))
+
+        check(bs[1])
+    }
+}
+
+class LockFreeIntBits {
+    private val bits = atomic(0)
+
+    private fun Int.mask() = 1 shl this
+
+    operator fun get(index: Int): Boolean = bits.value and index.mask() != 0
+
+    // User-defined private inline function
+    private inline fun bitUpdate(check: (Int) -> Boolean, upd: (Int) -> Int): Boolean {
+        bits.update {
+            if (check(it)) return false
+            upd(it)
+        }
+        return true
+    }
+
+    fun bitSet(index: Int): Boolean {
+        val mask = index.mask()
+        return bitUpdate({ it and mask != 0 }, { it or mask })
+    }
+
+    fun bitClear(index: Int): Boolean {
+        val mask = index.mask()
+        return bitUpdate({ it and mask == 0 }, { it and mask.inv() })
+    }
+}
+
+fun box(): String {
+    val testClass = LockFreeIntBitsTest()
+    testClass.testBasic()
+    return "OK"
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/basic/LockFreeLongCounterTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/basic/LockFreeLongCounterTest.kt
new file mode 100644
index 0000000..853ca9e
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/basic/LockFreeLongCounterTest.kt
@@ -0,0 +1,61 @@
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class LockFreeLongCounterTest {
+    private inline fun testWith(g: LockFreeLongCounter.() -> Long) {
+        val c = LockFreeLongCounter()
+        check(c.g() == 0L)
+        check(c.increment() == 1L)
+        check(c.g() == 1L)
+        check(c.increment() == 2L)
+        check(c.g() == 2L)
+    }
+
+    fun testBasic() = testWith { get() }
+
+    fun testGetInner() = testWith { getInner() }
+
+    fun testAdd2() {
+        val c = LockFreeLongCounter()
+        c.add2()
+        check(c.get() == 2L)
+        c.add2()
+        check(c.get() == 4L)
+    }
+
+    fun testSetM2() {
+        val c = LockFreeLongCounter()
+        c.setM2()
+        check(c.get() == -2L)
+    }
+}
+
+class LockFreeLongCounter {
+    private val counter = atomic(0L)
+
+    fun get(): Long = counter.value
+
+    fun increment(): Long = counter.incrementAndGet()
+
+    fun add2() = counter.getAndAdd(2)
+
+    fun setM2() {
+        counter.value = -2L // LDC instruction here
+    }
+
+    fun getInner(): Long = Inner().getFromOuter()
+
+    // testing how an inner class can get access to it
+    private inner class Inner {
+        fun getFromOuter(): Long = counter.value
+    }
+}
+
+fun box(): String {
+    val testClass = LockFreeLongCounterTest()
+    testClass.testBasic()
+    testClass.testAdd2()
+    testClass.testSetM2()
+    testClass.testGetInner()
+    return "OK"
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/basic/LockFreeQueueTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/basic/LockFreeQueueTest.kt
new file mode 100644
index 0000000..ae36b81
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/basic/LockFreeQueueTest.kt
@@ -0,0 +1,55 @@
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class LockFreeQueueTest {
+    fun testBasic() {
+        val q = LockFreeQueue()
+        check(q.dequeue() == -1)
+        q.enqueue(42)
+        check(q.dequeue() == 42)
+        check(q.dequeue() == -1)
+        q.enqueue(1)
+        q.enqueue(2)
+        check(q.dequeue() == 1)
+        check(q.dequeue() == 2)
+        check(q.dequeue() == -1)
+    }
+}
+
+// MS-queue
+public class LockFreeQueue {
+    private val head = atomic(Node(0))
+    private val tail = atomic(head.value)
+
+    private class Node(val value: Int) {
+        val next = atomic<Node?>(null)
+    }
+
+    public fun enqueue(value: Int) {
+        val node = Node(value)
+        tail.loop { curTail ->
+            val curNext = curTail.next.value
+            if (curNext != null) {
+                tail.compareAndSet(curTail, curNext)
+                return@loop
+            }
+            if (curTail.next.compareAndSet(null, node)) {
+                tail.compareAndSet(curTail, node)
+                return
+            }
+        }
+    }
+
+    public fun dequeue(): Int {
+        head.loop { curHead ->
+            val next = curHead.next.value ?: return -1
+            if (head.compareAndSet(curHead, next)) return next.value
+        }
+    }
+}
+
+fun box(): String {
+    val testClass = LockFreeQueueTest()
+    testClass.testBasic()
+    return "OK"
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/basic/LockFreeStackTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/basic/LockFreeStackTest.kt
new file mode 100644
index 0000000..f4c674a
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/basic/LockFreeStackTest.kt
@@ -0,0 +1,71 @@
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class LockFreeStackTest {
+
+    fun testClear() {
+        val s = LockFreeStack<String>()
+        check(s.isEmpty())
+        s.pushLoop("A")
+        check(!s.isEmpty())
+        s.clear()
+        check(s.isEmpty())
+    }
+
+    fun testPushPopLoop() {
+        val s = LockFreeStack<String>()
+        check(s.isEmpty())
+        s.pushLoop("A")
+        check(!s.isEmpty())
+        check(s.popLoop() == "A")
+        check(s.isEmpty())
+    }
+
+    fun testPushPopUpdate() {
+        val s = LockFreeStack<String>()
+        check(s.isEmpty())
+        s.pushUpdate("A")
+        check(!s.isEmpty())
+        check(s.popUpdate() == "A")
+        check(s.isEmpty())
+    }
+}
+
+class LockFreeStack<T> {
+    private val top = atomic<Node<T>?>(null)
+
+    private class Node<T>(val value: T, val next: Node<T>?)
+
+    fun isEmpty() = top.value == null
+
+    fun clear() { top.value = null }
+
+    fun pushLoop(value: T) {
+        top.loop { cur ->
+            val upd = Node(value, cur)
+            if (top.compareAndSet(cur, upd)) return
+        }
+    }
+
+    fun popLoop(): T? {
+        top.loop { cur ->
+            if (cur == null) return null
+            if (top.compareAndSet(cur, cur.next)) return cur.value
+        }
+    }
+
+    fun pushUpdate(value: T) {
+        top.update { cur -> Node(value, cur) }
+    }
+
+    fun popUpdate(): T? =
+        top.getAndUpdate { cur -> cur?.next } ?.value
+}
+
+fun box(): String {
+    val testClass = LockFreeStackTest()
+    testClass.testClear()
+    testClass.testPushPopLoop()
+    testClass.testPushPopUpdate()
+    return "OK"
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/basic/LockTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/basic/LockTest.kt
new file mode 100644
index 0000000..28afe70
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/basic/LockTest.kt
@@ -0,0 +1,29 @@
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class LockTest {
+    private val inProgressLock = atomic(false)
+
+    fun testLock() {
+        var result = ""
+        if (inProgressLock.tryAcquire()) {
+            result = "OK"
+        }
+        assertEquals("OK", result)
+    }
+}
+
+// This function will be removed by transformer
+@Suppress("NOTHING_TO_INLINE")
+private inline fun AtomicBoolean.tryAcquire(): Boolean = compareAndSet(false, true)
+
+// This function is here to test if the Kotlin metadata still consistent after transform
+// It is used in ReflectionTest, DO NOT REMOVE
+@Suppress("UNUSED_PARAMETER")
+fun <AA, BB : Number> String.reflectionTest(mapParam: Map<in AA, BB>): List<BB> = error("no impl")
+
+fun box(): String {
+    val testClass = LockTest()
+    testClass.testLock()
+    return "OK"
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/basic/LoopTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/basic/LoopTest.kt
new file mode 100644
index 0000000..f227d56
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/basic/LoopTest.kt
@@ -0,0 +1,97 @@
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class LoopTest {
+    private val a = atomic(0)
+    private val r = atomic<A>(A("aaaa"))
+    private val rs = atomic<String>("bbbb")
+
+    private 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 AtomicInt.extensionLoop(to: Int): Int {
+        loop { cur ->
+            if (compareAndSet(cur, to)) return value
+            return 777
+        }
+    }
+
+    private inline fun AtomicInt.extensionLoopMixedReceivers(to: Int): Int {
+        loop { cur ->
+            compareAndSet(cur, to)
+            a.compareAndSet(cur, to)
+            return value
+        }
+    }
+
+    private inline fun AtomicInt.extensionLoopRecursive(to: Int): Int {
+        loop { cur ->
+            compareAndSet(cur, to)
+            a.extensionLoop(5)
+            return value
+        }
+    }
+
+    private inline fun AtomicInt.returnExtensionLoop(to: Int): Int =
+        loop { cur ->
+            lazySet(cur + 10)
+            return if (compareAndSet(cur, to)) value else incrementAndGet()
+        }
+
+    private inline fun AtomicRef<A>.casLoop(to: String): String = loop { cur ->
+        if (compareAndSet(cur, A(to))) {
+            val res = value.s
+            return "${res}_AtomicRef<A>"
+        }
+    }
+
+    private inline fun AtomicRef<String>.casLoop(to: String): String = loop { cur ->
+        if (compareAndSet(cur, to)) return "${value}_AtomicRef<String>"
+    }
+
+        fun testDeclarationWithEqualNames() {
+        check(r.casLoop("kk") == "kk_AtomicRef<A>")
+        check(rs.casLoop("pp") == "pp_AtomicRef<String>")
+    }
+
+    fun testIntExtensionLoops() {
+        check(casLoop(5) == 5)
+        assertEquals(a.extensionLoop(66), 66)
+        check(a.returnExtensionLoop(777) == 77)
+    }
+
+    abstract class Segment<S : Segment<S>>(val id: Int)
+    class SemaphoreSegment(id: Int) : Segment<SemaphoreSegment>(id)
+
+    private inline fun <S : Segment<S>> AtomicRef<S>.foo(
+        id: Int,
+        startFrom: S
+    ) {
+        startFrom.getSegmentId()
+    }
+
+    private inline fun <S : Segment<S>> S.getSegmentId(): Int {
+        var cur: S = this
+        return cur.id
+    }
+
+    fun testInlineFunWithTypeParameter() {
+        val s = SemaphoreSegment(0)
+        val sref = atomic(s)
+        sref.foo(0, s)
+    }
+}
+
+fun box(): String {
+    val testClass = LoopTest()
+    testClass.testIntExtensionLoops()
+    testClass.testDeclarationWithEqualNames()
+    testClass.testInlineFunWithTypeParameter()
+    return "OK"
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/basic/MultiInitTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/basic/MultiInitTest.kt
new file mode 100644
index 0000000..724f4b4
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/basic/MultiInitTest.kt
@@ -0,0 +1,30 @@
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class MultiInitTest {
+    fun testBasic() {
+        val t = MultiInit()
+        check(t.incA() == 1)
+        check(t.incA() == 2)
+        check(t.incB() == 1)
+        check(t.incB() == 2)
+    }
+}
+
+class MultiInit {
+    private val a = atomic(0)
+    private val b = atomic(0)
+
+    fun incA() = a.incrementAndGet()
+    fun incB() = b.incrementAndGet()
+
+    companion object {
+        fun foo() {} // just to force some clinit in outer file
+    }
+}
+
+fun box(): String {
+    val testClass = MultiInitTest()
+    testClass.testBasic()
+    return "OK"
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/basic/ParameterizedInlineFunExtensionTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/basic/ParameterizedInlineFunExtensionTest.kt
new file mode 100644
index 0000000..b16cf02
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/basic/ParameterizedInlineFunExtensionTest.kt
@@ -0,0 +1,20 @@
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class ParameterizedInlineFunExtensionTest {
+
+    internal inline fun <S> AtomicRef<S>.foo(res1: S, res2: S, foo: (S) -> S): S { return res2 }
+
+    private val tail = atomic("aaa")
+
+    fun testClose() {
+        val res = tail.foo("bbb", "ccc") { s -> s }
+        assertEquals("ccc", res)
+    }
+}
+
+fun box(): String {
+    val testClass = ParameterizedInlineFunExtensionTest()
+    testClass.testClose()
+    return "OK"
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/basic/PropertyDeclarationTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/basic/PropertyDeclarationTest.kt
new file mode 100644
index 0000000..3a240a4
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/basic/PropertyDeclarationTest.kt
@@ -0,0 +1,34 @@
+import kotlinx.atomicfu.*
+import kotlinx.atomicfu.locks.*
+import kotlin.test.*
+
+class PropertyDeclarationTest {
+    private val a: AtomicInt
+    private val head: AtomicRef<String>
+    private val lateIntArr: AtomicIntArray
+    private val lateRefArr: AtomicArray<String?>
+    private val lock: ReentrantLock
+
+    init {
+        a = atomic(0)
+        head = atomic("AAA")
+        lateIntArr = AtomicIntArray(55)
+        lateRefArr = atomicArrayOfNulls<String?>(10)
+        lock = reentrantLock()
+    }
+
+    fun test() {
+        assertEquals(0, a.value)
+        check(head.compareAndSet("AAA", "BBB"))
+        assertEquals("BBB", head.value)
+        assertEquals(0, lateIntArr[35].value)
+        assertEquals(null, lateRefArr[5].value)
+        assertEquals(null, lock)
+    }
+}
+
+fun box(): String {
+    val testClass = PropertyDeclarationTest()
+    testClass.test()
+    return "OK"
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/basic/ScopeTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/basic/ScopeTest.kt
new file mode 100644
index 0000000..3ffe028
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/basic/ScopeTest.kt
@@ -0,0 +1,45 @@
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class AA(val value: Int) {
+    val b = B(value + 1)
+    val c = C(D(E(value + 1)))
+
+    fun updateToB(affected: Any): Boolean {
+        (affected as AtomicState).state.compareAndSet(this, b)
+        return (affected.state.value is B && (affected.state.value as B).value == value + 1)
+    }
+
+    fun manyProperties(affected: Any): Boolean {
+        (affected as AtomicState).state.compareAndSet(this, c.d.e)
+        return (affected.state.value is E && (affected.state.value as E).x == value + 1)
+    }
+}
+
+class B (val value: Int)
+
+class C (val d: D)
+class D (val e: E)
+class E (val x: Int)
+
+
+class AtomicState(value: Any) {
+    val state = atomic<Any?>(value)
+}
+
+class ScopeTest {
+    fun scopeTest() {
+        val a = AA(0)
+        val affected: Any = AtomicState(a)
+        check(a.updateToB(affected))
+        val a1 = AA(0)
+        val affected1: Any = AtomicState(a1)
+        check(a1.manyProperties(affected1))
+    }
+}
+
+fun box(): String {
+    val testClass = ScopeTest()
+    testClass.scopeTest()
+    return "OK"
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/basic/SimpleLockTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/basic/SimpleLockTest.kt
new file mode 100644
index 0000000..7f8d0ca
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/basic/SimpleLockTest.kt
@@ -0,0 +1,36 @@
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class SimpleLockTest {
+    fun withLock() {
+        val lock = SimpleLock()
+        val result = lock.withLock {
+            "OK"
+        }
+        assertEquals("OK", result)
+    }
+}
+
+class SimpleLock {
+    private val _locked = atomic(0)
+
+    fun <T> withLock(block: () -> T): T {
+        // this contrieves construct triggers Kotlin compiler to reuse local variable slot #2 for
+        // the exception in `finally` clause
+        try {
+            _locked.loop { locked ->
+                check(locked == 0)
+                if (!_locked.compareAndSet(0, 1)) return@loop // continue
+                return block()
+            }
+        } finally {
+            _locked.value = 0
+        }
+    }
+}
+
+fun box(): String {
+    val testClass = SimpleLockTest()
+    testClass.withLock()
+    return "OK"
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/basic/TopLevelTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/basic/TopLevelTest.kt
new file mode 100644
index 0000000..22fe616
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/basic/TopLevelTest.kt
@@ -0,0 +1,178 @@
+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)
+
+class TopLevelPrimitiveTest {
+
+    fun testTopLevelInt() {
+        a.value
+        check(a.value == 0)
+        check(a.getAndSet(3) == 0)
+        check(a.compareAndSet(3, 8))
+        a.lazySet(1)
+        check(a.value == 1)
+        check(a.getAndSet(2) == 1)
+        check(a.value == 2)
+        check(a.getAndIncrement() == 2)
+        check(a.value == 3)
+        check(a.getAndDecrement() == 3)
+        check(a.value == 2)
+        check(a.getAndAdd(2) == 2)
+        check(a.value == 4)
+        check(a.addAndGet(3) == 7)
+        check(a.value == 7)
+        check(a.incrementAndGet() == 8)
+        check(a.value == 8)
+        check(a.decrementAndGet() == 7)
+        check(a.value == 7)
+        a.compareAndSet(7, 10)
+    }
+
+    fun testTopLevelLong() {
+        check(b.value == 2424920024888888848)
+        b.lazySet(8424920024888888848)
+        check(b.value == 8424920024888888848)
+        check(b.getAndSet(8924920024888888848) == 8424920024888888848)
+        check(b.value == 8924920024888888848)
+        check(b.incrementAndGet() == 8924920024888888849)
+        check(b.value == 8924920024888888849)
+        check(b.getAndDecrement() == 8924920024888888849)
+        check(b.value == 8924920024888888848)
+        check(b.getAndAdd(100000000000000000) == 8924920024888888848)
+        check(b.value == 9024920024888888848)
+        check(b.addAndGet(-9223372036854775807) == -198452011965886959)
+        check(b.value == -198452011965886959)
+        check(b.incrementAndGet() == -198452011965886958)
+        check(b.value == -198452011965886958)
+        check(b.decrementAndGet() == -198452011965886959)
+        check(b.value == -198452011965886959)
+    }
+
+    fun testTopLevelBoolean() {
+        check(c.value)
+        c.lazySet(false)
+        check(!c.value)
+        check(!c.getAndSet(true))
+        check(c.compareAndSet(true, false))
+        check(!c.value)
+    }
+
+    fun testTopLevelRef() {
+        check(abcNode.value.b.c.d == 8)
+        val newNode = ANode(BNode(CNode(76)))
+        check(abcNode.getAndSet(newNode).b.c.d == 8)
+        check(abcNode.value.b.c.d == 76)
+        val l = IntArray(4){i -> i}
+        any.lazySet(l)
+        check((any.value as IntArray)[2] == 2)
+    }
+
+    fun testTopLevelArrayOfNulls() {
+        check(stringAtomicNullArr[0].value == null)
+        check(stringAtomicNullArr[0].compareAndSet(null, "aa"))
+        stringAtomicNullArr[1].lazySet("aa")
+        check(stringAtomicNullArr[0].value == stringAtomicNullArr[1].value)
+    }
+}
+
+class TopLevelArrayTest {
+
+    fun testIntArray() {
+        check(intArr[0].compareAndSet(0, 3))
+        check(intArr[1].value == 0)
+        intArr[0].lazySet(5)
+        check(intArr[0].value + intArr[1].value + intArr[2].value == 5)
+        check(intArr[0].compareAndSet(5, 10))
+        check(intArr[0].getAndDecrement() == 10)
+        check(intArr[0].value == 9)
+        intArr[2].value = 2
+        check(intArr[2].value == 2)
+        check(intArr[2].compareAndSet(2, 34))
+        check(intArr[2].value == 34)
+    }
+
+    fun testLongArray() {
+        longArr[0].value = 2424920024888888848
+        check(longArr[0].value == 2424920024888888848)
+        longArr[0].lazySet(8424920024888888848)
+        check(longArr[0].value == 8424920024888888848)
+        val ac = longArr[0].value
+        longArr[3].value = ac
+        check(longArr[3].getAndSet(8924920024888888848) == 8424920024888888848)
+        check(longArr[3].value == 8924920024888888848)
+        val ac1 = longArr[3].value
+        longArr[4].value = ac1
+        check(longArr[4].incrementAndGet() == 8924920024888888849)
+        check(longArr[4].value == 8924920024888888849)
+        check(longArr[4].getAndDecrement() == 8924920024888888849)
+        check(longArr[4].value == 8924920024888888848)
+        longArr[4].value = 8924920024888888848
+        check(longArr[4].getAndAdd(100000000000000000) == 8924920024888888848)
+        val ac2 = longArr[4].value
+        longArr[1].value = ac2
+        check(longArr[1].value == 9024920024888888848)
+        check(longArr[1].addAndGet(-9223372036854775807) == -198452011965886959)
+        check(longArr[1].value == -198452011965886959)
+        check(longArr[1].incrementAndGet() == -198452011965886958)
+        check(longArr[1].value == -198452011965886958)
+        check(longArr[1].decrementAndGet() == -198452011965886959)
+        check(longArr[1].value == -198452011965886959)
+    }
+
+    fun testBooleanArray() {
+        check(!booleanArr[1].value)
+        booleanArr[1].compareAndSet(false, true)
+        booleanArr[0].lazySet(true)
+        check(!booleanArr[2].getAndSet(true))
+        check(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
+        check(refArr[0].value!!.b.c.d == 2)
+        check(refArr[0].compareAndSet(a2, a3))
+        check(refArr[0].value!!.b.c.d == 3)
+        val r0 = refArr[0].value
+        refArr[3].value = r0
+        check(refArr[3].value!!.b.c.d == 3)
+        val a = abcNode.value
+        check(refArr[3].compareAndSet(a3, a))
+    }
+
+}
+
+data class ANode<T>(val b: T)
+data class BNode<T>(val c: T)
+data class CNode(val d: Int)
+
+fun box(): String {
+    val primitiveTest = TopLevelPrimitiveTest()
+    primitiveTest.testTopLevelInt()
+    primitiveTest.testTopLevelLong()
+    primitiveTest.testTopLevelBoolean()
+    primitiveTest.testTopLevelRef()
+    primitiveTest.testTopLevelArrayOfNulls()
+
+    val arrayTest = TopLevelArrayTest()
+    arrayTest.testIntArray()
+    arrayTest.testLongArray()
+    arrayTest.testBooleanArray()
+    arrayTest.testRefArray()
+    return "OK"
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/basic/UncheckedCastTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/basic/UncheckedCastTest.kt
new file mode 100644
index 0000000..d3fd78d
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/basic/UncheckedCastTest.kt
@@ -0,0 +1,54 @@
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+private val topLevelS = atomic<Any>(arrayOf("A", "B"))
+
+class UncheckedCastTest {
+    private val s = atomic<Any>("AAA")
+    private val bs = atomic<Any?>(null)
+
+    @Suppress("UNCHECKED_CAST")
+    fun testAtomicValUncheckedCast() {
+        assertEquals((s as AtomicRef<String>).value, "AAA")
+        bs.lazySet(arrayOf(arrayOf(Box(1), Box(2))))
+        assertEquals((bs as AtomicRef<Array<Array<Box>>>).value[0]!![0].b * 10, 10)
+    }
+
+    @Suppress("UNCHECKED_CAST")
+    fun testTopLevelValUnchekedCast() {
+        assertEquals((topLevelS as AtomicRef<Array<String>>).value[1], "B")
+    }
+
+    private data class Box(val b: Int)
+
+    @Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST")
+    private inline fun <T> AtomicRef<T>.getString(): String =
+        (this as AtomicRef<String>).value
+
+    fun testInlineFunc() {
+        assertEquals("AAA", s.getString())
+    }
+
+    private val a = atomicArrayOfNulls<Any?>(10)
+
+    fun testArrayValueUncheckedCast() {
+        a[0].value = "OK"
+        @Suppress("UNCHECKED_CAST")
+        assertEquals("OK", (a[0] as AtomicRef<String>).value)
+    }
+
+    fun testArrayValueUncheckedCastInlineFunc() {
+        a[0].value = "OK"
+        assertEquals("OK", a[0].getString())
+    }
+}
+
+fun box(): String {
+    val testClass = UncheckedCastTest()
+    testClass.testTopLevelValUnchekedCast()
+    testClass.testArrayValueUncheckedCast()
+    testClass.testArrayValueUncheckedCastInlineFunc()
+    testClass.testAtomicValUncheckedCast()
+    testClass.testInlineFunc()
+    return "OK"
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/locks/ReentrantLockTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/locks/ReentrantLockTest.kt
new file mode 100644
index 0000000..e04d7ad
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/locks/ReentrantLockTest.kt
@@ -0,0 +1,20 @@
+import kotlinx.atomicfu.locks.*
+import kotlin.test.*
+
+class ReentrantLockTest {
+    private val lock = reentrantLock()
+    private var state = 0
+
+    fun testLockField() {
+        lock.withLock {
+            state = 1
+        }
+        assertEquals(1, state)
+    }
+}
+
+fun box(): String {
+    val testClass = ReentrantLockTest()
+    testClass.testLockField()
+    return "OK"
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/locks/SynchronizedObjectTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/locks/SynchronizedObjectTest.kt
new file mode 100644
index 0000000..98efc96
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/locks/SynchronizedObjectTest.kt
@@ -0,0 +1,21 @@
+import kotlinx.atomicfu.locks.*
+import kotlin.test.*
+
+class SynchronizedObjectTest : SynchronizedObject() {
+
+    fun testSync() {
+        val result = synchronized(this) { bar() }
+        assertEquals("OK", result)
+    }
+
+    private fun bar(): String =
+        synchronized(this) {
+            "OK"
+        }
+}
+
+fun box(): String {
+    val testClass = SynchronizedObjectTest()
+    testClass.testSync()
+    return "OK"
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-runtime/build.gradle.kts b/plugins/atomicfu/atomicfu-runtime/build.gradle.kts
new file mode 100644
index 0000000..65b7ca0
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-runtime/build.gradle.kts
@@ -0,0 +1,33 @@
+description = "Atomicfu Runtime"
+
+plugins {
+    kotlin("js")
+    `maven-publish`
+}
+
+group = "org.jetbrains.kotlin"
+
+repositories {
+    mavenLocal()
+    jcenter()
+}
+
+kotlin {
+    js()
+
+    sourceSets {
+        js().compilations["main"].defaultSourceSet {
+            dependencies {
+                compileOnly(kotlin("stdlib-js"))
+            }
+        }
+    }
+}
+
+publishing {
+    publications {
+        create<MavenPublication>("maven") {
+            from(components["kotlin"])
+        }
+    }
+}
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-runtime/gradle.properties b/plugins/atomicfu/atomicfu-runtime/gradle.properties
new file mode 100644
index 0000000..860acd1
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-runtime/gradle.properties
@@ -0,0 +1 @@
+kotlin.js.compiler=both
\ No newline at end of file
diff --git a/plugins/atomicfu/atomicfu-runtime/src/main/kotlin/atomicfu.kt b/plugins/atomicfu/atomicfu-runtime/src/main/kotlin/atomicfu.kt
new file mode 100644
index 0000000..1e776d3
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-runtime/src/main/kotlin/atomicfu.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2010-2020 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 kotlinx.atomicfu
+
+internal inline fun <T> atomicfu_getValue(`atomicfu$getter`: () -> T, `atomicfu$setter`: (T) -> Unit): T {
+    return `atomicfu$getter`()
+}
+
+internal inline fun <T> atomicfu_setValue(value: T, `atomicfu$getter`: () -> T, `atomicfu$setter`: (T) -> Unit): Unit {
+    `atomicfu$setter`(value)
+}
+
+internal inline fun <T> atomicfu_lazySet(value: T, `atomicfu$getter`: () -> T, `atomicfu$setter`: (T) -> Unit): Unit {
+    `atomicfu$setter`(value)
+}
+
+internal inline fun <T> atomicfu_compareAndSet(expect: T, update: T, `atomicfu$getter`: () -> T, `atomicfu$setter`: (T) -> Unit): Boolean {
+    if (`atomicfu$getter`() == expect) {
+        `atomicfu$setter`(update)
+        return true
+    } else {
+        return false
+    }
+}
+
+internal inline fun <T> atomicfu_getAndSet(value: T, `atomicfu$getter`: () -> T, `atomicfu$setter`: (T) -> Unit): T {
+    val oldValue = `atomicfu$getter`()
+    `atomicfu$setter`(value)
+    return oldValue
+}
+
+internal inline fun atomicfu_getAndIncrement(`atomicfu$getter`: () -> Int, `atomicfu$setter`: (Int) -> Unit): Int {
+    val oldValue = `atomicfu$getter`()
+    `atomicfu$setter`(oldValue + 1)
+    return oldValue
+}
+
+internal inline fun atomicfu_getAndIncrement(`atomicfu$getter`: () -> Long, `atomicfu$setter`: (Long) -> Unit): Long {
+    val oldValue = `atomicfu$getter`()
+    `atomicfu$setter`(oldValue + 1)
+    return oldValue
+}
+
+internal inline fun atomicfu_incrementAndGet(`atomicfu$getter`: () -> Int, `atomicfu$setter`: (Int) -> Unit): Int {
+    `atomicfu$setter`(`atomicfu$getter`() + 1)
+    return `atomicfu$getter`()
+}
+
+internal inline fun atomicfu_incrementAndGet(`atomicfu$getter`: () -> Long, `atomicfu$setter`: (Long) -> Unit): Long {
+    `atomicfu$setter`(`atomicfu$getter`() + 1)
+    return `atomicfu$getter`()
+}
+
+internal inline fun atomicfu_getAndDecrement(`atomicfu$getter`: () -> Int, `atomicfu$setter`: (Int) -> Unit): Int {
+    val oldValue = `atomicfu$getter`()
+    `atomicfu$setter`(oldValue - 1)
+    return oldValue
+}
+
+internal inline fun atomicfu_getAndDecrement(`atomicfu$getter`: () -> Long, `atomicfu$setter`: (Long) -> Unit): Long {
+    val oldValue = `atomicfu$getter`()
+    `atomicfu$setter`(oldValue - 1)
+    return oldValue
+}
+
+internal inline fun atomicfu_decrementAndGet(`atomicfu$getter`: () -> Int, `atomicfu$setter`: (Int) -> Unit): Int {
+    `atomicfu$setter`(`atomicfu$getter`() - 1)
+    return `atomicfu$getter`()
+}
+
+internal inline fun atomicfu_decrementAndGet(`atomicfu$getter`: () -> Long, `atomicfu$setter`: (Long) -> Unit): Long {
+    `atomicfu$setter`(`atomicfu$getter`() - 1)
+    return `atomicfu$getter`()
+}
+
+internal inline fun atomicfu_getAndAdd(value: Int, `atomicfu$getter`: () -> Int, `atomicfu$setter`: (Int) -> Unit): Int {
+    val oldValue = `atomicfu$getter`()
+    `atomicfu$setter`(oldValue + value)
+    return oldValue
+}
+
+internal inline fun atomicfu_getAndAdd(value: Long, `atomicfu$getter`: () -> Long, `atomicfu$setter`: (Long) -> Unit): Long {
+    val oldValue = `atomicfu$getter`()
+    `atomicfu$setter`(oldValue + value)
+    return oldValue
+}
+
+internal inline fun atomicfu_addAndGet(value: Int, `atomicfu$getter`: () -> Int, `atomicfu$setter`: (Int) -> Unit): Int {
+    `atomicfu$setter`(`atomicfu$getter`() + value)
+    return `atomicfu$getter`()
+}
+
+internal inline fun atomicfu_addAndGet(value: Long, `atomicfu$getter`: () -> Long, `atomicfu$setter`: (Long) -> Unit): Long {
+    `atomicfu$setter`(`atomicfu$getter`() + value)
+    return `atomicfu$getter`()
+}
+
+internal inline fun <T> atomicfu_loop(action: (T) -> Unit, `atomicfu$getter`: () -> T, `atomicfu$setter`: (T) -> Unit): Nothing {
+    val cur = `atomicfu$getter`()
+    while (true) {
+        action(cur)
+    }
+}
+
+internal inline fun <T> atomicfu_update(function: (T) -> T, `atomicfu$getter`: () -> T, `atomicfu$setter`: (T) -> Unit) {
+    while (true) {
+        val cur = `atomicfu$getter`()
+        val upd = function(cur)
+        if (atomicfu_compareAndSet(cur, upd, `atomicfu$getter`, `atomicfu$setter`)) return
+    }
+}
+
+internal inline fun <T> atomicfu_getAndUpdate(function: (T) -> T, `atomicfu$getter`: () -> T, `atomicfu$setter`: (T) -> Unit): T {
+    while (true) {
+        val cur = `atomicfu$getter`()
+        val upd = function(cur)
+        if (atomicfu_compareAndSet(cur, upd, `atomicfu$getter`, `atomicfu$setter`)) return cur
+    }
+}
+
+internal inline fun <T> atomicfu_updateAndGet(function: (T) -> T, `atomicfu$getter`: () -> T, `atomicfu$setter`: (T) -> Unit): T {
+    while (true) {
+        val cur = `atomicfu$getter`()
+        val upd = function(cur)
+        if (atomicfu_compareAndSet(cur, upd, `atomicfu$getter`, `atomicfu$setter`)) return upd
+    }
+}
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index 619cf70..8b40365 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -308,6 +308,9 @@
         ":kotlin-serialization-unshaded",
         ":wasm:wasm.ir"
 
+include ":kotlinx-atomicfu-compiler-plugin",
+        ":kotlinx-atomicfu-runtime",
+        ":atomicfu"
 
 include ":compiler:fir:cones",
         ":compiler:fir:tree",
@@ -581,6 +584,10 @@
 project(':kotlin-serialization').projectDir = file("$rootDir/libraries/tools/kotlin-serialization")
 project(':kotlin-serialization-unshaded').projectDir = file("$rootDir/libraries/tools/kotlin-serialization-unshaded")
 
+project(':kotlinx-atomicfu-compiler-plugin').projectDir = file("$rootDir/plugins/atomicfu/atomicfu-compiler")
+project(':kotlinx-atomicfu-runtime').projectDir = file("$rootDir/plugins/atomicfu/atomicfu-runtime")
+project(':atomicfu').projectDir = file("$rootDir/libraries/tools/atomicfu")
+
 // Uncomment to use locally built protobuf-relocated
 // includeBuild("dependencies/protobuf")
 if (buildProperties.isKotlinNativeEnabled) {