[JVM] Add PSI <-> Bytecode declarations mapping to classfiles
diff --git a/compiler/arguments/resources/kotlin-compiler-arguments.json b/compiler/arguments/resources/kotlin-compiler-arguments.json
index 52e961f..83d7c82 100644
--- a/compiler/arguments/resources/kotlin-compiler-arguments.json
+++ b/compiler/arguments/resources/kotlin-compiler-arguments.json
@@ -6690,6 +6690,49 @@
                                 }
                             },
                             {
+                                "name": "Xgenerate-psi-mapping",
+                                "shortName": null,
+                                "deprecatedName": null,
+                                "description": {
+                                    "current": "Generate PSIMappingMetadata annotation with psi-to-bytecode mapping",
+                                    "valueInVersions": []
+                                },
+                                "delimiter": null,
+                                "affectsCompilationOutcome": true,
+                                "valueType": {
+                                    "type": "org.jetbrains.kotlin.arguments.dsl.types.BooleanType",
+                                    "isNullable": {
+                                        "current": false,
+                                        "valueInVersions": []
+                                    },
+                                    "defaultValue": {
+                                        "current": false,
+                                        "valueInVersions": []
+                                    }
+                                },
+                                "valueDescription": {
+                                    "current": null,
+                                    "valueInVersions": []
+                                },
+                                "releaseVersionsMetadata": {
+                                    "introducedVersion": "2.3.0",
+                                    "stabilizedVersion": null,
+                                    "deprecatedVersion": null,
+                                    "removedVersion": null
+                                },
+                                "argumentType": {
+                                    "type": "org.jetbrains.kotlin.arguments.dsl.types.BooleanType",
+                                    "isNullable": {
+                                        "current": false,
+                                        "valueInVersions": []
+                                    },
+                                    "defaultValue": {
+                                        "current": false,
+                                        "valueInVersions": []
+                                    }
+                                }
+                            },
+                            {
                                 "name": "Xgenerate-strict-metadata-version",
                                 "shortName": null,
                                 "deprecatedName": null,
diff --git a/compiler/arguments/src/org/jetbrains/kotlin/arguments/description/JvmCompilerArguments.kt b/compiler/arguments/src/org/jetbrains/kotlin/arguments/description/JvmCompilerArguments.kt
index 66c966d..6cfa43c 100644
--- a/compiler/arguments/src/org/jetbrains/kotlin/arguments/description/JvmCompilerArguments.kt
+++ b/compiler/arguments/src/org/jetbrains/kotlin/arguments/description/JvmCompilerArguments.kt
@@ -989,4 +989,15 @@
             introducedVersion = KotlinReleaseVersion.v2_3_20,
         )
     }
+
+
+    compilerArgument {
+        name = "Xgenerate-psi-mapping"
+        description = "Generate PSIMappingMetadata annotation with psi-to-bytecode mapping".asReleaseDependent()
+        valueType = BooleanType.defaultFalse
+
+        lifecycle(
+            introducedVersion = KotlinReleaseVersion.v2_3_0
+        )
+    }
 }
diff --git a/compiler/backend.common.jvm/src/org/jetbrains/kotlin/PSIMetadataMappingEntry.kt b/compiler/backend.common.jvm/src/org/jetbrains/kotlin/PSIMetadataMappingEntry.kt
new file mode 100644
index 0000000..74c7ac1
--- /dev/null
+++ b/compiler/backend.common.jvm/src/org/jetbrains/kotlin/PSIMetadataMappingEntry.kt
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin
+
+class PSIMetadataMappingEntry(
+    val name: String,
+    val signature: String,
+    val file: String,
+    val packageName: String,
+    val startOffset: Int,
+    val endOffset: Int,
+)
\ No newline at end of file
diff --git a/compiler/backend.common.jvm/src/org/jetbrains/kotlin/codegen/AsmUtil.java b/compiler/backend.common.jvm/src/org/jetbrains/kotlin/codegen/AsmUtil.java
index 1e91f84..b571c9c 100644
--- a/compiler/backend.common.jvm/src/org/jetbrains/kotlin/codegen/AsmUtil.java
+++ b/compiler/backend.common.jvm/src/org/jetbrains/kotlin/codegen/AsmUtil.java
@@ -76,6 +76,12 @@
 
     public static final String LOCAL_FUNCTION_VARIABLE_PREFIX = "$fun$";
 
+    public static final String PSI_MAPPING_HEADER = "#PSI_MAPPING";
+
+    public static final String PSI_MAPPING_VERSION = "1.0";
+
+    public static int UTF_8_CONSTANT_MAX_LEN = 65535;
+
     private static final ImmutableMap<Integer, JvmPrimitiveType> primitiveTypeByAsmSort;
     private static final ImmutableMap<Type, Type> primitiveTypeByBoxedType;
 
diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JvmBackendConfig.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JvmBackendConfig.kt
index 252c798..a162162 100644
--- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JvmBackendConfig.kt
+++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JvmBackendConfig.kt
@@ -119,5 +119,7 @@
 
     val generateDebugMetadataV2: Boolean = languageVersionSettings.apiVersion >= ApiVersion.KOTLIN_2_3
 
+    val generatePsiMetadata: Boolean = configuration.getBoolean(JVMConfigurationKeys.GENERATE_PSI_MAPPING)
+
     val implicitJvmExposeBoxed: Boolean = languageVersionSettings.getFlag(JvmAnalysisFlags.implicitJvmExposeBoxed)
 }
diff --git a/compiler/build-tools/kotlin-build-tools-api/api/kotlin-build-tools-api.api b/compiler/build-tools/kotlin-build-tools-api/api/kotlin-build-tools-api.api
index 64b320d..571e274 100644
--- a/compiler/build-tools/kotlin-build-tools-api/api/kotlin-build-tools-api.api
+++ b/compiler/build-tools/kotlin-build-tools-api/api/kotlin-build-tools-api.api
@@ -659,6 +659,7 @@
 	public static final field X_ENHANCED_COROUTINES_DEBUGGING Lorg/jetbrains/kotlin/buildtools/api/arguments/JvmCompilerArguments$JvmCompilerArgument;
 	public static final field X_ENHANCE_TYPE_PARAMETER_TYPES_TO_DEF_NOT_NULL Lorg/jetbrains/kotlin/buildtools/api/arguments/JvmCompilerArguments$JvmCompilerArgument;
 	public static final field X_FRIEND_PATHS Lorg/jetbrains/kotlin/buildtools/api/arguments/JvmCompilerArguments$JvmCompilerArgument;
+	public static final field X_GENERATE_PSI_MAPPING Lorg/jetbrains/kotlin/buildtools/api/arguments/JvmCompilerArguments$JvmCompilerArgument;
 	public static final field X_GENERATE_STRICT_METADATA_VERSION Lorg/jetbrains/kotlin/buildtools/api/arguments/JvmCompilerArguments$JvmCompilerArgument;
 	public static final field X_IGNORED_ANNOTATIONS_FOR_BRIDGES Lorg/jetbrains/kotlin/buildtools/api/arguments/JvmCompilerArguments$JvmCompilerArgument;
 	public static final field X_INDY_ALLOW_ANNOTATED_LAMBDAS Lorg/jetbrains/kotlin/buildtools/api/arguments/JvmCompilerArguments$JvmCompilerArgument;
diff --git a/compiler/build-tools/kotlin-build-tools-api/gen/org/jetbrains/kotlin/buildtools/api/arguments/JvmCompilerArguments.kt b/compiler/build-tools/kotlin-build-tools-api/gen/org/jetbrains/kotlin/buildtools/api/arguments/JvmCompilerArguments.kt
index 24b70e4..6e63e76 100644
--- a/compiler/build-tools/kotlin-build-tools-api/gen/org/jetbrains/kotlin/buildtools/api/arguments/JvmCompilerArguments.kt
+++ b/compiler/build-tools/kotlin-build-tools-api/gen/org/jetbrains/kotlin/buildtools/api/arguments/JvmCompilerArguments.kt
@@ -265,6 +265,16 @@
         JvmCompilerArgument("X_FRIEND_PATHS", KotlinReleaseVersion(1, 2, 70))
 
     /**
+     * Generate PSIMappingMetadata annotation with psi-to-bytecode mapping
+     *
+     * WARNING: this option is EXPERIMENTAL and it may be changed in the future without notice or may be removed entirely.
+     */
+    @JvmField
+    @ExperimentalCompilerArgument
+    public val X_GENERATE_PSI_MAPPING: JvmCompilerArgument<Boolean> =
+        JvmCompilerArgument("X_GENERATE_PSI_MAPPING", KotlinReleaseVersion(2, 3, 0))
+
+    /**
      * Generate metadata with strict version semantics (see the KDoc entry on 'Metadata.extraInt').
      *
      * WARNING: this option is EXPERIMENTAL and it may be changed in the future without notice or may be removed entirely.
diff --git a/compiler/build-tools/kotlin-build-tools-impl/gen/org/jetbrains/kotlin/buildtools/internal/arguments/JvmCompilerArgumentsImpl.kt b/compiler/build-tools/kotlin-build-tools-impl/gen/org/jetbrains/kotlin/buildtools/internal/arguments/JvmCompilerArgumentsImpl.kt
index b3218d9..e82f5ae 100644
--- a/compiler/build-tools/kotlin-build-tools-impl/gen/org/jetbrains/kotlin/buildtools/internal/arguments/JvmCompilerArgumentsImpl.kt
+++ b/compiler/build-tools/kotlin-build-tools-impl/gen/org/jetbrains/kotlin/buildtools/internal/arguments/JvmCompilerArgumentsImpl.kt
@@ -57,6 +57,7 @@
 import org.jetbrains.kotlin.buildtools.`internal`.arguments.JvmCompilerArgumentsImpl.Companion.X_ENHANCED_COROUTINES_DEBUGGING
 import org.jetbrains.kotlin.buildtools.`internal`.arguments.JvmCompilerArgumentsImpl.Companion.X_ENHANCE_TYPE_PARAMETER_TYPES_TO_DEF_NOT_NULL
 import org.jetbrains.kotlin.buildtools.`internal`.arguments.JvmCompilerArgumentsImpl.Companion.X_FRIEND_PATHS
+import org.jetbrains.kotlin.buildtools.`internal`.arguments.JvmCompilerArgumentsImpl.Companion.X_GENERATE_PSI_MAPPING
 import org.jetbrains.kotlin.buildtools.`internal`.arguments.JvmCompilerArgumentsImpl.Companion.X_GENERATE_STRICT_METADATA_VERSION
 import org.jetbrains.kotlin.buildtools.`internal`.arguments.JvmCompilerArgumentsImpl.Companion.X_IGNORED_ANNOTATIONS_FOR_BRIDGES
 import org.jetbrains.kotlin.buildtools.`internal`.arguments.JvmCompilerArgumentsImpl.Companion.X_INDY_ALLOW_ANNOTATED_LAMBDAS
@@ -195,6 +196,7 @@
     if (X_ENHANCE_TYPE_PARAMETER_TYPES_TO_DEF_NOT_NULL in this) { arguments.enhanceTypeParameterTypesToDefNotNull = get(X_ENHANCE_TYPE_PARAMETER_TYPES_TO_DEF_NOT_NULL)}
     if (X_ENHANCED_COROUTINES_DEBUGGING in this) { arguments.enhancedCoroutinesDebugging = get(X_ENHANCED_COROUTINES_DEBUGGING)}
     if (X_FRIEND_PATHS in this) { arguments.friendPaths = get(X_FRIEND_PATHS).map { it.absolutePathStringOrThrow() }.toTypedArray()}
+    if (X_GENERATE_PSI_MAPPING in this) { arguments.generatePsiMapping = get(X_GENERATE_PSI_MAPPING)}
     if (X_GENERATE_STRICT_METADATA_VERSION in this) { arguments.strictMetadataVersionSemantics = get(X_GENERATE_STRICT_METADATA_VERSION)}
     if (X_IGNORED_ANNOTATIONS_FOR_BRIDGES in this) { arguments.ignoredAnnotationsForBridges = get(X_IGNORED_ANNOTATIONS_FOR_BRIDGES).toTypedArray()}
     if (X_INDY_ALLOW_ANNOTATED_LAMBDAS in this) { arguments.indyAllowAnnotatedLambdas = get(X_INDY_ALLOW_ANNOTATED_LAMBDAS)}
@@ -281,6 +283,7 @@
     try { this[X_ENHANCE_TYPE_PARAMETER_TYPES_TO_DEF_NOT_NULL] = arguments.enhanceTypeParameterTypesToDefNotNull } catch (_: NoSuchMethodError) {  }
     try { this[X_ENHANCED_COROUTINES_DEBUGGING] = arguments.enhancedCoroutinesDebugging } catch (_: NoSuchMethodError) {  }
     try { this[X_FRIEND_PATHS] = arguments.friendPaths.mapOrEmpty { Path(it) } } catch (_: NoSuchMethodError) {  }
+    try { this[X_GENERATE_PSI_MAPPING] = arguments.generatePsiMapping } catch (_: NoSuchMethodError) {  }
     try { this[X_GENERATE_STRICT_METADATA_VERSION] = arguments.strictMetadataVersionSemantics } catch (_: NoSuchMethodError) {  }
     try { this[X_IGNORED_ANNOTATIONS_FOR_BRIDGES] = arguments.ignoredAnnotationsForBridges.toListOrEmpty() } catch (_: NoSuchMethodError) {  }
     try { this[X_INDY_ALLOW_ANNOTATED_LAMBDAS] = arguments.indyAllowAnnotatedLambdas } catch (_: NoSuchMethodError) {  }
@@ -365,6 +368,7 @@
     if (X_ENHANCE_TYPE_PARAMETER_TYPES_TO_DEF_NOT_NULL in this) { arguments.enhanceTypeParameterTypesToDefNotNull = get(X_ENHANCE_TYPE_PARAMETER_TYPES_TO_DEF_NOT_NULL)}
     if (X_ENHANCED_COROUTINES_DEBUGGING in this) { arguments.enhancedCoroutinesDebugging = get(X_ENHANCED_COROUTINES_DEBUGGING)}
     if (X_FRIEND_PATHS in this) { arguments.friendPaths = get(X_FRIEND_PATHS).map { it.absolutePathStringOrThrow() }.toTypedArray()}
+    if (X_GENERATE_PSI_MAPPING in this) { arguments.generatePsiMapping = get(X_GENERATE_PSI_MAPPING)}
     if (X_GENERATE_STRICT_METADATA_VERSION in this) { arguments.strictMetadataVersionSemantics = get(X_GENERATE_STRICT_METADATA_VERSION)}
     if (X_IGNORED_ANNOTATIONS_FOR_BRIDGES in this) { arguments.ignoredAnnotationsForBridges = get(X_IGNORED_ANNOTATIONS_FOR_BRIDGES).toTypedArray()}
     if (X_INDY_ALLOW_ANNOTATED_LAMBDAS in this) { arguments.indyAllowAnnotatedLambdas = get(X_INDY_ALLOW_ANNOTATED_LAMBDAS)}
@@ -507,6 +511,9 @@
     public val X_FRIEND_PATHS: JvmCompilerArgument<List<java.nio.`file`.Path>> =
         JvmCompilerArgument("X_FRIEND_PATHS")
 
+    public val X_GENERATE_PSI_MAPPING: JvmCompilerArgument<Boolean> =
+        JvmCompilerArgument("X_GENERATE_PSI_MAPPING")
+
     public val X_GENERATE_STRICT_METADATA_VERSION: JvmCompilerArgument<Boolean> =
         JvmCompilerArgument("X_GENERATE_STRICT_METADATA_VERSION")
 
diff --git a/compiler/cli/cli-base/gen/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt b/compiler/cli/cli-base/gen/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt
index d87f54e3..d44c352 100644
--- a/compiler/cli/cli-base/gen/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt
+++ b/compiler/cli/cli-base/gen/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt
@@ -186,6 +186,16 @@
         }
 
     @Argument(
+        value = "-Xgenerate-psi-mapping",
+        description = "Generate PSIMappingMetadata annotation with psi-to-bytecode mapping",
+    )
+    var generatePsiMapping: Boolean = false
+        set(value) {
+            checkFrozen()
+            field = value
+        }
+
+    @Argument(
         value = "-Xgenerate-strict-metadata-version",
         description = "Generate metadata with strict version semantics (see the KDoc entry on 'Metadata.extraInt').",
     )
diff --git a/compiler/cli/cli-base/gen/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArgumentsCopyGenerated.kt b/compiler/cli/cli-base/gen/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArgumentsCopyGenerated.kt
index c0e8699..9cf5fff 100644
--- a/compiler/cli/cli-base/gen/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArgumentsCopyGenerated.kt
+++ b/compiler/cli/cli-base/gen/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArgumentsCopyGenerated.kt
@@ -31,6 +31,7 @@
     to.expression = from.expression
     to.friendPaths = from.friendPaths.copyOf()
     to.ignoredAnnotationsForBridges = from.ignoredAnnotationsForBridges.copyOf()
+    to.generatePsiMapping = from.generatePsiMapping
     to.includeRuntime = from.includeRuntime
     to.indyAllowAnnotatedLambdas = from.indyAllowAnnotatedLambdas
     to.inheritMultifileParts = from.inheritMultifileParts
diff --git a/compiler/cli/cli-jvm/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt b/compiler/cli/cli-jvm/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt
index b94c954..e31682e 100644
--- a/compiler/cli/cli-jvm/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt
+++ b/compiler/cli/cli-jvm/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt
@@ -313,6 +313,8 @@
         )
         module.configureFromArgs(arguments)
 
+        put(JVMConfigurationKeys.GENERATE_PSI_MAPPING, arguments.generatePsiMapping)
+
         ModuleChunk(listOf(module))
     }
 }
diff --git a/compiler/cli/cli-jvm/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt b/compiler/cli/cli-jvm/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt
index a5dd0fd..6572be3 100644
--- a/compiler/cli/cli-jvm/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt
+++ b/compiler/cli/cli-jvm/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt
@@ -127,6 +127,9 @@
                         "Supported modes: ${JvmWhenGenerationScheme.entries.joinToString { it.description }}"
             )
         }
+
+        val generatePsiMapping = arguments.generatePsiMapping
+        put(JVMConfigurationKeys.GENERATE_PSI_MAPPING, generatePsiMapping)
     }
 
     if (arguments.valueClasses) {
diff --git a/compiler/config.jvm/gen/org/jetbrains/kotlin/config/JVMConfigurationKeys.kt b/compiler/config.jvm/gen/org/jetbrains/kotlin/config/JVMConfigurationKeys.kt
index 6bb7152..eef0706 100644
--- a/compiler/config.jvm/gen/org/jetbrains/kotlin/config/JVMConfigurationKeys.kt
+++ b/compiler/config.jvm/gen/org/jetbrains/kotlin/config/JVMConfigurationKeys.kt
@@ -175,6 +175,9 @@
     @JvmField
     val IGNORED_ANNOTATIONS_FOR_BRIDGES = CompilerConfigurationKey.create<List<String>>("IGNORED_ANNOTATIONS_FOR_BRIDGES")
 
+    @JvmField
+    val GENERATE_PSI_MAPPING = CompilerConfigurationKey.create<Boolean>("Generate PSIMappingMetadata annotation with psi-to-bytecode mapping")
+
 }
 
 var CompilerConfiguration.outputDirectory: File?
@@ -365,3 +368,9 @@
     get() = getList(JVMConfigurationKeys.IGNORED_ANNOTATIONS_FOR_BRIDGES)
     set(value) { put(JVMConfigurationKeys.IGNORED_ANNOTATIONS_FOR_BRIDGES, value) }
 
+var CompilerConfiguration.generatePsiMapping: Boolean
+    get() = getBoolean(JVMConfigurationKeys.GENERATE_PSI_MAPPING)
+    set(value) { put(JVMConfigurationKeys.GENERATE_PSI_MAPPING, value) }
+
+
+
diff --git a/compiler/config/configuration-keys-generator/src/org/jetbrains/kotlin/config/keys/generator/JvmConfigurationKeysContainer.kt b/compiler/config/configuration-keys-generator/src/org/jetbrains/kotlin/config/keys/generator/JvmConfigurationKeysContainer.kt
index 6f385d0..24abb2a 100644
--- a/compiler/config/configuration-keys-generator/src/org/jetbrains/kotlin/config/keys/generator/JvmConfigurationKeysContainer.kt
+++ b/compiler/config/configuration-keys-generator/src/org/jetbrains/kotlin/config/keys/generator/JvmConfigurationKeysContainer.kt
@@ -60,4 +60,6 @@
     val EXPRESSION_TO_EVALUATE by key<String>("Expression to evaluate in script mode.", throwOnNull = false)
     val WHEN_GENERATION_SCHEME by key<JvmWhenGenerationScheme>("Specifies generation scheme for type-checking 'when' expressions.")
     val IGNORED_ANNOTATIONS_FOR_BRIDGES by key<List<String>>("Annotations fqNames that shall be skipped while copying the annotations from the target to the bridge functions.")
+    val GENERATE_PSI_MAPPING by key<Boolean>("Generate PSIMappingMetadata annotation with psi-to-bytecode mapping")
+
 }
diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/backend/Fir2IrFakeOverrideStrategy.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/backend/Fir2IrFakeOverrideStrategy.kt
index 4aa25f4..ec63af6 100644
--- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/backend/Fir2IrFakeOverrideStrategy.kt
+++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/backend/Fir2IrFakeOverrideStrategy.kt
@@ -163,7 +163,10 @@
         if (!fir2IrExtensions.shouldGenerateDelegatedMember(delegateTargetFromBaseType)) return
 
         when (overridableMember) {
-            is IrSimpleFunction -> overridableMember.updateDeclarationHeader()
+            is IrSimpleFunction -> {
+                overridableMember.updateDeclarationHeader()
+                overridableMember.offsetSourceForPSIMapping = delegateTargetFromBaseType
+            }
             is IrProperty -> {
                 overridableMember.updateDeclarationHeader()
                 overridableMember.getter?.updateDeclarationHeader()
diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/Fir2IrDataClassMembersGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/Fir2IrDataClassMembersGenerator.kt
index 9ffd53d..305d1eb 100644
--- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/Fir2IrDataClassMembersGenerator.kt
+++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/Fir2IrDataClassMembersGenerator.kt
@@ -261,6 +261,7 @@
                         val valueParameter = constructorParameters[index - 1]
                         val irProperty = irDataClassMembersGenerator.getProperty(valueParameter)
                         irDataClassMembersGenerator.generateComponentFunction(irFunction, irProperty)
+                        irFunction.offsetSourceForPSIMapping = irProperty
                     }
                 }
             }
diff --git a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt
index 979efdc..a2ff2b7 100644
--- a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt
+++ b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt
@@ -6,6 +6,7 @@
 package org.jetbrains.kotlin.backend.jvm.codegen
 
 import com.intellij.util.ArrayUtil
+import org.jetbrains.kotlin.PSIMetadataMappingEntry
 import org.jetbrains.kotlin.backend.common.lower.ANNOTATION_IMPLEMENTATION
 import org.jetbrains.kotlin.backend.jvm.*
 import org.jetbrains.kotlin.backend.jvm.codegen.AnnotationCodegen.Companion.annotationClass
@@ -18,6 +19,9 @@
 import org.jetbrains.kotlin.backend.jvm.metadata.MetadataSerializer
 import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap.mapKotlinToJava
 import org.jetbrains.kotlin.codegen.AsmUtil
+import org.jetbrains.kotlin.codegen.AsmUtil.PSI_MAPPING_HEADER
+import org.jetbrains.kotlin.codegen.AsmUtil.PSI_MAPPING_VERSION
+import org.jetbrains.kotlin.codegen.AsmUtil.UTF_8_CONSTANT_MAX_LEN
 import org.jetbrains.kotlin.codegen.VersionIndependentOpcodes
 import org.jetbrains.kotlin.codegen.inline.*
 import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter
@@ -28,6 +32,7 @@
 import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
 import org.jetbrains.kotlin.descriptors.DescriptorVisibility
 import org.jetbrains.kotlin.descriptors.Modality
+import org.jetbrains.kotlin.ir.IrElement
 import org.jetbrains.kotlin.ir.PsiSourceManager
 import org.jetbrains.kotlin.ir.builders.declarations.addFunction
 import org.jetbrains.kotlin.ir.declarations.*
@@ -62,6 +67,7 @@
 import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
 import org.jetbrains.org.objectweb.asm.*
 import org.jetbrains.org.objectweb.asm.commons.Method
+import org.jetbrains.org.objectweb.asm.tree.MethodNode
 import java.io.File
 
 class ClassCodegen private constructor(
@@ -155,17 +161,18 @@
             generatePermittedSubclasses()
         }
 
+        val psiMetadataMappingEntries = mutableListOf<PSIMetadataMappingEntry>()
+
         // Generating a method node may cause the addition of a field with an initializer if an inline function
         // call uses `assert` and the JVM assertions mode is enabled. To avoid concurrent modification errors,
         // there is a very specific generation order.
         // 1. Any method other than `<clinit>` can add a field and a `<clinit>` statement:
         for (method in irClass.declarations.filterIsInstance<IrFunction>()) {
-            if (method.name.asString() != "<clinit>" &&
-                method.origin != IrDeclarationOrigin.INLINE_LAMBDA &&
-                method.origin != IrDeclarationOrigin.ADAPTER_FOR_FUN_INTERFACE_CONSTRUCTOR &&
-                !(method.origin == IrDeclarationOrigin.ADAPTER_FOR_CALLABLE_REFERENCE && method.body == null)
-            ) {
-                generateMethod(method, smap)
+            if (method.name.asString() != "<clinit>" && method.origin != IrDeclarationOrigin.INLINE_LAMBDA && method.origin != IrDeclarationOrigin.ADAPTER_FOR_FUN_INTERFACE_CONSTRUCTOR && !(method.origin == IrDeclarationOrigin.ADAPTER_FOR_CALLABLE_REFERENCE && method.body == null)) {
+                val node = generateMethod(method, smap) ?: continue
+                if (config.generatePsiMetadata) {
+                    collectPSIMetadata(method, node, psiMetadataMappingEntries)
+                }
             }
         }
         // 2. `<clinit>` itself can add a field, but the statement is generated via the `return init` hack:
@@ -197,6 +204,99 @@
         visitor.done(config.generateSmapCopyToAnnotation)
         jvmMethodSignatureClashDetector.reportErrorsTo(context.ktDiagnosticReporter)
         jvmFieldSignatureClashDetector.reportErrorsTo(context.ktDiagnosticReporter)
+
+        if (config.generatePsiMetadata) {
+            writePSIMetadata(psiMetadataMappingEntries)
+        }
+    }
+
+    private val IrElement.finalPSISource: IrElement
+        get() {
+            if (offsetSourceForPSIMapping == null) return this
+            val res = offsetSourceForPSIMapping!!.finalPSISource
+            offsetSourceForPSIMapping = res
+            return res
+        }
+
+    private fun collectPSIMetadata(
+        method: IrFunction,
+        node: MethodNode,
+        entries: MutableList<PSIMetadataMappingEntry>,
+    ) {
+        val offsetSource = method.finalPSISource
+        if (offsetSource.startOffset < 0 || offsetSource.endOffset < 0) return
+        entries.add(
+            PSIMetadataMappingEntry(
+                node.name,
+                node.desc,
+                irClass.file.name,
+                irClass.file.packageFqName.asString(),
+                offsetSource.startOffset,
+                offsetSource.endOffset
+            )
+        )
+    }
+
+    private fun writePSIMetadata(entries: List<PSIMetadataMappingEntry>) {
+        if (entries.isEmpty()) return
+
+        val stringPool = linkedMapOf<String, Int>()
+        var stringPoolCurrentIndex = 0
+
+        fun addStringToPool(str: String) {
+            stringPool.computeIfAbsent(str) { stringPoolCurrentIndex++ }
+        }
+
+        entries.forEach { entry ->
+            addStringToPool(entry.name)
+            addStringToPool(entry.signature)
+            addStringToPool(entry.file)
+            addStringToPool(entry.packageName)
+        }
+
+        val psiMappingsParts = mutableListOf<StringBuilder>()
+
+        var builderCounter = 0
+        lateinit var currentBuilder: StringBuilder
+
+        fun createNewPart(isFirst: Boolean = false) {
+            currentBuilder = StringBuilder().also {
+                it.appendLine("${PSI_MAPPING_HEADER}_${builderCounter++}")
+                if (isFirst) {
+                    it.appendLine(PSI_MAPPING_VERSION)
+                    it.appendLine("${stringPool.size}")
+                }
+                psiMappingsParts.add(it)
+            }
+        }
+
+        createNewPart(isFirst = true)
+
+        for (stringFromPool in stringPool.keys) {
+            if (currentBuilder.length + stringFromPool.length > UTF_8_CONSTANT_MAX_LEN) {
+                createNewPart()
+            }
+            currentBuilder.appendLine(stringFromPool)
+        }
+
+        fun constantIndex(x: String) = stringPool[x]
+
+        entries.forEach { entry ->
+            val encodedEntry =
+                "${constantIndex(entry.file)};${constantIndex(entry.packageName)};${constantIndex(entry.name)};${constantIndex(entry.signature)};${entry.startOffset};${entry.endOffset}"
+            if (currentBuilder.length + encodedEntry.length > UTF_8_CONSTANT_MAX_LEN) {
+                createNewPart()
+            }
+            currentBuilder.appendLine(encodedEntry)
+        }
+
+        val psiMetadata = visitor.newAnnotation(PSI_MAPPING_METADATA_ASM_TYPE.descriptor, true)
+        val arrayVisitor = psiMetadata.visitArray("mappingParts")
+        psiMappingsParts.forEach { mappingPart ->
+            arrayVisitor.visit(null, mappingPart.toString())
+        }
+        arrayVisitor.visitEnd()
+        psiMetadata.visitEnd()
     }
 
     private fun shouldSkipCodeGenerationAccordingToGenerationFilter(): Boolean {
@@ -430,10 +530,10 @@
         return SMAPAndMethodNode(cloneMethodNode(node), smap)
     }
 
-    private fun generateMethod(method: IrFunction, classSMAP: SourceMapper) {
+    private fun generateMethod(method: IrFunction, classSMAP: SourceMapper): MethodNode? {
         if (method.isFakeOverride) {
             jvmMethodSignatureClashDetector.trackFakeOverrideMethod(method)
-            return
+            return null
         }
 
         val (node, smap) = generateMethodNode(method)
@@ -477,6 +577,8 @@
             null -> Unit
             else -> error("Incorrect metadata source $metadata for:\n${method.dump()}")
         }
+
+        return node
     }
 
     private fun generateInnerAndOuterClasses() {
diff --git a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt
index be7cdeb..ad3bb82 100644
--- a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt
+++ b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt
@@ -14,6 +14,7 @@
 import org.jetbrains.kotlin.backend.jvm.mapping.mapClass
 import org.jetbrains.kotlin.backend.jvm.mapping.mapSupertype
 import org.jetbrains.kotlin.builtins.StandardNames.FqNames
+import org.jetbrains.kotlin.builtins.StandardNames.KOTLIN_INTERNAL_FQ_NAME
 import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
 import org.jetbrains.kotlin.codegen.AsmUtil
 import org.jetbrains.kotlin.codegen.FrameMapBase
@@ -22,6 +23,7 @@
 import org.jetbrains.kotlin.codegen.inline.SourceMapper
 import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter
 import org.jetbrains.kotlin.codegen.state.JvmBackendConfig
+import org.jetbrains.kotlin.codegen.topLevelClassAsmType
 import org.jetbrains.kotlin.descriptors.ClassKind
 import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
 import org.jetbrains.kotlin.descriptors.Modality
@@ -35,6 +37,7 @@
 import org.jetbrains.kotlin.ir.util.*
 import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities
 import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.name.Name
 import org.jetbrains.kotlin.name.JvmStandardClassIds
 import org.jetbrains.kotlin.resolve.jvm.AsmTypes
 import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmClassSignature
@@ -377,3 +380,6 @@
     val callerTypeParam = caller.typeParameters.singleOrNull() ?: return false
     return calleeTypeArg.name == callerTypeParam.name && calleeTypeArg.parent.kotlinFqName == callerTypeParam.parent.kotlinFqName
 }
+
+val PSI_MAPPING_METADATA_ASM_TYPE: Type = KOTLIN_INTERNAL_FQ_NAME
+    .child(Name.identifier("PSIMappingMetadata")).topLevelClassAsmType()
\ No newline at end of file
diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSyntheticAccessorGenerator.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSyntheticAccessorGenerator.kt
index 8cb01c9..225c0df 100644
--- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSyntheticAccessorGenerator.kt
+++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSyntheticAccessorGenerator.kt
@@ -186,6 +186,7 @@
                 accessor.annotations += declaration.annotations
                 declaration.annotations = emptyList()
                 declaration.parameters.forEach { it.annotations = emptyList() }
+                accessor.offsetSourceForPSIMapping = declaration
             }
         }
 
diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmDefaultUtils.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmDefaultUtils.kt
index 69a8f07..25cf56e 100644
--- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmDefaultUtils.kt
+++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmDefaultUtils.kt
@@ -123,6 +123,7 @@
         dispatchReceiverParameter?.type = irClass.defaultType
         annotations = fakeOverride.annotations
         copyCorrespondingPropertyFrom(fakeOverride)
+        offsetSourceForPSIMapping = fakeOverride
     }
 }
 
diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/FakeOverrideCopier.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/FakeOverrideCopier.kt
index 9f73511..c26614e 100644
--- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/FakeOverrideCopier.kt
+++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/FakeOverrideCopier.kt
@@ -13,6 +13,7 @@
 import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
 import org.jetbrains.kotlin.ir.util.TypeRemapper
 import org.jetbrains.kotlin.ir.util.copyAnnotations
+import org.jetbrains.kotlin.ir.util.offsetSourceForPSIMapping
 
 internal class FakeOverrideCopier(
     private val valueParameters: MutableMap<IrValueParameterSymbol, IrValueParameterSymbol>,
@@ -46,6 +47,7 @@
             }
             parameters = declaration.parameters.map { copyValueParameter(it, this) }
             returnType = typeRemapper.remapType(declaration.returnType)
+            offsetSourceForPSIMapping = declaration
         }
     }
 
diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/AdditionalIrUtils.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/AdditionalIrUtils.kt
index 8b5b901..b81e152 100644
--- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/AdditionalIrUtils.kt
+++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/AdditionalIrUtils.kt
@@ -363,3 +363,5 @@
 
 fun IrClass.selectSAMOverriddenFunction(): IrSimpleFunction = selectSAMOverriddenFunctionOrNull()
     ?: error("${render()} should have a single abstract method to be a type of function reference")
+
+var IrElement.offsetSourceForPSIMapping: IrElement? by irAttribute(copyByDefault = false)
\ No newline at end of file
diff --git a/libraries/stdlib/src/kotlin/internal/Annotations.kt b/libraries/stdlib/src/kotlin/internal/Annotations.kt
index 5cb0181..4779784 100644
--- a/libraries/stdlib/src/kotlin/internal/Annotations.kt
+++ b/libraries/stdlib/src/kotlin/internal/Annotations.kt
@@ -136,3 +136,7 @@
 @Target(AnnotationTarget.FILE)
 @Retention(AnnotationRetention.SOURCE)
 internal annotation class SuppressBytecodeGeneration
+
+@Target(AnnotationTarget.CLASS)
+@PublishedApi
+internal annotation class PSIMappingMetadata(val mappingParts: Array<String>)
\ No newline at end of file