diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml
index d21b4a1fd4..aa2c52ab11 100644
--- a/.gitlab-ci.d/buildtest.yml
+++ b/.gitlab-ci.d/buildtest.yml
@@ -479,20 +479,36 @@ build-gprof-gcov:
   variables:
     IMAGE: ubuntu2004
     CONFIGURE_ARGS: --enable-gprof --enable-gcov
-    TARGETS: aarch64-softmmu ppc64-softmmu s390x-softmmu x86_64-softmmu
+    TARGETS: aarch64-softmmu ppc64-linux-user
   artifacts:
     expire_in: 1 days
     paths:
       - build
 
-check-gprof-gcov:
+# This is special as the target list is dynamic
+build-gcov:
+  extends: .native_build_job_template
+  needs:
+    job: amd64-ubuntu2004-container
+  before_script:
+    - TARGETS=$(.gitlab-ci.d/pick-targets.py)
+  variables:
+    IMAGE: ubuntu2004
+    CONFIGURE_ARGS: --enable-gcov
+  artifacts:
+    expire_in: 1 days
+    paths:
+      - build
+      
+# This is special
+check-gcov:
   extends: .native_test_job_template
   needs:
-    - job: build-gprof-gcov
+    - job: build-gcov
       artifacts: true
   variables:
     IMAGE: ubuntu2004
-    MAKE_CHECK_ARGS: check
+    MAKE_CHECK_ARGS: check check-avocado
   after_script:
     - cd build
     - gcovr --xml-pretty --exclude-unreachable-branches --print-summary
diff --git a/.gitlab-ci.d/pick-targets.py b/.gitlab-ci.d/pick-targets.py
new file mode 100755
index 0000000000..db1eff0119
--- /dev/null
+++ b/.gitlab-ci.d/pick-targets.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python3
+#
+# pick-targets: pick a set of targets that are worth testing.
+#
+# Running code coverage is too expensive to run over all the builds.
+# Try and figure out a subset of targets that would be worth running
+# for the files touched between origin/master and the current HEAD.
+#
+# Copyright (C) 2022 Linaro Ltd
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import os
+import os.path
+import sys
+import subprocess
+
+# Dumb mapping from a target directory name to a list of
+# targets. Should we bother for those we know we don't have compilers for?
+multi_targets = {
+    'arm'        : ['arm', 'aarch64'],
+    'i386'       : ['i386', 'x86_64'],
+    'microblaze' : ['microblaze' ], # no softmmu, 'microblazel' ],
+    'mips'       : ['mips', 'mips64', 'mips64el', 'mipsel' ],
+    'ppc'        : ['ppc', 'ppc64' ],
+    'riscv'      : ['riscv32', 'riscv64'],
+    'sh4'        : ['sh4', 'sh4eb'],
+    'sparc'      : ['sparc', 'sparc64'],
+    'xtensa'     : ['xtensa', 'xtensaeb']
+}
+
+def map_dir_to_targets(name):
+    if name in multi_targets:
+        return multi_targets[name]
+    else:
+        return name
+
+namespace = "qemu-project"
+if len(sys.argv) >= 2:
+    namespace = sys.argv[1]
+
+cwd = os.getcwd()
+reponame = os.path.basename(cwd)
+repourl = f"https://gitlab.com/{namespace}/{reponame}"
+
+# Add remote, fetch master and save the common ancestor
+subprocess.check_call(["git", "remote", "add", "pick-target", repourl])
+subprocess.check_call(["git", "fetch", "pick-target", "master"],
+                      stdout=subprocess.DEVNULL,
+                      stderr=subprocess.DEVNULL)
+
+ancestor = subprocess.check_output(["git", "merge-base",
+                                    "pick-target/master", "HEAD"],
+                                   universal_newlines=True)
+
+ancestor = ancestor.strip()
+
+subprocess.check_call(["git", "remote", "rm", "pick-target"])
+
+# calculate the diff and extract list of touched files
+diff = subprocess.check_output(["git", "diff", "--numstat",
+                                 f"{ancestor}..HEAD"])
+
+files = [l.split("\t")[2] for l in diff.decode().split("\n") if "\t" in l]
+
+# Build options to track
+system = False
+user = False
+targets = []
+
+for f in files:
+    if f.startswith("hw"):
+        system = True
+    if f.startswith("linux-user"):
+        user = True
+    if f.startswith("target"):
+        t = f.split("/")[1]
+        targets.extend(map_dir_to_targets(t))
+
+target_list = []
+for t in sorted(set(targets)):
+    if system:
+        target_list.append(f"{t}-softmmu")
+    if user:
+        target_list.append(f"{t}-linux-user")
+
+print(" ".join(target_list))
