Fork fdo rules bzl files

PiperOrigin-RevId: 791601077
Change-Id: Ie1fada81a29a228ba03ce9f328c77f405648f0a2
diff --git a/cc/extensions.bzl b/cc/extensions.bzl
index 4b06794..11abe00 100644
--- a/cc/extensions.bzl
+++ b/cc/extensions.bzl
@@ -41,6 +41,10 @@
 load("@rules_cc//cc/private/rules_impl:cc_test.bzl", _cc_test = "cc_test")
 load("@rules_cc//cc/private/rules_impl:objc_import.bzl", _objc_import = "objc_import")
 load("@rules_cc//cc/private/rules_impl:objc_library.bzl", _objc_library = "objc_library")
+load("@rules_cc//cc/private/rules_impl:fdo/fdo_prefetch_hints.bzl", _fdo_prefetch_hints = "fdo_prefetch_hints")
+load("@rules_cc//cc/private/rules_impl:fdo/fdo_profile.bzl", _fdo_profile = "fdo_profile")
+load("@rules_cc//cc/private/rules_impl:fdo/memprof_profile.bzl", _memprof_profile = "memprof_profile")
+load("@rules_cc//cc/private/rules_impl:fdo/propeller_optimize.bzl", _propeller_optimize = "propeller_optimize")
 
 cc_binary = _cc_binary
 cc_import = _cc_import
@@ -50,6 +54,10 @@
 cc_test = _cc_test
 objc_import = _objc_import
 objc_library = _objc_library
+fdo_prefetch_hints = _fdo_prefetch_hints
+fdo_profile = _fdo_profile
+memprof_profile = _memprof_profile
+propeller_optimize = _propeller_optimize
             """,
         )
     else:
@@ -64,6 +72,10 @@
 cc_test = native.cc_test
 objc_import = native.objc_import
 objc_library = native.objc_library
+fdo_prefetch_hints = native.fdo_prefetch_hints
+fdo_profile = native.fdo_profile
+memprof_profile = getattr(native, "memprof_profile", None) # only in Bazel 7+
+propeller_optimize = native.propeller_optimize
             """,
         )
 
diff --git a/cc/private/rules_impl/fdo/fdo_context.bzl b/cc/private/rules_impl/fdo/fdo_context.bzl
new file mode 100644
index 0000000..4f4bc97
--- /dev/null
+++ b/cc/private/rules_impl/fdo/fdo_context.bzl
@@ -0,0 +1,469 @@
+# Copyright 2024 The Bazel Authors. All rights reserved.
+#
+# 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.
+"""FDO context describes how C++ FDO compilation should be done."""
+
+load("@bazel_skylib//lib:paths.bzl", "paths")
+load("//cc/common:cc_common.bzl", "cc_common")
+load(":fdo/fdo_prefetch_hints.bzl", "FdoPrefetchHintsInfo")
+load(":fdo/fdo_profile.bzl", "FdoProfileInfo")
+load(":fdo/memprof_profile.bzl", "MemProfProfileInfo")
+load(":fdo/propeller_optimize.bzl", "PropellerOptimizeInfo")
+
+def _create_fdo_context(
+        ctx,
+        *,
+        llvm_profdata,
+        all_files,
+        zipper,
+        cc_toolchain_config_info,
+        coverage_enabled,
+        _fdo_prefetch_hints,
+        _propeller_optimize,
+        _memprof_profile,
+        _fdo_optimize,
+        _fdo_profile,
+        _xfdo_profile,
+        _csfdo_profile,
+        _proto_profile):
+    """Creates FDO context when it should be available.
+
+    When `-c opt` is used it parses values of FDO related flags, processes the
+    input Files and return Files ready to be used for FDO.
+
+    Args:
+      ctx: (SubruleContext) used to create actions and obtain configuration
+      llvm_profdata: (File) llvm-profdata executable
+      all_files: (depset(File)) Files needed to run llvm-profdata
+      zipper: (File) zip tool, used to unpact the profiles
+      cc_toolchain_config_info: (CcToolchainConfigInfo) Used to check CPU value, should be removed
+      coverage_enabled: (bool) Is code coverage enabled
+      _fdo_prefetch_hints: (Target) Pointed to by --fdo_prefetch_hints
+      _propeller_optimize: (Target) Pointed to by --propeller_optimize
+      _memprof_profile: (Target) Pointed to by --memprof_profile
+      _fdo_optimize: (Target) Pointed to by --fdo_optimize
+      _fdo_profile: (Target) Pointed to by --fdo_profile
+      _xfdo_profile: (Target) Pointed to by --xbinary_fdo
+      _csfdo_profile: (Target) Pointed to by --cs_fdo_profile
+      _proto_profile: (Target) Pointed to by --proto_profile_path
+    Returns:
+      (FDOContext) A structure with following fields:
+      - branch_fdo_profile (struct|None)
+         - branch_fdo_mode ("auto_fdo"|"xbinary_fdo"|"llvm_fdo"|llvm_cs_fdo")
+         - profile_artifact (File)
+      - proto_profile_artifact (File)
+      - prefetch_hints_artifact (File),
+      - propeller_optimize_info (PropellerOptimizeInfo)
+      - memprof_profile_artifact (File)
+    """
+    cpp_config = ctx.fragments.cpp
+    if cpp_config.compilation_mode() != "opt":
+        return struct()
+
+    # Propeller optimize cc and ld profiles
+    cc_profile = _symlink_to(
+        ctx,
+        name_prefix = "fdo",
+        absolute_path = cpp_config.propeller_optimize_absolute_cc_profile(),
+        progress_message = "Symlinking LLVM Propeller Profile %{input}",
+    )
+    ld_profile = _symlink_to(
+        ctx,
+        name_prefix = "fdo",
+        absolute_path = cpp_config.propeller_optimize_absolute_ld_profile(),
+        progress_message = "Symlinking LLVM Propeller Profile %{input}",
+    )
+    if cc_profile or ld_profile:
+        propeller_optimize_info = PropellerOptimizeInfo(
+            cc_profile = cc_profile,
+            ld_profile = ld_profile,
+        )
+    elif _propeller_optimize:
+        propeller_optimize_info = _propeller_optimize[PropellerOptimizeInfo]
+    else:
+        propeller_optimize_info = None
+
+    # Attempt to fetch the memprof profile input from an explicit flag or as part of the
+    # fdo_profile rule. The former overrides the latter. Also handle the case where the
+    # fdo_profile rule is specified using fdo_optimize.
+    mem_prof_profile = None
+    if _memprof_profile:
+        mem_prof_profile = _memprof_profile[MemProfProfileInfo]
+    elif _fdo_profile and _fdo_profile[FdoProfileInfo].memprof_artifact:
+        mem_prof_profile = MemProfProfileInfo(artifact = _fdo_profile[FdoProfileInfo].memprof_artifact)
+    elif _fdo_optimize and FdoProfileInfo in _fdo_optimize and _fdo_optimize[FdoProfileInfo].memprof_artifact:
+        mem_prof_profile = MemProfProfileInfo(artifact = _fdo_optimize[FdoProfileInfo].memprof_artifact)
+    elif _xfdo_profile and FdoProfileInfo in _xfdo_profile and _xfdo_profile[FdoProfileInfo].memprof_artifact:
+        # This is needed for configless AutoFDO which supplies the profile via a select statement in the XFDO profile.
+        # We don't intend to support actual XFDO with memprof.
+        mem_prof_profile = MemProfProfileInfo(artifact = _xfdo_profile[FdoProfileInfo].memprof_artifact)
+
+    fdo_inputs = None
+    if cpp_config.fdo_path():
+        # TODO(b/333997009): computation of cpp_config.fdo_path in CppConfiguration class is convoluted, simplify it
+        # fdoZip should be set if the profile is a path, fdoInputFile if it is an artifact, but never both
+        fdo_inputs = FdoProfileInfo(absolute_path = cpp_config.fdo_path())
+    elif _fdo_optimize:
+        if FdoProfileInfo in _fdo_optimize:
+            fdo_inputs = _fdo_optimize[FdoProfileInfo]
+        elif _fdo_optimize[DefaultInfo].files:
+            if len(_fdo_optimize[DefaultInfo].files.to_list()) != 1:
+                fail("--fdo_optimize does not point to a single target")
+            [fdo_optimize_artifact] = _fdo_optimize[DefaultInfo].files.to_list()
+            if fdo_optimize_artifact.short_path != _fdo_optimize.label.package + "/" + _fdo_optimize.label.name:
+                fail("--fdo_optimize points to a target that is not an input file or an fdo_profile rule")
+            fdo_inputs = FdoProfileInfo(artifact = fdo_optimize_artifact)
+    elif _fdo_profile:
+        fdo_inputs = _fdo_profile[FdoProfileInfo]
+    elif _xfdo_profile:
+        fdo_inputs = _xfdo_profile[FdoProfileInfo]
+
+    cs_fdo_input = None
+    if cpp_config.cs_fdo_path():
+        cs_fdo_input = FdoProfileInfo(absolute_path = cpp_config.cs_fdo_path())
+    elif _csfdo_profile:
+        cs_fdo_input = _csfdo_profile[FdoProfileInfo]
+
+    # If --noproto_profile is in effect, there is no proto profile.
+    # If --proto_profile_path=<label> is passed, that profile is used.
+    # If AutoFDO is not in effect, no profile is used.
+    # If AutoFDO is in effect, a file called proto.profile next to the AutoFDO
+    # profile is used, if it exists.
+    proto_profile_artifact = None
+    if not cpp_config.proto_profile() and _proto_profile:
+        fail("--proto_profile_path cannot be set if --proto_profile is false")
+    if _proto_profile:
+        proto_profile_artifact = _symlink_to(
+            ctx,
+            name_prefix = "fdo",
+            artifact = _proto_profile,
+            progress_message = "Symlinking protobuf profile %{input}",
+        )
+    elif cpp_config.proto_profile():
+        proto_profile_artifact = getattr(fdo_inputs, "proto_profile_artifact", None)
+
+    branch_fdo_profile = None
+    if fdo_inputs:
+        branch_fdo_modes = {
+            ".afdo": "auto_fdo",
+            ".profdata": "llvm_fdo",
+            ".profraw": "llvm_fdo",
+            ".xfdo": "xbinary_fdo",
+            ".zip": "llvm_fdo",
+        }
+        extension = paths.split_extension(_basename(fdo_inputs))[1]
+        if extension not in branch_fdo_modes:
+            fail("invalid extension for FDO profile file")
+        branch_fdo_mode = branch_fdo_modes[extension]
+
+        if branch_fdo_mode == "llvm_fdo":
+            # Check if this is LLVM_CS_FDO
+            if cs_fdo_input:
+                branch_fdo_mode = "llvm_cs_fdo"
+
+            if _xfdo_profile:
+                fail("--xbinary_fdo only accepts *.xfdo and *.afdo")
+
+        if coverage_enabled:
+            fail("coverage mode is not compatible with FDO optimization")
+
+        # This tries to convert LLVM profiles to the indexed format if necessary.
+        if branch_fdo_mode == "llvm_fdo":
+            profile_artifact = _convert_llvm_raw_profile_to_indexed(
+                ctx,
+                "fdo",
+                fdo_inputs,
+                llvm_profdata,
+                all_files,
+                zipper,
+                cc_toolchain_config_info,
+            )
+        elif branch_fdo_mode in ["auto_fdo", "xbinary_fdo"]:
+            profile_artifact = _symlink_input(
+                ctx,
+                "fdo",
+                fdo_inputs,
+                "Symlinking FDO profile %{input}",
+            )
+        else:  # branch_fdo_mode == "llvm_cs_fdo":
+            non_cs_profile_artifact = _convert_llvm_raw_profile_to_indexed(
+                ctx,
+                "fdo",
+                fdo_inputs,
+                llvm_profdata,
+                all_files,
+                zipper,
+                cc_toolchain_config_info,
+            )
+            cs_profile_artifact = _convert_llvm_raw_profile_to_indexed(
+                ctx,
+                "csfdo",
+                cs_fdo_input,
+                llvm_profdata,
+                all_files,
+                zipper,
+                cc_toolchain_config_info,
+            )
+            profile_artifact = _merge_llvm_profiles(
+                ctx,
+                "mergedfdo",
+                llvm_profdata,
+                all_files,
+                non_cs_profile_artifact,
+                cs_profile_artifact,
+                "MergedCS.profdata",
+            )
+
+        branch_fdo_profile = struct(
+            branch_fdo_mode = branch_fdo_mode,
+            profile_artifact = profile_artifact,
+        )
+
+    prefetch_hints_artifact = None
+    if _fdo_prefetch_hints:
+        prefetch_hints_artifact = _symlink_input(
+            ctx,
+            "fdo",
+            _fdo_prefetch_hints[FdoPrefetchHintsInfo],
+            "Symlinking LLVM Cache Prefetch Hints Profile %{input}",
+        )
+
+    memprof_profile_artifact = _get_mem_prof_profile_artifact(zipper, mem_prof_profile, ctx)
+
+    return struct(
+        branch_fdo_profile = branch_fdo_profile,
+        prefetch_hints_artifact = prefetch_hints_artifact,
+        propeller_optimize_info = propeller_optimize_info,
+        memprof_profile_artifact = memprof_profile_artifact,
+        proto_profile_artifact = proto_profile_artifact,
+    )
+
+def _convert_llvm_raw_profile_to_indexed(
+        ctx,
+        name_prefix,
+        fdo_inputs,
+        llvm_profdata,
+        all_files,
+        zipper,
+        cc_toolchain_config_info):
+    """This function checks the input profile format and converts it to the indexed format (.profdata) if necessary."""
+    basename = _basename(fdo_inputs)
+    if basename.endswith(".profdata"):
+        return _symlink_input(ctx, name_prefix, fdo_inputs, "Symlinking LLVM Profile %{input}")
+
+    if basename.endswith(".zip"):
+        if not zipper:
+            fail("Zipped profiles are not supported with platforms/toolchains before toolchain-transitions are implemented.")
+
+        zip_profile_artifact = _symlink_input(ctx, name_prefix, fdo_inputs, "Symlinking LLVM ZIP Profile %{input}")
+
+        # TODO(b/333997009): find a way to avoid hard-coding cpu architecture here
+        cpu = cc_toolchain_config_info.target_cpu()
+        if "k8" == cpu:
+            raw_profile_file_name = name_prefix + "/" + ctx.label.name + "/" + "fdocontrolz_profile.profraw"
+        else:
+            raw_profile_file_name = name_prefix + "/" + ctx.label.name + "/" + "fdocontrolz_profile-" + cpu + ".profraw"
+
+        raw_profile_artifact = ctx.actions.declare_file(raw_profile_file_name)
+
+        # We invoke different binaries depending on whether the unzip_fdo tool
+        # is available. When it isn't, unzip_fdo is aliased to the generic
+        # zipper tool, which takes different command-line arguments.
+
+        args = ctx.actions.args()
+        if zipper.path.endswith("unzip_fdo"):
+            args.add("--profile_zip", zip_profile_artifact)
+            args.add("--cpu", cpu)
+            args.add("--output_file", raw_profile_artifact)
+        else:
+            args.add("xf", zip_profile_artifact)
+            args.add("-d", raw_profile_artifact.dirname)
+        ctx.actions.run(
+            mnemonic = "LLVMUnzipProfileAction",
+            executable = zipper,
+            arguments = [args],
+            inputs = [zip_profile_artifact],
+            outputs = [raw_profile_artifact],
+            progress_message = "LLVMUnzipProfileAction: Generating %{output}",
+        )
+    else:  # .profraw
+        raw_profile_artifact = _symlink_input(ctx, name_prefix, fdo_inputs, "Symlinking LLVM Raw Profile %{input}")
+
+    if not llvm_profdata:
+        fail("llvm-profdata not available with this crosstool, needed for profile conversion")
+
+    name = name_prefix + "/" + ctx.label.name + "/" + paths.replace_extension(basename, ".profdata")
+    profile_artifact = ctx.actions.declare_file(name)
+    ctx.actions.run(
+        mnemonic = "LLVMProfDataAction",
+        executable = llvm_profdata,
+        tools = [all_files],
+        arguments = [ctx.actions.args().add("merge").add("-o").add(profile_artifact).add(raw_profile_artifact)],
+        inputs = [raw_profile_artifact],
+        outputs = [profile_artifact],
+        use_default_shell_env = True,
+        progress_message = "LLVMProfDataAction: Generating %{output}",
+    )
+
+    return profile_artifact
+
+def _merge_llvm_profiles(
+        ctx,
+        name_prefix,
+        llvm_profdata,
+        all_files,
+        profile1,
+        profile2,
+        merged_output_name):
+    """This function merges profile1 and profile2 and generates merged_output."""
+    profile_artifact = ctx.actions.declare_file(name_prefix + "/" + ctx.label.name + "/" + merged_output_name)
+
+    # Merge LLVM profiles.
+    ctx.actions.run(
+        mnemonic = "LLVMProfDataMergeAction",
+        executable = llvm_profdata,
+        tools = [all_files],
+        arguments = [ctx.actions.args().add("merge").add("-o").add(profile_artifact).add(profile1).add(profile2)],
+        inputs = [profile1, profile2],
+        outputs = [profile_artifact],
+        use_default_shell_env = True,
+        progress_message = "LLVMProfDataAction: Generating %{output}",
+    )
+    return profile_artifact
+
+def _basename(inputs):
+    if getattr(inputs, "artifact", None):
+        return inputs.artifact.basename
+    else:
+        return paths.basename(inputs.absolute_path)
+
+def _symlink_input(ctx, name_prefix, fdo_inputs, progress_message, basename = None):
+    return _symlink_to(
+        ctx,
+        name_prefix,
+        progress_message,
+        getattr(fdo_inputs, "artifact", None),
+        getattr(fdo_inputs, "absolute_path", None),
+        basename,
+    )
+
+def _symlink_to(ctx, name_prefix, progress_message, artifact = None, absolute_path = None, basename = None):
+    """Symlinks either an absolute path or file to a unique location."""
+    if artifact:
+        if not basename:
+            basename = artifact.basename
+        name = name_prefix + "/" + ctx.label.name + "/" + basename
+        output = ctx.actions.declare_file(name)
+        ctx.actions.symlink(
+            output = output,
+            target_file = artifact,
+            progress_message = progress_message,
+        )
+        return output
+    elif absolute_path:
+        if not basename:
+            basename = paths.basename(absolute_path)
+        name = name_prefix + "/" + ctx.label.name + "/" + basename
+        output = ctx.actions.declare_file(name)
+        cc_common.absolute_symlink(
+            ctx = ctx,
+            output = output,
+            target_path = absolute_path,
+            progress_message = progress_message,
+        )
+        return output
+    else:
+        return None
+
+def _get_mem_prof_profile_artifact(zipper, memprof_profile, ctx):
+    """This function symlinks the memprof profile (after unzipping as needed)."""
+    if not memprof_profile:
+        return None
+
+    basename = _basename(memprof_profile)
+
+    # If the profile file is already in the desired format, symlink to it and return.
+    if basename.endswith(".profdata"):
+        return _symlink_input(ctx, "memprof", memprof_profile, "Symlinking MemProf Profile %{input}", basename = "memprof.profdata")
+
+    if not basename.endswith(".zip"):
+        fail("Expected zipped memprof profile.")
+
+    if not zipper:
+        fail("Zipped profiles are not supported with platforms/toolchains before " +
+             "toolchain-transitions are implemented.")
+
+    zip_profile_artifact = _symlink_input(ctx, "memprof", memprof_profile, "Symlinking MemProf ZIP Profile %{input}")
+
+    profile_artifact = ctx.actions.declare_file("memprof/" + ctx.label.name + "/memprof.profdata")
+
+    # We invoke different binaries depending on whether the unzip_fdo tool
+    # is available. When it isn't, unzip_fdo is aliased to the generic
+    # zipper tool, which takes different command-line arguments.
+    args = ctx.actions.args()
+    if zipper.path.endswith("unzip_fdo"):
+        args.add("--profile_zip", zip_profile_artifact)
+        args.add("--memprof")
+        args.add("--output_file", profile_artifact)
+    else:
+        args.add("xf", zip_profile_artifact)
+        args.add("-d", profile_artifact.dirname)
+    ctx.actions.run(
+        mnemonic = "LLVMUnzipProfileAction",
+        executable = zipper,
+        arguments = [args],
+        inputs = [zip_profile_artifact],
+        outputs = [profile_artifact],
+        progress_message = "MemProfUnzipProfileAction: Generating %{output}",
+    )
+    return profile_artifact
+
+create_fdo_context = subrule(
+    implementation = _create_fdo_context,
+    fragments = ["cpp"],
+    attrs = {
+        "_fdo_optimize": attr.label(
+            default = configuration_field(fragment = "cpp", name = "fdo_optimize"),
+            allow_files = True,
+            providers = [[DefaultInfo], [FdoProfileInfo]],
+        ),
+        "_xfdo_profile": attr.label(
+            default = configuration_field(fragment = "cpp", name = "xbinary_fdo"),
+            providers = [FdoProfileInfo],
+        ),
+        "_fdo_profile": attr.label(
+            default = configuration_field(fragment = "cpp", name = "fdo_profile"),
+            providers = [FdoProfileInfo],
+        ),
+        "_csfdo_profile": attr.label(
+            default = configuration_field(fragment = "cpp", name = "cs_fdo_profile"),
+            providers = [FdoProfileInfo],
+        ),
+        "_fdo_prefetch_hints": attr.label(
+            default = configuration_field(fragment = "cpp", name = "fdo_prefetch_hints"),
+            providers = [FdoPrefetchHintsInfo],
+        ),
+        "_propeller_optimize": attr.label(
+            default = configuration_field(fragment = "cpp", name = "propeller_optimize"),
+            providers = [PropellerOptimizeInfo],
+        ),
+        "_memprof_profile": attr.label(
+            default = configuration_field(fragment = "cpp", name = "memprof_profile"),
+            providers = [MemProfProfileInfo],
+        ),
+        "_proto_profile": attr.label(
+            default = configuration_field(fragment = "cpp", name = "proto_profile_path"),
+            allow_single_file = True,
+        ),
+    },  # buildifier: disable=unsorted-dict-items
+)
diff --git a/cc/private/rules_impl/fdo/fdo_prefetch_hints.bzl b/cc/private/rules_impl/fdo/fdo_prefetch_hints.bzl
new file mode 100644
index 0000000..6696b7b
--- /dev/null
+++ b/cc/private/rules_impl/fdo/fdo_prefetch_hints.bzl
@@ -0,0 +1,47 @@
+# Copyright 2024 The Bazel Authors. All rights reserved.
+#
+# 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.
+
+"""Starlark implementation of fdo_prefetch_hints rule."""
+
+FdoPrefetchHintsInfo = provider(
+    doc = "Contains the profile used for prefetch hints",
+    fields = ["artifact"],
+)
+
+def _impl(ctx):
+    return FdoPrefetchHintsInfo(artifact = ctx.file.profile)
+
+fdo_prefetch_hints = rule(
+    implementation = _impl,
+    doc = """
+<p>Represents an FDO prefetch hints profile that is either in the workspace.
+Examples:</p>
+
+<pre><code class="lang-starlark">
+fdo_prefetch_hints(
+    name = "hints",
+    profile = "//path/to/hints:profile.afdo",
+)
+</code></pre>""",
+    attrs = {
+        "profile": attr.label(
+            allow_single_file = [".afdo"],
+            mandatory = True,
+            doc = """
+Label of the hints profile. The hints file has the .afdo extension
+The label can also point to an fdo_absolute_path_profile rule.""",
+        ),
+    },
+    provides = [FdoPrefetchHintsInfo],
+)
diff --git a/cc/private/rules_impl/fdo/fdo_profile.bzl b/cc/private/rules_impl/fdo/fdo_profile.bzl
new file mode 100644
index 0000000..3d0ccb8
--- /dev/null
+++ b/cc/private/rules_impl/fdo/fdo_profile.bzl
@@ -0,0 +1,61 @@
+# Copyright 2024 The Bazel Authors. All rights reserved.
+#
+# 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.
+
+"""Starlark implementation of fdo_profile rule."""
+
+FdoProfileInfo = provider(
+    doc = "Contains the profile used for FDO",
+    fields = ["artifact", "absolute_path", "proto_profile_artifact", "memprof_artifact"],
+)
+
+def _impl(ctx):
+    return FdoProfileInfo(
+        artifact = ctx.file.profile,
+        proto_profile_artifact = ctx.file.proto_profile,
+        memprof_artifact = ctx.file.memprof_profile,
+    )
+
+fdo_profile = rule(
+    implementation = _impl,
+    doc = """
+
+<p>Represents an FDO profile that is in the workspace.
+Example:</p>
+
+<pre><code class="lang-starlark">
+fdo_profile(
+    name = "fdo",
+    profile = "//path/to/fdo:profile.zip",
+)
+</code></pre>""",
+    attrs = {
+        "profile": attr.label(
+            allow_single_file = [".profraw", ".profdata", ".zip", ".afdo", ".xfdo"],
+            mandatory = True,
+            doc = """
+Label of the FDO profile or a rule which generates it. The FDO file can have one of the
+following extensions: .profraw for unindexed LLVM profile, .profdata for indexed LLVM
+profile, .zip that holds an LLVM profraw profile, .afdo for AutoFDO profile, .xfdo for
+XBinary profile. The label can also point to an fdo_absolute_path_profile rule.""",
+        ),
+        "proto_profile": attr.label(allow_single_file = True, doc = """
+Label of the protobuf profile."""),
+        "memprof_profile": attr.label(allow_single_file = [".profdata", ".zip"], doc = """
+Label of the MemProf profile. The profile is expected to have
+either a .profdata extension (for an indexed/symbolized memprof
+profile), or a .zip extension for a zipfile containing a memprof.profdata
+file."""),
+    },  # buildifier: disable=unsorted-dict-items
+    provides = [FdoProfileInfo],
+)
diff --git a/cc/private/rules_impl/fdo/memprof_profile.bzl b/cc/private/rules_impl/fdo/memprof_profile.bzl
new file mode 100644
index 0000000..a2489f0
--- /dev/null
+++ b/cc/private/rules_impl/fdo/memprof_profile.bzl
@@ -0,0 +1,51 @@
+# Copyright 2024 The Bazel Authors. All rights reserved.
+#
+# 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.
+
+"""Starlark implementation of memprof_profile rule."""
+
+MemProfProfileInfo = provider(
+    doc = "Contains the memprof profile",
+    fields = ["artifact"],
+)
+
+def _impl(ctx):
+    return MemProfProfileInfo(artifact = ctx.file.profile)
+
+memprof_profile = rule(
+    implementation = _impl,
+    doc = """
+<p>Represents a MEMPROF profile that is in the workspace.
+Example:</p>
+
+<pre><code class="lang-starlark">
+memprof_profile(
+    name = "memprof",
+    profile = "//path/to/memprof:profile.afdo",
+)
+
+</code></pre>""",
+    attrs = {
+        "profile": attr.label(
+            allow_single_file = [".profdata", ".zip"],
+            mandatory = True,
+            doc = """
+Label of the MEMPROF profile. The profile is expected to have
+either a .profdata extension (for an indexed/symbolized memprof
+profile), or a .zip extension for a zipfile containing a memprof.profdata
+file.
+The label can also point to an fdo_absolute_path_profile rule.""",
+        ),
+    },
+    provides = [MemProfProfileInfo],
+)
diff --git a/cc/private/rules_impl/fdo/propeller_optimize.bzl b/cc/private/rules_impl/fdo/propeller_optimize.bzl
new file mode 100644
index 0000000..3e3e8ce
--- /dev/null
+++ b/cc/private/rules_impl/fdo/propeller_optimize.bzl
@@ -0,0 +1,58 @@
+# Copyright 2024 The Bazel Authors. All rights reserved.
+#
+# 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.
+
+"""Starlark implementation of propeller_optimize rule."""
+
+PropellerOptimizeInfo = provider(
+    doc = "Contains the profile used for propeller",
+    fields = ["cc_profile", "ld_profile"],
+)
+
+def _impl(ctx):
+    return PropellerOptimizeInfo(
+        cc_profile = ctx.file.cc_profile,
+        ld_profile = ctx.file.ld_profile,
+    )
+
+propeller_optimize = rule(
+    implementation = _impl,
+    doc = """
+<p>Represents a Propeller optimization profile in the workspace.
+Example:</p>
+
+<pre><code class="lang-starlark">
+propeller_optimize(
+    name = "layout",
+    cc_profile = "//path:cc_profile.txt",
+    ld_profile = "//path:ld_profile.txt"
+)
+</code></pre>""",
+    attrs = {
+        "cc_profile": attr.label(
+            allow_single_file = [".txt"],
+            mandatory = True,
+            doc = """
+Label of the profile passed to the various compile actions.  This file has
+the .txt extension.""",
+        ),
+        "ld_profile": attr.label(
+            allow_single_file = [".txt"],
+            mandatory = True,
+            doc = """
+Label of the profile passed to the link action.  This file has
+the .txt extension.""",
+        ),
+    },
+    provides = [PropellerOptimizeInfo],
+)
diff --git a/cc/toolchains/fdo_prefetch_hints.bzl b/cc/toolchains/fdo_prefetch_hints.bzl
index 5bc5bed..7124d4d 100644
--- a/cc/toolchains/fdo_prefetch_hints.bzl
+++ b/cc/toolchains/fdo_prefetch_hints.bzl
@@ -13,4 +13,6 @@
 # limitations under the License.
 """fdo_prefetch_hints rule"""
 
-fdo_prefetch_hints = native.fdo_prefetch_hints  # buildifier: disable=native-cc-fdo-prefetch-hints
+load("@cc_compatibility_proxy//:proxy.bzl", _fdo_prefetch_hints = "fdo_prefetch_hints")
+
+fdo_prefetch_hints = _fdo_prefetch_hints
diff --git a/cc/toolchains/fdo_profile.bzl b/cc/toolchains/fdo_profile.bzl
index 1e23303..1046189 100644
--- a/cc/toolchains/fdo_profile.bzl
+++ b/cc/toolchains/fdo_profile.bzl
@@ -13,4 +13,6 @@
 # limitations under the License.
 """fdo_profile rule"""
 
-fdo_profile = native.fdo_profile  # buildifier: disable=native-cc-fdo-profile
+load("@cc_compatibility_proxy//:proxy.bzl", _fdo_profile = "fdo_profile")
+
+fdo_profile = _fdo_profile
diff --git a/cc/toolchains/memprof_profile.bzl b/cc/toolchains/memprof_profile.bzl
index 1e0014b..302e698 100644
--- a/cc/toolchains/memprof_profile.bzl
+++ b/cc/toolchains/memprof_profile.bzl
@@ -13,4 +13,6 @@
 # limitations under the License.
 """memprof_profile rule"""
 
-memprof_profile = native.memprof_profile
+load("@cc_compatibility_proxy//:proxy.bzl", _memprof_profile = "memprof_profile")
+
+memprof_profile = _memprof_profile
diff --git a/cc/toolchains/propeller_optimize.bzl b/cc/toolchains/propeller_optimize.bzl
index 153388f..fd8549c 100644
--- a/cc/toolchains/propeller_optimize.bzl
+++ b/cc/toolchains/propeller_optimize.bzl
@@ -13,4 +13,6 @@
 # limitations under the License.
 """propeller_optimize rule"""
 
-propeller_optimize = native.propeller_optimize
+load("@cc_compatibility_proxy//:proxy.bzl", _propeller_optimize = "propeller_optimize")
+
+propeller_optimize = _propeller_optimize