Guard every x86 intrinsic header in the toolchain probe, and declare mkdtemp on macOS (#2313)

This commit is contained in:
Chris Tate
2026-10-25 23:44:47 -05:00
committed by GitHub
4 changed files with 68 additions and 10 deletions
@@ -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 <assert.h>
#include <errno.h>
@@ -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("<stdatomic.h>");
});
test.skipIf(clangExecutable === undefined)(
"the implicit toolchain probe guards target-specific compiler headers",
async () => {
// clang ships its x86 intrinsic headers (<emmintrin.h>, ...) 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>" && /intrin\.h>$|^<cpuid\.h>$|^<arm_(neon|acle)\.h>$/.test(include),
);
expect(includes).toContain("<emmintrin.h>");
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 ||
@@ -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 {
@@ -195,15 +195,25 @@ export async function implicitDependencyProbeIncludes(rtDir: string): Promise<st
return [...includes].sort();
}
function implicitDependencyIncludeDirective(include: string): string {
const targetCondition =
include === "<cpuid.h>" || include === "<immintrin.h>"
? " && (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64))"
: include === "<intrin.h>"
? " && defined(_WIN32)"
: include === "<arm64_neon.h>" || include === "<arm_acle.h>" || include === "<arm_neon.h>"
? " && (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 (<emmintrin.h>,
* <immintrin.h>, ...), <x86intrin.h>, <x86gprintrin.h> and <cpuid.h>. */
function x86OnlyHeader(include: string): boolean {
return include === "<cpuid.h>" || /^<(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 === "<intrin.h>"
? " && defined(_WIN32)"
: include === "<arm64_neon.h>" || include === "<arm_acle.h>" || include === "<arm_neon.h>"
? " && (defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64))"
: "";
return `#if __has_include(${include})${targetCondition}\n#include ${include}\n#endif\n`;
}