[clang] [clang] Skip host default include paths for GPU triples (PR #192821)

Yaxun Liu via cfe-commits cfe-commits at lists.llvm.org
Sat Apr 18 20:13:36 PDT 2026


https://github.com/yxsamliu created https://github.com/llvm/llvm-project/pull/192821

GPU compiles are freestanding. They should not search the host system's
`/usr/include` or `/usr/local/include` by default.

A concrete example is

  clang --target=amdgcn-amd-amdhsa -ffreestanding -nogpulib -c foo.c

If `foo.c` contains `#include <string.h>`, Clang should not read the host
header `/usr/include/string.h`. That header belongs to the host libc, not
to the GPU target. On current `main`, however, GPU triples still fall
through to the generic default-include logic, so host include paths are
added and builds can fail on host-only headers such as
`bits/libc-header-start.h`.

Fix this in two parts:

1. In `InitHeaderSearch`, treat all GPU triples (`triple.isGPU()`) like
   other targets whose include paths are managed by the driver, and skip
   the generic host default-include fallback.
2. In the AMDGPU and NVPTX toolchains, add clang's resource-directory
   builtin headers explicitly (`stddef.h`, `float.h`, `stdint.h`, ...).
   These are still needed, but they should come from clang's builtin
   header directory, not from the host libc.

Add a lit test that checks the printed include search list for GPU
triples and verifies that `/usr/include` and `/usr/local/include` are
not present.

Related: PR #177665.



>From ec53b32b33c3c5931542bed77f25272c1eb512e9 Mon Sep 17 00:00:00 2001
From: "Yaxun (Sam) Liu" <yaxun.liu at amd.com>
Date: Fri, 17 Apr 2026 13:33:56 -0400
Subject: [PATCH] [clang] Skip host default include paths for GPU triples

GPU compiles are freestanding. They should not search the host system's
`/usr/include` or `/usr/local/include` by default.

A concrete example is

  clang --target=amdgcn-amd-amdhsa -ffreestanding -nogpulib -c foo.c

If `foo.c` contains `#include <string.h>`, Clang should not read the host
header `/usr/include/string.h`. That header belongs to the host libc, not
to the GPU target. On current `main`, however, GPU triples still fall
through to the generic default-include logic, so host include paths are
added and builds can fail on host-only headers such as
`bits/libc-header-start.h`.

Fix this in two parts:

1. In `InitHeaderSearch`, treat all GPU triples (`triple.isGPU()`) like
   other targets whose include paths are managed by the driver, and skip
   the generic host default-include fallback.
2. In the AMDGPU and NVPTX toolchains, add clang's resource-directory
   builtin headers explicitly (`stddef.h`, `float.h`, `stdint.h`, ...).
   These are still needed, but they should come from clang's builtin
   header directory, not from the host libc.

Add a lit test that checks the printed include search list for GPU
triples and verifies that `/usr/include` and `/usr/local/include` are
not present.

Related: PR #177665.
---
 clang/lib/Driver/ToolChains/AMDGPU.cpp        | 17 +++++++++++++++--
 clang/lib/Driver/ToolChains/Cuda.cpp          | 17 +++++++++++++++--
 clang/lib/Lex/InitHeaderSearch.cpp            | 10 ++++++++++
 .../gpu-targets-no-host-default-includes.c    | 19 +++++++++++++++++++
 4 files changed, 59 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/Preprocessor/gpu-targets-no-host-default-includes.c

diff --git a/clang/lib/Driver/ToolChains/AMDGPU.cpp b/clang/lib/Driver/ToolChains/AMDGPU.cpp
index ff4c781d51348..e229635745dc9 100644
--- a/clang/lib/Driver/ToolChains/AMDGPU.cpp
+++ b/clang/lib/Driver/ToolChains/AMDGPU.cpp
@@ -879,8 +879,21 @@ void AMDGPUToolChain::addClangWarningOptions(ArgStringList &CC1Args) const {
 
 void AMDGPUToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
                                                 ArgStringList &CC1Args) const {
-  if (DriverArgs.hasArg(options::OPT_nostdinc) ||
-      DriverArgs.hasArg(options::OPT_nostdlibinc))
+  if (DriverArgs.hasArg(options::OPT_nostdinc))
+    return;
+
+  // Add clang's builtin headers (`stddef.h`, `float.h`, `stdint.h`, ...).
+  // These are compiler-provided headers, not host libc headers.
+  //
+  // Example: an amdgcn compile may still need `float.h`, but it should come
+  // from clang's resource directory, not from the host system's `/usr/include`.
+  if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) {
+    SmallString<128> ResourceDirInclude(getDriver().ResourceDir);
+    llvm::sys::path::append(ResourceDirInclude, "include");
+    addSystemInclude(DriverArgs, CC1Args, ResourceDirInclude);
+  }
+
+  if (DriverArgs.hasArg(options::OPT_nostdlibinc))
     return;
 
   // Add multilib variant include paths in priority order.
diff --git a/clang/lib/Driver/ToolChains/Cuda.cpp b/clang/lib/Driver/ToolChains/Cuda.cpp
index dcde82cdf4d85..7c248e3c433f9 100644
--- a/clang/lib/Driver/ToolChains/Cuda.cpp
+++ b/clang/lib/Driver/ToolChains/Cuda.cpp
@@ -790,8 +790,21 @@ void NVPTXToolChain::addClangTargetOptions(
 
 void NVPTXToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
                                                ArgStringList &CC1Args) const {
-  if (DriverArgs.hasArg(options::OPT_nostdinc) ||
-      DriverArgs.hasArg(options::OPT_nostdlibinc))
+  if (DriverArgs.hasArg(options::OPT_nostdinc))
+    return;
+
+  // Add clang's builtin headers (`stddef.h`, `float.h`, `stdint.h`, ...).
+  // These are compiler-provided headers, not host libc headers.
+  //
+  // Example: an nvptx compile may still need `float.h`, but it should come
+  // from clang's resource directory, not from the host system's `/usr/include`.
+  if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) {
+    SmallString<128> ResourceDirInclude(getDriver().ResourceDir);
+    llvm::sys::path::append(ResourceDirInclude, "include");
+    addSystemInclude(DriverArgs, CC1Args, ResourceDirInclude);
+  }
+
+  if (DriverArgs.hasArg(options::OPT_nostdlibinc))
     return;
 
   // Add multilib variant include paths in priority order.
diff --git a/clang/lib/Lex/InitHeaderSearch.cpp b/clang/lib/Lex/InitHeaderSearch.cpp
index e894086b66e76..7204602dc5514 100644
--- a/clang/lib/Lex/InitHeaderSearch.cpp
+++ b/clang/lib/Lex/InitHeaderSearch.cpp
@@ -210,6 +210,16 @@ void InitHeaderSearch::AddDefaultCIncludePaths(const llvm::Triple &triple,
 
 bool InitHeaderSearch::ShouldAddDefaultIncludePaths(
     const llvm::Triple &triple) {
+  // GPU targets are freestanding, so they should not search the host system's
+  // `/usr/include` or `/usr/local/include`.
+  //
+  // Example: `clang --target=amdgcn-amd-amdhsa -ffreestanding -nogpulib`
+  // compiling a file with `#include <string.h>` should not read the host
+  // glibc header `/usr/include/string.h`. GPU toolchains add their own include
+  // paths explicitly, so skip the generic host fallback here.
+  if (triple.isGPU())
+    return false;
+
   switch (triple.getOS()) {
   case llvm::Triple::AIX:
   case llvm::Triple::DragonFly:
diff --git a/clang/test/Preprocessor/gpu-targets-no-host-default-includes.c b/clang/test/Preprocessor/gpu-targets-no-host-default-includes.c
new file mode 100644
index 0000000000000..952c4b98c0fe9
--- /dev/null
+++ b/clang/test/Preprocessor/gpu-targets-no-host-default-includes.c
@@ -0,0 +1,19 @@
+// Check that GPU targets do not get the host system's default C include
+// paths (e.g. /usr/include, /usr/local/include) appended to the search list.
+// GPU toolchains are freestanding and manage their own include paths.
+//
+// Use `-v -E` on the source with `-nogpulib` to print the toolchain's
+// include search list and verify the host paths are absent.
+
+// RUN: %clang --target=amdgcn-amd-amdhsa -nogpulib -v -E -x c %s \
+// RUN:   -o /dev/null 2>&1 | FileCheck %s
+
+// RUN: %clang --target=amdgcn--amdhsa -nogpulib -v -E -x c %s \
+// RUN:   -o /dev/null 2>&1 | FileCheck %s
+
+// RUN: %clang --target=nvptx64-nvidia-cuda -nogpulib -nogpuinc -v -E -x c %s \
+// RUN:   -o /dev/null 2>&1 | FileCheck %s
+
+// CHECK-NOT: /usr/include{{$}}
+// CHECK-NOT: /usr/local/include{{$}}
+// CHECK:     End of search list.



More information about the cfe-commits mailing list