Add the support to LLVM's Context Sensitive FDO

LLVM recently implemented Context Sensitive FDO/PGO (CSFDO/CSPGO) which uses a
post-inline instrumentation to record more accurate profile information for the
inlined code. It improves the effectiveness of post-inline optimizations.

This CL adds the support to CSFDO:
(1) Add new options:
    --cs_fdo_instrument=<csfdo_instru_path>,
    --cs_profile=<csfdo_label>
(2) Add two features: CS_FDO_INSTRUMENT and CS_FDO_OPTIMIZE.
(2) Add a new BranchFdoMode: LLVM_CS_FDO.
(3) Merge CSFDO profile and regular FDO profile.
(4) Pass CSFDO options to the back-end invocations.
(5) Distribute the profile to build hosts in LLVM_CS_FDO optimize build. This is
    not needed for regular FDO as the profile is read in during pre-LTO passes. We
    need this step in CSFDO because the profile is read-in and used in the back-end.

RELNOTES: Add new options --cs_fdo_instrument and --cs_profile to support
LLVM's context-sensitive FDO (CSFDO).
PiperOrigin-RevId: 242685802
diff --git a/site/docs/cc-toolchain-config-reference.md b/site/docs/cc-toolchain-config-reference.md
index 5e8b05e..bef1804 100644
--- a/site/docs/cc-toolchain-config-reference.md
+++ b/site/docs/cc-toolchain-config-reference.md
@@ -976,6 +976,35 @@
        of <code>.o</code> files and the compiler and linker need to know this.
    </td>
   </tr>
+  <tr>
+   <td><strong><code>fdo_instrument_path</code></strong>
+   </td>
+   <td>compile, link</td>
+   <td> Path to the directory that stores FDO instrumentation profile.
+   </td>
+  </tr>
+  <tr>
+   <td><strong><code>fdo_profile_path</code></strong>
+   </td>
+   <td>compile</td>
+   <td> Path to FDO profile.
+   </td>
+  </tr>
+  <tr>
+   <td><strong><code>fdo_prefetch_hints_path</code></strong>
+   </td>
+   <td>compile</td>
+   <td> Path to the cache prefetch profile.
+   </td>
+  </tr>
+  <tr>
+   <td><strong><code>csfdo_instrument_path</code></strong>
+   </td>
+   <td>compile, link</td>
+   <td> Path to the directory that stores context sensitive FDO
+        instrumentation profile.
+   </td>
+  </tr>
 </table>
 
 
@@ -1094,6 +1123,8 @@
     <li>Adds <code>include_paths</code> (if not present) feature to the top of the toolchain</li>
     <li>Adds <code>fdo_instrument</code> (if not present) feature to the top of the toolchain</li>
     <li>Adds <code>fdo_optimize</code> (if not present) feature to the top of the toolchain</li>
+    <li>Adds <code>cs_fdo_instrument</code> (if not present) feature to the top of the toolchain</li>
+    <li>Adds <code>cs_fdo_optimize</code> (if not present) feature to the top of the toolchain</li>
     <li>Adds <code>fdo_prefetch_hints</code> (if not present) feature to the top of the toolchain</li>
     <li>Adds <code>autofdo</code> (if not present) feature to the top of the toolchain</li>
     <li>Adds <code>build_interface_libraries</code> (if not present) feature to the top of the toolchain</li>
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java
index 447539b..a19f37e 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java
@@ -844,14 +844,19 @@
 
     allFeatures.addAll(getCoverageFeatures(cppConfiguration));
 
-    String fdoInstrument = cppConfiguration.getFdoInstrument();
-    if (fdoInstrument != null && !allUnsupportedFeatures.contains(CppRuleClasses.FDO_INSTRUMENT)) {
-      allFeatures.add(CppRuleClasses.FDO_INSTRUMENT);
+    if (!allUnsupportedFeatures.contains(CppRuleClasses.FDO_INSTRUMENT)) {
+      if (cppConfiguration.getFdoInstrument() != null) {
+        allFeatures.add(CppRuleClasses.FDO_INSTRUMENT);
+      } else {
+        if (cppConfiguration.getCSFdoInstrument() != null) {
+          allFeatures.add(CppRuleClasses.CS_FDO_INSTRUMENT);
+        }
+      }
     }
 
     FdoContext.BranchFdoProfile branchFdoProvider = toolchain.getFdoContext().getBranchFdoProfile();
     if (branchFdoProvider != null && cppConfiguration.getCompilationMode() == CompilationMode.OPT) {
-      if (branchFdoProvider.isLlvmFdo()
+      if ((branchFdoProvider.isLlvmFdo() || branchFdoProvider.isLlvmCSFdo())
           && !allUnsupportedFeatures.contains(CppRuleClasses.FDO_OPTIMIZE)) {
         allFeatures.add(CppRuleClasses.FDO_OPTIMIZE);
         // For LLVM, support implicit enabling of ThinLTO for FDO unless it has been
@@ -861,6 +866,9 @@
           allFeatures.add(CppRuleClasses.ENABLE_FDO_THINLTO);
         }
       }
+      if (branchFdoProvider.isLlvmCSFdo()) {
+        allFeatures.add(CppRuleClasses.CS_FDO_OPTIMIZE);
+      }
       if (branchFdoProvider.isAutoFdo()) {
         allFeatures.add(CppRuleClasses.AUTOFDO);
         // For LLVM, support implicit enabling of ThinLTO for AFDO unless it has been
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelper.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelper.java
index e1ddaf1..81e7d5c 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelper.java
@@ -94,11 +94,16 @@
       FeatureConfiguration featureConfiguration,
       FdoContext fdoContext,
       String fdoInstrument,
+      String csFdoInstrument,
       CppConfiguration cppConfiguration) {
     if (featureConfiguration.isEnabled(CppRuleClasses.FDO_INSTRUMENT)) {
       variablesBuilder.put(
           CompileBuildVariables.FDO_INSTRUMENT_PATH.getVariableName(), fdoInstrument);
     }
+    if (featureConfiguration.isEnabled(CppRuleClasses.CS_FDO_INSTRUMENT)) {
+      variablesBuilder.put(
+          CompileBuildVariables.CS_FDO_INSTRUMENT_PATH.getVariableName(), csFdoInstrument);
+    }
 
     // FDO is disabled -> do nothing.
     Preconditions.checkNotNull(fdoContext);
@@ -126,7 +131,7 @@
               branchFdoProfile.getProfileArtifact().getExecPathString());
         }
         if (featureConfiguration.isEnabled(CppRuleClasses.FDO_OPTIMIZE)) {
-          if (branchFdoProfile.isLlvmFdo()) {
+          if (branchFdoProfile.isLlvmFdo() || branchFdoProfile.isLlvmCSFdo()) {
             variablesBuilder.put(
                 CompileBuildVariables.FDO_PROFILE_PATH.getVariableName(),
                 branchFdoProfile.getProfileArtifact().getExecPathString());
@@ -1440,6 +1445,7 @@
           featureConfiguration,
           fdoContext,
           cppConfiguration.getFdoInstrument(),
+          cppConfiguration.getCSFdoInstrument(),
           cppConfiguration);
     }
     return CompileBuildVariables.setupVariablesOrReportRuleError(
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainAttributesProvider.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainAttributesProvider.java
index 6f0a7b6..ff03413 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainAttributesProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainAttributesProvider.java
@@ -78,7 +78,6 @@
   private final String cpu;
   private final Artifact ifsoBuilder;
   private final Artifact linkDynamicLibraryTool;
-  private final FdoProfileProvider fdoOptimizeProvider;
   private final TransitiveInfoCollection fdoOptimize;
   private final ImmutableList<Artifact> fdoOptimizeArtifacts;
   private final FdoPrefetchHintsProvider fdoPrefetch;
@@ -92,7 +91,9 @@
   private final AdditionalBuildVariablesComputer additionalBuildVariablesComputer;
   private final CcToolchainConfigInfo ccToolchainConfigInfo;
   private final String toolchainIdentifier;
+  private final FdoProfileProvider fdoOptimizeProvider;
   private final FdoProfileProvider fdoProfileProvider;
+  private final FdoProfileProvider csFdoProfileProvider;
   private final FdoProfileProvider xfdoProfileProvider;
   private final Label ccToolchainLabel;
   private final TransitiveInfoCollection staticRuntimeLib;
@@ -167,6 +168,9 @@
     this.fdoProfileProvider =
         ruleContext.getPrerequisite(
             CcToolchainRule.FDO_PROFILE_ATTR, Mode.TARGET, FdoProfileProvider.PROVIDER);
+    this.csFdoProfileProvider =
+        ruleContext.getPrerequisite(
+            CcToolchainRule.CSFDO_PROFILE_ATTR, Mode.TARGET, FdoProfileProvider.PROVIDER);
     this.xfdoProfileProvider =
         ruleContext.getPrerequisite(
             CcToolchainRule.XFDO_PROFILE_ATTR, Mode.TARGET, FdoProfileProvider.PROVIDER);
@@ -348,6 +352,10 @@
     return fdoProfileProvider;
   }
 
+  public FdoProfileProvider getCSFdoProfileProvider() {
+    return csFdoProfileProvider;
+  }
+
   public FdoProfileProvider getXFdoProfileProvider() {
     return xfdoProfileProvider;
   }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainProvider.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainProvider.java
index f25ee30..b845343 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainProvider.java
@@ -613,6 +613,11 @@
     return cppConfiguration;
   }
 
+  /** Return context-sensitive fdo instrumentation path. */
+  public String getCSFdoInstrument() {
+    return cppConfiguration.getCSFdoInstrument();
+  }
+
   /** Returns build variables to be templated into the crosstool. */
   public CcToolchainVariables getBuildVariables(
       BuildOptions buildOptions, CppConfiguration cppConfiguration) {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainRule.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainRule.java
index 4a4949a..ff9ccc4 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainRule.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainRule.java
@@ -42,6 +42,7 @@
   public static final String TARGET_LIBC_TOP_ATTR = ":target_libc_top";
   public static final String FDO_OPTIMIZE_ATTR = ":fdo_optimize";
   public static final String FDO_PROFILE_ATTR = ":fdo_profile";
+  public static final String CSFDO_PROFILE_ATTR = ":csfdo_profile";
   public static final String XFDO_PROFILE_ATTR = ":xfdo_profile";
   public static final String TOOLCHAIN_CONFIG_ATTR = "toolchain_config";
 
@@ -101,6 +102,12 @@
           (rule, attributes, cppConfig) ->
               cppConfig.getFdoProfileLabelUnsafeSinceItCanReturnValueFromWrongConfiguration());
 
+  private static final LabelLateBoundDefault<?> CSFDO_PROFILE_VALUE =
+      LabelLateBoundDefault.fromTargetConfiguration(
+          CppConfiguration.class,
+          null,
+          (rule, attributes, cppConfig) -> cppConfig.getCSFdoProfileLabel());
+
   private static final LabelLateBoundDefault<?> XFDO_PROFILE_VALUE =
       LabelLateBoundDefault.fromTargetConfiguration(
           CppConfiguration.class,
@@ -324,6 +331,11 @@
                 .mandatoryProviders(ImmutableList.of(FdoProfileProvider.PROVIDER.id()))
                 .value(FDO_PROFILE_VALUE))
         .add(
+            attr(CSFDO_PROFILE_ATTR, LABEL)
+                .allowedRuleClasses("fdo_profile")
+                .mandatoryProviders(ImmutableList.of(FdoProfileProvider.PROVIDER.id()))
+                .value(CSFDO_PROFILE_VALUE))
+        .add(
             attr(":fdo_prefetch_hints", LABEL)
                 .allowedRuleClasses("fdo_prefetch_hints")
                 .mandatoryProviders(ImmutableList.of(FdoPrefetchHintsProvider.PROVIDER.id()))
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CompileBuildVariables.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CompileBuildVariables.java
index e8d823a..3eb49bb 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CompileBuildVariables.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CompileBuildVariables.java
@@ -91,6 +91,8 @@
   FDO_INSTRUMENT_PATH("fdo_instrument_path"),
   /** Path to the fdo profile artifact */
   FDO_PROFILE_PATH("fdo_profile_path"),
+  /** Path to the context sensitive fdo instrument artifact */
+  CS_FDO_INSTRUMENT_PATH("cs_fdo_instrument_path"),
   /** Path to the cache prefetch profile artifact */
   FDO_PREFETCH_HINTS_PATH("fdo_prefetch_hints_path"),
   /** Variable for includes that compiler needs to include into sources. */
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppActionConfigs.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppActionConfigs.java
index e35bd74..2e0c3d6 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppActionConfigs.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppActionConfigs.java
@@ -297,6 +297,47 @@
                         "    }",
                         "  }")));
       }
+
+      if (!existingFeatureNames.contains(CppRuleClasses.CS_FDO_INSTRUMENT)) {
+        featureBuilder.add(
+            getFeature(
+                Joiner.on("\n")
+                    .join(
+                        "  name: 'cs_fdo_instrument'",
+                        "  provides: 'csprofile'",
+                        "  flag_set {",
+                        "    action: 'c-compile'",
+                        "    action: 'c++-compile'",
+                        "    action: 'lto-backend'",
+                        "    action: 'c++-link-dynamic-library'",
+                        "    action: 'c++-link-nodeps-dynamic-library'",
+                        "    action: 'c++-link-executable'",
+                        "    flag_group {",
+                        "      expand_if_all_available: 'cs_fdo_instrument_path'",
+                        "      flag: '-fcs-profile-generate=%{cs_fdo_instrument_path}'",
+                        "    }",
+                        "  }")));
+      }
+
+      if (!existingFeatureNames.contains(CppRuleClasses.CS_FDO_OPTIMIZE)) {
+        featureBuilder.add(
+            getFeature(
+                Joiner.on("\n")
+                    .join(
+                        "  name: 'cs_fdo_optimize'",
+                        "  provides: 'csprofile'",
+                        "  flag_set {",
+                        "    action: 'lto-backend'",
+                        "    flag_group {",
+                        "      expand_if_all_available: 'fdo_profile_path'",
+                        "      flag: '-fprofile-use=%{fdo_profile_path}'",
+                        "      flag: '-Xclang-only=-Wno-profile-instr-unprofiled'",
+                        "      flag: '-Xclang-only=-Wno-profile-instr-out-of-date'",
+                        "      flag: '-fprofile-correction'",
+                        "    }",
+                        "  }")));
+      }
+
       if (!existingFeatureNames.contains(CppRuleClasses.FDO_PREFETCH_HINTS)) {
         featureBuilder.add(
             getFeature(
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java
index c797ca1..afddfa4 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java
@@ -302,6 +302,10 @@
     return cppOptions.isFdo();
   }
 
+  public boolean isCSFdo() {
+    return cppOptions.isCSFdo();
+  }
+
   /**
    * Returns whether or not to strip the binaries.
    */
@@ -540,6 +544,10 @@
     return fdoOptimizeLabel;
   }
 
+  public String getCSFdoInstrument() {
+    return cppOptions.csFdoInstrumentForBuild;
+  }
+
   Label getFdoPrefetchHintsLabel() {
     if (isThisHostConfigurationDoNotUseWillBeRemovedFor129045294()) {
       // We don't want FDO in the host configuration
@@ -566,6 +574,10 @@
     return cppOptions.fdoProfileLabel;
   }
 
+  public Label getCSFdoProfileLabel() {
+    return cppOptions.csFdoProfileLabel;
+  }
+
   /**
    * @deprecated Unsafe because it returns a value from target configuration even in the host
    *     configuration.
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppHelper.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppHelper.java
index c9634bf..61b10ea 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppHelper.java
@@ -599,6 +599,9 @@
         return featureConfiguration.isEnabled(CppRuleClasses.XBINARYFDO) ? "XFDO" : null;
       }
     }
+    if (cppConfiguration.isCSFdo()) {
+      return "CSFDO";
+    }
     if (cppConfiguration.isFdo()) {
       return "FDO";
     }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppOptions.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppOptions.java
index 9322ef4..af39bbf 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppOptions.java
@@ -420,6 +420,18 @@
   }
 
   @Option(
+      name = "cs_fdo_instrument",
+      defaultValue = "null",
+      implicitRequirements = {"--copt=-Wno-error"},
+      documentationCategory = OptionDocumentationCategory.OUTPUT_PARAMETERS,
+      effectTags = {OptionEffectTag.AFFECTS_OUTPUTS},
+      help =
+          "Generate binaries with context sensitive FDO instrumentation. With Clang/LLVM compiler, "
+              + "it also accepts the directory name under which the raw profile file(s) will be "
+              + "dumped at runtime.")
+  public String csFdoInstrumentForBuild;
+
+  @Option(
       name = "xbinary_fdo",
       defaultValue = "null",
       documentationCategory = OptionDocumentationCategory.OUTPUT_PARAMETERS,
@@ -462,6 +474,18 @@
   public Label fdoProfileLabel;
 
   @Option(
+      name = "cs_fdo_profile",
+      defaultValue = "null",
+      category = "flags",
+      converter = LabelConverter.class,
+      documentationCategory = OptionDocumentationCategory.OUTPUT_PARAMETERS,
+      effectTags = {OptionEffectTag.AFFECTS_OUTPUTS},
+      help =
+          "The cs_fdo_profile representing the context sensitive profile to be used for"
+              + " optimization.")
+  public Label csFdoProfileLabel;
+
+  @Option(
       name = "enable_fdo_profile_absolute_path",
       defaultValue = "true",
       documentationCategory = OptionDocumentationCategory.OUTPUT_PARAMETERS,
@@ -905,6 +929,7 @@
     host.stripBinaries = StripMode.ALWAYS;
     host.fdoOptimizeForBuild = fdoOptimizeForBuild;
     host.fdoProfileLabel = fdoProfileLabel;
+    host.csFdoProfileLabel = csFdoProfileLabel;
     host.xfdoProfileLabel = xfdoProfileLabel;
     host.inmemoryDotdFiles = inmemoryDotdFiles;
 
@@ -928,4 +953,10 @@
   public boolean isFdo() {
     return getFdoOptimize() != null || fdoInstrumentForBuild != null || fdoProfileLabel != null;
   }
+
+  /** Returns true if targets under this configuration should apply CSFdo. */
+  public boolean isCSFdo() {
+    return ((getFdoOptimize() != null || fdoProfileLabel != null)
+        && (csFdoInstrumentForBuild != null || csFdoProfileLabel != null));
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java
index 6686bff..87c280a 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java
@@ -348,9 +348,15 @@
    */
   public static final String FDO_INSTRUMENT = "fdo_instrument";
 
+  /** A string constant for the cs_fdo_instrument feature. */
+  public static final String CS_FDO_INSTRUMENT = "cs_fdo_instrument";
+
   /** A string constant for the fdo_optimize feature. */
   public static final String FDO_OPTIMIZE = "fdo_optimize";
 
+  /** A string constant for the cs_fdo_optimize feature. */
+  public static final String CS_FDO_OPTIMIZE = "cs_fdo_optimize";
+
   /** A string constant for the cache prefetch hints feature. */
   public static final String FDO_PREFETCH_HINTS = "fdo_prefetch_hints";
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoContext.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoContext.java
index 7fed311..2b7766a 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoContext.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoContext.java
@@ -40,6 +40,9 @@
 
     /** Instrumentation-based FDO implemented on LLVM. */
     LLVM_FDO,
+
+    /** Instrumentation-based Context Sensitive FDO implemented on LLVM. */
+    LLVM_CS_FDO,
   }
 
   /** A POJO encapsulating the branch profiling configuration. */
@@ -68,6 +71,10 @@
       return branchFdoMode == BranchFdoMode.LLVM_FDO;
     }
 
+    public boolean isLlvmCSFdo() {
+      return branchFdoMode == BranchFdoMode.LLVM_CS_FDO;
+    }
+
     public Artifact getProfileArtifact() {
       return profileArtifact;
     }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoHelper.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoHelper.java
index 124ac70..687373a 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoHelper.java
@@ -44,6 +44,7 @@
       CppToolchainInfo toolchainInfo)
       throws InterruptedException, RuleErrorException {
     FdoInputFile fdoInputFile = null;
+    FdoInputFile csFdoInputFile = null;
     FdoInputFile prefetchHints = null;
     Artifact protoProfileArtifact = null;
     Pair<FdoInputFile, Artifact> fdoInputs = null;
@@ -87,6 +88,23 @@
           != null) {
         fdoInputs = getFdoInputs(ruleContext, attributes.getXFdoProfileProvider());
       }
+
+      Pair<FdoInputFile, Artifact> csFdoInputs = null;
+      if (cppConfiguration.getCSFdoProfileLabel() != null) {
+        csFdoInputs = getFdoInputs(ruleContext, attributes.getCSFdoProfileProvider());
+      }
+      if (csFdoInputs != null) {
+        csFdoInputFile = csFdoInputs.getFirst();
+      }
+    }
+
+    if (ruleContext.hasErrors()) {
+      return null;
+    }
+
+    if (fdoInputs != null) {
+      fdoInputFile = fdoInputs.getFirst();
+      protoProfileArtifact = fdoInputs.getSecond();
     }
 
     if (ruleContext.hasErrors()) {
@@ -115,6 +133,12 @@
         ruleContext.ruleError("invalid extension for FDO profile file.");
         return null;
       }
+      // Check if this is LLVM_CS_FDO
+      if (branchFdoMode == BranchFdoMode.LLVM_FDO) {
+        if (csFdoInputFile != null) {
+          branchFdoMode = BranchFdoMode.LLVM_CS_FDO;
+        }
+      }
       if (branchFdoMode != BranchFdoMode.XBINARY_FDO
           && cppConfiguration.getXFdoProfileLabelUnsafeSinceItCanReturnValueFromWrongConfiguration()
               != null) {
@@ -130,7 +154,7 @@
       if (branchFdoMode == BranchFdoMode.LLVM_FDO) {
         profileArtifact =
             convertLLVMRawProfileToIndexed(
-                attributes, fdoInputFile, toolchainInfo, ruleContext, cppConfiguration);
+                attributes, fdoInputFile, toolchainInfo, ruleContext, cppConfiguration, "fdo");
         if (ruleContext.hasErrors()) {
           return null;
         }
@@ -144,6 +168,33 @@
             profileArtifact,
             fdoInputFile,
             "Symlinking FDO profile " + fdoInputFile.getBasename());
+      } else if (branchFdoMode == BranchFdoMode.LLVM_CS_FDO) {
+        Artifact nonCSProfileArtifact =
+            convertLLVMRawProfileToIndexed(
+                attributes, fdoInputFile, toolchainInfo, ruleContext, cppConfiguration, "fdo");
+        if (ruleContext.hasErrors()) {
+          return null;
+        }
+        Artifact csProfileArtifact =
+            convertLLVMRawProfileToIndexed(
+                attributes, csFdoInputFile, toolchainInfo, ruleContext, cppConfiguration, "csfdo");
+        if (ruleContext.hasErrors()) {
+          return null;
+        }
+        if (nonCSProfileArtifact != null && csProfileArtifact != null) {
+          profileArtifact =
+              mergeLLVMProfiles(
+                  attributes,
+                  toolchainInfo,
+                  ruleContext,
+                  nonCSProfileArtifact,
+                  csProfileArtifact,
+                  "mergedfdo",
+                  "MergedCS.profdata");
+          if (ruleContext.hasErrors()) {
+            return null;
+          }
+        }
       }
       branchFdoProfile =
           new FdoContext.BranchFdoProfile(branchFdoMode, profileArtifact, protoProfileArtifact);
@@ -209,6 +260,43 @@
     }
   }
 
+  /** This function merges profile1 and profile2 and generates mergedOutput. */
+  private static Artifact mergeLLVMProfiles(
+      CcToolchainAttributesProvider attributes,
+      CppToolchainInfo toolchainInfo,
+      RuleContext ruleContext,
+      Artifact profile1,
+      Artifact profile2,
+      String fdoUniqueArtifactName,
+      String mergedOutput) {
+    Artifact profileArtifact =
+        ruleContext.getUniqueDirectoryArtifact(
+            fdoUniqueArtifactName, mergedOutput, ruleContext.getBinOrGenfilesDirectory());
+
+    // Merge LLVM profiles.
+    ruleContext.registerAction(
+        new SpawnAction.Builder()
+            .addInput(profile1)
+            .addInput(profile2)
+            .addTransitiveInputs(attributes.getAllFilesMiddleman())
+            .addOutput(profileArtifact)
+            .useDefaultShellEnvironment()
+            .setExecutable(toolchainInfo.getToolPathFragment(Tool.LLVM_PROFDATA))
+            .setProgressMessage("LLVMProfDataAction: Generating %s", profileArtifact.prettyPrint())
+            .setMnemonic("LLVMProfDataMergeAction")
+            .addCommandLine(
+                CustomCommandLine.builder()
+                    .add("merge")
+                    .add("-o")
+                    .addExecPath(profileArtifact)
+                    .addExecPath(profile1)
+                    .addExecPath(profile2)
+                    .build())
+            .build(ruleContext));
+
+    return profileArtifact;
+  }
+
   /*
    * This function checks the format of the input profile data and converts it to
    * the indexed format (.profdata) if necessary.
@@ -218,14 +306,15 @@
       FdoInputFile fdoProfile,
       CppToolchainInfo toolchainInfo,
       RuleContext ruleContext,
-      CppConfiguration cppConfiguration) {
+      CppConfiguration cppConfiguration,
+      String fdoUniqueArtifactName) {
     if (cppConfiguration.isThisHostConfigurationDoNotUseWillBeRemovedFor129045294()) {
       return null;
     }
 
     Artifact profileArtifact =
         ruleContext.getUniqueDirectoryArtifact(
-            "fdo",
+            fdoUniqueArtifactName,
             getLLVMProfileFileName(fdoProfile, CppFileTypes.LLVM_PROFILE),
             ruleContext.getBinOrGenfilesDirectory());
 
@@ -263,12 +352,14 @@
       }
       rawProfileArtifact =
           ruleContext.getUniqueDirectoryArtifact(
-              "fdo", rawProfileFileName, ruleContext.getBinOrGenfilesDirectory());
+              fdoUniqueArtifactName, rawProfileFileName, ruleContext.getBinOrGenfilesDirectory());
 
       // Symlink to the zipped profile file to extract the contents.
       Artifact zipProfileArtifact =
           ruleContext.getUniqueDirectoryArtifact(
-              "fdo", fdoProfile.getBasename(), ruleContext.getBinOrGenfilesDirectory());
+              fdoUniqueArtifactName,
+              fdoProfile.getBasename(),
+              ruleContext.getBinOrGenfilesDirectory());
       symlinkTo(
           ruleContext,
           zipProfileArtifact,
@@ -297,7 +388,7 @@
     } else {
       rawProfileArtifact =
           ruleContext.getUniqueDirectoryArtifact(
-              "fdo",
+              fdoUniqueArtifactName,
               getLLVMProfileFileName(fdoProfile, CppFileTypes.LLVM_PROFILE_RAW),
               ruleContext.getBinOrGenfilesDirectory());
       symlinkTo(
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkBuildVariables.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkBuildVariables.java
index ac2da3d..a79102e 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkBuildVariables.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/LinkBuildVariables.java
@@ -75,7 +75,11 @@
    * Presence of this variable indicates that files were compiled with fission (debug info is in
    * .dwo files instead of .o files and linker needs to know).
    */
-  IS_USING_FISSION("is_using_fission");
+  IS_USING_FISSION("is_using_fission"),
+  /** Path to the fdo instrument. */
+  FDO_INSTRUMENT_PATH("fdo_instrument_path"),
+  /** Path to the context sensitive fdo instrument. */
+  CS_FDO_INSTRUMENT_PATH("cs_fdo_instrument_path");
 
   private final String variableName;
 
@@ -224,7 +228,11 @@
       Preconditions.checkArgument(fdoContext.getBranchFdoProfile() == null);
       String fdoInstrument = cppConfiguration.getFdoInstrument();
       Preconditions.checkNotNull(fdoInstrument);
-      buildVariables.addStringVariable("fdo_instrument_path", fdoInstrument);
+      buildVariables.addStringVariable(FDO_INSTRUMENT_PATH.getVariableName(), fdoInstrument);
+    } else if (featureConfiguration.isEnabled(CppRuleClasses.CS_FDO_INSTRUMENT)) {
+      String csFdoInstrument = ccToolchainProvider.getCSFdoInstrument();
+      Preconditions.checkNotNull(csFdoInstrument);
+      buildVariables.addStringVariable(CS_FDO_INSTRUMENT_PATH.getVariableName(), csFdoInstrument);
     }
 
     Iterable<String> userLinkFlagsWithLtoIndexingIfNeeded;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/LtoBackendArtifacts.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/LtoBackendArtifacts.java
index ec921b2..c8b2121 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/LtoBackendArtifacts.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/LtoBackendArtifacts.java
@@ -253,6 +253,12 @@
     buildVariablesBuilder.addStringVariable(
         "thinlto_input_bitcode_file", bitcodeFile.getExecPath().toString());
     addProfileForLtoBackend(builder, fdoContext, featureConfiguration, buildVariablesBuilder);
+    // Add the context sensitive instrument path to the backend.
+    if (featureConfiguration.isEnabled(CppRuleClasses.CS_FDO_INSTRUMENT)) {
+      buildVariablesBuilder.addStringVariable(
+          CompileBuildVariables.CS_FDO_INSTRUMENT_PATH.getVariableName(),
+          ccToolchain.getCSFdoInstrument());
+    }
 
     if (generateDwo) {
       dwoFile =
@@ -303,6 +309,7 @@
       builder.addInput(fdoContext.getPrefetchHintsArtifact());
     }
     if (!featureConfiguration.isEnabled(CppRuleClasses.AUTOFDO)
+        && !featureConfiguration.isEnabled(CppRuleClasses.CS_FDO_OPTIMIZE)
         && !featureConfiguration.isEnabled(CppRuleClasses.XBINARYFDO)) {
       return;
     }