fix(wasm): what the review found
-flto in a wasm configuration's compileFlags or linkFlags reached the link, where LTO code generation runs without the backend options that keep exception handling: every landing pad was dropped, and the build succeeded with throws that escaped their catch blocks. -fno-exceptions there made every `import std;` fail on the std module built with exceptions, and a wasm shared library failed to link against the eh/ libc++, which is static archives built without -fPIC. Build() refuses all three now, each with a line that says why, before it makes a directory or starts a thread. The sysroot check sat in BuildStdPcm, which the linter calls as well. There the error only dropped the compile command, so lint ran no AST rule on a wasm configuration and reported it clean. It also refused static libraries, which never link, fired after the shader and external threads had started, and called a missing sysroot, or one without the target, "no C++ exception support". It is part of the same early check now, which first says when the sysroot or the target's directory is missing, and asks for libunwind only where something links. C sources got only the signal.h define. setjmp.h refused to compile in C, and a C object given -wasm-enable-sjlj through compileFlags used the legacy exception encoding next to C++ objects in the standard one, a mix engines refuse to load. They get WasmCompileFlags now, like the C++ units, a variant pass's flags included. The baseline probes include simd: V8 and SpiderMonkey switch SIMD off on x86 CPUs without SSE4.1, which virtual machines often present, and such an engine passed the exception-handling probe only to fail compiling the module; the manifest tests expect it on every entry. The Flatpak pins WASI_SDK_EXCEPTIONS=DUAL, which wasi-sdk 34 defaults to only with clang 23 or newer, and checks that the eh/ libunwind got installed: the wasm tests skip without it, so CI would otherwise pass a Flatpak whose every wasm build fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
fec206839e
commit
45b9f5cbb7
7 changed files with 103 additions and 42 deletions
|
|
@ -493,6 +493,54 @@ namespace {
|
|||
walk(config, config.name);
|
||||
return error;
|
||||
}
|
||||
|
||||
// What a wasm32 configuration cannot be built with now that every unit
|
||||
// has C++ exceptions, and what its sysroot has to have for that; empty
|
||||
// for any other target and when nothing is wrong. Each would otherwise
|
||||
// fail at the first compile or link with a message that does not say
|
||||
// why, or not fail at all.
|
||||
std::string WasmBuildError(const Configuration& config) {
|
||||
if (!config.target.starts_with("wasm32")) return "";
|
||||
for (const std::vector<std::string>* flags : { &config.compileFlags, &config.linkFlags }) {
|
||||
for (const std::string& flag : *flags) {
|
||||
// The compile has the backend options that keep exception
|
||||
// handling; LTO code generation at the link does not, so it
|
||||
// drops every landing pad and the build succeeds with throws
|
||||
// that escape their catch blocks (WebAssembly/wasi-sdk#629).
|
||||
if (flag.starts_with("-flto")) {
|
||||
return std::format("{}: {} is not supported on wasm32: LTO code generation drops C++ exception handling (WebAssembly/wasi-sdk#629), so every throw would escape its catch", config.name, flag);
|
||||
}
|
||||
// The std module is built with exceptions, and a unit built
|
||||
// without them cannot import it.
|
||||
if (flag == "-fno-exceptions" || flag == "-fno-cxx-exceptions") {
|
||||
return std::format("{}: {} is not supported on wasm32: wasm builds have C++ exceptions, the std module included, and a unit compiled without them cannot import std", config.name, flag);
|
||||
}
|
||||
}
|
||||
}
|
||||
// wasi-sdk's exception support does not cover shared libraries: the
|
||||
// libc++ under lib/<triple>/eh is static archives built without -fPIC.
|
||||
if (config.type == ConfigurationType::LibraryDynamic) {
|
||||
return std::format("{}: wasm32 shared libraries are not supported: the exception-enabled libc++ they would link has static archives only", config.name);
|
||||
}
|
||||
const fs::path sysroot = config.sysroot;
|
||||
if (!fs::is_directory(sysroot)) {
|
||||
return std::format("no WASI sysroot at {}: the Flatpak bundles one, and CRAFTER_BUILD_WASI_SYSROOT selects another", sysroot.string());
|
||||
}
|
||||
const fs::path libDir = sysroot / "lib" / NormalizeTarget(config.target);
|
||||
if (!fs::is_directory(libDir)) {
|
||||
return std::format("the WASI sysroot at {} has nothing for {} (no {})", sysroot.string(), config.target, libDir.string());
|
||||
}
|
||||
// Only a link takes the exception-enabled libraries: a static
|
||||
// library's objects compile against the headers alone. The driver
|
||||
// looks in eh/ first and then in the triple's own directory, where a
|
||||
// sysroot built with exceptions only (WASI_SDK_EXCEPTIONS=ON) has them.
|
||||
// Without them the first link fails with "unable to find library
|
||||
// -lunwind", which says nothing about why it was asked for.
|
||||
if (config.type == ConfigurationType::Executable && !fs::exists(libDir / "eh" / "libunwind.a") && !fs::exists(libDir / "libunwind.a")) {
|
||||
return std::format("the WASI sysroot at {} has no C++ exception support (no {}): wasm builds use WebAssembly exception handling and need the exception-enabled libc++ and libunwind that wasi-sdk 33 and later ship. The Flatpak bundles such a sysroot; CRAFTER_BUILD_WASI_SYSROOT selects another", sysroot.string(), (libDir / "eh" / "libunwind.a").string());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, std::shared_future<BuildResult>>& depResults, std::mutex& depMutex) {
|
||||
|
|
@ -553,6 +601,11 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
|
|||
if (config.sysroot.empty() && config.target.starts_with("wasm32")) {
|
||||
config.sysroot = GetToolchain().wasiSysroot.string();
|
||||
}
|
||||
// As early as the duplicate-module check, so a refused build leaves no
|
||||
// directory behind and starts no thread, but after the sysroot is known.
|
||||
if (std::string refused = WasmBuildError(config); !refused.empty()) {
|
||||
return {std::move(refused), false, {}};
|
||||
}
|
||||
|
||||
fs::path buildDir = config.BuildDir();
|
||||
fs::path outputDir = config.BinDir();
|
||||
|
|
@ -870,8 +923,7 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
|
|||
// Same target-aware setup as the C++ compile path (line 459-): wasm32
|
||||
// rejects -march, silently ignores -mtune, and needs --sysroot to find
|
||||
// wasi-libc headers. Build the prefix once so all C compiles share it.
|
||||
const bool cIsWasm = config.target.starts_with("wasm32");
|
||||
std::string cArchFlags = cIsWasm
|
||||
std::string cArchFlags = isWasm
|
||||
? std::string()
|
||||
: std::format(" -march={} -mtune={}", config.march, config.mtune);
|
||||
if (!config.sysroot.empty()) {
|
||||
|
|
@ -880,11 +932,13 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
|
|||
// The C++ side gets it through GetBaseCommand.
|
||||
cArchFlags += std::format(" --sysroot={}", GetToolchain().mingwSysroot.string());
|
||||
}
|
||||
if (cIsWasm) {
|
||||
// Matches the C++ path's wasi flag set so any libc shim defines
|
||||
// (e.g. _WASI_EMULATED_SIGNAL → signal.h shims) are visible to
|
||||
// C dependencies that drag in signal.h transitively.
|
||||
cArchFlags += " -D_WASI_EMULATED_SIGNAL";
|
||||
if (isWasm) {
|
||||
// The C++ path's wasm flags, all of them, a variant pass's included.
|
||||
// With only the signal.h define, wasi-libc's setjmp.h refused to
|
||||
// compile in C, and a C object given -wasm-enable-sjlj through
|
||||
// compileFlags used LLVM's legacy exception encoding next to C++
|
||||
// objects in the standard one, a mix engines refuse to load.
|
||||
cArchFlags += WasmCompileFlags(config);
|
||||
}
|
||||
for (const fs::path& cFile : config.cFiles) {
|
||||
files += std::format(" {}_source.o ", (buildDir / cFile.filename()).string());
|
||||
|
|
|
|||
Loading…
Reference in a new issue