Build support for passing a software cache prefetching hints file.

This flag assumes llvm supports an experimental 'prefetch-hints-flag' flag. The Bazel support simply plumbs the provided file through to the compiler.

The flag is independent from the other fdo_ flags, and may be applied in any combination with them.

RELNOTES: Introduce build support for providing cache prefetch hints.
PiperOrigin-RevId: 196916547
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/CcRules.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/CcRules.java
index b21b211..be7f016 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/CcRules.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/CcRules.java
@@ -34,6 +34,7 @@
 import com.google.devtools.build.lib.rules.cpp.CppOptions;
 import com.google.devtools.build.lib.rules.cpp.CppRuleClasses.CcIncludeScanningRule;
 import com.google.devtools.build.lib.rules.cpp.CpuTransformer;
+import com.google.devtools.build.lib.rules.cpp.FdoPrefetchHintsRule;
 import com.google.devtools.build.lib.rules.cpp.FdoProfileRule;
 import com.google.devtools.build.lib.rules.platform.PlatformRules;
 
@@ -73,6 +74,7 @@
     builder.addRuleDefinition(new BazelCcImportRule());
     builder.addRuleDefinition(new CcIncludeScanningRule());
     builder.addRuleDefinition(new FdoProfileRule());
+    builder.addRuleDefinition(new FdoPrefetchHintsRule());
   }
 
   @Override
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 ebf00f8..21ccb4a 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
@@ -843,6 +843,9 @@
         allFeatures.add(CppRuleClasses.ENABLE_AFDO_THINLTO);
       }
     }
+    if (cppConfiguration.getFdoPrefetchHintsLabel() != null) {
+      allRequestedFeaturesBuilder.add(CppRuleClasses.FDO_PREFETCH_HINTS);
+    }
     if (cppConfiguration.isLipoOptimizationOrInstrumentation()) {
       // Map LIPO to ThinLTO for LLVM builds.
       if (toolchain.isLLVMCompiler() && fdoMode != FdoMode.OFF) {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchain.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchain.java
index 5209e1c..5dc9e75 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchain.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchain.java
@@ -182,6 +182,29 @@
     return pathPrefix.getRelative(path);
   }
 
+  private Artifact getPrefetchHintsArtifact(
+      FdoInputFile prefetchHintsFile, RuleContext ruleContext) {
+    Artifact prefetchHintsArtifact = null;
+    if (prefetchHintsFile != null) {
+      prefetchHintsArtifact = prefetchHintsFile.getArtifact();
+      if (prefetchHintsArtifact == null) {
+        prefetchHintsArtifact =
+            ruleContext.getUniqueDirectoryArtifact(
+                "fdo",
+                prefetchHintsFile.getAbsolutePath().getBaseName(),
+                ruleContext.getBinOrGenfilesDirectory());
+        ruleContext.registerAction(
+            new SymlinkAction(
+                ruleContext.getActionOwner(),
+                PathFragment.create(prefetchHintsFile.getAbsolutePath().getPathString()),
+                prefetchHintsArtifact,
+                "Symlinking LLVM Cache Prefetch Hints Profile "
+                    + prefetchHintsFile.getAbsolutePath().getPathString()));
+      }
+    }
+    return prefetchHintsArtifact;
+  }
+
   /*
    * This function checks the format of the input profile data and converts it to
    * the indexed format (.profdata) if necessary.
@@ -317,7 +340,14 @@
     CppToolchainInfo toolchainInfo = getCppToolchainInfo(ruleContext, cppConfiguration);
 
     PathFragment fdoZip = null;
+    FdoInputFile prefetchHints = null;
     if (configuration.getCompilationMode() == CompilationMode.OPT) {
+      if (cppConfiguration.getFdoPrefetchHintsLabel() != null) {
+        FdoPrefetchHintsProvider provider =
+            ruleContext.getPrerequisite(
+                ":fdo_prefetch_hints", Mode.TARGET, FdoPrefetchHintsProvider.PROVIDER);
+        prefetchHints = provider.getInputFile();
+      }
       if (cppConfiguration.getFdoPath() != null) {
         fdoZip = cppConfiguration.getFdoPath();
       } else if (cppConfiguration.getFdoOptimizeLabel() != null) {
@@ -342,10 +372,11 @@
         FdoProfileProvider fdoProvider =
             ruleContext.getPrerequisite(
                 CcToolchainRule.FDO_PROFILE_ATTR, Mode.TARGET, FdoProfileProvider.PROVIDER);
+        FdoInputFile inputFile = fdoProvider.getInputFile();
         fdoZip =
-            fdoProvider.getFdoPath() != null
-                ? fdoProvider.getFdoPath()
-                : fdoProvider.getProfileArtifact().getPath().asFragment();
+            inputFile.getAbsolutePath() != null
+                ? inputFile.getAbsolutePath()
+                : inputFile.getArtifact().getPath().asFragment();
       }
     }
 
@@ -373,7 +404,11 @@
 
     SkyKey fdoKey =
         FdoSupportValue.key(
-            cppConfiguration.getLipoMode(), fdoZip, cppConfiguration.getFdoInstrument(), fdoMode);
+            cppConfiguration.getLipoMode(),
+            fdoZip,
+            prefetchHints,
+            cppConfiguration.getFdoInstrument(),
+            fdoMode);
 
     SkyFunction.Environment skyframeEnv = ruleContext.getAnalysisEnvironment().getSkyframeEnv();
     FdoSupportValue fdoSupport;
@@ -527,6 +562,8 @@
         return null;
       }
     }
+    Artifact hintsArtifact = getPrefetchHintsArtifact(prefetchHints, ruleContext);
+    ProfileArtifacts profileArtifacts = new ProfileArtifacts(profileArtifact, hintsArtifact);
 
     reportInvalidOptions(ruleContext, toolchainInfo);
 
@@ -578,7 +615,7 @@
             .addNativeDeclaredProvider(ccProvider)
             .addNativeDeclaredProvider(templateVariableInfo)
             .addProvider(
-                fdoSupport.getFdoSupport().createFdoSupportProvider(ruleContext, profileArtifact))
+                fdoSupport.getFdoSupport().createFdoSupportProvider(ruleContext, profileArtifacts))
             .setFilesToBuild(crosstool)
             .addProvider(RunfilesProvider.simple(Runfiles.EMPTY));
 
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 a7bf17b..bdba988 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
@@ -85,6 +85,12 @@
           null,
           (rule, attributes, cppConfig) -> cppConfig.getFdoProfileLabel());
 
+  private static final LabelLateBoundDefault<?> FDO_PREFETCH_HINTS =
+      LabelLateBoundDefault.fromTargetConfiguration(
+      CppConfiguration.class,
+      null,
+      (rule, attributes, cppConfig) -> cppConfig.getFdoPrefetchHintsLabel());
+
   /**
    * Returns true if zipper should be loaded. We load the zipper executable if FDO optimization is
    * enabled through --fdo_optimize or --fdo_profile
@@ -179,6 +185,11 @@
                 .mandatoryProviders(ImmutableList.of(FdoProfileProvider.PROVIDER.id()))
                 .value(FDO_PROFILE_VALUE))
         .add(
+            attr(":fdo_prefetch_hints", LABEL)
+                .allowedRuleClasses("fdo_prefetch_hints")
+                .mandatoryProviders(ImmutableList.of(FdoPrefetchHintsProvider.PROVIDER.id()))
+                .value(FDO_PREFETCH_HINTS))
+        .add(
             attr(TransitiveLipoInfoProvider.LIPO_CONTEXT_COLLECTOR, LABEL)
                 .cfg(LipoContextCollectorTransition.INSTANCE)
                 .value(CppRuleClasses.LIPO_CONTEXT_COLLECTOR)
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 6b6247e..b0938b8 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
@@ -94,6 +94,8 @@
   FDO_INSTRUMENT_PATH("fdo_instrument_path"),
   /** Path to the fdo profile artifact */
   FDO_PROFILE_PATH("fdo_profile_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. */
   INCLUDES("includes");
 
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 f1ec0a1..efd741a 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
@@ -31,6 +31,8 @@
     MAC
   }
 
+  // Note:  these configs won't be added to the crosstools that defines no_legacy_features feature
+  // (e.g. ndk, apple, enclave crosstools). Those need to be modified separately.
   public static String getCppActionConfigs(
       CppPlatform platform,
       ImmutableSet<String> existingFeatureNames,
@@ -402,6 +404,21 @@
                     "  }",
                     "}"),
                 ifTrue(
+                    !existingFeatureNames.contains(CppRuleClasses.FDO_PREFETCH_HINTS),
+                    "feature {",
+                    "  name: 'fdo_prefetch_hints'",
+                    "  flag_set {",
+                    "    action: 'c-compile'",
+                    "    action: 'c++-compile'",
+                    "    action: 'lto-backend'",
+                    "    expand_if_all_available: 'fdo_prefetch_hints_path'",
+                    "    flag_group {",
+                    "      flag: '-Xclang-only=-mllvm'",
+                    "      flag: '-Xclang-only=-prefetch-hints-file=%{fdo_prefetch_hints_path}'",
+                    "    }",
+                    "  }",
+                    "}"),
+                ifTrue(
                     !existingFeatureNames.contains(CppRuleClasses.AUTOFDO),
                     "feature {",
                     "  name: 'autofdo'",
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 4761c2b..568f418 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
@@ -1333,6 +1333,10 @@
     return fdoOptimizeLabel;
   }
 
+  public Label getFdoPrefetchHintsLabel() {
+    return cppOptions.getFdoPrefetchHintsLabel();
+  }
+
   public Label getFdoProfileLabel() {
     return cppOptions.getFdoProfileLabel();
   }
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 3192605..713dd2b 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
@@ -480,6 +480,24 @@
     return enableLipoSettings() ? fdoOptimizeForBuild : null;
   }
 
+  @Option(
+    name = "fdo_prefetch_hints",
+    defaultValue = "null",
+    converter = LabelConverter.class,
+    category = "flags",
+    documentationCategory = OptionDocumentationCategory.OUTPUT_PARAMETERS,
+    effectTags = {OptionEffectTag.AFFECTS_OUTPUTS},
+    help = "Use cache prefetch hints."
+  )
+  public Label fdoPrefetchHintsLabel;
+
+  /**
+   * Returns the --fdo_prefetch_hints value.
+   */
+  public Label getFdoPrefetchHintsLabel() {
+    return fdoPrefetchHintsLabel;
+  }
+
   /**
    * Returns the --autofdo_lipo_data value for this configuration. This is false except for data
    * configurations under LIPO builds.
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 da10c03..2862cbc 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
@@ -372,6 +372,11 @@
   public static final String FDO_OPTIMIZE = "fdo_optimize";
 
   /**
+   * A string constant for the cache prefetch hints feature.
+   */
+  public static final String FDO_PREFETCH_HINTS = "fdo_prefetch_hints";
+
+  /**
    * A string constant for the autofdo feature.
    */
   public static final String AUTOFDO = "autofdo";
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoInputFile.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoInputFile.java
new file mode 100644
index 0000000..d964403
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoInputFile.java
@@ -0,0 +1,103 @@
+// Copyright 2018 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.
+package com.google.devtools.build.lib.rules.cpp;
+
+import com.google.common.base.Preconditions;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode;
+import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import java.util.Objects;
+
+/**
+ * Value object reused by fdo configurations that may be either an artifact or a path.
+ */
+@Immutable
+public final class FdoInputFile {
+
+  private final Artifact artifact;
+  private final PathFragment absolutePath;
+
+  private FdoInputFile(Artifact artifact, PathFragment absolutePath) {
+    Preconditions.checkArgument((artifact == null) != (absolutePath == null));
+    Preconditions.checkArgument(absolutePath == null || absolutePath.isAbsolute());
+    this.artifact = artifact;
+    this.absolutePath = absolutePath;
+  }
+
+  public Artifact getArtifact() {
+    return artifact;
+  }
+
+  public PathFragment getAbsolutePath() {
+    return absolutePath;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+
+    if (!(o instanceof FdoInputFile)) {
+      return false;
+    }
+
+    FdoInputFile that = (FdoInputFile) o;
+    return Objects.equals(this.artifact, that.artifact)
+        && Objects.equals(this.absolutePath, that.absolutePath);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(artifact, absolutePath);
+  }
+
+  public static FdoInputFile create(RuleContext ruleContext) {
+
+    boolean isLabel = ruleContext.attributes().isAttributeValueExplicitlySpecified("profile");
+    boolean isAbsolutePath =
+        ruleContext.attributes().isAttributeValueExplicitlySpecified("absolute_path_profile");
+
+    if (isLabel == isAbsolutePath) {
+      ruleContext.ruleError("exactly one of profile and absolute_path_profile should be specified");
+      return null;
+    }
+
+    if (isLabel) {
+      Artifact artifact = ruleContext.getPrerequisiteArtifact("profile", Mode.TARGET);
+      if (!artifact.isSourceArtifact()) {
+        ruleContext.attributeError("profile", " the target is not an input file");
+      }
+      return new FdoInputFile(artifact, null);
+    } else {
+      if (!ruleContext.getFragment(CppConfiguration.class).isFdoAbsolutePathEnabled()) {
+        ruleContext.attributeError(
+            "absolute_path_profile",
+            "this attribute cannot be used when --enable_fdo_profile_absolute_path is false");
+        return null;
+      }
+      String pathString = ruleContext.getExpander().expand("absolute_path_profile");
+      PathFragment absolutePath = PathFragment.create(pathString);
+      if (!absolutePath.isAbsolute()) {
+        ruleContext.attributeError(
+            "absolute_path_profile",
+            String.format("%s is not an absolute path", absolutePath.getPathString()));
+        return null;
+      }
+      return new FdoInputFile(null, absolutePath);
+    }
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoPrefetchHints.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoPrefetchHints.java
new file mode 100644
index 0000000..7dcff8f
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoPrefetchHints.java
@@ -0,0 +1,42 @@
+// Copyright 2018 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.
+package com.google.devtools.build.lib.rules.cpp;
+
+import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
+import com.google.devtools.build.lib.analysis.RuleConfiguredTargetFactory;
+import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.analysis.Runfiles;
+import com.google.devtools.build.lib.analysis.RunfilesProvider;
+import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+
+/** Implementation for the {@code fdo_prefetch_hints} rule. */
+@Immutable
+public final class FdoPrefetchHints implements RuleConfiguredTargetFactory {
+  @Override
+  public ConfiguredTarget create(RuleContext ruleContext)
+      throws RuleErrorException, ActionConflictException {
+
+    FdoInputFile inputFile = FdoInputFile.create(ruleContext);
+    if (ruleContext.hasErrors()) {
+      return null;
+    }
+
+    return new RuleConfiguredTargetBuilder(ruleContext)
+        .addNativeDeclaredProvider(new FdoPrefetchHintsProvider(inputFile))
+        .addProvider(RunfilesProvider.simple(Runfiles.EMPTY))
+        .build();
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoPrefetchHintsProvider.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoPrefetchHintsProvider.java
new file mode 100644
index 0000000..e7a7ebf
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoPrefetchHintsProvider.java
@@ -0,0 +1,37 @@
+// Copyright 2018 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.
+package com.google.devtools.build.lib.rules.cpp;
+
+import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.packages.NativeInfo;
+import com.google.devtools.build.lib.packages.NativeProvider;
+
+/** Provider that contains the profile used for prefetch hints. */
+@Immutable
+public final class FdoPrefetchHintsProvider extends NativeInfo {
+  public static final NativeProvider<FdoPrefetchHintsProvider> PROVIDER =
+      new NativeProvider<FdoPrefetchHintsProvider>(
+          FdoPrefetchHintsProvider.class, "FdoPrefetchHintsInfo") {};
+
+  private final FdoInputFile fdoInputFile;
+
+  public FdoPrefetchHintsProvider(FdoInputFile fdoInputFile) {
+    super(PROVIDER);
+    this.fdoInputFile = fdoInputFile;
+  }
+
+  public FdoInputFile getInputFile() {
+    return fdoInputFile;
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoPrefetchHintsRule.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoPrefetchHintsRule.java
new file mode 100644
index 0000000..c706474
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoPrefetchHintsRule.java
@@ -0,0 +1,78 @@
+// Copyright 2018 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.
+package com.google.devtools.build.lib.rules.cpp;
+
+import static com.google.devtools.build.lib.packages.Attribute.attr;
+import static com.google.devtools.build.lib.packages.BuildType.LABEL;
+
+import com.google.devtools.build.lib.analysis.BaseRuleClasses;
+import com.google.devtools.build.lib.analysis.RuleDefinition;
+import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
+import com.google.devtools.build.lib.packages.RuleClass;
+import com.google.devtools.build.lib.syntax.Type;
+import com.google.devtools.build.lib.util.FileType;
+import com.google.devtools.build.lib.util.FileTypeSet;
+
+/** {@code fdo_prefetch_hints} rule class. */
+public final class FdoPrefetchHintsRule implements RuleDefinition {
+  @Override
+  public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) {
+    return builder
+        .requiresConfigurationFragments(CppConfiguration.class)
+        /* <!-- #BLAZE_RULE(fdo_prefetch_hints).ATTRIBUTE(profile) -->
+        Label of the hints profile. The hints file has the .afdo extension
+        The label can also point to an fdo_absolute_path_profile rule.
+        <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+        .add(
+            attr("profile", LABEL)
+                .allowedFileTypes(
+                    FileTypeSet.of(
+                        FileType.of(".afdo")))
+                .singleArtifact())
+        /* <!-- #BLAZE_RULE(fdo_profile).ATTRIBUTE(absolute_path_profile) -->
+        Absolute path to the FDO profile. The FDO file may only have the .afdo extension.
+        <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+        .add(attr("absolute_path_profile", Type.STRING))
+        .advertiseProvider(FdoPrefetchHintsProvider.class)
+        .build();
+  }
+
+  @Override
+  public Metadata getMetadata() {
+    return RuleDefinition.Metadata.builder()
+        .name("fdo_prefetch_hints")
+        .ancestors(BaseRuleClasses.BaseRule.class)
+        .factoryClass(FdoPrefetchHints.class)
+        .build();
+  }
+}
+
+/*<!-- #BLAZE_RULE (NAME = fdo_prefetch_hints, TYPE = LIBRARY, FAMILY = Cpp) -->
+
+<p>Represents an FDO prefetch hints profile that is either in the workspace or at a specified
+absolute path.
+Examples:</p>
+
+<pre class="code">
+fdo_prefetch_hints(
+    name = "hints",
+    profile = "//path/to/hints:profile.afdo",
+)
+
+fdo_profile(
+  name = "hints_abs",
+  absolute_path_profile = "/absolute/path/profile.afdo",
+)
+</pre>
+<!-- #END_BLAZE_RULE -->*/
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoProfile.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoProfile.java
index 2e4131f..0fbfe85 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoProfile.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoProfile.java
@@ -13,7 +13,6 @@
 // limitations under the License.
 package com.google.devtools.build.lib.rules.cpp;
 
-import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
 import com.google.devtools.build.lib.analysis.ConfiguredTarget;
 import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
@@ -21,9 +20,7 @@
 import com.google.devtools.build.lib.analysis.RuleContext;
 import com.google.devtools.build.lib.analysis.Runfiles;
 import com.google.devtools.build.lib.analysis.RunfilesProvider;
-import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
-import com.google.devtools.build.lib.vfs.PathFragment;
 
 /** Implementation for the {@code fdo_profile} rule. */
 @Immutable
@@ -32,42 +29,13 @@
   public ConfiguredTarget create(RuleContext ruleContext)
       throws RuleErrorException, ActionConflictException {
 
-    FdoProfileProvider provider;
-
-    boolean isLabel = ruleContext.attributes().isAttributeValueExplicitlySpecified("profile");
-    boolean isAbsolutePath =
-        ruleContext.attributes().isAttributeValueExplicitlySpecified("absolute_path_profile");
-
-    if (isLabel == isAbsolutePath) {
-      ruleContext.ruleError("exactly one of profile and absolute_path_profile should be specified");
+    FdoInputFile inputFile = FdoInputFile.create(ruleContext);
+    if (ruleContext.hasErrors()) {
       return null;
     }
 
-    if (isLabel) {
-      Artifact artifact = ruleContext.getPrerequisiteArtifact("profile", Mode.TARGET);
-      if (!artifact.isSourceArtifact()) {
-        ruleContext.attributeError("profile", " the target is not an input file");
-      }
-      provider = FdoProfileProvider.fromArtifact(artifact);
-    } else {
-      if (!ruleContext.getFragment(CppConfiguration.class).isFdoAbsolutePathEnabled()) {
-        ruleContext.attributeError(
-            "absolute_path_profile",
-            "this attribute cannot be used when --enable_fdo_profile_absolute_path is false");
-        return null;
-      }
-      String path = ruleContext.getExpander().expand("absolute_path_profile");
-      PathFragment fdoPath = PathFragment.create(path);
-      if (!fdoPath.isAbsolute()) {
-        ruleContext.attributeError(
-            "absolute_path_profile",
-            String.format("%s is not an absolute path", fdoPath.getPathString()));
-      }
-      provider = FdoProfileProvider.fromAbsolutePath(fdoPath);
-    }
-
     return new RuleConfiguredTargetBuilder(ruleContext)
-        .addNativeDeclaredProvider(provider)
+        .addNativeDeclaredProvider(new FdoProfileProvider(inputFile))
         .addProvider(RunfilesProvider.simple(Runfiles.EMPTY))
         .build();
   }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoProfileProvider.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoProfileProvider.java
index b5c73bd..876d164 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoProfileProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoProfileProvider.java
@@ -13,11 +13,9 @@
 // limitations under the License.
 package com.google.devtools.build.lib.rules.cpp;
 
-import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 import com.google.devtools.build.lib.packages.NativeInfo;
 import com.google.devtools.build.lib.packages.NativeProvider;
-import com.google.devtools.build.lib.vfs.PathFragment;
 
 /** Provider that contains the profile used for FDO. */
 @Immutable
@@ -25,28 +23,14 @@
   public static final NativeProvider<FdoProfileProvider> PROVIDER =
       new NativeProvider<FdoProfileProvider>(FdoProfileProvider.class, "FdoProfileInfo") {};
 
-  private final Artifact profileArtifact;
-  private final PathFragment fdoPath;
+  private final FdoInputFile fdoInputFile;
 
-  private FdoProfileProvider(Artifact profileArtifact, PathFragment fdoPath) {
+  public FdoProfileProvider(FdoInputFile fdoInputFile) {
     super(PROVIDER);
-    this.profileArtifact = profileArtifact;
-    this.fdoPath = fdoPath;
+    this.fdoInputFile = fdoInputFile;
   }
 
-  public static FdoProfileProvider fromAbsolutePath(PathFragment fdoPath) {
-    return new FdoProfileProvider(/* profileArtifact= */ null, fdoPath);
-  }
-
-  public static FdoProfileProvider fromArtifact(Artifact profileArtifact) {
-    return new FdoProfileProvider(profileArtifact, /* fdoPath= */ null);
-  }
-
-  public Artifact getProfileArtifact() {
-    return profileArtifact;
-  }
-
-  public PathFragment getFdoPath() {
-    return fdoPath;
+  public FdoInputFile getInputFile() {
+    return fdoInputFile;
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoSupport.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoSupport.java
index e10ca70..f657c07 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoSupport.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoSupport.java
@@ -605,13 +605,20 @@
       FeatureConfiguration featureConfiguration,
       FdoSupportProvider fdoSupportProvider) {
 
+    ImmutableMap.Builder<String, String> variablesBuilder = ImmutableMap.builder();
+
+    if ((fdoSupportProvider != null)
+        && (fdoSupportProvider.getPrefetchHintsArtifact() != null)) {
+      variablesBuilder.put(
+          CompileBuildVariables.FDO_PREFETCH_HINTS_PATH.getVariableName(),
+          fdoSupportProvider.getPrefetchHintsArtifact().getExecPathString());
+    }
+
     // FDO is disabled -> do nothing.
     if ((fdoInstrument == null) && (fdoRoot == null)) {
       return ImmutableMap.of();
     }
 
-    ImmutableMap.Builder<String, String> variablesBuilder = ImmutableMap.builder();
-
     if (featureConfiguration.isEnabled(CppRuleClasses.FDO_INSTRUMENT)) {
       variablesBuilder.put(
           CompileBuildVariables.FDO_INSTRUMENT_PATH.getVariableName(), fdoInstrument);
@@ -663,19 +670,21 @@
     LipoContextProvider lipoContextProvider =
         toolchain.isLLVMCompiler() ? null : CppHelper.getLipoContextProvider(ruleContext);
 
+    ImmutableSet.Builder<Artifact> auxiliaryInputs = ImmutableSet.builder();
+
+    if (fdoSupportProvider.getPrefetchHintsArtifact() != null) {
+      auxiliaryInputs.add(fdoSupportProvider.getPrefetchHintsArtifact());
+    }
     // If --fdo_optimize was not specified, we don't have any additional inputs.
     if (fdoProfile == null) {
-      return ImmutableSet.of();
+      return auxiliaryInputs.build();
     } else if (fdoMode == FdoMode.LLVM_FDO || fdoMode == FdoMode.AUTO_FDO) {
-      ImmutableSet.Builder<Artifact> auxiliaryInputs = ImmutableSet.builder();
       auxiliaryInputs.add(fdoSupportProvider.getProfileArtifact());
       if (lipoContextProvider != null) {
         auxiliaryInputs.addAll(getAutoFdoImports(ruleContext, sourceExecPath, lipoContextProvider));
       }
       return auxiliaryInputs.build();
     } else {
-      ImmutableSet.Builder<Artifact> auxiliaryInputs = ImmutableSet.builder();
-
       PathFragment objectName =
           FileSystemUtils.appendExtension(outputName, usePic ? ".pic.o" : ".o");
 
@@ -803,18 +812,22 @@
    * AutoFDO is disabled, no build variable is added and returns null.
    */
   @ThreadSafe
-  public Artifact buildProfileForLtoBackend(
+  public ProfileArtifacts buildProfileForLtoBackend(
       FdoSupportProvider fdoSupportProvider,
       FeatureConfiguration featureConfiguration,
       CcToolchainVariables.Builder buildVariables,
       RuleContext ruleContext) {
+    Artifact prefetch = fdoSupportProvider.getPrefetchHintsArtifact();
+    if (prefetch != null) {
+      buildVariables.addStringVariable("fdo_prefetch_hints_path", prefetch.getExecPathString());
+    }
     if (!featureConfiguration.isEnabled(CppRuleClasses.AUTOFDO)) {
-      return null;
+      return new ProfileArtifacts(null, prefetch);
     }
 
     Artifact profile = fdoSupportProvider.getProfileArtifact();
     buildVariables.addStringVariable("fdo_profile_path", profile.getExecPathString());
-    return profile;
+    return new ProfileArtifacts(profile, prefetch);
   }
 
   /**
@@ -827,20 +840,20 @@
   }
 
   public FdoSupportProvider createFdoSupportProvider(
-      RuleContext ruleContext, Artifact profileArtifact) {
+      RuleContext ruleContext, ProfileArtifacts profiles) {
     if (fdoRoot == null) {
-      return new FdoSupportProvider(this, null, null);
+      return new FdoSupportProvider(this, profiles, null);
     }
 
     if (fdoMode == FdoMode.LLVM_FDO) {
-      Preconditions.checkState(profileArtifact != null);
-      return new FdoSupportProvider(this, profileArtifact, null);
+      Preconditions.checkState(profiles != null && profiles.getProfileArtifact() != null);
+      return new FdoSupportProvider(this, profiles, null);
     }
 
     Preconditions.checkState(fdoPath != null);
     PathFragment profileRootRelativePath = getAutoProfileRootRelativePath(fdoProfile);
 
-    profileArtifact =
+    Artifact profileArtifact =
         ruleContext
             .getAnalysisEnvironment()
             .getDerivedArtifact(fdoPath.getRelative(profileRootRelativePath), fdoRoot);
@@ -854,7 +867,11 @@
       gcdaArtifacts.put(path, gcdaArtifact);
     }
 
-    return new FdoSupportProvider(this, profileArtifact, gcdaArtifacts.build());
+    return new FdoSupportProvider(
+        this,
+        new ProfileArtifacts(
+            profileArtifact, profiles == null ? null : profiles.getPrefetchHintsArtifact()),
+        gcdaArtifacts.build());
   }
 
   /**
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoSupportProvider.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoSupportProvider.java
index 72715f0..2ff6105 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoSupportProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoSupportProvider.java
@@ -28,14 +28,14 @@
 @AutoCodec
 public class FdoSupportProvider implements TransitiveInfoProvider {
   private final FdoSupport fdoSupport;
-  private final Artifact profileArtifact;
+  private final ProfileArtifacts profileArtifacts;
   private final ImmutableMap<PathFragment, Artifact> gcdaArtifacts;
 
   @AutoCodec.Instantiator
-  public FdoSupportProvider(FdoSupport fdoSupport, Artifact profileArtifact,
+  public FdoSupportProvider(FdoSupport fdoSupport, ProfileArtifacts profileArtifacts,
       ImmutableMap<PathFragment, Artifact> gcdaArtifacts) {
     this.fdoSupport = fdoSupport;
-    this.profileArtifact = profileArtifact;
+    this.profileArtifacts = profileArtifacts;
     this.gcdaArtifacts = gcdaArtifacts;
   }
 
@@ -43,7 +43,10 @@
     return fdoSupport;
   }
   public Artifact getProfileArtifact() {
-    return profileArtifact;
+    return profileArtifacts != null ? profileArtifacts.getProfileArtifact() : null;
+  }
+  public Artifact getPrefetchHintsArtifact() {
+    return profileArtifacts != null ? profileArtifacts.getPrefetchHintsArtifact() : null;
   }
   public ImmutableMap<PathFragment, Artifact> getGcdaArtifacts() {
     return gcdaArtifacts;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoSupportValue.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoSupportValue.java
index 83c8173..f6bad1f 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoSupportValue.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/FdoSupportValue.java
@@ -47,20 +47,33 @@
 
     private final LipoMode lipoMode;
     private final PathFragment fdoZip;
+    private final FdoInputFile fdoPrefetchHintsFile;
     private final String fdoInstrument;
     private final FdoMode fdoMode;
 
-    private Key(LipoMode lipoMode, PathFragment fdoZip, String fdoInstrument, FdoMode fdoMode) {
+    private Key(
+        LipoMode lipoMode,
+        PathFragment fdoZip,
+        FdoInputFile fdoPrefetchHintsFile,
+        String fdoInstrument,
+        FdoMode fdoMode) {
       this.lipoMode = lipoMode;
       this.fdoZip = fdoZip;
+      this.fdoPrefetchHintsFile = fdoPrefetchHintsFile;
       this.fdoInstrument = fdoInstrument;
       this.fdoMode = fdoMode;
     }
 
     @AutoCodec.Instantiator
     @AutoCodec.VisibleForSerialization
-    static Key of(LipoMode lipoMode, PathFragment fdoZip, String fdoInstrument, FdoMode fdoMode) {
-      return interner.intern(new Key(lipoMode, fdoZip, fdoInstrument, fdoMode));
+    static Key of(
+        LipoMode lipoMode,
+        PathFragment fdoZip,
+        FdoInputFile fdoPrefetchHintsFile,
+        String fdoInstrument,
+        FdoMode fdoMode) {
+      return interner.intern(
+          new Key(lipoMode, fdoZip, fdoPrefetchHintsFile, fdoInstrument, fdoMode));
     }
 
     public LipoMode getLipoMode() {
@@ -71,6 +84,10 @@
       return fdoZip;
     }
 
+    public FdoInputFile getFdoPrefetchHintsFile() {
+      return fdoPrefetchHintsFile;
+    }
+
     public String getFdoInstrument() {
       return fdoInstrument;
     }
@@ -92,13 +109,14 @@
       Key that = (Key) o;
       return Objects.equals(this.lipoMode, that.lipoMode)
           && Objects.equals(this.fdoZip, that.fdoZip)
+          && Objects.equals(this.fdoPrefetchHintsFile, that.fdoPrefetchHintsFile)
           && Objects.equals(this.fdoMode, that.fdoMode)
           && Objects.equals(this.fdoInstrument, that.fdoInstrument);
     }
 
     @Override
     public int hashCode() {
-      return Objects.hash(lipoMode, fdoZip, fdoInstrument);
+      return Objects.hash(lipoMode, fdoZip, fdoPrefetchHintsFile, fdoInstrument);
     }
 
     @Override
@@ -118,7 +136,11 @@
   }
 
   public static SkyKey key(
-      LipoMode lipoMode, PathFragment fdoZip, String fdoInstrument, FdoMode fdoMode) {
-    return Key.of(lipoMode, fdoZip, fdoInstrument, fdoMode);
+      LipoMode lipoMode,
+      PathFragment fdoZip,
+      FdoInputFile fdoPrefetchHintsFile,
+      String fdoInstrument,
+      FdoMode fdoMode) {
+    return Key.of(lipoMode, fdoZip, fdoPrefetchHintsFile, fdoInstrument, fdoMode);
   }
 }
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 df6dba7..f3e6d68 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
@@ -230,10 +230,18 @@
     // The input to the LTO backend step is the bitcode file.
     buildVariablesBuilder.addStringVariable(
         "thinlto_input_bitcode_file", bitcodeFile.getExecPath().toString());
-    Artifact autoFdoProfile = fdoSupport.getFdoSupport().buildProfileForLtoBackend(
-        fdoSupport, featureConfiguration, buildVariablesBuilder, ruleContext);
-    if (autoFdoProfile != null) {
-      builder.addInput(autoFdoProfile);
+    ProfileArtifacts profileArtifacts =
+        Preconditions.checkNotNull(
+            fdoSupport
+                .getFdoSupport()
+                .buildProfileForLtoBackend(
+                    fdoSupport, featureConfiguration, buildVariablesBuilder, ruleContext));
+
+    if (profileArtifacts.getProfileArtifact() != null) {
+      builder.addInput(profileArtifacts.getProfileArtifact());
+    }
+    if (profileArtifacts.getPrefetchHintsArtifact() != null) {
+      builder.addInput(profileArtifacts.getPrefetchHintsArtifact());
     }
 
     if (generateDwo) {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/ProfileArtifacts.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/ProfileArtifacts.java
new file mode 100644
index 0000000..1b46d99
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/ProfileArtifacts.java
@@ -0,0 +1,43 @@
+// Copyright 2018 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.
+
+package com.google.devtools.build.lib.rules.cpp;
+
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+
+/**
+ * ProfileArtifacts groups together Artifacts that may be used as inputs for feedback-driven
+ * optimization.
+ */
+@Immutable
+public class ProfileArtifacts {
+  private final Artifact profileArtifact;
+  private final Artifact prefetchHintsArtifact;
+
+  public ProfileArtifacts(Artifact profileArtifact) {
+    this(profileArtifact, null);
+  }
+  public ProfileArtifacts(Artifact profileArtifact, Artifact prefetchHintsArtifact) {
+    this.profileArtifact = profileArtifact;
+    this.prefetchHintsArtifact = prefetchHintsArtifact;
+  }
+
+  public Artifact getProfileArtifact() {
+    return profileArtifact;
+  }
+  public Artifact getPrefetchHintsArtifact() {
+    return prefetchHintsArtifact;
+  }
+}