From 55c43d9f27717cf3b402a69c00645740a956f449 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Sun, 25 Oct 2026 23:15:32 -0500 Subject: [PATCH] Guard every x86 intrinsic header in the toolchain probe, and declare mkdtemp on macOS The implicit-toolchain probe includes every <...> header the runtime and vendored sources name, each behind __has_include. clang ships its x86 intrinsic headers for every target, so on arm64 macOS __has_include finds (included by scr_bytes.c and scr_string.c), and including it there is an #error. The probe guarded only and by target, so on arm64 macOS it failed, native cache warming reported that it could not validate the compiler toolchain, and builds ran without the persistent cache. The probe now guards every x86 intrinsic header family (*mmintrin.h, , ) and by target. ts7-process.test.c defines _POSIX_C_SOURCE, which hides mkdtemp on macOS; it now also defines _DARWIN_C_SOURCE there, as host.c does. --- packages/compiler/native/ts7-process.test.c | 4 ++ .../src/backend/native-toolchain.test.ts | 41 +++++++++++++++++++ .../compiler/src/backend/native-toolchain.ts | 5 ++- .../backend/native/compiler-fingerprint.ts | 28 +++++++++---- 4 files changed, 68 insertions(+), 10 deletions(-) diff --git a/packages/compiler/native/ts7-process.test.c b/packages/compiler/native/ts7-process.test.c index b6d760abd..85de63db1 100644 --- a/packages/compiler/native/ts7-process.test.c +++ b/packages/compiler/native/ts7-process.test.c @@ -1,6 +1,10 @@ #ifndef _WIN32 #define _POSIX_C_SOURCE 200809L #endif +/* macOS declares mkdtemp only with the Darwin extensions visible. */ +#if defined(__APPLE__) && !defined(_DARWIN_C_SOURCE) +#define _DARWIN_C_SOURCE 1 +#endif #include "ts7-process.h" #include #include diff --git a/packages/compiler/src/backend/native-toolchain.test.ts b/packages/compiler/src/backend/native-toolchain.test.ts index f1bcde404..0ac21e853 100644 --- a/packages/compiler/src/backend/native-toolchain.test.ts +++ b/packages/compiler/src/backend/native-toolchain.test.ts @@ -24,6 +24,7 @@ import { compileLibArchive, executableSectionEliminationFlags, executableNativeEnvironmentFingerprint, + implicitDependencyIncludeDirective, implicitDependencyProbeIncludes, parseLinkTraceFiles, resolveBuildCacheRoot, @@ -624,6 +625,46 @@ test("implicit dependency seeds include separately compiled vendor system header expect(includes).toContain(""); }); +test.skipIf(clangExecutable === undefined)( + "the implicit toolchain probe guards target-specific compiler headers", + async () => { + // clang ships its x86 intrinsic headers (, ...) for every + // target, so __has_include finds them on arm64, where including them is + // an #error, and the same holds for the ARM headers on x86. The probe + // must guard them by target. Only the compiler's own freestanding + // headers are visible here, so the result does not depend on the host's + // C library. + const includes = (await implicitDependencyProbeIncludes(runtimeSrcDir())).filter( + (include) => + include !== "" && /intrin\.h>$|^$|^$/.test(include), + ); + expect(includes).toContain(""); + const resourceDir = execFileSync(clangExecutable!, ["-print-resource-dir"], { + encoding: "utf8", + }).trim(); + const source = includes.map(implicitDependencyIncludeDirective).join(""); + for (const target of ["aarch64-unknown-linux-gnu", "x86_64-unknown-linux-gnu"]) { + const result = spawnSync( + clangExecutable!, + [ + `--target=${target}`, + "-ffreestanding", + "-nostdinc", + "-isystem", + join(resourceDir, "include"), + "-fsyntax-only", + "-x", + "c", + "-", + ], + { input: source, encoding: "utf8" }, + ); + expect(result.stderr, target).toBe(""); + expect(result.status, target).toBe(0); + } + }, +); + test.skipIf( process.platform === "win32" || clangExecutable === undefined || diff --git a/packages/compiler/src/backend/native-toolchain.ts b/packages/compiler/src/backend/native-toolchain.ts index 05f1f7122..6c024c6a5 100644 --- a/packages/compiler/src/backend/native-toolchain.ts +++ b/packages/compiler/src/backend/native-toolchain.ts @@ -26,7 +26,10 @@ export { export { CcCompileError, subprocessFailureDetail } from "./native/process.js"; export { compileLibArchive } from "./native/library.js"; export { localizeLibraryObjects } from "./native/object-merge.js"; -export { implicitDependencyProbeIncludes } from "./native/compiler-fingerprint.js"; +export { + implicitDependencyIncludeDirective, + implicitDependencyProbeIncludes, +} from "./native/compiler-fingerprint.js"; export { parseLinkTraceFiles, nativeLinkerDependencyPaths } from "./native/linker-fingerprint.js"; export { clearCcCaches, stageRuntimeObjects } from "./native/runtime-objects.js"; export { diff --git a/packages/compiler/src/backend/native/compiler-fingerprint.ts b/packages/compiler/src/backend/native/compiler-fingerprint.ts index ec427ce0a..6f92707f6 100644 --- a/packages/compiler/src/backend/native/compiler-fingerprint.ts +++ b/packages/compiler/src/backend/native/compiler-fingerprint.ts @@ -195,15 +195,25 @@ export async function implicitDependencyProbeIncludes(rtDir: string): Promise" || include === "" - ? " && (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64))" - : include === "" - ? " && defined(_WIN32)" - : include === "" || include === "" || include === "" - ? " && (defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64))" - : ""; +/** Compiler-provided x86 headers: clang ships them for every target, so + * __has_include finds them on arm64 too, where including them is an + * #error. Covers the SSE/AVX intrinsic families (, + * , ...), , and . */ +function x86OnlyHeader(include: string): boolean { + return include === "" || /^<(x86|x86gpr|[a-z]*mm)intrin\.h>$/.test(include); +} + +/** One guarded line of the implicit-toolchain probe source: the header is + * included only when the compiler can find it and it belongs to the target + * being probed. */ +export function implicitDependencyIncludeDirective(include: string): string { + const targetCondition = x86OnlyHeader(include) + ? " && (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64))" + : include === "" + ? " && defined(_WIN32)" + : include === "" || include === "" || include === "" + ? " && (defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64))" + : ""; return `#if __has_include(${include})${targetCondition}\n#include ${include}\n#endif\n`; }