feat: feature-detected browser-wasm variants (relaxed-SIMD) + variant-aware runtime
Some checks failed
CI / build-test-release (pull_request) Failing after 7m19s
Some checks failed
CI / build-test-release (pull_request) Failing after 7m19s
The browser wasm pipeline hardcoded -msimd128 for every wasm32 target and baked a single wasm URL into index.html, so newer codegen features that aren't yet baseline across engines (relaxed SIMD today; threads, future SIMD revisions later) couldn't be adopted without dropping the browsers that lack them. Add a general, feature-parameterized mechanism owned entirely by Crafter.Build: - Configuration::wasmVariants declares N codegen variants (label, extra -m flags, runtime probes). Build() compiles the baseline plus one outputName.<label>.wasm per variant, recompiling the whole graph (incl. dep libs + std PCM) with the variant's flags — relaxed-SIMD is per-TU codegen, not a link switch. wasmVariantFlags folds into VariantId so each variant's objects/PCMs land in their own build+bin dir. - EnableWasiBrowserRuntime emits a variants.json manifest (label -> url + probes), preferred-first with the baseline as the universal fallback. - The shipped runtime.js runs inlined wasm-feature-detect probes (relaxed-simd, simd, tail-call, bulk-memory, exception-handling, threads), picks the first variant whose probes all pass, and falls back to the single baked CRAFTER_WASM_URL when no manifest is present (backward compatible). - EnableWasiRelaxedSimdVariant registers the relaxed-SIMD variant — the motivating case (Chrome 114+/Firefox 120+ enable it by default; Safari still flag-gates it as of mid-2026). Verified end to end: a wasm32-wasip1 build emits both wasi-hello.wasm and wasi-hello.relaxed-simd.wasm + variants.json; Firefox selects the relaxed-simd variant and runs it. Resolves #24 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
dbee23c564
commit
022ada70a6
7 changed files with 395 additions and 5 deletions
|
|
@ -44,6 +44,21 @@ export namespace Crafter {
|
|||
std::string value;
|
||||
};
|
||||
|
||||
// A browser-wasm codegen variant. The same wasm32 target is compiled more
|
||||
// than once, each pass adding `flags` to every translation unit, so newer
|
||||
// ISA features (relaxed SIMD today; threads, future SIMD revisions, … later)
|
||||
// can be adopted without dropping engines that lack them. Each non-empty
|
||||
// `label` emits an additional `outputName.<label>.wasm` alongside the
|
||||
// baseline `outputName.wasm`; `probes` names the runtime feature-detect
|
||||
// checks (see wasi-runtime/runtime.js's probe registry) that must all pass
|
||||
// for a browser to be served this variant. The first variant whose probes
|
||||
// pass wins; the baseline is the universal fallback.
|
||||
struct WasmVariant {
|
||||
std::string label;
|
||||
std::vector<std::string> flags;
|
||||
std::vector<std::string> probes;
|
||||
};
|
||||
|
||||
enum class ConfigurationType {
|
||||
Executable,
|
||||
LibraryStatic,
|
||||
|
|
@ -157,6 +172,21 @@ export namespace Crafter {
|
|||
std::vector<ExternalDependency> externalDependencies;
|
||||
std::vector<std::string> compileFlags;
|
||||
std::vector<std::string> linkFlags;
|
||||
// Browser-wasm feature-detected variants. Empty (the default) builds a
|
||||
// single outputName.wasm with the baseline wasm flag set. Non-empty
|
||||
// builds the baseline plus one outputName.<label>.wasm per entry, each
|
||||
// recompiling the whole build graph with that entry's `flags`;
|
||||
// EnableWasiBrowserRuntime emits a variants.json manifest the shipped
|
||||
// runtime.js uses to pick the right variant per browser. Populate via
|
||||
// EnableWasiRelaxedSimdVariant for the common case.
|
||||
std::vector<WasmVariant> wasmVariants;
|
||||
// Extra wasm codegen flags injected into this configuration's compile +
|
||||
// link command for the active variant pass. Set across the whole build
|
||||
// graph by the variant driver inside Build(); part of VariantId so each
|
||||
// variant's objects/PCMs land in their own build+bin dir and never
|
||||
// clobber the baseline's. Not for direct project use — declare
|
||||
// wasmVariants instead.
|
||||
std::vector<std::string> wasmVariantFlags;
|
||||
std::vector<Test> tests;
|
||||
CRAFTER_API void GetInterfacesAndImplementations(std::span<fs::path> interfaces, std::span<fs::path> implementations);
|
||||
// Declare a test. Sources default to `tests/<name>/main.cpp` resolved
|
||||
|
|
@ -199,6 +229,13 @@ export namespace Crafter {
|
|||
compileKey += "|F:";
|
||||
compileKey += f;
|
||||
}
|
||||
// Wasm variant codegen flags perturb every object/PCM, so they must
|
||||
// key into a distinct build+bin dir from the baseline (and from
|
||||
// each other) to avoid clobbering.
|
||||
for (const std::string& f : wasmVariantFlags) {
|
||||
compileKey += "|W:";
|
||||
compileKey += f;
|
||||
}
|
||||
std::size_t configHash = std::hash<std::string>{}(compileKey);
|
||||
return std::format("{}-{}-{}-{}-{:08x}", name, target, march, mtune, configHash);
|
||||
}
|
||||
|
|
@ -237,6 +274,17 @@ export namespace Crafter {
|
|||
// outputName so renaming the binary later requires another call.
|
||||
CRAFTER_API void EnableWasiBrowserRuntime(Configuration& cfg);
|
||||
|
||||
// Register the relaxed-SIMD codegen variant on cfg. Builds an additional
|
||||
// outputName.relaxed-simd.wasm compiled with -mrelaxed-simd (FMA,
|
||||
// dot-product, relaxed swizzle/laneselect, …); the shipped runtime serves
|
||||
// it only to engines that report relaxed-SIMD support (Chrome 114+, Firefox
|
||||
// 120+) and falls back to the baseline outputName.wasm everywhere else
|
||||
// (e.g. Safari, which still gates relaxed SIMD behind a flag as of mid
|
||||
// 2026). Idempotent. Call before EnableWasiBrowserRuntime so the emitted
|
||||
// variants.json manifest includes it. A no-op for non-wasm targets at build
|
||||
// time (no wasm is produced), but harmless to declare unconditionally.
|
||||
CRAFTER_API void EnableWasiRelaxedSimdVariant(Configuration& cfg);
|
||||
|
||||
// View over the project's args with simple query helpers. Has(flag) for
|
||||
// boolean switches; Get(prefix) for valued options (e.g. Get("--prefix=")
|
||||
// returns the substring after the equals). Definitions are out-of-line
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue