2026-07-23 01:24:42 +02:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
|
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
2026-07-22 18:16:38 +02:00
|
|
|
|
feat: feature-detected browser-wasm variants (relaxed-SIMD) + variant-aware runtime
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>
2026-06-15 15:14:18 +00:00
|
|
|
import std;
|
|
|
|
|
import Crafter.Build;
|
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
|
using namespace Crafter;
|
|
|
|
|
|
|
|
|
|
extern "C" int setenv(const char* name, const char* value, int overwrite);
|
|
|
|
|
|
|
|
|
|
// Covers the browser-wasm variant plumbing that does NOT require a wasm
|
|
|
|
|
// toolchain run: EnableWasiRelaxedSimdVariant's registration, the
|
|
|
|
|
// variants.json manifest EnableWasiBrowserRuntime emits, and the VariantId
|
|
|
|
|
// perturbation that keeps each variant's objects in their own build dir. The
|
|
|
|
|
// actual multi-.wasm production is exercised by a real wasm build (manual /
|
|
|
|
|
// integration), not here — same boundary the WasiBrowserRuntime test draws.
|
|
|
|
|
namespace {
|
2026-07-23 01:24:42 +02:00
|
|
|
std::int32_t Failures = 0;
|
feat: feature-detected browser-wasm variants (relaxed-SIMD) + variant-aware runtime
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>
2026-06-15 15:14:18 +00:00
|
|
|
void Check(bool cond, std::string_view msg) {
|
2026-07-23 01:24:42 +02:00
|
|
|
if (!cond) { std::println(std::cerr, "FAIL: {}", msg); ++Failures; }
|
feat: feature-detected browser-wasm variants (relaxed-SIMD) + variant-aware runtime
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>
2026-06-15 15:14:18 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
|
// ── EnableWasiRelaxedSimdVariant: registration + idempotency ──────────
|
|
|
|
|
{
|
|
|
|
|
Configuration cfg;
|
|
|
|
|
cfg.target = "wasm32-wasip1";
|
|
|
|
|
EnableWasiRelaxedSimdVariant(cfg);
|
|
|
|
|
Check(cfg.wasmVariants.size() == 1, "one variant registered");
|
|
|
|
|
if (!cfg.wasmVariants.empty()) {
|
|
|
|
|
const WasmVariant& v = cfg.wasmVariants.front();
|
|
|
|
|
Check(v.label == "relaxed-simd", "label is relaxed-simd");
|
|
|
|
|
Check(v.flags.size() == 1 && v.flags.front() == "-mrelaxed-simd", "flag is -mrelaxed-simd");
|
|
|
|
|
Check(v.probes.size() == 1 && v.probes.front() == "relaxed-simd", "probe is relaxed-simd");
|
|
|
|
|
}
|
|
|
|
|
EnableWasiRelaxedSimdVariant(cfg);
|
|
|
|
|
Check(cfg.wasmVariants.size() == 1, "second call is idempotent");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── wasmVariantFlags perturbs VariantId / BuildDir / BinDir ──────────
|
|
|
|
|
// Configuration owns move-only members (unique_ptr<Module>), so build the
|
|
|
|
|
// two cases independently rather than copying.
|
|
|
|
|
{
|
|
|
|
|
auto make = [] {
|
|
|
|
|
Configuration c;
|
|
|
|
|
c.path = "/tmp/wasm-variant-id-test";
|
|
|
|
|
c.name = "wv"; c.outputName = "wv";
|
|
|
|
|
c.target = "wasm32-wasip1";
|
|
|
|
|
c.type = ConfigurationType::Executable;
|
|
|
|
|
return c;
|
|
|
|
|
};
|
|
|
|
|
Configuration a = make();
|
|
|
|
|
Configuration b = make();
|
|
|
|
|
b.wasmVariantFlags = {"-mrelaxed-simd"};
|
|
|
|
|
Check(a.VariantId() != b.VariantId(), "wasmVariantFlags perturbs VariantId");
|
|
|
|
|
Check(a.BuildDir() != b.BuildDir(), "wasmVariantFlags perturbs BuildDir");
|
|
|
|
|
Check(a.BinDir() != b.BinDir(), "wasmVariantFlags perturbs BinDir");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── variants.json manifest contents ──────────────────────────────────
|
|
|
|
|
{
|
|
|
|
|
fs::path repoWasi = fs::current_path() / "wasi-runtime";
|
|
|
|
|
fs::path systemHome = "/usr/local/share/crafter-build";
|
|
|
|
|
if (fs::exists(repoWasi / "runtime.js")) {
|
|
|
|
|
setenv("CRAFTER_BUILD_HOME", fs::current_path().c_str(), 1);
|
|
|
|
|
} else if (fs::exists(systemHome / "wasi-runtime" / "runtime.js")) {
|
|
|
|
|
setenv("CRAFTER_BUILD_HOME", systemHome.c_str(), 1);
|
|
|
|
|
} else {
|
|
|
|
|
std::println(std::cerr, "wasi-runtime assets not found; skipping manifest check");
|
2026-07-23 01:24:42 +02:00
|
|
|
return Failures ? 1 : 77;
|
feat: feature-detected browser-wasm variants (relaxed-SIMD) + variant-aware runtime
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>
2026-06-15 15:14:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Configuration cfg;
|
|
|
|
|
cfg.path = fs::temp_directory_path() / "crafter-build-wasm-variants-test";
|
|
|
|
|
cfg.name = "wvhelper";
|
|
|
|
|
cfg.outputName = "wvhelper";
|
|
|
|
|
cfg.target = "wasm32-wasip1";
|
|
|
|
|
cfg.type = ConfigurationType::Executable;
|
|
|
|
|
|
|
|
|
|
std::error_code ec;
|
|
|
|
|
fs::remove_all(cfg.path, ec);
|
|
|
|
|
fs::create_directories(cfg.path);
|
|
|
|
|
|
|
|
|
|
EnableWasiRelaxedSimdVariant(cfg);
|
|
|
|
|
EnableWasiBrowserRuntime(cfg);
|
|
|
|
|
|
|
|
|
|
const fs::path* variants = nullptr;
|
|
|
|
|
for (const fs::path& f : cfg.files) {
|
|
|
|
|
if (f.filename() == "variants.json") variants = &f;
|
|
|
|
|
}
|
|
|
|
|
Check(variants != nullptr, "variants.json registered");
|
|
|
|
|
if (variants) {
|
|
|
|
|
std::ifstream vin(*variants);
|
|
|
|
|
std::string j{std::istreambuf_iterator<char>(vin), std::istreambuf_iterator<char>()};
|
|
|
|
|
// relaxed-simd entry, with its labelled url + probe.
|
|
|
|
|
Check(j.find("\"label\":\"relaxed-simd\"") != std::string::npos, "manifest has relaxed-simd label");
|
|
|
|
|
Check(j.find("\"wvhelper.relaxed-simd.wasm\"") != std::string::npos, "manifest has labelled url");
|
|
|
|
|
Check(j.find("\"relaxed-simd\"") != std::string::npos, "manifest has relaxed-simd probe");
|
test(wasm): exceptions in a wasm build, and a sysroot without them
WasmExceptions builds a fixture for wasm32-wasip1 and runs it under
wasmtime. It throws through four frames whose destructors have to run,
catches the exception, catches one that vector::at throws, and with an
argument lets one escape main, which has to fail the run. With the
previous flags the fixture does not compile ("cannot use 'throw' with
exceptions disabled"), and each part of turning exceptions on fails it
in its own way, from a BMI built without them to the legacy encoding
wasmtime refuses.
WasiSysrootWithoutExceptions takes a scratch sysroot through three
states, no libunwind, libunwind under eh/ and libunwind in the
triple's own directory, and checks that only the first stops the
build, with the message naming the library it looked for. It needs no
working wasm toolchain, so it also runs where the others skip.
WasmHelloRuns and WasmExceptions treat a sysroot without the
exception-enabled libraries as missing and skip. The manifest tests
expect the exception-handling probe on every entry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 05:54:48 +01:00
|
|
|
// Every file is built with exceptions, so every entry needs them
|
|
|
|
|
// besides its own features; the baseline needs nothing else.
|
|
|
|
|
Check(j.find("\"probes\":[\"relaxed-simd\",\"exception-handling\"]") != std::string::npos, "relaxed-simd entry also probes for exception handling");
|
|
|
|
|
Check(j.find("\"url\":\"wvhelper.wasm\",\"probes\":[\"exception-handling\"]") != std::string::npos, "baseline entry probes for exception handling only");
|
feat: feature-detected browser-wasm variants (relaxed-SIMD) + variant-aware runtime
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>
2026-06-15 15:14:18 +00:00
|
|
|
// baseline fallback entry.
|
|
|
|
|
Check(j.find("\"wvhelper.wasm\"") != std::string::npos, "manifest has baseline url");
|
|
|
|
|
// The relaxed-simd variant must precede the baseline so the runtime
|
|
|
|
|
// prefers it when probes pass.
|
|
|
|
|
auto relaxedPos = j.find("relaxed-simd");
|
|
|
|
|
auto baselinePos = j.find("\"wvhelper.wasm\"");
|
2026-07-23 01:24:42 +02:00
|
|
|
Check(relaxedPos != std::string::npos && baselinePos != std::string::npos && relaxedPos < baselinePos, "relaxed-simd entry precedes baseline");
|
feat: feature-detected browser-wasm variants (relaxed-SIMD) + variant-aware runtime
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>
2026-06-15 15:14:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fs::remove_all(cfg.path, ec);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-23 01:24:42 +02:00
|
|
|
if (Failures > 0) {
|
|
|
|
|
std::println(std::cerr, "{} assertions failed", Failures);
|
feat: feature-detected browser-wasm variants (relaxed-SIMD) + variant-aware runtime
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>
2026-06-15 15:14:18 +00:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|