diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfigurationValue.java b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfigurationValue.java
index 9d9cfde8559d33..c4362b3ae4347b 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfigurationValue.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfigurationValue.java
@@ -15,6 +15,7 @@
 package com.google.devtools.build.lib.analysis.config;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableCollection;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -44,6 +45,7 @@
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.skyframe.SkyValue;
 import com.google.devtools.common.options.TriState;
+import com.google.devtools.common.options.OptionsParsingException;
 import java.io.PrintStream;
 import java.util.HashMap;
 import java.util.List;
@@ -143,6 +145,8 @@ public void reportInvalidOptions(EventHandler reporter) {
     }
   }
 
+  private final RegexFilter instrumentationFilter;
+
   /**
    * Compute the test environment, which, at configuration level, is a pair consisting of the
    * statically set environment variables with their values and the set of environment variables to
@@ -298,6 +302,19 @@ private static ImmutableSortedMap<Class<? extends Fragment>, Fragment> getConfig
     this.reservedActionMnemonics = reservedActionMnemonics;
     this.commandLineLimits = new CommandLineLimits(options.minParamFileSize);
     this.defaultFeatures = FeatureSet.parse(options.defaultFeatures);
+
+    if (!options.instrumentationFilterFragment.isEmpty()) {
+      // A regex-based instrumentation filter instance is formed by concatenating
+      // string values of --experimental_instrumentation_filter_fragment.
+      try {
+        this.instrumentationFilter = new RegexFilter.RegexFilterConverter().convert(
+          Joiner.on(",").join(options.instrumentationFilterFragment));
+      } catch (OptionsParsingException e) {
+        throw new RuntimeException(e);
+      }
+    } else {
+      this.instrumentationFilter = options.instrumentationFilter;
+    }
   }
 
   @Override
@@ -566,7 +583,7 @@ public Iterable<String> getVariableShellEnvironment() {
    * identify targets to be instrumented in the coverage mode.
    */
   public RegexFilter getInstrumentationFilter() {
-    return options.instrumentationFilter;
+    return instrumentationFilter;
   }
 
   /**
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/CoreOptions.java b/src/main/java/com/google/devtools/build/lib/analysis/config/CoreOptions.java
index 8d576d909e7bb7..a995ea208c45ee 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/config/CoreOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/config/CoreOptions.java
@@ -17,6 +17,7 @@
 import static com.google.common.collect.ImmutableList.toImmutableList;
 import static com.google.common.collect.ImmutableMap.toImmutableMap;
 
+import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.analysis.config.CoreOptionConverters.LabelListConverter;
@@ -274,6 +275,22 @@ public class CoreOptions extends FragmentOptions implements Cloneable {
               + "instrumented unless --instrument_test_targets is enabled.")
   public RegexFilter instrumentationFilter;
 
+  @Option(
+      name = "experimental_instrumentation_filter_fragment",
+      allowMultiple = true,
+      defaultValue = "null",
+      documentationCategory = OptionDocumentationCategory.OUTPUT_PARAMETERS,
+      effectTags = {OptionEffectTag.AFFECTS_OUTPUTS},
+      help =
+          "When coverage is enabled, only rules with names included by the "
+              + "specified regex-based filter will be instrumented. Rules prefixed "
+              + "with '-' are excluded instead. Note that only non-test rules are "
+              + "instrumented unless --instrument_test_targets is enabled. "
+              + "Excluded filters always override included ones. This option can be used "
+              + "multiple times. If this option is provided --instrumentation_filter "
+              + "has no effect.")
+  public List<String> instrumentationFilterFragment;
+
   @Option(
       name = "instrument_test_targets",
       defaultValue = "false",
diff --git a/src/test/java/com/google/devtools/build/lib/starlark/StarlarkRuleContextTest.java b/src/test/java/com/google/devtools/build/lib/starlark/StarlarkRuleContextTest.java
index 4a502f7cda6269..35ea39dd809ef7 100644
--- a/src/test/java/com/google/devtools/build/lib/starlark/StarlarkRuleContextTest.java
+++ b/src/test/java/com/google/devtools/build/lib/starlark/StarlarkRuleContextTest.java
@@ -3020,6 +3020,80 @@ private void setUpCoverageInstrumentedTest() throws Exception {
         """);
   }
 
+  @Test
+  public void testCoverageExperimentalInstrumentedFragmentCoverageDisabled() throws Exception {
+    setUpCoverageInstrumentedTest();
+    useConfiguration(
+        "--nocollect_code_coverage",
+        "--experimental_instrumentation_filter_fragment=.");
+    StarlarkRuleContext ruleContext = createRuleContext("//test:foo");
+    setRuleContext(ruleContext);
+    Object result = ev.eval("ruleContext.coverage_instrumented()");
+    assertThat((Boolean) result).isFalse();
+  }
+
+  @Test
+  public void testCoverageExperimentalInstrumentedFragmentFalseForSourceFileLabel() throws Exception {
+    setUpCoverageInstrumentedTest();
+    useConfiguration(
+        "--collect_code_coverage",
+        "--experimental_instrumentation_filter_fragment=:foo",
+        "--experimental_instrumentation_filter_fragment=:bar");
+    setRuleContext(createRuleContext("//test:foo"));
+    Object result = ev.eval("ruleContext.coverage_instrumented(ruleContext.attr.srcs[0])");
+    assertThat((Boolean) result).isFalse();
+  }
+
+  @Test
+  public void testCoverageExperimentalInstrumentedFragmentDoesNotMatchFilter() throws Exception {
+    setUpCoverageInstrumentedTest();
+    useConfiguration(
+        "--collect_code_coverage",
+        "--experimental_instrumentation_filter_fragment=:foo",
+        "--experimental_instrumentation_filter_fragment=-:bar");
+    setRuleContext(createRuleContext("//test:bar"));
+    Object result = ev.eval("ruleContext.coverage_instrumented()");
+    assertThat((Boolean) result).isFalse();
+  }
+
+  @Test
+  public void testCoverageExperimentalInstrumentedFragmentMatchesFilter() throws Exception {
+    setUpCoverageInstrumentedTest();
+    useConfiguration(
+        "--collect_code_coverage",
+        "--experimental_instrumentation_filter_fragment=:foo",
+        "--experimental_instrumentation_filter_fragment=:bar");
+    setRuleContext(createRuleContext("//test:foo"));
+    Object result = ev.eval("ruleContext.coverage_instrumented()");
+    assertThat((Boolean) result).isTrue();
+  }
+
+  @Test
+  public void testCoverageExperimentalInstrumentedFragmentDoesNotMatchFilterNonDefaultLabel() throws Exception {
+    setUpCoverageInstrumentedTest();
+    useConfiguration(
+        "--collect_code_coverage",
+        "--experimental_instrumentation_filter_fragment=:foo",
+        // --instrumentation_filter has no effect because --experimental_instrumentation_filter_fragment is used.
+        "--instrumentation_filter=:bar");
+    setRuleContext(createRuleContext("//test:foo"));
+    // //test:bar does not match :foo, though //test:foo would.
+    Object result = ev.eval("ruleContext.coverage_instrumented(ruleContext.attr.deps[0])");
+    assertThat((Boolean) result).isFalse();
+  }
+
+  @Test
+  public void testCoverageExperimentalInstrumentedFragmentMatchesFilterNonDefaultLabel() throws Exception {
+    setUpCoverageInstrumentedTest();
+    useConfiguration(
+        "--collect_code_coverage",
+        "--experimental_instrumentation_filter_fragment=:bar");
+    setRuleContext(createRuleContext("//test:foo"));
+    // //test:bar does match :bar, though //test:foo would not.
+    Object result = ev.eval("ruleContext.coverage_instrumented(ruleContext.attr.deps[0])");
+    assertThat((Boolean) result).isTrue();
+  }
+
   @Test
   public void testCoverageInstrumentedCoverageDisabled() throws Exception {
     setUpCoverageInstrumentedTest();