[llvm] f8f6c0b - Revert "[LLVM] Add GNU make jobserver support (#145131)"

Yaxun Liu via llvm-commits llvm-commits at lists.llvm.org
Fri Oct 3 07:36:55 PDT 2025


Author: Yaxun (Sam) Liu
Date: 2025-10-03T10:35:03-04:00
New Revision: f8f6c0b6ecb9d87ead48246d4fadf6048207375d

URL: https://github.com/llvm/llvm-project/commit/f8f6c0b6ecb9d87ead48246d4fadf6048207375d
DIFF: https://github.com/llvm/llvm-project/commit/f8f6c0b6ecb9d87ead48246d4fadf6048207375d.diff

LOG: Revert "[LLVM] Add GNU make jobserver support (#145131)"

revert this patch due to failure in unittests/Support, e.g.

https://lab.llvm.org/buildbot/#/builders/33/builds/24178/steps/6/logs/FAIL__LLVM-Unit__SupportTests_61

This reverts commit ffc503edd0a2d07121232fe204e480fc29631a90.

Added: 
    

Modified: 
    clang/include/clang/Driver/Options.td
    clang/lib/Driver/ToolChains/Clang.cpp
    clang/test/Driver/hip-options.hip
    clang/test/Driver/linker-wrapper.c
    clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
    clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td
    llvm/include/llvm/Support/ThreadPool.h
    llvm/include/llvm/Support/Threading.h
    llvm/lib/Support/CMakeLists.txt
    llvm/lib/Support/Parallel.cpp
    llvm/lib/Support/ThreadPool.cpp
    llvm/lib/Support/Threading.cpp
    llvm/unittests/Support/CMakeLists.txt

Removed: 
    llvm/include/llvm/Support/Jobserver.h
    llvm/lib/Support/Jobserver.cpp
    llvm/lib/Support/Unix/Jobserver.inc
    llvm/lib/Support/Windows/Jobserver.inc
    llvm/unittests/Support/JobserverTest.cpp


################################################################################
diff  --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 5a48f0bcf65e5..2ef609831637e 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -1258,9 +1258,8 @@ def offload_compression_level_EQ : Joined<["--"], "offload-compression-level=">,
   HelpText<"Compression level for offload device binaries (HIP only)">;
 
 def offload_jobs_EQ : Joined<["--"], "offload-jobs=">,
-  HelpText<"Specify the number of threads to use for device offloading tasks "
-           "during compilation. Can be a positive integer or the string "
-           "'jobserver' to use the make-style jobserver from the environment.">;
+  HelpText<"Specify the number of threads to use for device offloading tasks"
+           " during compilation.">;
 
 defm offload_via_llvm : BoolFOption<"offload-via-llvm",
   LangOpts<"OffloadViaLLVM">, DefaultFalse,

diff  --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 684cc0902916f..412a176006bc0 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -9224,20 +9224,14 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA,
   addOffloadCompressArgs(Args, CmdArgs);
 
   if (Arg *A = Args.getLastArg(options::OPT_offload_jobs_EQ)) {
-    StringRef Val = A->getValue();
-
-    if (Val.equals_insensitive("jobserver"))
-      CmdArgs.push_back(Args.MakeArgString("--wrapper-jobs=jobserver"));
-    else {
-      int NumThreads;
-      if (Val.getAsInteger(10, NumThreads) || NumThreads <= 0) {
-        C.getDriver().Diag(diag::err_drv_invalid_int_value)
-            << A->getAsString(Args) << Val;
-      } else {
-        CmdArgs.push_back(
-            Args.MakeArgString("--wrapper-jobs=" + Twine(NumThreads)));
-      }
-    }
+    int NumThreads;
+    if (StringRef(A->getValue()).getAsInteger(10, NumThreads) ||
+        NumThreads <= 0)
+      C.getDriver().Diag(diag::err_drv_invalid_int_value)
+          << A->getAsString(Args) << A->getValue();
+    else
+      CmdArgs.push_back(
+          Args.MakeArgString("--wrapper-jobs=" + Twine(NumThreads)));
   }
 
   const char *Exec =

diff  --git a/clang/test/Driver/hip-options.hip b/clang/test/Driver/hip-options.hip
index 09f1ffa62d348..6206020d76db6 100644
--- a/clang/test/Driver/hip-options.hip
+++ b/clang/test/Driver/hip-options.hip
@@ -254,9 +254,3 @@
 // RUN:   --offload-arch=gfx1100 --offload-new-driver --offload-jobs=0x4 %s 2>&1 | \
 // RUN:   FileCheck -check-prefix=INVJOBS %s
 // INVJOBS: clang: error: invalid integral value '0x4' in '--offload-jobs=0x4'
-
-// RUN: %clang -### -Werror --target=x86_64-unknown-linux-gnu -nogpuinc -nogpulib \
-// RUN:   --offload-arch=gfx1100 --offload-new-driver --offload-jobs=jobserver %s 2>&1 | \
-// RUN:   FileCheck -check-prefix=JOBSV %s
-// JOBSV: clang-linker-wrapper{{.*}} "--wrapper-jobs=jobserver"
-

diff  --git a/clang/test/Driver/linker-wrapper.c b/clang/test/Driver/linker-wrapper.c
index 1c0fb9644ef54..c060dae7bb154 100644
--- a/clang/test/Driver/linker-wrapper.c
+++ b/clang/test/Driver/linker-wrapper.c
@@ -114,8 +114,6 @@ __attribute__((visibility("protected"), used)) int x;
 // RUN:   -fembed-offload-object=%t.out
 // RUN: clang-linker-wrapper --dry-run --host-triple=x86_64-unknown-linux-gnu --wrapper-jobs=4 \
 // RUN: --linker-path=/usr/bin/ld %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=CUDA-PAR
-// RUN: clang-linker-wrapper --dry-run --host-triple=x86_64-unknown-linux-gnu --wrapper-jobs=jobserver \
-// RUN: --linker-path=/usr/bin/ld %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=CUDA-PAR
 
 // CUDA-PAR: fatbinary{{.*}}-64 --create {{.*}}.fatbin
 

diff  --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
index 4d5b956031674..1419b8c90a625 100644
--- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
+++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
@@ -1295,18 +1295,12 @@ int main(int Argc, char **Argv) {
 
   parallel::strategy = hardware_concurrency(1);
   if (auto *Arg = Args.getLastArg(OPT_wrapper_jobs)) {
-    StringRef Val = Arg->getValue();
-    if (Val.equals_insensitive("jobserver"))
-      parallel::strategy = jobserver_concurrency();
-    else {
-      unsigned Threads = 0;
-      if (!llvm::to_integer(Val, Threads) || Threads == 0)
-        reportError(createStringError(
-            "%s: expected a positive integer or 'jobserver', got '%s'",
-            Arg->getSpelling().data(), Val.data()));
-      else
-        parallel::strategy = hardware_concurrency(Threads);
-    }
+    unsigned Threads = 0;
+    if (!llvm::to_integer(Arg->getValue(), Threads) || Threads == 0)
+      reportError(createStringError("%s: expected a positive integer, got '%s'",
+                                    Arg->getSpelling().data(),
+                                    Arg->getValue()));
+    parallel::strategy = hardware_concurrency(Threads);
   }
 
   if (Args.hasArg(OPT_wrapper_time_trace_eq)) {

diff  --git a/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td b/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td
index 87f911c749bf6..fa73e02fd5178 100644
--- a/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td
+++ b/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td
@@ -53,8 +53,7 @@ def wrapper_time_trace_granularity : Joined<["--"], "wrapper-time-trace-granular
 
 def wrapper_jobs : Joined<["--"], "wrapper-jobs=">,
   Flags<[WrapperOnlyOption]>, MetaVarName<"<number>">,
-  HelpText<"Sets the number of parallel jobs for device linking. Can be a "
-            "positive integer or 'jobserver'.">;
+  HelpText<"Sets the number of parallel jobs to use for device linking">;
 
 def override_image : Joined<["--"], "override-image=">,
   Flags<[WrapperOnlyOption]>, MetaVarName<"<kind=file>">,

diff  --git a/llvm/include/llvm/Support/Jobserver.h b/llvm/include/llvm/Support/Jobserver.h
deleted file mode 100644
index 6bee3b5671d55..0000000000000
--- a/llvm/include/llvm/Support/Jobserver.h
+++ /dev/null
@@ -1,162 +0,0 @@
-//===- llvm/Support/Jobserver.h - Jobserver Client --------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defines a client for the GNU Make jobserver protocol. This allows
-// LLVM tools to coordinate parallel execution with a parent `make` process.
-//
-// The jobserver protocol is a mechanism for GNU Make to share its pool of
-// available "job slots" with the subprocesses it invokes. This is particularly
-// useful for tools that can perform parallel operations themselves (e.g., a
-// multi-threaded linker or compiler). By participating in this protocol, a
-// tool can ensure the total number of concurrent jobs does not exceed the
-// limit specified by the user (e.g., `make -j8`).
-//
-// How it works:
-//
-// 1. Establishment:
-//    A child process discovers the jobserver by inspecting the `MAKEFLAGS`
-//    environment variable. If a jobserver is active, this variable will
-//    contain a `--jobserver-auth=<value>` argument. The format of `<value>`
-//    determines how to communicate with the server.
-//
-// 2. The Implicit Slot:
-//    Every command invoked by `make` is granted one "implicit" job slot. This
-//    means a tool can always perform at least one unit of work without needing
-//    to communicate with the jobserver. This implicit slot should NEVER be
-//    released back to the jobserver.
-//
-// 3. Acquiring and Releasing Slots:
-//    On POSIX systems, the jobserver is implemented as a pipe. The
-//    `--jobserver-auth` value specifies either a path to a named pipe
-//    (`fifo:PATH`) or a pair of file descriptors (`R,W`). The pipe is
-//    pre-loaded with single-character tokens, one for each available job slot.
-//
-//    - To acquire an additional slot, a client reads a single-character token
-//      from the pipe.
-//    - To release a slot, the client must write the *exact same* character
-//      token back to the pipe.
-//
-//    It is critical that a client releases all acquired slots before it exits,
-//    even in cases of error, to avoid deadlocking the build.
-//
-// Example:
-//    A multi-threaded linker invoked by `make -j8` wants to use multiple
-//    threads. It first checks for the jobserver. It knows it has one implicit
-//    slot, so it can use one thread. It then tries to acquire 7 more slots by
-//    reading 7 tokens from the jobserver pipe. If it only receives 3 tokens,
-//    it knows it can use a total of 1 (implicit) + 3 (acquired) = 4 threads.
-//    Before exiting, it must write the 3 tokens it read back to the pipe.
-//
-// For more context, see:
-//   - GNU Make manual on job slots:
-//     https://www.gnu.org/software/make/manual/html_node/Job-Slots.html
-//   - LLVM RFC discussion on jobserver support:
-//     https://discourse.llvm.org/t/rfc-adding-gnu-make-jobserver-
-//     support-to-llvm-for-coordinated-parallelism/87034
-//   - Ninja’s jobserver support PR:
-//     https://github.com/ninja-build/ninja/pull/2506
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_SUPPORT_JOBSERVER_H
-#define LLVM_SUPPORT_JOBSERVER_H
-
-#include "llvm/ADT/StringRef.h"
-#include <memory>
-#include <string>
-
-namespace llvm {
-
-/// A JobSlot represents a single job slot that can be acquired from or released
-/// to a jobserver pool. This class is move-only.
-class JobSlot {
-public:
-  /// Default constructor creates an invalid instance.
-  JobSlot() = default;
-
-  // Move operations are allowed.
-  JobSlot(JobSlot &&Other) noexcept : Value(Other.Value) {
-    Other.Value = kInvalidValue;
-  }
-  JobSlot &operator=(JobSlot &&Other) noexcept {
-    if (this != &Other) {
-      this->Value = Other.Value;
-      Other.Value = kInvalidValue;
-    }
-    return *this;
-  }
-
-  // Copy operations are disallowed.
-  JobSlot(const JobSlot &) = delete;
-  JobSlot &operator=(const JobSlot &) = delete;
-
-  /// Returns true if this instance is valid (either implicit or explicit).
-  bool isValid() const { return Value >= 0; }
-
-  /// Returns true if this instance represents the implicit job slot.
-  bool isImplicit() const { return Value == kImplicitValue; }
-
-  static JobSlot createExplicit(uint8_t V) {
-    return JobSlot(static_cast<int16_t>(V));
-  }
-
-  static JobSlot createImplicit() { return JobSlot(kImplicitValue); }
-
-  uint8_t getExplicitValue() const;
-  bool isExplicit() const { return isValid() && !isImplicit(); }
-
-private:
-  friend class JobserverClient;
-  friend class JobserverClientImpl;
-
-  JobSlot(int16_t V) : Value(V) {}
-
-  /// The jobserver pipe carries explicit tokens (bytes 0–255). We reserve two
-  /// sentinels in Value for special cases:
-  ///   kInvalidValue  (-1): no slot held
-  ///   kImplicitValue (INT16_MAX): implicit slot granted at startup (no pipe
-  ///   I/O)
-  ///
-  /// We use int16_t so Value can store 0–255 explicit tokens and
-  /// sentinels without overflow, enforces fixed 16-bit width, and avoids
-  /// unsigned/signed mix-ups.
-  static constexpr int16_t kInvalidValue = -1;
-  static constexpr int16_t kImplicitValue = INT16_MAX;
-  int16_t Value = kInvalidValue;
-};
-
-/// The public interface for a jobserver client.
-/// This client is a lazy-initialized singleton that is created on first use.
-class JobserverClient {
-public:
-  virtual ~JobserverClient();
-
-  /// Tries to acquire a job slot from the pool. On failure (e.g., if the pool
-  /// is empty), this returns an invalid JobSlot instance. The first successful
-  /// call will always return the implicit slot.
-  virtual JobSlot tryAcquire() = 0;
-
-  /// Releases a job slot back to the pool.
-  virtual void release(JobSlot Slot) = 0;
-
-  /// Returns the number of job slots available, as determined on first use.
-  /// This value is cached. Returns 0 if no jobserver is active.
-  virtual unsigned getNumJobs() const = 0;
-
-  /// Returns the singleton instance of the JobserverClient.
-  /// The instance is created on the first call to this function.
-  /// Returns a nullptr if no jobserver is configured or an error occurs.
-  static JobserverClient *getInstance();
-
-  /// Resets the singleton instance. For testing purposes only.
-  static void resetForTesting();
-};
-
-} // end namespace llvm
-
-#endif // LLVM_SUPPORT_JOBSERVER_H

diff  --git a/llvm/include/llvm/Support/ThreadPool.h b/llvm/include/llvm/Support/ThreadPool.h
index c20efc7396b79..c26681c25c8f6 100644
--- a/llvm/include/llvm/Support/ThreadPool.h
+++ b/llvm/include/llvm/Support/ThreadPool.h
@@ -16,7 +16,6 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/Config/llvm-config.h"
 #include "llvm/Support/Compiler.h"
-#include "llvm/Support/Jobserver.h"
 #include "llvm/Support/RWMutex.h"
 #include "llvm/Support/Threading.h"
 #include "llvm/Support/thread.h"
@@ -181,7 +180,6 @@ class LLVM_ABI StdThreadPool : public ThreadPoolInterface {
   void grow(int requested);
 
   void processTasks(ThreadPoolTaskGroup *WaitingForGroup);
-  void processTasksWithJobserver();
 
   /// Threads in flight
   std::vector<llvm::thread> Threads;
@@ -210,8 +208,6 @@ class LLVM_ABI StdThreadPool : public ThreadPoolInterface {
 
   /// Maximum number of threads to potentially grow this pool to.
   const unsigned MaxThreadCount;
-
-  JobserverClient *TheJobserver = nullptr;
 };
 #endif // LLVM_ENABLE_THREADS
 

diff  --git a/llvm/include/llvm/Support/Threading.h b/llvm/include/llvm/Support/Threading.h
index 88846807f111a..d3fe0a57ee44e 100644
--- a/llvm/include/llvm/Support/Threading.h
+++ b/llvm/include/llvm/Support/Threading.h
@@ -142,11 +142,6 @@ constexpr bool llvm_is_multithreaded() { return LLVM_ENABLE_THREADS; }
     /// the thread shall remain on the actual CPU socket.
     LLVM_ABI std::optional<unsigned>
     compute_cpu_socket(unsigned ThreadPoolNum) const;
-
-    /// If true, the thread pool will attempt to coordinate with a GNU Make
-    /// jobserver, acquiring a job slot before processing a task. If no
-    /// jobserver is found in the environment, this is ignored.
-    bool UseJobserver = false;
   };
 
   /// Build a strategy from a number of threads as a string provided in \p Num.
@@ -215,19 +210,6 @@ constexpr bool llvm_is_multithreaded() { return LLVM_ENABLE_THREADS; }
     return S;
   }
 
-  /// Returns a thread strategy that attempts to coordinate with a GNU Make
-  /// jobserver. The number of active threads will be limited by the number of
-  /// available job slots. If no jobserver is detected in the environment, this
-  /// strategy falls back to the default hardware_concurrency() behavior.
-  inline ThreadPoolStrategy jobserver_concurrency() {
-    ThreadPoolStrategy S;
-    S.UseJobserver = true;
-    // We can still request all threads be created, as they will simply
-    // block waiting for a job slot if the jobserver is the limiting factor.
-    S.ThreadsRequested = 0; // 0 means 'use all available'
-    return S;
-  }
-
   /// Return the current thread id, as used in various OS system calls.
   /// Note that not all platforms guarantee that the value returned will be
   /// unique across the entire system, so portable code should not assume

diff  --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt
index 42b21b5e62029..7da972f372c5b 100644
--- a/llvm/lib/Support/CMakeLists.txt
+++ b/llvm/lib/Support/CMakeLists.txt
@@ -207,7 +207,6 @@ add_llvm_component_library(LLVMSupport
   InstructionCost.cpp
   IntEqClasses.cpp
   IntervalMap.cpp
-  Jobserver.cpp
   JSON.cpp
   KnownBits.cpp
   KnownFPClass.cpp

diff  --git a/llvm/lib/Support/Jobserver.cpp b/llvm/lib/Support/Jobserver.cpp
deleted file mode 100644
index 9f726eb37506f..0000000000000
--- a/llvm/lib/Support/Jobserver.cpp
+++ /dev/null
@@ -1,259 +0,0 @@
-//===- llvm/Support/Jobserver.cpp - Jobserver Client Implementation -------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "llvm/Support/Jobserver.h"
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/Statistic.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/Config/llvm-config.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/Error.h"
-#include "llvm/Support/raw_ostream.h"
-
-#include <atomic>
-#include <memory>
-#include <mutex>
-#include <new>
-
-#define DEBUG_TYPE "jobserver"
-
-using namespace llvm;
-
-namespace {
-struct FdPair {
-  int Read = -1;
-  int Write = -1;
-  bool isValid() const { return Read >= 0 && Write >= 0; }
-};
-
-struct JobserverConfig {
-  enum Mode {
-    None,
-    PosixFifo,
-    PosixPipe,
-    Win32Semaphore,
-  };
-  Mode TheMode = None;
-  std::string Path;
-  FdPair PipeFDs;
-};
-
-/// A helper function that checks if `Input` starts with `Prefix`.
-/// If it does, it removes the prefix from `Input`, assigns the remainder to
-/// `Value`, and returns true. Otherwise, it returns false.
-bool getPrefixedValue(StringRef Input, StringRef Prefix, StringRef &Value) {
-  if (Input.consume_front(Prefix)) {
-    Value = Input;
-    return true;
-  }
-  return false;
-}
-
-/// A helper function to parse a string in the format "R,W" where R and W are
-/// non-negative integers representing file descriptors. It populates the
-/// `ReadFD` and `WriteFD` output parameters. Returns true on success.
-static std::optional<FdPair> getFileDescriptorPair(StringRef Input) {
-  FdPair FDs;
-  if (Input.consumeInteger(10, FDs.Read))
-    return std::nullopt;
-  if (!Input.consume_front(","))
-    return std::nullopt;
-  if (Input.consumeInteger(10, FDs.Write))
-    return std::nullopt;
-  if (!Input.empty() || !FDs.isValid())
-    return std::nullopt;
-  return FDs;
-}
-
-/// Parses the `MAKEFLAGS` environment variable string to find jobserver
-/// arguments. It splits the string into space-separated arguments and searches
-/// for `--jobserver-auth` or `--jobserver-fds`. Based on the value of these
-/// arguments, it determines the jobserver mode (Pipe, FIFO, or Semaphore) and
-/// connection details (file descriptors or path).
-Expected<JobserverConfig> parseNativeMakeFlags(StringRef MakeFlags) {
-  JobserverConfig Config;
-  if (MakeFlags.empty())
-    return Config;
-
-  // Split the MAKEFLAGS string into arguments.
-  SmallVector<StringRef, 8> Args;
-  SplitString(MakeFlags, Args);
-
-  // If '-n' (dry-run) is present as a legacy flag (not starting with '-'),
-  // disable the jobserver.
-  if (!Args.empty() && !Args[0].starts_with("-") && Args[0].contains('n'))
-    return Config;
-
-  // Iterate through arguments to find jobserver flags.
-  // Note that make may pass multiple --jobserver-auth flags; the last one wins.
-  for (StringRef Arg : Args) {
-    StringRef Value;
-    if (getPrefixedValue(Arg, "--jobserver-auth=", Value)) {
-      // Try to parse as a file descriptor pair first.
-      if (auto FDPair = getFileDescriptorPair(Value)) {
-        Config.TheMode = JobserverConfig::PosixPipe;
-        Config.PipeFDs = *FDPair;
-      } else {
-        StringRef FifoPath;
-        // If not FDs, try to parse as a named pipe (fifo).
-        if (getPrefixedValue(Value, "fifo:", FifoPath)) {
-          Config.TheMode = JobserverConfig::PosixFifo;
-          Config.Path = FifoPath.str();
-        } else {
-          // Otherwise, assume it's a Windows semaphore.
-          Config.TheMode = JobserverConfig::Win32Semaphore;
-          Config.Path = Value.str();
-        }
-      }
-    } else if (getPrefixedValue(Arg, "--jobserver-fds=", Value)) {
-      // This is an alternative, older syntax for the pipe-based server.
-      if (auto FDPair = getFileDescriptorPair(Value)) {
-        Config.TheMode = JobserverConfig::PosixPipe;
-        Config.PipeFDs = *FDPair;
-      } else {
-        return createStringError(inconvertibleErrorCode(),
-                                 "Invalid file descriptor pair in MAKEFLAGS");
-      }
-    }
-  }
-
-// Perform platform-specific validation.
-#ifdef _WIN32
-  if (Config.TheMode == JobserverConfig::PosixFifo ||
-      Config.TheMode == JobserverConfig::PosixPipe)
-    return createStringError(
-        inconvertibleErrorCode(),
-        "FIFO/Pipe-based jobserver is not supported on Windows");
-#else
-  if (Config.TheMode == JobserverConfig::Win32Semaphore)
-    return createStringError(
-        inconvertibleErrorCode(),
-        "Semaphore-based jobserver is not supported on this platform");
-#endif
-  return Config;
-}
-
-std::once_flag GJobserverOnceFlag;
-JobserverClient *GJobserver = nullptr;
-
-} // namespace
-
-namespace llvm {
-class JobserverClientImpl : public JobserverClient {
-  bool IsInitialized = false;
-  std::atomic<bool> HasImplicitSlot{true};
-  unsigned NumJobs = 0;
-
-public:
-  JobserverClientImpl(const JobserverConfig &Config);
-  ~JobserverClientImpl() override;
-
-  JobSlot tryAcquire() override;
-  void release(JobSlot Slot) override;
-  unsigned getNumJobs() const override { return NumJobs; }
-
-  bool isValid() const { return IsInitialized; }
-
-private:
-#if defined(LLVM_ON_UNIX)
-  int ReadFD = -1;
-  int WriteFD = -1;
-  std::string FifoPath;
-#elif defined(_WIN32)
-  void *Semaphore = nullptr;
-#endif
-};
-} // namespace llvm
-
-// Include the platform-specific parts of the class.
-#if defined(LLVM_ON_UNIX)
-#include "Unix/Jobserver.inc"
-#elif defined(_WIN32)
-#include "Windows/Jobserver.inc"
-#else
-// Dummy implementation for unsupported platforms.
-JobserverClientImpl::JobserverClientImpl(const JobserverConfig &Config) {}
-JobserverClientImpl::~JobserverClientImpl() = default;
-JobSlot JobserverClientImpl::tryAcquire() { return JobSlot(); }
-void JobserverClientImpl::release(JobSlot Slot) {}
-#endif
-
-namespace llvm {
-JobserverClient::~JobserverClient() = default;
-
-uint8_t JobSlot::getExplicitValue() const {
-  assert(isExplicit() && "Cannot get value of implicit or invalid slot");
-  return static_cast<uint8_t>(Value);
-}
-
-/// This is the main entry point for acquiring a jobserver client. It uses a
-/// std::call_once to ensure the singleton `GJobserver` instance is created
-/// safely in a multi-threaded environment. On first call, it reads the
-/// `MAKEFLAGS` environment variable, parses it, and attempts to construct and
-/// initialize a `JobserverClientImpl`. If successful, the global instance is
-/// stored in `GJobserver`. Subsequent calls will return the existing instance.
-JobserverClient *JobserverClient::getInstance() {
-  std::call_once(GJobserverOnceFlag, []() {
-    LLVM_DEBUG(
-        dbgs()
-        << "JobserverClient::getInstance() called for the first time.\n");
-    const char *MakeFlagsEnv = getenv("MAKEFLAGS");
-    if (!MakeFlagsEnv) {
-      errs() << "Warning: failed to create jobserver client due to MAKEFLAGS "
-                "environment variable not found\n";
-      return;
-    }
-
-    LLVM_DEBUG(dbgs() << "Found MAKEFLAGS = \"" << MakeFlagsEnv << "\"\n");
-
-    auto ConfigOrErr = parseNativeMakeFlags(MakeFlagsEnv);
-    if (Error Err = ConfigOrErr.takeError()) {
-      errs() << "Warning: failed to create jobserver client due to invalid "
-                "MAKEFLAGS environment variable: "
-             << toString(std::move(Err)) << "\n";
-      return;
-    }
-
-    JobserverConfig Config = *ConfigOrErr;
-    if (Config.TheMode == JobserverConfig::None) {
-      errs() << "Warning: failed to create jobserver client due to jobserver "
-                "mode missing in MAKEFLAGS environment variable\n";
-      return;
-    }
-
-    if (Config.TheMode == JobserverConfig::PosixPipe) {
-#if defined(LLVM_ON_UNIX)
-      if (!areFdsValid(Config.PipeFDs.Read, Config.PipeFDs.Write)) {
-        errs() << "Warning: failed to create jobserver client due to invalid "
-                  "Pipe FDs in MAKEFLAGS environment variable\n";
-        return;
-      }
-#endif
-    }
-
-    auto Client = std::make_unique<JobserverClientImpl>(Config);
-    if (Client->isValid()) {
-      LLVM_DEBUG(dbgs() << "Jobserver client created successfully!\n");
-      GJobserver = Client.release();
-    } else
-      errs() << "Warning: jobserver client initialization failed.\n";
-  });
-  return GJobserver;
-}
-
-/// For testing purposes only. This function resets the singleton instance by
-/// destroying the existing client and re-initializing the `std::once_flag`.
-/// This allows tests to simulate the first-time initialization of the
-/// jobserver client multiple times.
-void JobserverClient::resetForTesting() {
-  delete GJobserver;
-  GJobserver = nullptr;
-  // Re-construct the std::once_flag in place to reset the singleton state.
-  new (&GJobserverOnceFlag) std::once_flag();
-}
-} // namespace llvm

diff  --git a/llvm/lib/Support/Parallel.cpp b/llvm/lib/Support/Parallel.cpp
index 8e0c724accb36..3ac6fc74fd3e0 100644
--- a/llvm/lib/Support/Parallel.cpp
+++ b/llvm/lib/Support/Parallel.cpp
@@ -7,17 +7,12 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Support/Parallel.h"
-#include "llvm/ADT/ScopeExit.h"
 #include "llvm/Config/llvm-config.h"
-#include "llvm/Support/ExponentialBackoff.h"
-#include "llvm/Support/Jobserver.h"
 #include "llvm/Support/ManagedStatic.h"
 #include "llvm/Support/Threading.h"
 
 #include <atomic>
 #include <future>
-#include <memory>
-#include <mutex>
 #include <thread>
 #include <vector>
 
@@ -54,9 +49,6 @@ class Executor {
 class ThreadPoolExecutor : public Executor {
 public:
   explicit ThreadPoolExecutor(ThreadPoolStrategy S) {
-    if (S.UseJobserver)
-      TheJobserver = JobserverClient::getInstance();
-
     ThreadCount = S.compute_thread_count();
     // Spawn all but one of the threads in another thread as spawning threads
     // can take a while.
@@ -77,10 +69,6 @@ class ThreadPoolExecutor : public Executor {
     });
   }
 
-  // To make sure the thread pool executor can only be created with a parallel
-  // strategy.
-  ThreadPoolExecutor() = delete;
-
   void stop() {
     {
       std::lock_guard<std::mutex> Lock(Mutex);
@@ -123,62 +111,15 @@ class ThreadPoolExecutor : public Executor {
   void work(ThreadPoolStrategy S, unsigned ThreadID) {
     threadIndex = ThreadID;
     S.apply_thread_strategy(ThreadID);
-    // Note on jobserver deadlock avoidance:
-    // GNU Make grants each invoked process one implicit job slot. Our
-    // JobserverClient models this by returning an implicit JobSlot on the
-    // first successful tryAcquire() in a process. This guarantees forward
-    // progress without requiring a dedicated "always-on" thread here.
-
-    static thread_local std::unique_ptr<ExponentialBackoff> Backoff;
-
     while (true) {
-      if (TheJobserver) {
-        // Jobserver-mode scheduling:
-        // - Acquire one job slot (with exponential backoff to avoid busy-wait).
-        // - While holding the slot, drain and run tasks from the local queue.
-        // - Release the slot when the queue is empty or when shutting down.
-        // Rationale: Holding a slot amortizes acquire/release overhead over
-        // multiple tasks and avoids requeue/yield churn, while still enforcing
-        // the jobserver’s global concurrency limit. With K available slots,
-        // up to K workers run tasks in parallel; within each worker tasks run
-        // sequentially until the local queue is empty.
-        ExponentialBackoff Backoff(std::chrono::hours(24));
-        JobSlot Slot;
-        do {
-          if (Stop)
-            return;
-          Slot = TheJobserver->tryAcquire();
-          if (Slot.isValid())
-            break;
-        } while (Backoff.waitForNextAttempt());
-
-        auto SlotReleaser = llvm::make_scope_exit(
-            [&] { TheJobserver->release(std::move(Slot)); });
-
-        while (true) {
-          std::function<void()> Task;
-          {
-            std::unique_lock<std::mutex> Lock(Mutex);
-            Cond.wait(Lock, [&] { return Stop || !WorkStack.empty(); });
-            if (Stop && WorkStack.empty())
-              return;
-            if (WorkStack.empty())
-              break;
-            Task = std::move(WorkStack.back());
-            WorkStack.pop_back();
-          }
-          Task();
-        }
-      } else {
-        std::unique_lock<std::mutex> Lock(Mutex);
-        Cond.wait(Lock, [&] { return Stop || !WorkStack.empty(); });
-        if (Stop)
-          break;
-        auto Task = std::move(WorkStack.back());
-        WorkStack.pop_back();
-        Lock.unlock();
-        Task();
-      }
+      std::unique_lock<std::mutex> Lock(Mutex);
+      Cond.wait(Lock, [&] { return Stop || !WorkStack.empty(); });
+      if (Stop)
+        break;
+      auto Task = std::move(WorkStack.back());
+      WorkStack.pop_back();
+      Lock.unlock();
+      Task();
     }
   }
 
@@ -189,20 +130,9 @@ class ThreadPoolExecutor : public Executor {
   std::promise<void> ThreadsCreated;
   std::vector<std::thread> Threads;
   unsigned ThreadCount;
-
-  JobserverClient *TheJobserver = nullptr;
 };
 
-// A global raw pointer to the executor. Lifetime is managed by the
-// objects created within createExecutor().
-static Executor *TheExec = nullptr;
-static std::once_flag Flag;
-
-// This function will be called exactly once to create the executor.
-// It contains the necessary platform-specific logic. Since functions
-// called by std::call_once cannot return value, we have to set the
-// executor as a global variable.
-void createExecutor() {
+Executor *Executor::getDefaultExecutor() {
 #ifdef _WIN32
   // The ManagedStatic enables the ThreadPoolExecutor to be stopped via
   // llvm_shutdown() which allows a "clean" fast exit, e.g. via _exit(). This
@@ -226,22 +156,16 @@ void createExecutor() {
                        ThreadPoolExecutor::Deleter>
       ManagedExec;
   static std::unique_ptr<ThreadPoolExecutor> Exec(&(*ManagedExec));
-  TheExec = Exec.get();
+  return Exec.get();
 #else
   // ManagedStatic is not desired on other platforms. When `Exec` is destroyed
   // by llvm_shutdown(), worker threads will clean up and invoke TLS
   // destructors. This can lead to race conditions if other threads attempt to
   // access TLS objects that have already been destroyed.
   static ThreadPoolExecutor Exec(strategy);
-  TheExec = &Exec;
+  return &Exec;
 #endif
 }
-
-Executor *Executor::getDefaultExecutor() {
-  // Use std::call_once to lazily and safely initialize the executor.
-  std::call_once(Flag, createExecutor);
-  return TheExec;
-}
 } // namespace
 } // namespace detail
 

diff  --git a/llvm/lib/Support/ThreadPool.cpp b/llvm/lib/Support/ThreadPool.cpp
index 69602688cf3fd..c304f0f45360b 100644
--- a/llvm/lib/Support/ThreadPool.cpp
+++ b/llvm/lib/Support/ThreadPool.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 //
-//
 // This file implements a crude C++11 based thread pool.
 //
 //===----------------------------------------------------------------------===//
@@ -15,8 +14,6 @@
 
 #include "llvm/Config/llvm-config.h"
 
-#include "llvm/ADT/ScopeExit.h"
-#include "llvm/Support/ExponentialBackoff.h"
 #include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/Threading.h"
 #include "llvm/Support/raw_ostream.h"
@@ -36,10 +33,7 @@ ThreadPoolInterface::~ThreadPoolInterface() = default;
 #if LLVM_ENABLE_THREADS
 
 StdThreadPool::StdThreadPool(ThreadPoolStrategy S)
-    : Strategy(S), MaxThreadCount(S.compute_thread_count()) {
-  if (Strategy.UseJobserver)
-    TheJobserver = JobserverClient::getInstance();
-}
+    : Strategy(S), MaxThreadCount(S.compute_thread_count()) {}
 
 void StdThreadPool::grow(int requested) {
   llvm::sys::ScopedWriter LockGuard(ThreadsLock);
@@ -51,15 +45,7 @@ void StdThreadPool::grow(int requested) {
     Threads.emplace_back([this, ThreadID] {
       set_thread_name(formatv("llvm-worker-{0}", ThreadID));
       Strategy.apply_thread_strategy(ThreadID);
-      // Note on jobserver deadlock avoidance:
-      // GNU Make grants each invoked process one implicit job slot.
-      // JobserverClient::tryAcquire() returns that implicit slot on the first
-      // successful call in a process, ensuring forward progress without a
-      // dedicated "always-on" thread.
-      if (TheJobserver)
-        processTasksWithJobserver();
-      else
-        processTasks(nullptr);
+      processTasks(nullptr);
     });
   }
 }
@@ -147,96 +133,6 @@ void StdThreadPool::processTasks(ThreadPoolTaskGroup *WaitingForGroup) {
   }
 }
 
-/// Main loop for worker threads when using a jobserver.
-/// This function uses a two-level queue; it first acquires a job slot from the
-/// external jobserver, then retrieves a task from the internal queue.
-/// This allows the thread pool to cooperate with build systems like `make -j`.
-void StdThreadPool::processTasksWithJobserver() {
-  while (true) {
-    // Acquire a job slot from the external jobserver.
-    // This polls for a slot and yields the thread to avoid a high-CPU wait.
-    JobSlot Slot;
-    // The timeout for the backoff can be very long, as the shutdown
-    // is checked on each iteration. The sleep duration is capped by MaxWait
-    // in ExponentialBackoff, so shutdown latency is not a problem.
-    ExponentialBackoff Backoff(std::chrono::hours(24));
-    bool AcquiredToken = false;
-    do {
-      // Return if the thread pool is shutting down.
-      {
-        std::unique_lock<std::mutex> LockGuard(QueueLock);
-        if (!EnableFlag)
-          return;
-      }
-
-      Slot = TheJobserver->tryAcquire();
-      if (Slot.isValid()) {
-        AcquiredToken = true;
-        break;
-      }
-    } while (Backoff.waitForNextAttempt());
-
-    if (!AcquiredToken) {
-      // This is practically unreachable with a 24h timeout and indicates a
-      // deeper problem if hit.
-      report_fatal_error("Timed out waiting for jobserver token.");
-    }
-
-    // `make_scope_exit` guarantees the job slot is released, even if the
-    // task throws or we exit early. This prevents deadlocking the build.
-    auto SlotReleaser =
-        make_scope_exit([&] { TheJobserver->release(std::move(Slot)); });
-
-    // While we hold a job slot, process tasks from the internal queue.
-    while (true) {
-      std::function<void()> Task;
-      ThreadPoolTaskGroup *GroupOfTask = nullptr;
-
-      {
-        std::unique_lock<std::mutex> LockGuard(QueueLock);
-
-        // Wait until a task is available or the pool is shutting down.
-        QueueCondition.wait(LockGuard,
-                            [&] { return !EnableFlag || !Tasks.empty(); });
-
-        // If shutting down and the queue is empty, the thread can terminate.
-        if (!EnableFlag && Tasks.empty())
-          return;
-
-        // If the queue is empty, we're done processing tasks for now.
-        // Break the inner loop to release the job slot.
-        if (Tasks.empty())
-          break;
-
-        // A task is available. Mark it as active before releasing the lock
-        // to prevent race conditions with `wait()`.
-        ++ActiveThreads;
-        Task = std::move(Tasks.front().first);
-        GroupOfTask = Tasks.front().second;
-        if (GroupOfTask != nullptr)
-          ++ActiveGroups[GroupOfTask];
-        Tasks.pop_front();
-      } // The queue lock is released.
-
-      // Run the task. The job slot remains acquired during execution.
-      Task();
-
-      // The task has finished. Update the active count and notify any waiters.
-      {
-        std::lock_guard<std::mutex> LockGuard(QueueLock);
-        --ActiveThreads;
-        if (GroupOfTask != nullptr) {
-          auto A = ActiveGroups.find(GroupOfTask);
-          if (--(A->second) == 0)
-            ActiveGroups.erase(A);
-        }
-        // If all tasks are complete, notify any waiting threads.
-        if (workCompletedUnlocked(nullptr))
-          CompletionCondition.notify_all();
-      }
-    }
-  }
-}
 bool StdThreadPool::workCompletedUnlocked(ThreadPoolTaskGroup *Group) const {
   if (Group == nullptr)
     return !ActiveThreads && Tasks.empty();

diff  --git a/llvm/lib/Support/Threading.cpp b/llvm/lib/Support/Threading.cpp
index 9da357a7ebb91..693de0e6400fb 100644
--- a/llvm/lib/Support/Threading.cpp
+++ b/llvm/lib/Support/Threading.cpp
@@ -14,7 +14,6 @@
 #include "llvm/Support/Threading.h"
 #include "llvm/Config/config.h"
 #include "llvm/Config/llvm-config.h"
-#include "llvm/Support/Jobserver.h"
 
 #include <cassert>
 #include <optional>
@@ -52,10 +51,6 @@ int llvm::get_physical_cores() { return -1; }
 static int computeHostNumHardwareThreads();
 
 unsigned llvm::ThreadPoolStrategy::compute_thread_count() const {
-  if (UseJobserver)
-    if (auto JS = JobserverClient::getInstance())
-      return JS->getNumJobs();
-
   int MaxThreadCount =
       UseHyperThreads ? computeHostNumHardwareThreads() : get_physical_cores();
   if (MaxThreadCount <= 0)

diff  --git a/llvm/lib/Support/Unix/Jobserver.inc b/llvm/lib/Support/Unix/Jobserver.inc
deleted file mode 100644
index 53bf7f288ca1f..0000000000000
--- a/llvm/lib/Support/Unix/Jobserver.inc
+++ /dev/null
@@ -1,195 +0,0 @@
-//===- llvm/Support/Unix/Jobserver.inc - Unix Jobserver Impl ----*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// This file implements the UNIX-specific parts of the JobserverClient class.
-//
-//===----------------------------------------------------------------------===//
-
-#include <atomic>
-#include <cassert>
-#include <cerrno>
-#include <fcntl.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-namespace {
-/// Returns true if the given file descriptor is a FIFO (named pipe).
-bool isFifo(int FD) {
-  struct stat StatBuf;
-  if (::fstat(FD, &StatBuf) != 0)
-    return false;
-  return S_ISFIFO(StatBuf.st_mode);
-}
-
-/// Returns true if the given file descriptors are valid.
-bool areFdsValid(int ReadFD, int WriteFD) {
-  if (ReadFD == -1 || WriteFD == -1)
-    return false;
-  // Check if the file descriptors are actually valid by checking their flags.
-  return ::fcntl(ReadFD, F_GETFD) != -1 && ::fcntl(WriteFD, F_GETFD) != -1;
-}
-} // namespace
-
-/// The constructor sets up the client based on the provided configuration.
-/// For pipe-based jobservers, it duplicates the inherited file descriptors,
-/// sets them to close-on-exec, and makes the read descriptor non-blocking.
-/// For FIFO-based jobservers, it opens the named pipe. After setup, it drains
-/// all available tokens from the jobserver to determine the total number of
-/// available jobs (`NumJobs`), then immediately releases them back.
-JobserverClientImpl::JobserverClientImpl(const JobserverConfig &Config) {
-  switch (Config.TheMode) {
-  case JobserverConfig::PosixPipe: {
-    // Duplicate the read and write file descriptors.
-    int NewReadFD = ::dup(Config.PipeFDs.Read);
-    if (NewReadFD < 0)
-      return;
-    int NewWriteFD = ::dup(Config.PipeFDs.Write);
-    if (NewWriteFD < 0) {
-      ::close(NewReadFD);
-      return;
-    }
-    // Set the new descriptors to be closed automatically on exec().
-    if (::fcntl(NewReadFD, F_SETFD, FD_CLOEXEC) == -1 ||
-        ::fcntl(NewWriteFD, F_SETFD, FD_CLOEXEC) == -1) {
-      ::close(NewReadFD);
-      ::close(NewWriteFD);
-      return;
-    }
-    // Set the read descriptor to non-blocking.
-    int flags = ::fcntl(NewReadFD, F_GETFL, 0);
-    if (flags == -1 || ::fcntl(NewReadFD, F_SETFL, flags | O_NONBLOCK) == -1) {
-      ::close(NewReadFD);
-      ::close(NewWriteFD);
-      return;
-    }
-    ReadFD = NewReadFD;
-    WriteFD = NewWriteFD;
-    break;
-  }
-  case JobserverConfig::PosixFifo:
-    // Open the FIFO for reading. It must be non-blocking and close-on-exec.
-    ReadFD = ::open(Config.Path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC);
-    if (ReadFD < 0 || !isFifo(ReadFD)) {
-      if (ReadFD >= 0)
-        ::close(ReadFD);
-      ReadFD = -1;
-      return;
-    }
-    FifoPath = Config.Path;
-    // The write FD is opened on-demand in release().
-    WriteFD = -1;
-    break;
-  default:
-    return;
-  }
-
-  IsInitialized = true;
-  // Determine the total number of jobs by acquiring all available slots and
-  // then immediately releasing them.
-  SmallVector<JobSlot, 8> Slots;
-  while (true) {
-    auto S = tryAcquire();
-    if (!S.isValid())
-      break;
-    Slots.push_back(std::move(S));
-  }
-  NumJobs = Slots.size();
-  assert(NumJobs >= 1 && "Invalid number of jobs");
-  for (auto &S : Slots)
-    release(std::move(S));
-}
-
-/// The destructor closes any open file descriptors.
-JobserverClientImpl::~JobserverClientImpl() {
-  if (ReadFD >= 0)
-    ::close(ReadFD);
-  if (WriteFD >= 0)
-    ::close(WriteFD);
-}
-
-/// Tries to acquire a job slot. The first call to this function will always
-/// successfully acquire the single "implicit" slot that is granted to every
-/// process started by `make`. Subsequent calls attempt to read a one-byte
-/// token from the jobserver's read pipe. A successful read grants one
-/// explicit job slot. The read is non-blocking; if no token is available,
-/// it fails and returns an invalid JobSlot.
-JobSlot JobserverClientImpl::tryAcquire() {
-  if (!IsInitialized)
-    return JobSlot();
-
-  // The first acquisition is always for the implicit slot.
-  if (HasImplicitSlot.exchange(false, std::memory_order_acquire)) {
-    LLVM_DEBUG(dbgs() << "Acquired implicit job slot.\n");
-    return JobSlot::createImplicit();
-  }
-
-  char Token;
-  ssize_t Ret;
-  LLVM_DEBUG(dbgs() << "Attempting to read token from FD " << ReadFD << ".\n");
-  // Loop to retry on EINTR (interrupted system call).
-  do {
-    Ret = ::read(ReadFD, &Token, 1);
-  } while (Ret < 0 && errno == EINTR);
-
-  if (Ret == 1) {
-    LLVM_DEBUG(dbgs() << "Acquired explicit token '" << Token << "'.\n");
-    return JobSlot::createExplicit(static_cast<uint8_t>(Token));
-  }
-
-  LLVM_DEBUG(dbgs() << "Failed to acquire job slot, read returned " << Ret
-                    << ".\n");
-  return JobSlot();
-}
-
-/// Releases a job slot back to the pool. If the slot is implicit, it simply
-/// resets a flag. If the slot is explicit, it writes the character token
-/// associated with the slot back into the jobserver's write pipe. For FIFO
-/// jobservers, this may require opening the FIFO for writing if it hasn't
-/// been already.
-void JobserverClientImpl::release(JobSlot Slot) {
-  if (!Slot.isValid())
-    return;
-
-  // Releasing the implicit slot just makes it available for the next acquire.
-  if (Slot.isImplicit()) {
-    LLVM_DEBUG(dbgs() << "Released implicit job slot.\n");
-    [[maybe_unused]] bool was_already_released =
-        HasImplicitSlot.exchange(true, std::memory_order_release);
-    assert(!was_already_released && "Implicit slot released twice");
-    return;
-  }
-
-  uint8_t Token = Slot.getExplicitValue();
-  LLVM_DEBUG(dbgs() << "Releasing explicit token '" << (char)Token << "' to FD "
-                    << WriteFD << ".\n");
-
-  // For FIFO-based jobservers, the write FD might not be open yet.
-  // Open it on the first release.
-  if (WriteFD < 0) {
-    LLVM_DEBUG(dbgs() << "WriteFD is invalid, opening FIFO: " << FifoPath
-                      << "\n");
-    WriteFD = ::open(FifoPath.c_str(), O_WRONLY | O_CLOEXEC);
-    if (WriteFD < 0) {
-      LLVM_DEBUG(dbgs() << "Failed to open FIFO for writing.\n");
-      return;
-    }
-    LLVM_DEBUG(dbgs() << "Opened FIFO as new WriteFD: " << WriteFD << "\n");
-  }
-
-  ssize_t Written;
-  // Loop to retry on EINTR (interrupted system call).
-  do {
-    Written = ::write(WriteFD, &Token, 1);
-  } while (Written < 0 && errno == EINTR);
-
-  if (Written <= 0) {
-    LLVM_DEBUG(dbgs() << "Failed to write token to pipe, write returned "
-                      << Written << "\n");
-  }
-}

diff  --git a/llvm/lib/Support/Windows/Jobserver.inc b/llvm/lib/Support/Windows/Jobserver.inc
deleted file mode 100644
index 79028eee4b302..0000000000000
--- a/llvm/lib/Support/Windows/Jobserver.inc
+++ /dev/null
@@ -1,79 +0,0 @@
-//==- llvm/Support/Windows/Jobserver.inc - Windows Jobserver Impl -*- C++ -*-=//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// This file implements the Windows-specific parts of the JobserverClient class.
-// On Windows, the jobserver is implemented using a named semaphore.
-//
-//===----------------------------------------------------------------------===//
-
-#include "llvm/Support/Windows/WindowsSupport.h"
-#include <atomic>
-#include <cassert>
-
-namespace llvm {
-/// The constructor for the Windows jobserver client. It attempts to open a
-/// handle to an existing named semaphore, the name of which is provided by
-/// GNU make in the --jobserver-auth argument. If the semaphore is opened
-/// successfully, the client is marked as initialized.
-JobserverClientImpl::JobserverClientImpl(const JobserverConfig &Config) {
-  Semaphore = (void *)::OpenSemaphoreA(SEMAPHORE_MODIFY_STATE | SYNCHRONIZE,
-                                       FALSE, Config.Path.c_str());
-  if (Semaphore != nullptr)
-    IsInitialized = true;
-}
-
-/// The destructor closes the handle to the semaphore, releasing the resource.
-JobserverClientImpl::~JobserverClientImpl() {
-  if (Semaphore != nullptr)
-    ::CloseHandle((HANDLE)Semaphore);
-}
-
-/// Tries to acquire a job slot. The first call always returns the implicit
-/// slot. Subsequent calls use a non-blocking wait on the semaphore
-/// (`WaitForSingleObject` with a timeout of 0). If the wait succeeds, the
-/// semaphore's count is decremented, and an explicit job slot is acquired.
-/// If the wait times out, it means no slots are available, and an invalid
-/// slot is returned.
-JobSlot JobserverClientImpl::tryAcquire() {
-  if (!IsInitialized)
-    return JobSlot();
-
-  // First, grant the implicit slot.
-  if (HasImplicitSlot.exchange(false, std::memory_order_acquire)) {
-    return JobSlot::createImplicit();
-  }
-
-  // Try to acquire a slot from the semaphore without blocking.
-  if (::WaitForSingleObject((HANDLE)Semaphore, 0) == WAIT_OBJECT_0) {
-    // The explicit token value is arbitrary on Windows, as the semaphore
-    // count is the real resource.
-    return JobSlot::createExplicit(1);
-  }
-
-  return JobSlot(); // Invalid slot
-}
-
-/// Releases a job slot back to the pool. If the slot is implicit, it simply
-/// resets a flag. For an explicit slot, it increments the semaphore's count
-/// by one using `ReleaseSemaphore`, making the slot available to other
-/// processes.
-void JobserverClientImpl::release(JobSlot Slot) {
-  if (!IsInitialized || !Slot.isValid())
-    return;
-
-  if (Slot.isImplicit()) {
-    [[maybe_unused]] bool was_already_released =
-        HasImplicitSlot.exchange(true, std::memory_order_release);
-    assert(!was_already_released && "Implicit slot released twice");
-    return;
-  }
-
-  // Release the slot by incrementing the semaphore count.
-  (void)::ReleaseSemaphore((HANDLE)Semaphore, 1, NULL);
-}
-} // namespace llvm

diff  --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt
index 25efa00c5abfd..d1dfb1dc4a722 100644
--- a/llvm/unittests/Support/CMakeLists.txt
+++ b/llvm/unittests/Support/CMakeLists.txt
@@ -52,7 +52,6 @@ add_llvm_unittest(SupportTests
   IndexedAccessorTest.cpp
   InstructionCostTest.cpp
   InterleavedRangeTest.cpp
-  JobserverTest.cpp
   JSONTest.cpp
   KnownBitsTest.cpp
   LEB128Test.cpp

diff  --git a/llvm/unittests/Support/JobserverTest.cpp b/llvm/unittests/Support/JobserverTest.cpp
deleted file mode 100644
index 36d5f2e68dab5..0000000000000
--- a/llvm/unittests/Support/JobserverTest.cpp
+++ /dev/null
@@ -1,437 +0,0 @@
-//===- llvm/unittest/Support/JobserverTest.cpp ----------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// Jobserver.h unit tests.
-///
-//===----------------------------------------------------------------------===//
-
-#include "llvm/Support/Jobserver.h"
-#include "llvm/Config/llvm-config.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/Parallel.h"
-#include "llvm/Support/ThreadPool.h"
-#include "llvm/Support/raw_ostream.h"
-#include "gtest/gtest.h"
-#include <future>
-#include <random>
-#include <stdlib.h>
-
-#if defined(LLVM_ON_UNIX)
-#include "llvm/ADT/SmallString.h"
-#include "llvm/Support/FileSystem.h"
-#include <atomic>
-#include <condition_variable>
-#include <fcntl.h>
-#include <mutex>
-#include <sys/stat.h>
-#include <thread>
-#include <unistd.h>
-#elif defined(_WIN32)
-#include <windows.h>
-#endif
-
-#define DEBUG_TYPE "jobserver-test"
-
-using namespace llvm;
-
-namespace {
-
-// RAII helper to set an environment variable for the duration of a test.
-class ScopedEnvironment {
-  std::string Name;
-  std::string OldValue;
-  bool HadOldValue;
-
-public:
-  ScopedEnvironment(const char *Name, const char *Value) : Name(Name) {
-#if defined(_WIN32)
-    char *Old = nullptr;
-    size_t OldLen;
-    errno_t err = _dupenv_s(&Old, &OldLen, Name);
-    if (err == 0 && Old != nullptr) {
-      HadOldValue = true;
-      OldValue = Old;
-      free(Old);
-    } else {
-      HadOldValue = false;
-    }
-    _putenv_s(Name, Value);
-#else
-    const char *Old = getenv(Name);
-    if (Old) {
-      HadOldValue = true;
-      OldValue = Old;
-    } else {
-      HadOldValue = false;
-    }
-    setenv(Name, Value, 1);
-#endif
-  }
-
-  ~ScopedEnvironment() {
-#if defined(_WIN32)
-    if (HadOldValue)
-      _putenv_s(Name.c_str(), OldValue.c_str());
-    else
-      // On Windows, setting an environment variable to an empty string
-      // unsets it, making getenv() return NULL.
-      _putenv_s(Name.c_str(), "");
-#else
-    if (HadOldValue)
-      setenv(Name.c_str(), OldValue.c_str(), 1);
-    else
-      unsetenv(Name.c_str());
-#endif
-  }
-};
-
-TEST(Jobserver, Slot) {
-  // Default constructor creates an invalid slot.
-  JobSlot S1;
-  EXPECT_FALSE(S1.isValid());
-  EXPECT_FALSE(S1.isImplicit());
-
-  // Create an implicit slot.
-  JobSlot S2 = JobSlot::createImplicit();
-  EXPECT_TRUE(S2.isValid());
-  EXPECT_TRUE(S2.isImplicit());
-
-  // Create an explicit slot.
-  JobSlot S3 = JobSlot::createExplicit(42);
-  EXPECT_TRUE(S3.isValid());
-  EXPECT_FALSE(S3.isImplicit());
-
-  // Test move construction.
-  JobSlot S4 = std::move(S2);
-  EXPECT_TRUE(S4.isValid());
-  EXPECT_TRUE(S4.isImplicit());
-  EXPECT_FALSE(S2.isValid()); // S2 is now invalid.
-
-  // Test move assignment.
-  S1 = std::move(S3);
-  EXPECT_TRUE(S1.isValid());
-  EXPECT_FALSE(S1.isImplicit());
-  EXPECT_FALSE(S3.isValid()); // S3 is now invalid.
-}
-
-// Test fixture for parsing tests to ensure the singleton state is
-// reset between each test case.
-class JobserverParsingTest : public ::testing::Test {
-protected:
-  void TearDown() override { JobserverClient::resetForTesting(); }
-};
-
-TEST_F(JobserverParsingTest, NoMakeflags) {
-  // No MAKEFLAGS, should be null.
-  ScopedEnvironment Env("MAKEFLAGS", "");
-  // On Unix, setting an env var to "" makes getenv() return an empty
-  // string, not NULL. We must call unsetenv() to test the case where
-  // the variable is truly not present.
-#if !defined(_WIN32)
-  unsetenv("MAKEFLAGS");
-#endif
-  EXPECT_EQ(JobserverClient::getInstance(), nullptr);
-}
-
-TEST_F(JobserverParsingTest, EmptyMakeflags) {
-  // Empty MAKEFLAGS, should be null.
-  ScopedEnvironment Env("MAKEFLAGS", "");
-  EXPECT_EQ(JobserverClient::getInstance(), nullptr);
-}
-
-TEST_F(JobserverParsingTest, DryRunFlag) {
-  // Dry-run flag 'n', should be null.
-  ScopedEnvironment Env("MAKEFLAGS", "n -j --jobserver-auth=fifo:/tmp/foo");
-  EXPECT_EQ(JobserverClient::getInstance(), nullptr);
-}
-
-// Separate fixture for non-threaded client tests.
-class JobserverClientTest : public JobserverParsingTest {};
-
-#if defined(LLVM_ON_UNIX)
-// RAII helper to create and clean up a temporary FIFO file.
-class ScopedFifo {
-  SmallString<128> Path;
-  bool IsValid = false;
-
-public:
-  ScopedFifo() {
-    // To get a unique, non-colliding name for a FIFO, we use the
-    // createTemporaryFile function to reserve a name in the filesystem.
-    std::error_code EC =
-        sys::fs::createTemporaryFile("jobserver-test", "fifo", Path);
-    if (EC)
-      return;
-    // Then we immediately remove the regular file it created, but keep the
-    // unique path.
-    sys::fs::remove(Path);
-    // Finally, we create the FIFO at that safe, unique path.
-    if (mkfifo(Path.c_str(), 0600) != 0)
-      return;
-    IsValid = true;
-  }
-
-  ~ScopedFifo() {
-    if (IsValid)
-      sys::fs::remove(Path);
-  }
-
-  const char *c_str() const { return Path.data(); }
-  bool isValid() const { return IsValid; }
-};
-
-TEST_F(JobserverClientTest, UnixClientFifo) {
-  // This test covers basic FIFO client creation and behavior with an empty
-  // FIFO. No job tokens are available.
-  ScopedFifo F;
-  ASSERT_TRUE(F.isValid());
-
-  // Intentionally inserted \t in environment string.
-  std::string Makeflags = " \t -j4\t \t--jobserver-auth=fifo:";
-  Makeflags += F.c_str();
-  ScopedEnvironment Env("MAKEFLAGS", Makeflags.c_str());
-
-  JobserverClient *Client = JobserverClient::getInstance();
-  ASSERT_NE(Client, nullptr);
-
-  // Get the implicit token.
-  JobSlot S1 = Client->tryAcquire();
-  EXPECT_TRUE(S1.isValid());
-  EXPECT_TRUE(S1.isImplicit());
-
-  // FIFO is empty, next acquire fails.
-  JobSlot S2 = Client->tryAcquire();
-  EXPECT_FALSE(S2.isValid());
-
-  // Release does not write to the pipe for the implicit token.
-  Client->release(std::move(S1));
-
-  // Re-acquire the implicit token.
-  S1 = Client->tryAcquire();
-  EXPECT_TRUE(S1.isValid());
-}
-
-#if LLVM_ENABLE_THREADS
-// Test fixture for tests that use the jobserver strategy. It creates a
-// temporary FIFO, sets MAKEFLAGS, and provides a helper to pre-load the FIFO
-// with job tokens, simulating `make -jN`.
-class JobserverStrategyTest : public JobserverParsingTest {
-protected:
-  std::unique_ptr<ScopedFifo> TheFifo;
-  std::thread MakeThread;
-  std::atomic<bool> StopMakeThread{false};
-
-  void SetUp() override {
-    TheFifo = std::make_unique<ScopedFifo>();
-    ASSERT_TRUE(TheFifo->isValid());
-
-    std::string MakeFlags = "--jobserver-auth=fifo:";
-    MakeFlags += TheFifo->c_str();
-    setenv("MAKEFLAGS", MakeFlags.c_str(), 1);
-  }
-
-  void TearDown() override {
-    if (MakeThread.joinable()) {
-      StopMakeThread = true;
-      MakeThread.join();
-    }
-    unsetenv("MAKEFLAGS");
-    TheFifo.reset();
-    JobserverClient::resetForTesting();
-  }
-
-  // Starts a background thread that emulates `make`. It populates the FIFO
-  // with initial tokens and then recycles tokens released by clients.
-  void startMakeProxy(int NumInitialJobs) {
-    MakeThread = std::thread([this, NumInitialJobs]() {
-      LLVM_DEBUG(dbgs() << "[MakeProxy] Thread started.\n");
-      // Open the FIFO for reading and writing. This call does not block.
-      int RWFd = open(TheFifo->c_str(), O_RDWR);
-      LLVM_DEBUG(dbgs() << "[MakeProxy] Opened FIFO " << TheFifo->c_str()
-                        << " with O_RDWR, FD=" << RWFd << "\n");
-      if (RWFd == -1) {
-        LLVM_DEBUG(
-            dbgs()
-            << "[MakeProxy] ERROR: Failed to open FIFO with O_RDWR. Errno: "
-            << errno << "\n");
-        return;
-      }
-
-      // Populate with initial jobs.
-      LLVM_DEBUG(dbgs() << "[MakeProxy] Writing " << NumInitialJobs
-                        << " initial tokens.\n");
-      for (int i = 0; i < NumInitialJobs; ++i) {
-        if (write(RWFd, "+", 1) != 1) {
-          LLVM_DEBUG(dbgs()
-                     << "[MakeProxy] ERROR: Failed to write initial token " << i
-                     << ".\n");
-          close(RWFd);
-          return;
-        }
-      }
-      LLVM_DEBUG(dbgs() << "[MakeProxy] Finished writing initial tokens.\n");
-
-      // Make the read non-blocking so we can periodically check StopMakeThread.
-      int flags = fcntl(RWFd, F_GETFL, 0);
-      fcntl(RWFd, F_SETFL, flags | O_NONBLOCK);
-
-      while (!StopMakeThread) {
-        char Token;
-        ssize_t Ret = read(RWFd, &Token, 1);
-        if (Ret == 1) {
-          LLVM_DEBUG(dbgs() << "[MakeProxy] Read token '" << Token
-                            << "' to recycle.\n");
-          // A client released a token, 'make' makes it available again.
-          std::this_thread::sleep_for(std::chrono::microseconds(100));
-          ssize_t WRet;
-          do {
-            WRet = write(RWFd, &Token, 1);
-          } while (WRet < 0 && errno == EINTR);
-          if (WRet <= 0) {
-            LLVM_DEBUG(
-                dbgs()
-                << "[MakeProxy] ERROR: Failed to write recycled token.\n");
-            break; // Error, stop the proxy.
-          }
-          LLVM_DEBUG(dbgs()
-                     << "[MakeProxy] Wrote token '" << Token << "' back.\n");
-        } else if (Ret < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
-          LLVM_DEBUG(dbgs() << "[MakeProxy] ERROR: Read failed with errno "
-                            << errno << ".\n");
-          break; // Error, stop the proxy.
-        }
-        // Yield to prevent this thread from busy-waiting.
-        std::this_thread::sleep_for(std::chrono::milliseconds(1));
-      }
-      LLVM_DEBUG(dbgs() << "[MakeProxy] Thread stopping.\n");
-      close(RWFd);
-    });
-
-    // Give the proxy thread a moment to start and populate the FIFO.
-    // This is a simple way to avoid a race condition where the client starts
-    // before the initial tokens are in the pipe.
-    std::this_thread::sleep_for(std::chrono::milliseconds(50));
-  }
-};
-
-TEST_F(JobserverStrategyTest, ThreadPoolConcurrencyIsLimited) {
-  // This test simulates `make -j3`. We will have 1 implicit job slot and
-  // we will add 2 explicit job tokens to the FIFO, for a total of 3.
-  const int NumExplicitJobs = 2;
-  const int ConcurrencyLimit = NumExplicitJobs + 1; // +1 for the implicit slot
-  const int NumTasks = 8; // More tasks than available slots.
-
-  LLVM_DEBUG(dbgs() << "Calling startMakeProxy with " << NumExplicitJobs
-                    << " jobs.\n");
-  startMakeProxy(NumExplicitJobs);
-  LLVM_DEBUG(dbgs() << "MakeProxy is running.\n");
-
-  // Create the thread pool. Its constructor will call jobserver_concurrency()
-  // and create a client that reads from our pre-loaded FIFO.
-  StdThreadPool Pool(jobserver_concurrency());
-
-  std::atomic<int> ActiveTasks{0};
-  std::atomic<int> MaxActiveTasks{0};
-  std::atomic<int> CompletedTasks{0};
-  std::mutex M;
-  std::condition_variable CV;
-
-  // Dispatch more tasks than there are job slots. The pool should block
-  // and only run up to `ConcurrencyLimit` tasks at once.
-  for (int i = 0; i < NumTasks; ++i) {
-    Pool.async([&, i] {
-      // Track the number of concurrently running tasks.
-      int CurrentActive = ++ActiveTasks;
-      LLVM_DEBUG(dbgs() << "Task " << i << ": Active tasks: " << CurrentActive
-                        << "\n");
-      int OldMax = MaxActiveTasks.load();
-      while (CurrentActive > OldMax)
-        MaxActiveTasks.compare_exchange_weak(OldMax, CurrentActive);
-
-      std::this_thread::sleep_for(std::chrono::milliseconds(25));
-
-      --ActiveTasks;
-      if (++CompletedTasks == NumTasks) {
-        std::lock_guard<std::mutex> Lock(M);
-        CV.notify_one();
-      }
-    });
-  }
-
-  // Wait for all tasks to complete.
-  std::unique_lock<std::mutex> Lock(M);
-  CV.wait(Lock, [&] { return CompletedTasks == NumTasks; });
-
-  LLVM_DEBUG(dbgs() << "Test finished. Max active tasks was " << MaxActiveTasks
-                    << ".\n");
-  // The key assertion: the maximum number of concurrent tasks should
-  // not have exceeded the limit imposed by the jobserver.
-  EXPECT_LE(MaxActiveTasks, ConcurrencyLimit);
-  EXPECT_EQ(CompletedTasks, NumTasks);
-}
-
-TEST_F(JobserverStrategyTest, ParallelForIsLimited) {
-  // This test verifies that llvm::parallelFor respects the jobserver limit.
-  const int NumExplicitJobs = 3;
-  const int ConcurrencyLimit = NumExplicitJobs + 1; // +1 implicit
-  const int NumTasks = 20;
-
-  LLVM_DEBUG(dbgs() << "Calling startMakeProxy with " << NumExplicitJobs
-                    << " jobs.\n");
-  startMakeProxy(NumExplicitJobs);
-  LLVM_DEBUG(dbgs() << "MakeProxy is running.\n");
-
-  // Set the global strategy. parallelFor will use this.
-  parallel::strategy = jobserver_concurrency();
-
-  std::atomic<int> ActiveTasks{0};
-  std::atomic<int> MaxActiveTasks{0};
-
-  parallelFor(0, NumTasks, [&](int i) {
-    int CurrentActive = ++ActiveTasks;
-    LLVM_DEBUG(dbgs() << "Task " << i << ": Active tasks: " << CurrentActive
-                      << "\n");
-    int OldMax = MaxActiveTasks.load();
-    while (CurrentActive > OldMax)
-      MaxActiveTasks.compare_exchange_weak(OldMax, CurrentActive);
-
-    std::this_thread::sleep_for(std::chrono::milliseconds(20));
-    --ActiveTasks;
-  });
-
-  LLVM_DEBUG(dbgs() << "ParallelFor finished. Max active tasks was "
-                    << MaxActiveTasks << ".\n");
-  EXPECT_LE(MaxActiveTasks, ConcurrencyLimit);
-}
-
-TEST_F(JobserverStrategyTest, ParallelSortIsLimited) {
-  // This test serves as an integration test to ensure parallelSort completes
-  // correctly when running under the jobserver strategy. It doesn't directly
-  // measure concurrency but verifies correctness.
-  const int NumExplicitJobs = 3;
-  startMakeProxy(NumExplicitJobs);
-
-  parallel::strategy = jobserver_concurrency();
-
-  std::vector<int> V(1024);
-  // Fill with random data
-  std::mt19937 randEngine;
-  std::uniform_int_distribution<int> dist;
-  for (int &i : V)
-    i = dist(randEngine);
-
-  parallelSort(V.begin(), V.end());
-  ASSERT_TRUE(llvm::is_sorted(V));
-}
-
-#endif // LLVM_ENABLE_THREADS
-
-#endif // defined(LLVM_ON_UNIX)
-
-} // end anonymous namespace


        


More information about the llvm-commits mailing list