diff --git a/benchmarks/README.md b/benchmarks/README.md
index 9356e653..91c57d36 100644
--- a/benchmarks/README.md
+++ b/benchmarks/README.md
@@ -69,6 +69,48 @@ record its execution, it must:
and looking at the `dynamic_total_inst_count` column of the resulting CSV
output.
+ Anything within 80,000,000-120,000,000 counts as on target. This matches
+ `TARGET_INST_COUNT` in [`scripts/pca.R`](../scripts/pca.R), which also drops
+ any benchmark measuring below `MIN_DYNAMIC_INST_COUNT` (half the target)
+ outright. Benchmarks that are far over the target dominate the wall-clock time
+ of a suite run; benchmarks under the floor disappear from the PCA entirely.
+
+* The knob that sets the size of the workload should be read from a sibling
+ input file at run time rather than hard-coded as a constant in the source, so
+ that the benchmark can be retuned without a Docker rebuild. Read it *before*
+ `bench_start()` so that the I/O is not measured, and keep a compiled-in
+ fallback for when the file is absent.
+
+ C and C++ benchmarks can use `bench_read_long()` from
+ [`include/sightglass.h`](../include/sightglass.h):
+
+ ```c
+ /* Fallback tuned so that this benchmark executes ~100M Wasm instructions. */
+ #define ITERATIONS 293
+
+ int iterations = (int) bench_read_long("./shootout-base64.iterations.input",
+ ITERATIONS);
+ bench_start();
+ for (int i = 0; i < iterations; i++) { ... }
+ bench_end();
+ ```
+
+ Name the file after the benchmark and the knob (e.g.
+ `shootout-base64.iterations.input`) when a directory holds several benchmarks,
+ or just `default.input` when it holds one. Keep the compiled-in fallback in
+ sync with the checked-in input file.
+
+ Two cautions when adding a knob:
+
+ * If the benchmark prints anything derived from the knob, its
+ `.stdout.expected` must be regenerated.
+
+ * Check that the compiler has not folded the workload away. Scaling a loop
+ down can let LLVM collapse it into a constant; `shootout-nestedloop`
+ executed *zero* instructions for exactly this reason. Verify the count
+ responds to the knob, and use the `BLACK_BOX()` macro from
+ `include/sightglass.h` (or a `volatile` local) to keep the work opaque.
+
Many of the above requirements can be checked by running the `.wasm` file
through the `validate` command:
@@ -76,32 +118,7 @@ through the `validate` command:
$ cargo run -- validate path/to/benchmark.wasm
```
-## Compatibility Requirements for Native Execution
-
-Sightglass can also measure the performance of a subset of benchmarks compiled
-to native code (i.e., not WebAssembly). To compile these benchmarks without
-changing their source code, this involves a delicate interface with the [native
-engine] with some additional requirements beyond the [Minimal Technical
-Requirements] noted above:
-
-[native engine]: ../engines/native
-[Minimal Technical Requirements]: #minimal-technical-requirements
-
-* Generate an ELF shared library linked to the [native engine] shared library to
- provide definitions for `bench_start` and `bench_end`.
-
-* Rename the `main` function to `native_entry`. For C- and C++-based source this
- can be done with a simple define directive passed to `cc` (e.g.,
- `-Dmain=native_entry`).
-
-* Provide reproducible builds via a `Dockerfile.native` file (see
- [`build-native.sh`](./build-native.sh)).
-
-Note that support for native execution is optional: adding a WebAssembly
-benchmark does not imply the need to support its native equivalent — CI
-will not fail if it is not included.
-
-## Additional Requirements
+## Additional Desiderata
> Note: these requirements are lifted directly from the [the benchmarking
> RFC][rfc].
@@ -119,11 +136,6 @@ following requirements:
* A candidate program must be deterministic (modulo Wasm nondeterminism like
`memory.grow` failure).
-* A candidate program must have two associated input workloads: one small and
- one large. The small workload may be used by developers locally to get quick,
- ballpark numbers for whether further investment in an optimization is worth
- it, without waiting for the full, thorough benchmark suite to complete.
-
* Inputs should be given through I/O and results reported through I/O. This
ensures that the compiler cannot optimize the benchmark program away.
@@ -146,3 +158,50 @@ following requirements:
* The corpus of candidates should include programs that use a variety of
languages, compilers, and toolchains.
+
+## Benchmarks that cannot hit the ~100M instructions target
+
+A few benchmarks are deliberately out of band because the region they measure is
+a single indivisible operation whose smallest legal size still costs far more
+than ~100M instructions. Each is documented in its own `README.md`:
+
+| benchmark | instructions | why it cannot be reduced |
+| --- | --- | --- |
+| [`tract-onnx-image-classification`](./tract-onnx-image-classification/README.md) | ~6.6G | One MobileNetV2 forward pass; the model's input shape is fixed at 224x224. |
+| [`sqlite3`](./sqlite3/README.md) | ~459M | speedtest1's `szTest` is already at its minimum of 1. |
+| [`spidermonkey-markdown`](./spidermonkey/README.md) | ~289M | `marked`'s lazy inline-lexer regex compilation costs ~248M inside the measured region no matter how small the input is; `spidermonkey` is also in `build-all.sh`'s skip list, so only its input file can change. |
+| [`blind-sig`](./blind-sig/README.md) | ~245M | One RSA blind signature; the crate rejects moduli below 2048 bits. |
+
+Most of the `libsodium` subtests are also out of band, in both directions; see
+[`libsodium/README.md`](./libsodium/README.md). They are all built from one
+upstream test suite with a single per-test iteration count as the only knob, so
+a test whose body already costs more than 120M cannot be scaled down, and a few
+whose bodies are empty on Wasm cannot be scaled up.
+
+`noop` executes 0 instructions by design and is intended for measuring harness
+overhead.
+
+## Compatibility Requirements for Native Execution
+
+Sightglass can also measure the performance of a subset of benchmarks compiled
+to native code (i.e., not WebAssembly). To compile these benchmarks without
+changing their source code, this involves a delicate interface with the [native
+engine] with some additional requirements beyond the [Minimal Technical
+Requirements] noted above:
+
+[native engine]: ../engines/native
+[Minimal Technical Requirements]: #minimal-technical-requirements
+
+* Generate an ELF shared library linked to the [native engine] shared library to
+ provide definitions for `bench_start` and `bench_end`.
+
+* Rename the `main` function to `native_entry`. For C- and C++-based source this
+ can be done with a simple define directive passed to `cc` (e.g.,
+ `-Dmain=native_entry`).
+
+* Provide reproducible builds via a `Dockerfile.native` file (see
+ [`build-native.sh`](./build-native.sh)).
+
+Note that support for native execution is optional: adding a WebAssembly
+benchmark does not imply the need to support its native equivalent — CI
+will not fail if it is not included.
diff --git a/benchmarks/all.suite b/benchmarks/all.suite
index 7af79b4b..e62a0af7 100644
--- a/benchmarks/all.suite
+++ b/benchmarks/all.suite
@@ -79,15 +79,12 @@ libsodium/libsodium-secretbox7.wasm
libsodium/libsodium-secretbox8.wasm
libsodium/libsodium-secretbox_easy.wasm
libsodium/libsodium-secretbox_easy2.wasm
-libsodium/libsodium-secretstream.wasm
libsodium/libsodium-secretstream_xchacha20poly1305.wasm
libsodium/libsodium-shorthash.wasm
libsodium/libsodium-sign.wasm
libsodium/libsodium-siphashx24.wasm
libsodium/libsodium-sodium_core.wasm
libsodium/libsodium-sodium_utils.wasm
-libsodium/libsodium-sodium_utils2.wasm
-libsodium/libsodium-sodium_utils3.wasm
libsodium/libsodium-sodium_version.wasm
libsodium/libsodium-stream.wasm
libsodium/libsodium-stream2.wasm
diff --git a/benchmarks/blake3-simd/Dockerfile b/benchmarks/blake3-simd/Dockerfile
index 509b3024..d28a9491 100644
--- a/benchmarks/blake3-simd/Dockerfile
+++ b/benchmarks/blake3-simd/Dockerfile
@@ -1,21 +1,33 @@
# This two-phase Dockerfile allows us to avoid re-downloading APT packages and wasi-sdk with every
# build.
+ARG WASI_SDK_VERSION=28
+
# First, retrieve wasi-sdk:
FROM ubuntu:24.04 AS builder
+ARG WASI_SDK_VERSION
WORKDIR /
RUN apt update && apt install -y wget
-# Download and extract wasi-sdk.
-RUN wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-28/wasi-sdk-28.0-x86_64-linux.tar.gz
-RUN tar xvf wasi-sdk-28.0-x86_64-linux.tar.gz
+# Download and extract wasi-sdk for the host architecture, then normalize the install path to
+# `/wasi-sdk` so that the stage below does not have to know the version or architecture.
+RUN ARCH=$(uname -m); \
+ case "$ARCH" in \
+ x86_64) WASI_ARCH=x86_64 ;; \
+ aarch64 | arm64) WASI_ARCH=arm64 ;; \
+ *) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; \
+ esac; \
+ TARBALL=wasi-sdk-${WASI_SDK_VERSION}.0-${WASI_ARCH}-linux.tar.gz; \
+ wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION}/$TARBALL; \
+ tar xvf $TARBALL; \
+ mv wasi-sdk-${WASI_SDK_VERSION}.0-${WASI_ARCH}-linux /wasi-sdk
# Second, compile the benchmark to Wasm.
FROM ubuntu:24.04
WORKDIR /
-COPY --from=builder /wasi-sdk-28.0-x86_64-linux /wasi-sdk/
+COPY --from=builder /wasi-sdk /wasi-sdk/
RUN apt update && apt install -y git patch
# Set common env vars.
diff --git a/benchmarks/blake3-simd/README.md b/benchmarks/blake3-simd/README.md
index 0b072d39..652131da 100644
--- a/benchmarks/blake3-simd/README.md
+++ b/benchmarks/blake3-simd/README.md
@@ -1,6 +1,13 @@
# BLAKE3
-This benchmark is similar to [../blake3-scalar] and should return the same hash result, but the
-build compiles BLAKE3's hand-written SSE2 implementation (`blake3_sse2.c`): `wasm_sse_compat.h`
-maps its x86 SSE2 intrinsics onto Wasm SIMD (via ``), and a small patch forces
-BLAKE3's runtime dispatcher to select the SSE2 kernels on wasm.
+This benchmark is similar to [../blake3-scalar], but the build compiles BLAKE3's hand-written
+SSE2 implementation (`blake3_sse2.c`): `wasm_sse_compat.h` maps its x86 SSE2 intrinsics onto
+Wasm SIMD (via ``), and a small patch forces BLAKE3's runtime dispatcher to
+select the SSE2 kernels on wasm.
+
+Both benchmarks hash whatever is in their own `default.input`, and both are sized to execute
+~100M Wasm instructions (see [../README.md]). Because the SIMD kernels do more work per
+instruction, this benchmark needs a larger input than [../blake3-scalar] to hit that target, so
+the two `default.input` files — and therefore the two expected hashes — differ. Feeding both
+benchmarks the same input does still produce the same hash; that is how
+`benchmark.stderr.expected` here was cross-checked.
diff --git a/benchmarks/blake3-simd/benchmark.stderr.expected b/benchmarks/blake3-simd/benchmark.stderr.expected
index 8b6e73dc..c9a6f4e8 100644
--- a/benchmarks/blake3-simd/benchmark.stderr.expected
+++ b/benchmarks/blake3-simd/benchmark.stderr.expected
@@ -1,3 +1,3 @@
[blake3] hashing ./default.input
-[blake3] input size = 3100000
-[blake3] returned 059bb0ac5450537c4d30544423687821a498c9af13c23782eed5e69fde7730ea
+[blake3] input size = 7220000
+[blake3] returned b1961ab611c4ed5f1ba0339a3b445c43ecc26ae1ae7926f93c351f0d28c6b631
diff --git a/benchmarks/blake3-simd/default.input b/benchmarks/blake3-simd/default.input
index 42236e6f..d0c6c102 100644
Binary files a/benchmarks/blake3-simd/default.input and b/benchmarks/blake3-simd/default.input differ
diff --git a/benchmarks/blind-sig/README.md b/benchmarks/blind-sig/README.md
index 829ac58c..171ca84a 100644
--- a/benchmarks/blind-sig/README.md
+++ b/benchmarks/blind-sig/README.md
@@ -5,3 +5,23 @@ crate](https://crates.io/crates/blind-rsa-signatures). Generating these
signatures is a useful test of math on big integers.
The `blind-rsa-signatures` crate is licensed under the MIT license.
+
+## Instruction count
+
+This benchmark executes ~245M Wasm instructions, which is above the ~100M that
+the rest of the corpus targets (see `benchmarks/README.md`).
+
+The measured region is a single blind signature — blind, blind-sign, finalize —
+so the only knob is the modulus size baked into `secret.der`, and private-key
+RSA work scales roughly cubically with it. `secret.der` is already at the
+smallest size the crate accepts: `blind-rsa-signatures` rejects any modulus
+outside 2048–4096 bits, and 2048 bits is what brought this benchmark down from
+~1.53G instructions at the original 4096 bits.
+
+Getting to ~100M would need a non-standard sub-2048-bit modulus, which the crate
+will not load, so this is left out of band deliberately. It is still well above
+`scripts/pca.R`'s `MIN_DYNAMIC_INST_COUNT`, so it continues to participate in
+the PCA.
+
+`benchmark.stdout.expected` is the signature itself, so it must be regenerated
+whenever `secret.der` changes.
diff --git a/benchmarks/blind-sig/benchmark.stdout.expected b/benchmarks/blind-sig/benchmark.stdout.expected
index 98f3f622..a9937e84 100644
--- a/benchmarks/blind-sig/benchmark.stdout.expected
+++ b/benchmarks/blind-sig/benchmark.stdout.expected
@@ -1,16 +1,8 @@
-835F2D15A3D17F41D69F7CC87CE0DDCFFE36D8D7DF81621C60DF3E8749E50086
-9BFE20B7299F76DDAEEADCF400ABFAC5FA011D27AE63CD0F114F927D0FC9AA3E
-E21A59E78A588985E44B279E9C0C94221CCB612F5B8F34189E68B312FD29A5A5
-C6DF06A474EE406BDF3EC9615C7FD6C4F9791CFFC900F4CD926C1FE14AB6E977
-B25B785A180FD2D8EE69DC203258066FD8D20C977D88ECE58DFCACBDCA17F5FD
-9D0F34F61DDA5F4AF1D61161166B0643D8FC018FBB84A5E5B89EED284EB0C6B0
-154F99DF135D039CEFAF62CA397A02C8595432E8F43D18059B2C27B54B7D9CEC
-84281294576F8A8FF8A43A9CD97EAC5B2BDF2F18DCFC9B20CF09FCBEFB94821A
-9A0467A248523F6C4D33EFBAD114B648BD0B5BD1D256F95B1484E7522FAC0387
-35E8B2CDC7A85EF72CBF7697B649A891D89B88EB1F6DD1E8EE8C60BAD16A519D
-A429FD3055A3FAD5747B53B2AB669F20C88CBE0895FCAA69ACF6728C3471975C
-551E72B2087550196AFE50E0A99432A516D8C54FF45821E09D1C246738E59BC5
-5FC1A72BCA72A95CD66F75E83F7BF8112E53300213BAAA92257B9B28A06660E2
-3EBF69556A2156E1E54108D25329247D38DA77F7DE1948C6D90FC363C5EB88B4
-15B1DDED8F6369A4F0299EEBA7E8EAD4CDB52741FC660DCCF6724794E1675297
-CE48D3D4FFACB0BD3BF32AC5764D39D9CF9B8D1AF921F5244AB3F72AB44805F7
+372C5483F1D365CE9382DA0E1275E5FDA8E3B45BBE387DEE0E73B945294A9E0B
+37C5B16BA928EC2D491B104D02F78FAE8BEA776BFD7A068FE3B99F9C33091D82
+B61B8530271F47A1E09046CEA6873D503542FB85E8C3A62C736E76B6A9BBB7E8
+7C07009A5795A028C792A796602C41D095CB468A6FFC9987FDDEF1E924EEDCCD
+3CFF65713C3094C2176ADFAAB13DE24DE193D72B5A41E3CC8548518FD354DFB1
+CC3448354619BBCF0898B4658D5B8F078623AD9632798669134E701AFE4A3F1A
+9044B82311281BED0077A3EC1CB90EB0D50D9DB2404EFD1A1666AE655D1D3119
+5BA621FC9094A709FC81C0ACAD7D404759FDFB1A8D73ADE0D58E33A51D373F56
diff --git a/benchmarks/blind-sig/benchmark.wasm b/benchmarks/blind-sig/benchmark.wasm
index 937d8220..e8748de9 100755
Binary files a/benchmarks/blind-sig/benchmark.wasm and b/benchmarks/blind-sig/benchmark.wasm differ
diff --git a/benchmarks/blind-sig/secret.der b/benchmarks/blind-sig/secret.der
index b7e21fd4..c8ea6141 100644
Binary files a/benchmarks/blind-sig/secret.der and b/benchmarks/blind-sig/secret.der differ
diff --git a/benchmarks/build.sh b/benchmarks/build.sh
index 9e670848..58b06c1e 100755
--- a/benchmarks/build.sh
+++ b/benchmarks/build.sh
@@ -30,7 +30,10 @@ print_header() {
# To allow the use of symlinks in the benchmark directories (docker ignores them), we `tar` up the
# directory and `--dereference` (i.e., follow) all symlinks provided.
print_header "Create build context"
-TMP_TAR=$(mktemp /tmp/sightglass-benchmark-dir-XXXXXX.tar)
+# Keep the `XXXXXX` at the end of the template: BSD `mktemp` (macOS) only substitutes *trailing*
+# `X`s, so a template like `...-XXXXXX.tar` is returned verbatim, and the next run then dies with
+# `mkstemp failed: File exists`.
+TMP_TAR=$(mktemp /tmp/sightglass-benchmark-dir-XXXXXX)
# macOS's bsdtar bundles extended attributes (notably `com.apple.provenance`) that a Linux Docker
# daemon rejects when unpacking the build context (`lsetxattr ... operation not supported`); exclude
# them. GNU tar (used on CI) omits xattrs by default and lacks `--no-mac-metadata`, so only pass
diff --git a/benchmarks/gcc-loops/Dockerfile b/benchmarks/gcc-loops/Dockerfile
index df7c6cb9..51efbc2f 100644
--- a/benchmarks/gcc-loops/Dockerfile
+++ b/benchmarks/gcc-loops/Dockerfile
@@ -1,21 +1,33 @@
# This two-phase Dockerfile allows us to avoid re-downloading APT packages and wasi-sdk with every
# build.
+ARG WASI_SDK_VERSION=28
+
# First, retrieve wasi-sdk:
FROM ubuntu:24.04 AS builder
+ARG WASI_SDK_VERSION
WORKDIR /
RUN apt update && apt install -y wget
-# Download and extract wasi-sdk.
-RUN wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-28/wasi-sdk-28.0-x86_64-linux.tar.gz
-RUN tar xvf wasi-sdk-28.0-x86_64-linux.tar.gz
+# Download and extract wasi-sdk for the host architecture, then normalize the install path to
+# `/wasi-sdk` so that the stage below does not have to know the version or architecture.
+RUN ARCH=$(uname -m); \
+ case "$ARCH" in \
+ x86_64) WASI_ARCH=x86_64 ;; \
+ aarch64 | arm64) WASI_ARCH=arm64 ;; \
+ *) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; \
+ esac; \
+ TARBALL=wasi-sdk-${WASI_SDK_VERSION}.0-${WASI_ARCH}-linux.tar.gz; \
+ wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION}/$TARBALL; \
+ tar xvf $TARBALL; \
+ mv wasi-sdk-${WASI_SDK_VERSION}.0-${WASI_ARCH}-linux /wasi-sdk
# Second, compile the benchmark to Wasm.
FROM ubuntu:24.04
WORKDIR /
-COPY --from=builder /wasi-sdk-28.0-x86_64-linux /wasi-sdk/
+COPY --from=builder /wasi-sdk /wasi-sdk/
# Set common env vars.
ENV CC=/wasi-sdk/bin/clang
diff --git a/benchmarks/gcc-loops/benchmark.wasm b/benchmarks/gcc-loops/benchmark.wasm
index 77712647..25e913f4 100755
Binary files a/benchmarks/gcc-loops/benchmark.wasm and b/benchmarks/gcc-loops/benchmark.wasm differ
diff --git a/benchmarks/gcc-loops/default.input b/benchmarks/gcc-loops/default.input
new file mode 100644
index 00000000..88b2e783
--- /dev/null
+++ b/benchmarks/gcc-loops/default.input
@@ -0,0 +1 @@
+191
diff --git a/benchmarks/gcc-loops/default.stdout.expected b/benchmarks/gcc-loops/default.stdout.expected
index 1f0e8061..b71b9bbb 100644
--- a/benchmarks/gcc-loops/default.stdout.expected
+++ b/benchmarks/gcc-loops/default.stdout.expected
@@ -1 +1 @@
-Results: (1e2fdc4e): ed7aa26a b9b28d5b 67808d19 b4412829 132bc4c6 132bc4c6 b4412829 eb3492a9 7e51915b 0 61ffda0c f95b0406 495fecb4 ab6b4a02 a1d16823 bdaa178a 0 67808d19
+Results: (efa423bd): ed7aa26a b9b28d5b 67808d19 b4412829 132bc4c6 132bc4c6 b4412829 eb3492a9 7e51915b 0 61ffda0c f95b0406 495fecb4 ab6b4a02 7345af92 bdaa178a 0 67808d19
diff --git a/benchmarks/gcc-loops/gcc-loops.cpp b/benchmarks/gcc-loops/gcc-loops.cpp
index f9a3acfb..f4250944 100644
--- a/benchmarks/gcc-loops/gcc-loops.cpp
+++ b/benchmarks/gcc-loops/gcc-loops.cpp
@@ -346,7 +346,11 @@ int main(int argc,char* argv[]){
#ifdef SMALL_PROBLEM_SIZE
const int Mi = 1<<10;
#else
- const int Mi = 1<<18;
+ // The problem size is read from `./default.input` so that the workload can be retuned without
+ // recompiling; the fallback is tuned so that this benchmark executes ~100M Wasm instructions.
+ // `Mi` scales all of the kernels below uniformly, and the digests they print depend on it, so
+ // `default.stdout.expected` must be regenerated whenever it changes.
+ const int Mi = (int) bench_read_long("./default.input", 191);
#endif
init_memory(&ia[0], &ia[N]);
init_memory(&ib[0], &ib[N]);
diff --git a/benchmarks/gcc-loops/sightglass.h b/benchmarks/gcc-loops/sightglass.h
deleted file mode 100644
index a767e9e3..00000000
--- a/benchmarks/gcc-loops/sightglass.h
+++ /dev/null
@@ -1,37 +0,0 @@
-#ifndef sightglass_h
-#define sightglass_h 1
-
-/**
- * Call this function to indicate that recording should start. This call should be placed
- * immediately prior to the code to measure with sightglass-recorder. The attributes allow compilers
- * to generate the correct Wasm imports.
- */
-__attribute__((import_module("bench")))
-__attribute__((import_name("start")))
-void bench_start();
-
-/**
- * Call this function to indicate that recording should end. This call should be placed immediately
- * after the code to measure with sightglass-recorder. The attributes allow compilers to generate
- * the correct Wasm imports.
- */
-__attribute__((import_module("bench")))
-__attribute__((import_name("end")))
-void bench_end();
-
-/**
- * Call this function to prevent certain compiler-related optimizations related to knowing the value
- * of the passed variable.
- */
-#ifndef black_box
-static void _black_box(void *x)
-{
- (void)x;
-}
-static void (*volatile black_box)(void *x) = _black_box;
-#else
-void black_box(void *x);
-#endif
-#define BLACK_BOX(X) black_box((void *)&(X))
-
-#endif
diff --git a/benchmarks/gcc-loops/sightglass.h b/benchmarks/gcc-loops/sightglass.h
new file mode 120000
index 00000000..c4dfc720
--- /dev/null
+++ b/benchmarks/gcc-loops/sightglass.h
@@ -0,0 +1 @@
+../../include/sightglass.h
\ No newline at end of file
diff --git a/benchmarks/hashset/Dockerfile b/benchmarks/hashset/Dockerfile
index 07c4d7c2..c92e126f 100644
--- a/benchmarks/hashset/Dockerfile
+++ b/benchmarks/hashset/Dockerfile
@@ -1,21 +1,33 @@
# This two-phase Dockerfile allows us to avoid re-downloading APT packages and wasi-sdk with every
# build.
+ARG WASI_SDK_VERSION=28
+
# First, retrieve wasi-sdk:
FROM ubuntu:24.04 AS builder
+ARG WASI_SDK_VERSION
WORKDIR /
RUN apt update && apt install -y wget
-# Download and extract wasi-sdk.
-RUN wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-28/wasi-sdk-28.0-x86_64-linux.tar.gz
-RUN tar xvf wasi-sdk-28.0-x86_64-linux.tar.gz
+# Download and extract wasi-sdk for the host architecture, then normalize the install path to
+# `/wasi-sdk` so that the stage below does not have to know the version or architecture.
+RUN ARCH=$(uname -m); \
+ case "$ARCH" in \
+ x86_64) WASI_ARCH=x86_64 ;; \
+ aarch64 | arm64) WASI_ARCH=arm64 ;; \
+ *) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; \
+ esac; \
+ TARBALL=wasi-sdk-${WASI_SDK_VERSION}.0-${WASI_ARCH}-linux.tar.gz; \
+ wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION}/$TARBALL; \
+ tar xvf $TARBALL; \
+ mv wasi-sdk-${WASI_SDK_VERSION}.0-${WASI_ARCH}-linux /wasi-sdk
# Second, compile the benchmark to Wasm.
FROM ubuntu:24.04
WORKDIR /
-COPY --from=builder /wasi-sdk-28.0-x86_64-linux /wasi-sdk/
+COPY --from=builder /wasi-sdk /wasi-sdk/
# Set common env vars.
ENV CC=/wasi-sdk/bin/clang
diff --git a/benchmarks/hashset/HashSet.cpp b/benchmarks/hashset/HashSet.cpp
index 5c478a59..f666e6e7 100644
--- a/benchmarks/hashset/HashSet.cpp
+++ b/benchmarks/hashset/HashSet.cpp
@@ -13475,9 +13475,13 @@ void benchmark()
int main(int, char**)
{
+ // The iteration count is read from `./default.input` so that the workload can be retuned
+ // without recompiling; the fallback is tuned so that this benchmark executes ~100M Wasm
+ // instructions.
+ unsigned iterations = (unsigned)bench_read_long("./default.input", 33);
double before = currentTime();
bench_start();
- for (unsigned i = 0; i < 100; ++i)
+ for (unsigned i = 0; i < iterations; ++i)
benchmark();
bench_end();
double after = currentTime();
diff --git a/benchmarks/hashset/benchmark.wasm b/benchmarks/hashset/benchmark.wasm
index 60ed6219..bd576187 100755
Binary files a/benchmarks/hashset/benchmark.wasm and b/benchmarks/hashset/benchmark.wasm differ
diff --git a/benchmarks/hashset/default.input b/benchmarks/hashset/default.input
new file mode 100644
index 00000000..bb95160c
--- /dev/null
+++ b/benchmarks/hashset/default.input
@@ -0,0 +1 @@
+33
diff --git a/benchmarks/hashset/sightglass.h b/benchmarks/hashset/sightglass.h
deleted file mode 100644
index a767e9e3..00000000
--- a/benchmarks/hashset/sightglass.h
+++ /dev/null
@@ -1,37 +0,0 @@
-#ifndef sightglass_h
-#define sightglass_h 1
-
-/**
- * Call this function to indicate that recording should start. This call should be placed
- * immediately prior to the code to measure with sightglass-recorder. The attributes allow compilers
- * to generate the correct Wasm imports.
- */
-__attribute__((import_module("bench")))
-__attribute__((import_name("start")))
-void bench_start();
-
-/**
- * Call this function to indicate that recording should end. This call should be placed immediately
- * after the code to measure with sightglass-recorder. The attributes allow compilers to generate
- * the correct Wasm imports.
- */
-__attribute__((import_module("bench")))
-__attribute__((import_name("end")))
-void bench_end();
-
-/**
- * Call this function to prevent certain compiler-related optimizations related to knowing the value
- * of the passed variable.
- */
-#ifndef black_box
-static void _black_box(void *x)
-{
- (void)x;
-}
-static void (*volatile black_box)(void *x) = _black_box;
-#else
-void black_box(void *x);
-#endif
-#define BLACK_BOX(X) black_box((void *)&(X))
-
-#endif
diff --git a/benchmarks/hashset/sightglass.h b/benchmarks/hashset/sightglass.h
new file mode 120000
index 00000000..c4dfc720
--- /dev/null
+++ b/benchmarks/hashset/sightglass.h
@@ -0,0 +1 @@
+../../include/sightglass.h
\ No newline at end of file
diff --git a/benchmarks/intgemm-simd/Dockerfile b/benchmarks/intgemm-simd/Dockerfile
index b46e2035..88570a11 100644
--- a/benchmarks/intgemm-simd/Dockerfile
+++ b/benchmarks/intgemm-simd/Dockerfile
@@ -1,21 +1,33 @@
# This two-phase Dockerfile allows us to avoid re-downloading APT packages and wasi-sdk with every
# build.
+ARG WASI_SDK_VERSION=28
+
# First, retrieve wasi-sdk:
FROM ubuntu:24.04 AS builder
+ARG WASI_SDK_VERSION
WORKDIR /
RUN apt update && apt install -y wget
-# Download and extract wasi-sdk.
-RUN wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-28/wasi-sdk-28.0-x86_64-linux.tar.gz
-RUN tar xvf wasi-sdk-28.0-x86_64-linux.tar.gz
+# Download and extract wasi-sdk for the host architecture, then normalize the install path to
+# `/wasi-sdk` so that the stage below does not have to know the version or architecture.
+RUN ARCH=$(uname -m); \
+ case "$ARCH" in \
+ x86_64) WASI_ARCH=x86_64 ;; \
+ aarch64 | arm64) WASI_ARCH=arm64 ;; \
+ *) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; \
+ esac; \
+ TARBALL=wasi-sdk-${WASI_SDK_VERSION}.0-${WASI_ARCH}-linux.tar.gz; \
+ wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION}/$TARBALL; \
+ tar xvf $TARBALL; \
+ mv wasi-sdk-${WASI_SDK_VERSION}.0-${WASI_ARCH}-linux /wasi-sdk
# Second, compile the benchmark to Wasm.
FROM ubuntu:24.04
WORKDIR /
-COPY --from=builder /wasi-sdk-28.0-x86_64-linux /wasi-sdk/
+COPY --from=builder /wasi-sdk /wasi-sdk/
RUN apt update && apt install -y git cmake make patch
# Set common env vars.
diff --git a/benchmarks/libsodium/Dockerfile b/benchmarks/libsodium/Dockerfile
index 1a1255a7..6944b077 100644
--- a/benchmarks/libsodium/Dockerfile
+++ b/benchmarks/libsodium/Dockerfile
@@ -2,16 +2,33 @@
FROM ubuntu:22.04
RUN apt update && apt install -y wget xz-utils unzip git
-# Download and extract the latest stable version of Zig and retrieve the
-# `stable` branch of libsodium that can be built by it.
+# Download Zig and retrieve a pinned commit of libsodium that can be built by it.
+ARG ZIG_VERSION=0.12.0
WORKDIR /
-RUN wget https://ziglang.org/download/0.12.0/zig-linux-x86_64-0.12.0.tar.xz
-RUN tar --extract --verbose --file=zig-linux-x86_64-0.12.0.tar.xz
-ENV PATH="${PATH}:/zig-linux-x86_64-0.12.0"
+# Download Zig for the host architecture and normalize the install path to `/zig` so that the rest
+# of this file does not have to know the version or architecture.
+RUN ARCH=$(uname -m); \
+ case "$ARCH" in \
+ x86_64) ZIG_ARCH=x86_64 ;; \
+ aarch64 | arm64) ZIG_ARCH=aarch64 ;; \
+ *) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; \
+ esac; \
+ TARBALL=zig-linux-${ZIG_ARCH}-${ZIG_VERSION}.tar.xz; \
+ wget https://ziglang.org/download/${ZIG_VERSION}/$TARBALL; \
+ tar --extract --verbose --file=$TARBALL; \
+ mv zig-linux-${ZIG_ARCH}-${ZIG_VERSION} /zig
+ENV PATH="${PATH}:/zig"
RUN echo "zig version = $(zig version)"
-RUN wget https://github.com/jedisct1/libsodium/releases/download/1.0.20-RELEASE/libsodium-1.0.20.tar.gz
-RUN tar -xzf libsodium-1.0.20.tar.gz
-RUN mv /libsodium-1.0.20 /libsodium
+# Fetch libsodium at a pinned commit -- the `1.0.20-RELEASE` tag as of this writing. Pin by commit
+# hash rather than by tag or release tarball, both of which upstream can replace in place. This
+# directory produces one benchmark per file in `test/default/`, so a test that upstream renames or
+# deletes would otherwise silently change which `.wasm` files this build emits; with a hash, that
+# can only happen when we deliberately bump `LIBSODIUM_COMMIT` here.
+ARG LIBSODIUM_COMMIT=9511c982fb1d046470a8b42aa36556cdb7da15de
+RUN git init /libsodium \
+ && git -C /libsodium remote add origin https://github.com/jedisct1/libsodium \
+ && git -C /libsodium fetch --depth 1 origin ${LIBSODIUM_COMMIT} \
+ && git -C /libsodium checkout --detach ${LIBSODIUM_COMMIT}
# Build the benchmarks. Note how we patch the self-timing code with calls to the
# Sightglass `bench_start()` and `bench_end()` functions. Also note that each
diff --git a/benchmarks/libsodium/README.md b/benchmarks/libsodium/README.md
new file mode 100644
index 00000000..d0e1c868
--- /dev/null
+++ b/benchmarks/libsodium/README.md
@@ -0,0 +1,115 @@
+# libsodium
+
+The [libsodium](https://libsodium.org/) test suite, built as one Wasm benchmark per
+upstream test in `test/default/`. `add-bench-calls.diff` patches upstream's
+`test/default/cmptest.h` so that the real `main()` wraps the test body (which upstream
+renames to `xmain()`) in `bench_start()`/`bench_end()`.
+
+## Upstream pin
+
+Because the set of benchmarks here is derived from the set of files in upstream's
+`test/default/`, upstream renaming or deleting a test silently changes which `.wasm` files
+this directory produces — and leaves the old one behind as an untunable orphan. The
+`Dockerfile` therefore pins `LIBSODIUM_COMMIT` to a commit hash rather than to a tag or a
+release tarball, either of which upstream can replace in place. Bumping that hash is the
+only thing that can change the benchmark set; when you do bump it, diff the built `.wasm`
+names against the checked-in ones and add or remove `.input` files and `../*.suite` entries
+to match.
+
+## Tuning
+
+The only knob is how many times each test body runs. `main()` reads it from a sibling
+input file named after the test:
+
+```c
+iterations = (unsigned int) bench_read_long("./libsodium-" TEST_NAME ".input", ITERATIONS);
+bench_start();
+for (i = 0; i < iterations; i++) {
+ if (xmain() != 0) { abort(); }
+}
+bench_end();
+```
+
+`TEST_NAME` is already defined by every upstream test file before it includes
+`cmptest.h`, and it always matches the file stem, so `libsodium-auth7.wasm` reads
+`./libsodium-auth7.input`. The read happens before `bench_start()`, so it is not
+measured, and it falls back to the compiled-in `ITERATIONS` (which the `Dockerfile`
+sets to 1) when the file is absent.
+
+This means **retuning any of these benchmarks needs no Docker rebuild** — just edit the
+input file. `default.stdout.expected` is a single `0` and does not depend on the
+iteration count.
+
+There is deliberately no per-test surgery inside the `xmain()` bodies: they are upstream
+code, and keeping them untouched is what makes the patch small enough to rebase onto a
+new libsodium release.
+
+## Status
+
+45 of the 78 tests are within the 80M-120M band described in [../README.md](../README.md).
+The rest cannot get there with an iteration count as the only lever.
+
+### Over the band even at one iteration (27)
+
+One iteration of the test body already costs more than 120M instructions, so there is no
+integer iteration count that lands in the band.
+
+| test | instructions | test | instructions |
+| --- | --- | --- | --- |
+| `core_ristretto255` | 39.01G | `verify1` | 852.6M |
+| `core_ed25519` | 35.34G | `metamorphic` | 442.5M |
+| `box8` | 28.94G | `auth5` | 432.4M |
+| `pwhash_scrypt` | 21.23G | `secretbox8` | 423.0M |
+| `sign` | 15.20G | `box_easy2` | 384.2M |
+| `box7` | 9.70G | `xchacha20` | 316.8M |
+| `pwhash_argon2id` | 5.92G | `scalarmult8` | 292.9M |
+| `pwhash_argon2i` | 5.20G | `sodium_utils` | 242.9M |
+| `ed25519_convert` | 4.17G | `auth7` | 228.2M |
+| `core3` | 1.64G | `aead_aegis256` | 151.1M |
+| `pwhash_scrypt_ll` | 1.40G | `secretbox7` | 140.3M |
+| `secretbox_easy2` | 1.18G | `aead_aegis128l` | 136.1M |
+| `stream` | 1.12G | `onetimeauth7` | 131.6M |
+| `stream2` | 1.03G | | |
+
+### No measurable work on Wasm (6)
+
+These bodies are empty or near-empty once compiled to Wasm, so no iteration count can
+reach the band. They are left at 1 iteration rather than padded with a loop that would
+measure nothing but loop overhead.
+
+| test | instructions | why |
+| --- | --- | --- |
+| `aead_aes256gcm` | 65 | Guarded by `crypto_aead_aes256gcm_is_available()`, which is false on Wasm. |
+| `aead_aes256gcm2` | 27 | Same AES-NI availability check as `aead_aes256gcm`. |
+| `misuse` | 0 | Its body is `#ifdef HAVE_CATCHABLE_ABRT`, and `build.zig` only defines that for Linux and macOS, so on Wasm the test is just `return 0`. |
+| `onetimeauth2` | 0 | Its only statement is `printf("%d\n", crypto_onetimeauth_verify(...))`, and `cmptest.h` defines `printf(...)` to `do { } while(0)` — so the argument is never evaluated and the verify never runs. |
+| `sodium_core` | 236 | Only runtime CPU feature detection, all false on Wasm. |
+| `sodium_version` | 25 | Only version-string getters. |
+
+### Not deterministic (7)
+
+These seed libsodium's RNG from the OS (WASI `random_get`) and then size their work from
+it, so their instruction count varies between runs of the *same* Wasm. They cannot be
+tuned to a target at all.
+
+| test | observed range | note |
+| --- | --- | --- |
+| `box_easy2` | 221M - 3.79G | Cost is quadratic in a random message length. |
+| `secretbox_easy2` | 261M - 9.02G | Cost is quadratic in a random message length. |
+| `sodium_utils` | 243M - 377M | Over the band at any iteration count regardless. |
+| `aead_aegis256` | 151M - 153M | Over the band at any iteration count regardless. |
+| `aead_aegis128l` | 134M - 137M | Over the band at any iteration count regardless. |
+| `codecs` | 97M - 98M | In band; the jitter is ~1%, so it is left alone. |
+| `secretstream_xchacha20poly1305` | 98M - 102M | In band; the jitter is ~4%, so it is left alone. |
+
+Because of this, the single figures quoted for `box_easy2`, `secretbox_easy2`,
+`sodium_utils`, `aead_aegis256`, and `aead_aegis128l` in the table above are just whatever
+one run happened to produce.
+
+This conflicts with the determinism requirement in [../README.md](../README.md); it is a
+pre-existing property of these upstream tests rather than something the iteration knob
+introduced.
+
+## License
+
+libsodium is licensed under the ISC license.
diff --git a/benchmarks/libsodium/add-bench-calls.diff b/benchmarks/libsodium/add-bench-calls.diff
index ff194739..b7cf7911 100644
--- a/benchmarks/libsodium/add-bench-calls.diff
+++ b/benchmarks/libsodium/add-bench-calls.diff
@@ -1,8 +1,13 @@
diff --git a/test/default/cmptest.h b/test/default/cmptest.h
-index 54a02070..36d98736 100644
+index 54a0207..40507fd 100644
--- a/test/default/cmptest.h
+++ b/test/default/cmptest.h
-@@ -40,6 +40,7 @@ static unsigned char *guard_page;
+@@ -35,16 +35,17 @@
+
+ int xmain(void);
+
+ static unsigned char *guard_page;
+
#ifdef BENCHMARKS
# include
@@ -10,13 +15,44 @@ index 54a02070..36d98736 100644
# ifndef ITERATIONS
# define ITERATIONS 128
-@@ -150,13 +151,13 @@ int main(void)
+ # endif
+
+ struct {
+ void *pnt;
+ size_t size;
+@@ -136,32 +137,43 @@ static unsigned long long now(void)
+ (unsigned long long) tp.tv_usec;
+ #endif
+ }
+
+ int main(void)
+ {
+ unsigned long long ts_start;
+ unsigned long long ts_end;
++ unsigned int iterations;
+ unsigned int i;
+
+ if (sodium_init() != 0) {
+ return 99;
+ }
+
#ifndef __EMSCRIPTEN__
randombytes_set_implementation(&randombytes_salsa20_implementation);
#endif
- ts_start = now();
+- for (i = 0; i < ITERATIONS; i++) {
++ /* Sightglass: read this benchmark's iteration count from a sibling input file so that it can
++ be retuned to hit the target instruction count without rebuilding the Wasm. Falls back to
++ the compiled-in ITERATIONS when the file is missing. Read before bench_start() so the I/O
++ is not measured. */
++ iterations = (unsigned int) bench_read_long("./libsodium-" TEST_NAME ".input", ITERATIONS);
++ /* Sightglass: the timing below is not used (bench_start()/bench_end() do the measuring), but
++ zero these so the printf is deterministic rather than reading uninitialized locals. Keep
++ dividing by the ITERATIONS macro -- a nonzero compile-time constant -- so that an iteration
++ count of 0 in the input file cannot divide by zero. */
++ ts_start = ts_end = 0;
+ bench_start();
- for (i = 0; i < ITERATIONS; i++) {
++ for (i = 0; i < iterations; i++) {
if (xmain() != 0) {
abort();
}
@@ -26,3 +62,8 @@ index 54a02070..36d98736 100644
printf("%llu\n", 1000000ULL * (ts_end - ts_start) / ITERATIONS);
if (mempool_free_all() != 0) {
fprintf(stderr, "** memory leaks detected **\n");
+ return 99;
+ }
+ return 0;
+ }
+
diff --git a/benchmarks/libsodium/libsodium-aead_aegis128l.input b/benchmarks/libsodium/libsodium-aead_aegis128l.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-aead_aegis128l.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-aead_aegis128l.wasm b/benchmarks/libsodium/libsodium-aead_aegis128l.wasm
index 4a919c51..b15e9f92 100755
Binary files a/benchmarks/libsodium/libsodium-aead_aegis128l.wasm and b/benchmarks/libsodium/libsodium-aead_aegis128l.wasm differ
diff --git a/benchmarks/libsodium/libsodium-aead_aegis256.input b/benchmarks/libsodium/libsodium-aead_aegis256.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-aead_aegis256.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-aead_aegis256.wasm b/benchmarks/libsodium/libsodium-aead_aegis256.wasm
index f260b164..ea0a0df7 100755
Binary files a/benchmarks/libsodium/libsodium-aead_aegis256.wasm and b/benchmarks/libsodium/libsodium-aead_aegis256.wasm differ
diff --git a/benchmarks/libsodium/libsodium-aead_aes256gcm.input b/benchmarks/libsodium/libsodium-aead_aes256gcm.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-aead_aes256gcm.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-aead_aes256gcm.wasm b/benchmarks/libsodium/libsodium-aead_aes256gcm.wasm
index eae5a53a..372b1571 100755
Binary files a/benchmarks/libsodium/libsodium-aead_aes256gcm.wasm and b/benchmarks/libsodium/libsodium-aead_aes256gcm.wasm differ
diff --git a/benchmarks/libsodium/libsodium-aead_aes256gcm2.input b/benchmarks/libsodium/libsodium-aead_aes256gcm2.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-aead_aes256gcm2.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-aead_aes256gcm2.wasm b/benchmarks/libsodium/libsodium-aead_aes256gcm2.wasm
index 60cd3c74..0ad1c138 100755
Binary files a/benchmarks/libsodium/libsodium-aead_aes256gcm2.wasm and b/benchmarks/libsodium/libsodium-aead_aes256gcm2.wasm differ
diff --git a/benchmarks/libsodium/libsodium-aead_chacha20poly1305.input b/benchmarks/libsodium/libsodium-aead_chacha20poly1305.input
new file mode 100644
index 00000000..7facc899
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-aead_chacha20poly1305.input
@@ -0,0 +1 @@
+36
diff --git a/benchmarks/libsodium/libsodium-aead_chacha20poly1305.wasm b/benchmarks/libsodium/libsodium-aead_chacha20poly1305.wasm
index 3a109573..9b80bbc8 100755
Binary files a/benchmarks/libsodium/libsodium-aead_chacha20poly1305.wasm and b/benchmarks/libsodium/libsodium-aead_chacha20poly1305.wasm differ
diff --git a/benchmarks/libsodium/libsodium-aead_chacha20poly13052.input b/benchmarks/libsodium/libsodium-aead_chacha20poly13052.input
new file mode 100644
index 00000000..60d3b2f4
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-aead_chacha20poly13052.input
@@ -0,0 +1 @@
+15
diff --git a/benchmarks/libsodium/libsodium-aead_chacha20poly13052.wasm b/benchmarks/libsodium/libsodium-aead_chacha20poly13052.wasm
index 20efb427..f951a0f4 100755
Binary files a/benchmarks/libsodium/libsodium-aead_chacha20poly13052.wasm and b/benchmarks/libsodium/libsodium-aead_chacha20poly13052.wasm differ
diff --git a/benchmarks/libsodium/libsodium-aead_xchacha20poly1305.input b/benchmarks/libsodium/libsodium-aead_xchacha20poly1305.input
new file mode 100644
index 00000000..81b5c5d0
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-aead_xchacha20poly1305.input
@@ -0,0 +1 @@
+37
diff --git a/benchmarks/libsodium/libsodium-aead_xchacha20poly1305.wasm b/benchmarks/libsodium/libsodium-aead_xchacha20poly1305.wasm
index a39ba3b0..25d12047 100755
Binary files a/benchmarks/libsodium/libsodium-aead_xchacha20poly1305.wasm and b/benchmarks/libsodium/libsodium-aead_xchacha20poly1305.wasm differ
diff --git a/benchmarks/libsodium/libsodium-auth.input b/benchmarks/libsodium/libsodium-auth.input
new file mode 100644
index 00000000..078fa0fe
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-auth.input
@@ -0,0 +1 @@
+119
diff --git a/benchmarks/libsodium/libsodium-auth.wasm b/benchmarks/libsodium/libsodium-auth.wasm
index 5290a89b..8f2e0337 100755
Binary files a/benchmarks/libsodium/libsodium-auth.wasm and b/benchmarks/libsodium/libsodium-auth.wasm differ
diff --git a/benchmarks/libsodium/libsodium-auth2.input b/benchmarks/libsodium/libsodium-auth2.input
new file mode 100644
index 00000000..8c88d58d
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-auth2.input
@@ -0,0 +1 @@
+2399
diff --git a/benchmarks/libsodium/libsodium-auth2.wasm b/benchmarks/libsodium/libsodium-auth2.wasm
index 9f2cacf5..3728b017 100755
Binary files a/benchmarks/libsodium/libsodium-auth2.wasm and b/benchmarks/libsodium/libsodium-auth2.wasm differ
diff --git a/benchmarks/libsodium/libsodium-auth3.input b/benchmarks/libsodium/libsodium-auth3.input
new file mode 100644
index 00000000..8c8b8bca
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-auth3.input
@@ -0,0 +1 @@
+1179
diff --git a/benchmarks/libsodium/libsodium-auth3.wasm b/benchmarks/libsodium/libsodium-auth3.wasm
index 84fe310f..9b630ef1 100755
Binary files a/benchmarks/libsodium/libsodium-auth3.wasm and b/benchmarks/libsodium/libsodium-auth3.wasm differ
diff --git a/benchmarks/libsodium/libsodium-auth5.input b/benchmarks/libsodium/libsodium-auth5.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-auth5.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-auth5.wasm b/benchmarks/libsodium/libsodium-auth5.wasm
index d8f142af..2dfa5a67 100755
Binary files a/benchmarks/libsodium/libsodium-auth5.wasm and b/benchmarks/libsodium/libsodium-auth5.wasm differ
diff --git a/benchmarks/libsodium/libsodium-auth6.input b/benchmarks/libsodium/libsodium-auth6.input
new file mode 100644
index 00000000..000b1738
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-auth6.input
@@ -0,0 +1 @@
+1485
diff --git a/benchmarks/libsodium/libsodium-auth6.wasm b/benchmarks/libsodium/libsodium-auth6.wasm
index 1f88ec0e..d9ef2ad9 100755
Binary files a/benchmarks/libsodium/libsodium-auth6.wasm and b/benchmarks/libsodium/libsodium-auth6.wasm differ
diff --git a/benchmarks/libsodium/libsodium-auth7.input b/benchmarks/libsodium/libsodium-auth7.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-auth7.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-auth7.wasm b/benchmarks/libsodium/libsodium-auth7.wasm
index 8047e292..cf3f2fb5 100755
Binary files a/benchmarks/libsodium/libsodium-auth7.wasm and b/benchmarks/libsodium/libsodium-auth7.wasm differ
diff --git a/benchmarks/libsodium/libsodium-box.input b/benchmarks/libsodium/libsodium-box.input
new file mode 100644
index 00000000..f599e28b
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-box.input
@@ -0,0 +1 @@
+10
diff --git a/benchmarks/libsodium/libsodium-box.wasm b/benchmarks/libsodium/libsodium-box.wasm
index 6d33ded0..ad8db1c1 100755
Binary files a/benchmarks/libsodium/libsodium-box.wasm and b/benchmarks/libsodium/libsodium-box.wasm differ
diff --git a/benchmarks/libsodium/libsodium-box2.input b/benchmarks/libsodium/libsodium-box2.input
new file mode 100644
index 00000000..f599e28b
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-box2.input
@@ -0,0 +1 @@
+10
diff --git a/benchmarks/libsodium/libsodium-box2.wasm b/benchmarks/libsodium/libsodium-box2.wasm
index daf1d043..f169d939 100755
Binary files a/benchmarks/libsodium/libsodium-box2.wasm and b/benchmarks/libsodium/libsodium-box2.wasm differ
diff --git a/benchmarks/libsodium/libsodium-box7.input b/benchmarks/libsodium/libsodium-box7.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-box7.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-box7.wasm b/benchmarks/libsodium/libsodium-box7.wasm
index 1fc47a7b..52ce0a5e 100755
Binary files a/benchmarks/libsodium/libsodium-box7.wasm and b/benchmarks/libsodium/libsodium-box7.wasm differ
diff --git a/benchmarks/libsodium/libsodium-box8.input b/benchmarks/libsodium/libsodium-box8.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-box8.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-box8.wasm b/benchmarks/libsodium/libsodium-box8.wasm
index 78b25526..0380eebf 100755
Binary files a/benchmarks/libsodium/libsodium-box8.wasm and b/benchmarks/libsodium/libsodium-box8.wasm differ
diff --git a/benchmarks/libsodium/libsodium-box_easy.input b/benchmarks/libsodium/libsodium-box_easy.input
new file mode 100644
index 00000000..7ed6ff82
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-box_easy.input
@@ -0,0 +1 @@
+5
diff --git a/benchmarks/libsodium/libsodium-box_easy.wasm b/benchmarks/libsodium/libsodium-box_easy.wasm
index 3ecf181c..f5e3c4f6 100755
Binary files a/benchmarks/libsodium/libsodium-box_easy.wasm and b/benchmarks/libsodium/libsodium-box_easy.wasm differ
diff --git a/benchmarks/libsodium/libsodium-box_easy2.input b/benchmarks/libsodium/libsodium-box_easy2.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-box_easy2.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-box_easy2.wasm b/benchmarks/libsodium/libsodium-box_easy2.wasm
index 62dfdb3b..d5018e3d 100755
Binary files a/benchmarks/libsodium/libsodium-box_easy2.wasm and b/benchmarks/libsodium/libsodium-box_easy2.wasm differ
diff --git a/benchmarks/libsodium/libsodium-box_seal.input b/benchmarks/libsodium/libsodium-box_seal.input
new file mode 100644
index 00000000..0cfbf088
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-box_seal.input
@@ -0,0 +1 @@
+2
diff --git a/benchmarks/libsodium/libsodium-box_seal.wasm b/benchmarks/libsodium/libsodium-box_seal.wasm
index 12db4292..3e7295d9 100755
Binary files a/benchmarks/libsodium/libsodium-box_seal.wasm and b/benchmarks/libsodium/libsodium-box_seal.wasm differ
diff --git a/benchmarks/libsodium/libsodium-box_seed.input b/benchmarks/libsodium/libsodium-box_seed.input
new file mode 100644
index 00000000..4b9026d8
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-box_seed.input
@@ -0,0 +1 @@
+63
diff --git a/benchmarks/libsodium/libsodium-box_seed.wasm b/benchmarks/libsodium/libsodium-box_seed.wasm
index 0577434a..ca18e12d 100755
Binary files a/benchmarks/libsodium/libsodium-box_seed.wasm and b/benchmarks/libsodium/libsodium-box_seed.wasm differ
diff --git a/benchmarks/libsodium/libsodium-chacha20.input b/benchmarks/libsodium/libsodium-chacha20.input
new file mode 100644
index 00000000..b1bd38b6
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-chacha20.input
@@ -0,0 +1 @@
+13
diff --git a/benchmarks/libsodium/libsodium-chacha20.wasm b/benchmarks/libsodium/libsodium-chacha20.wasm
index 18e32da9..bd98c26c 100755
Binary files a/benchmarks/libsodium/libsodium-chacha20.wasm and b/benchmarks/libsodium/libsodium-chacha20.wasm differ
diff --git a/benchmarks/libsodium/libsodium-codecs.input b/benchmarks/libsodium/libsodium-codecs.input
new file mode 100644
index 00000000..b8626c4c
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-codecs.input
@@ -0,0 +1 @@
+4
diff --git a/benchmarks/libsodium/libsodium-codecs.wasm b/benchmarks/libsodium/libsodium-codecs.wasm
index 227cc49b..79fa5503 100755
Binary files a/benchmarks/libsodium/libsodium-codecs.wasm and b/benchmarks/libsodium/libsodium-codecs.wasm differ
diff --git a/benchmarks/libsodium/libsodium-core1.input b/benchmarks/libsodium/libsodium-core1.input
new file mode 100644
index 00000000..c649f9eb
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-core1.input
@@ -0,0 +1 @@
+50916
diff --git a/benchmarks/libsodium/libsodium-core1.wasm b/benchmarks/libsodium/libsodium-core1.wasm
index ad5a5468..52957b19 100755
Binary files a/benchmarks/libsodium/libsodium-core1.wasm and b/benchmarks/libsodium/libsodium-core1.wasm differ
diff --git a/benchmarks/libsodium/libsodium-core2.input b/benchmarks/libsodium/libsodium-core2.input
new file mode 100644
index 00000000..1d06db5e
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-core2.input
@@ -0,0 +1 @@
+51440
diff --git a/benchmarks/libsodium/libsodium-core2.wasm b/benchmarks/libsodium/libsodium-core2.wasm
index 585fb38c..7de83c1a 100755
Binary files a/benchmarks/libsodium/libsodium-core2.wasm and b/benchmarks/libsodium/libsodium-core2.wasm differ
diff --git a/benchmarks/libsodium/libsodium-core3.input b/benchmarks/libsodium/libsodium-core3.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-core3.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-core3.wasm b/benchmarks/libsodium/libsodium-core3.wasm
index dd702494..8d33c339 100755
Binary files a/benchmarks/libsodium/libsodium-core3.wasm and b/benchmarks/libsodium/libsodium-core3.wasm differ
diff --git a/benchmarks/libsodium/libsodium-core4.input b/benchmarks/libsodium/libsodium-core4.input
new file mode 100644
index 00000000..de2f8d1e
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-core4.input
@@ -0,0 +1 @@
+39124
diff --git a/benchmarks/libsodium/libsodium-core4.wasm b/benchmarks/libsodium/libsodium-core4.wasm
index 54bb4fed..0db2c077 100755
Binary files a/benchmarks/libsodium/libsodium-core4.wasm and b/benchmarks/libsodium/libsodium-core4.wasm differ
diff --git a/benchmarks/libsodium/libsodium-core5.input b/benchmarks/libsodium/libsodium-core5.input
new file mode 100644
index 00000000..1d06db5e
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-core5.input
@@ -0,0 +1 @@
+51440
diff --git a/benchmarks/libsodium/libsodium-core5.wasm b/benchmarks/libsodium/libsodium-core5.wasm
index 420e6108..d4ca99b5 100755
Binary files a/benchmarks/libsodium/libsodium-core5.wasm and b/benchmarks/libsodium/libsodium-core5.wasm differ
diff --git a/benchmarks/libsodium/libsodium-core6.input b/benchmarks/libsodium/libsodium-core6.input
new file mode 100644
index 00000000..de2f8d1e
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-core6.input
@@ -0,0 +1 @@
+39124
diff --git a/benchmarks/libsodium/libsodium-core6.wasm b/benchmarks/libsodium/libsodium-core6.wasm
index 05566281..61ff4bac 100755
Binary files a/benchmarks/libsodium/libsodium-core6.wasm and b/benchmarks/libsodium/libsodium-core6.wasm differ
diff --git a/benchmarks/libsodium/libsodium-core_ed25519.input b/benchmarks/libsodium/libsodium-core_ed25519.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-core_ed25519.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-core_ed25519.wasm b/benchmarks/libsodium/libsodium-core_ed25519.wasm
index ce42aa47..a00e7fe1 100755
Binary files a/benchmarks/libsodium/libsodium-core_ed25519.wasm and b/benchmarks/libsodium/libsodium-core_ed25519.wasm differ
diff --git a/benchmarks/libsodium/libsodium-core_ristretto255.input b/benchmarks/libsodium/libsodium-core_ristretto255.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-core_ristretto255.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-core_ristretto255.wasm b/benchmarks/libsodium/libsodium-core_ristretto255.wasm
index da0dbb0e..3f6f69a8 100755
Binary files a/benchmarks/libsodium/libsodium-core_ristretto255.wasm and b/benchmarks/libsodium/libsodium-core_ristretto255.wasm differ
diff --git a/benchmarks/libsodium/libsodium-ed25519_convert.input b/benchmarks/libsodium/libsodium-ed25519_convert.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-ed25519_convert.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-ed25519_convert.wasm b/benchmarks/libsodium/libsodium-ed25519_convert.wasm
index 44a89370..50c4ad78 100755
Binary files a/benchmarks/libsodium/libsodium-ed25519_convert.wasm and b/benchmarks/libsodium/libsodium-ed25519_convert.wasm differ
diff --git a/benchmarks/libsodium/libsodium-generichash.input b/benchmarks/libsodium/libsodium-generichash.input
new file mode 100644
index 00000000..1e8b3149
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-generichash.input
@@ -0,0 +1 @@
+6
diff --git a/benchmarks/libsodium/libsodium-generichash.wasm b/benchmarks/libsodium/libsodium-generichash.wasm
index ad28a9f8..6e0b6098 100755
Binary files a/benchmarks/libsodium/libsodium-generichash.wasm and b/benchmarks/libsodium/libsodium-generichash.wasm differ
diff --git a/benchmarks/libsodium/libsodium-generichash2.input b/benchmarks/libsodium/libsodium-generichash2.input
new file mode 100644
index 00000000..415196e4
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-generichash2.input
@@ -0,0 +1 @@
+118
diff --git a/benchmarks/libsodium/libsodium-generichash2.wasm b/benchmarks/libsodium/libsodium-generichash2.wasm
index 4c9457f0..7bb12280 100755
Binary files a/benchmarks/libsodium/libsodium-generichash2.wasm and b/benchmarks/libsodium/libsodium-generichash2.wasm differ
diff --git a/benchmarks/libsodium/libsodium-generichash3.input b/benchmarks/libsodium/libsodium-generichash3.input
new file mode 100644
index 00000000..4699eb3c
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-generichash3.input
@@ -0,0 +1 @@
+116
diff --git a/benchmarks/libsodium/libsodium-generichash3.wasm b/benchmarks/libsodium/libsodium-generichash3.wasm
index f89e6fa3..360a5702 100755
Binary files a/benchmarks/libsodium/libsodium-generichash3.wasm and b/benchmarks/libsodium/libsodium-generichash3.wasm differ
diff --git a/benchmarks/libsodium/libsodium-hash.input b/benchmarks/libsodium/libsodium-hash.input
new file mode 100644
index 00000000..52b9c41d
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-hash.input
@@ -0,0 +1 @@
+1061
diff --git a/benchmarks/libsodium/libsodium-hash.wasm b/benchmarks/libsodium/libsodium-hash.wasm
index 012a6bd3..b261b12c 100755
Binary files a/benchmarks/libsodium/libsodium-hash.wasm and b/benchmarks/libsodium/libsodium-hash.wasm differ
diff --git a/benchmarks/libsodium/libsodium-hash3.input b/benchmarks/libsodium/libsodium-hash3.input
new file mode 100644
index 00000000..3a335e43
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-hash3.input
@@ -0,0 +1 @@
+5719
diff --git a/benchmarks/libsodium/libsodium-hash3.wasm b/benchmarks/libsodium/libsodium-hash3.wasm
index 7d69c327..e3407406 100755
Binary files a/benchmarks/libsodium/libsodium-hash3.wasm and b/benchmarks/libsodium/libsodium-hash3.wasm differ
diff --git a/benchmarks/libsodium/libsodium-kdf.input b/benchmarks/libsodium/libsodium-kdf.input
new file mode 100644
index 00000000..c4597e53
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-kdf.input
@@ -0,0 +1 @@
+173
diff --git a/benchmarks/libsodium/libsodium-kdf.wasm b/benchmarks/libsodium/libsodium-kdf.wasm
index 28d4e2af..f062aa75 100755
Binary files a/benchmarks/libsodium/libsodium-kdf.wasm and b/benchmarks/libsodium/libsodium-kdf.wasm differ
diff --git a/benchmarks/libsodium/libsodium-kdf_hkdf.input b/benchmarks/libsodium/libsodium-kdf_hkdf.input
new file mode 100644
index 00000000..7ed6ff82
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-kdf_hkdf.input
@@ -0,0 +1 @@
+5
diff --git a/benchmarks/libsodium/libsodium-kdf_hkdf.wasm b/benchmarks/libsodium/libsodium-kdf_hkdf.wasm
index 8fcf2bb5..e27cf370 100755
Binary files a/benchmarks/libsodium/libsodium-kdf_hkdf.wasm and b/benchmarks/libsodium/libsodium-kdf_hkdf.wasm differ
diff --git a/benchmarks/libsodium/libsodium-keygen.input b/benchmarks/libsodium/libsodium-keygen.input
new file mode 100644
index 00000000..4b02a197
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-keygen.input
@@ -0,0 +1 @@
+900
diff --git a/benchmarks/libsodium/libsodium-keygen.wasm b/benchmarks/libsodium/libsodium-keygen.wasm
index 108a94d5..8c592a89 100755
Binary files a/benchmarks/libsodium/libsodium-keygen.wasm and b/benchmarks/libsodium/libsodium-keygen.wasm differ
diff --git a/benchmarks/libsodium/libsodium-kx.input b/benchmarks/libsodium/libsodium-kx.input
new file mode 100644
index 00000000..0cfbf088
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-kx.input
@@ -0,0 +1 @@
+2
diff --git a/benchmarks/libsodium/libsodium-kx.wasm b/benchmarks/libsodium/libsodium-kx.wasm
index aa8e816b..a20181a5 100755
Binary files a/benchmarks/libsodium/libsodium-kx.wasm and b/benchmarks/libsodium/libsodium-kx.wasm differ
diff --git a/benchmarks/libsodium/libsodium-metamorphic.input b/benchmarks/libsodium/libsodium-metamorphic.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-metamorphic.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-metamorphic.wasm b/benchmarks/libsodium/libsodium-metamorphic.wasm
index 73d9f230..66e5678f 100755
Binary files a/benchmarks/libsodium/libsodium-metamorphic.wasm and b/benchmarks/libsodium/libsodium-metamorphic.wasm differ
diff --git a/benchmarks/libsodium/libsodium-misuse.input b/benchmarks/libsodium/libsodium-misuse.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-misuse.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-misuse.wasm b/benchmarks/libsodium/libsodium-misuse.wasm
index 4738ccf6..8b2d059a 100755
Binary files a/benchmarks/libsodium/libsodium-misuse.wasm and b/benchmarks/libsodium/libsodium-misuse.wasm differ
diff --git a/benchmarks/libsodium/libsodium-onetimeauth.input b/benchmarks/libsodium/libsodium-onetimeauth.input
new file mode 100644
index 00000000..6dafa179
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-onetimeauth.input
@@ -0,0 +1 @@
+5350
diff --git a/benchmarks/libsodium/libsodium-onetimeauth.wasm b/benchmarks/libsodium/libsodium-onetimeauth.wasm
index 970d2252..f603f641 100755
Binary files a/benchmarks/libsodium/libsodium-onetimeauth.wasm and b/benchmarks/libsodium/libsodium-onetimeauth.wasm differ
diff --git a/benchmarks/libsodium/libsodium-onetimeauth2.input b/benchmarks/libsodium/libsodium-onetimeauth2.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-onetimeauth2.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-onetimeauth2.wasm b/benchmarks/libsodium/libsodium-onetimeauth2.wasm
index 4738ccf6..66918fc6 100755
Binary files a/benchmarks/libsodium/libsodium-onetimeauth2.wasm and b/benchmarks/libsodium/libsodium-onetimeauth2.wasm differ
diff --git a/benchmarks/libsodium/libsodium-onetimeauth7.input b/benchmarks/libsodium/libsodium-onetimeauth7.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-onetimeauth7.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-onetimeauth7.wasm b/benchmarks/libsodium/libsodium-onetimeauth7.wasm
index 817f7d53..1e2bbb3c 100755
Binary files a/benchmarks/libsodium/libsodium-onetimeauth7.wasm and b/benchmarks/libsodium/libsodium-onetimeauth7.wasm differ
diff --git a/benchmarks/libsodium/libsodium-pwhash_argon2i.input b/benchmarks/libsodium/libsodium-pwhash_argon2i.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-pwhash_argon2i.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-pwhash_argon2i.wasm b/benchmarks/libsodium/libsodium-pwhash_argon2i.wasm
index 12ac6345..0ae92ba7 100755
Binary files a/benchmarks/libsodium/libsodium-pwhash_argon2i.wasm and b/benchmarks/libsodium/libsodium-pwhash_argon2i.wasm differ
diff --git a/benchmarks/libsodium/libsodium-pwhash_argon2id.input b/benchmarks/libsodium/libsodium-pwhash_argon2id.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-pwhash_argon2id.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-pwhash_argon2id.wasm b/benchmarks/libsodium/libsodium-pwhash_argon2id.wasm
index a8f46722..225d2b5a 100755
Binary files a/benchmarks/libsodium/libsodium-pwhash_argon2id.wasm and b/benchmarks/libsodium/libsodium-pwhash_argon2id.wasm differ
diff --git a/benchmarks/libsodium/libsodium-pwhash_scrypt.input b/benchmarks/libsodium/libsodium-pwhash_scrypt.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-pwhash_scrypt.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-pwhash_scrypt.wasm b/benchmarks/libsodium/libsodium-pwhash_scrypt.wasm
index cade6ec2..77d0e04a 100755
Binary files a/benchmarks/libsodium/libsodium-pwhash_scrypt.wasm and b/benchmarks/libsodium/libsodium-pwhash_scrypt.wasm differ
diff --git a/benchmarks/libsodium/libsodium-pwhash_scrypt_ll.input b/benchmarks/libsodium/libsodium-pwhash_scrypt_ll.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-pwhash_scrypt_ll.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-pwhash_scrypt_ll.wasm b/benchmarks/libsodium/libsodium-pwhash_scrypt_ll.wasm
index 72f81fae..80edc67d 100755
Binary files a/benchmarks/libsodium/libsodium-pwhash_scrypt_ll.wasm and b/benchmarks/libsodium/libsodium-pwhash_scrypt_ll.wasm differ
diff --git a/benchmarks/libsodium/libsodium-randombytes.input b/benchmarks/libsodium/libsodium-randombytes.input
new file mode 100644
index 00000000..00750edc
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-randombytes.input
@@ -0,0 +1 @@
+3
diff --git a/benchmarks/libsodium/libsodium-randombytes.wasm b/benchmarks/libsodium/libsodium-randombytes.wasm
index e359ac8d..cc527320 100755
Binary files a/benchmarks/libsodium/libsodium-randombytes.wasm and b/benchmarks/libsodium/libsodium-randombytes.wasm differ
diff --git a/benchmarks/libsodium/libsodium-scalarmult.input b/benchmarks/libsodium/libsodium-scalarmult.input
new file mode 100644
index 00000000..1e8b3149
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-scalarmult.input
@@ -0,0 +1 @@
+6
diff --git a/benchmarks/libsodium/libsodium-scalarmult.wasm b/benchmarks/libsodium/libsodium-scalarmult.wasm
index aa8498f1..0bcb1c80 100755
Binary files a/benchmarks/libsodium/libsodium-scalarmult.wasm and b/benchmarks/libsodium/libsodium-scalarmult.wasm differ
diff --git a/benchmarks/libsodium/libsodium-scalarmult2.input b/benchmarks/libsodium/libsodium-scalarmult2.input
new file mode 100644
index 00000000..900731ff
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-scalarmult2.input
@@ -0,0 +1 @@
+64
diff --git a/benchmarks/libsodium/libsodium-scalarmult2.wasm b/benchmarks/libsodium/libsodium-scalarmult2.wasm
index 052bdc15..fcb06aa3 100755
Binary files a/benchmarks/libsodium/libsodium-scalarmult2.wasm and b/benchmarks/libsodium/libsodium-scalarmult2.wasm differ
diff --git a/benchmarks/libsodium/libsodium-scalarmult5.input b/benchmarks/libsodium/libsodium-scalarmult5.input
new file mode 100644
index 00000000..209e3ef4
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-scalarmult5.input
@@ -0,0 +1 @@
+20
diff --git a/benchmarks/libsodium/libsodium-scalarmult5.wasm b/benchmarks/libsodium/libsodium-scalarmult5.wasm
index b50ce058..970b286c 100755
Binary files a/benchmarks/libsodium/libsodium-scalarmult5.wasm and b/benchmarks/libsodium/libsodium-scalarmult5.wasm differ
diff --git a/benchmarks/libsodium/libsodium-scalarmult6.input b/benchmarks/libsodium/libsodium-scalarmult6.input
new file mode 100644
index 00000000..209e3ef4
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-scalarmult6.input
@@ -0,0 +1 @@
+20
diff --git a/benchmarks/libsodium/libsodium-scalarmult6.wasm b/benchmarks/libsodium/libsodium-scalarmult6.wasm
index 077742eb..a6ca10c0 100755
Binary files a/benchmarks/libsodium/libsodium-scalarmult6.wasm and b/benchmarks/libsodium/libsodium-scalarmult6.wasm differ
diff --git a/benchmarks/libsodium/libsodium-scalarmult7.input b/benchmarks/libsodium/libsodium-scalarmult7.input
new file mode 100644
index 00000000..f599e28b
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-scalarmult7.input
@@ -0,0 +1 @@
+10
diff --git a/benchmarks/libsodium/libsodium-scalarmult7.wasm b/benchmarks/libsodium/libsodium-scalarmult7.wasm
index d6d3c70f..3723879c 100755
Binary files a/benchmarks/libsodium/libsodium-scalarmult7.wasm and b/benchmarks/libsodium/libsodium-scalarmult7.wasm differ
diff --git a/benchmarks/libsodium/libsodium-scalarmult8.input b/benchmarks/libsodium/libsodium-scalarmult8.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-scalarmult8.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-scalarmult8.wasm b/benchmarks/libsodium/libsodium-scalarmult8.wasm
index 901a76c9..213ff122 100755
Binary files a/benchmarks/libsodium/libsodium-scalarmult8.wasm and b/benchmarks/libsodium/libsodium-scalarmult8.wasm differ
diff --git a/benchmarks/libsodium/libsodium-scalarmult_ed25519.input b/benchmarks/libsodium/libsodium-scalarmult_ed25519.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-scalarmult_ed25519.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-scalarmult_ed25519.wasm b/benchmarks/libsodium/libsodium-scalarmult_ed25519.wasm
index db5fb5ef..14357992 100755
Binary files a/benchmarks/libsodium/libsodium-scalarmult_ed25519.wasm and b/benchmarks/libsodium/libsodium-scalarmult_ed25519.wasm differ
diff --git a/benchmarks/libsodium/libsodium-scalarmult_ristretto255.input b/benchmarks/libsodium/libsodium-scalarmult_ristretto255.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-scalarmult_ristretto255.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-scalarmult_ristretto255.wasm b/benchmarks/libsodium/libsodium-scalarmult_ristretto255.wasm
index 6ef98081..5f7fc791 100755
Binary files a/benchmarks/libsodium/libsodium-scalarmult_ristretto255.wasm and b/benchmarks/libsodium/libsodium-scalarmult_ristretto255.wasm differ
diff --git a/benchmarks/libsodium/libsodium-secretbox.input b/benchmarks/libsodium/libsodium-secretbox.input
new file mode 100644
index 00000000..a9ea7c01
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-secretbox.input
@@ -0,0 +1 @@
+2245
diff --git a/benchmarks/libsodium/libsodium-secretbox.wasm b/benchmarks/libsodium/libsodium-secretbox.wasm
index 1b14fbf5..e0c06800 100755
Binary files a/benchmarks/libsodium/libsodium-secretbox.wasm and b/benchmarks/libsodium/libsodium-secretbox.wasm differ
diff --git a/benchmarks/libsodium/libsodium-secretbox2.input b/benchmarks/libsodium/libsodium-secretbox2.input
new file mode 100644
index 00000000..71529907
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-secretbox2.input
@@ -0,0 +1 @@
+3721
diff --git a/benchmarks/libsodium/libsodium-secretbox2.wasm b/benchmarks/libsodium/libsodium-secretbox2.wasm
index 85f7acd9..bb8859c8 100755
Binary files a/benchmarks/libsodium/libsodium-secretbox2.wasm and b/benchmarks/libsodium/libsodium-secretbox2.wasm differ
diff --git a/benchmarks/libsodium/libsodium-secretbox7.input b/benchmarks/libsodium/libsodium-secretbox7.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-secretbox7.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-secretbox7.wasm b/benchmarks/libsodium/libsodium-secretbox7.wasm
index 26127c38..bfe75a35 100755
Binary files a/benchmarks/libsodium/libsodium-secretbox7.wasm and b/benchmarks/libsodium/libsodium-secretbox7.wasm differ
diff --git a/benchmarks/libsodium/libsodium-secretbox8.input b/benchmarks/libsodium/libsodium-secretbox8.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-secretbox8.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-secretbox8.wasm b/benchmarks/libsodium/libsodium-secretbox8.wasm
index 086d0e99..8546dc27 100755
Binary files a/benchmarks/libsodium/libsodium-secretbox8.wasm and b/benchmarks/libsodium/libsodium-secretbox8.wasm differ
diff --git a/benchmarks/libsodium/libsodium-secretbox_easy.input b/benchmarks/libsodium/libsodium-secretbox_easy.input
new file mode 100644
index 00000000..3bb8a497
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-secretbox_easy.input
@@ -0,0 +1 @@
+464
diff --git a/benchmarks/libsodium/libsodium-secretbox_easy.wasm b/benchmarks/libsodium/libsodium-secretbox_easy.wasm
index 276adbca..02dd9f23 100755
Binary files a/benchmarks/libsodium/libsodium-secretbox_easy.wasm and b/benchmarks/libsodium/libsodium-secretbox_easy.wasm differ
diff --git a/benchmarks/libsodium/libsodium-secretbox_easy2.input b/benchmarks/libsodium/libsodium-secretbox_easy2.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-secretbox_easy2.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-secretbox_easy2.wasm b/benchmarks/libsodium/libsodium-secretbox_easy2.wasm
index 62bfc438..a24f9a32 100755
Binary files a/benchmarks/libsodium/libsodium-secretbox_easy2.wasm and b/benchmarks/libsodium/libsodium-secretbox_easy2.wasm differ
diff --git a/benchmarks/libsodium/libsodium-secretstream.wasm b/benchmarks/libsodium/libsodium-secretstream.wasm
deleted file mode 100755
index 0ae88bf5..00000000
Binary files a/benchmarks/libsodium/libsodium-secretstream.wasm and /dev/null differ
diff --git a/benchmarks/libsodium/libsodium-secretstream_xchacha20poly1305.input b/benchmarks/libsodium/libsodium-secretstream_xchacha20poly1305.input
new file mode 100644
index 00000000..b1e7d265
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-secretstream_xchacha20poly1305.input
@@ -0,0 +1 @@
+61
diff --git a/benchmarks/libsodium/libsodium-secretstream_xchacha20poly1305.wasm b/benchmarks/libsodium/libsodium-secretstream_xchacha20poly1305.wasm
index 989d0d96..11866c3d 100755
Binary files a/benchmarks/libsodium/libsodium-secretstream_xchacha20poly1305.wasm and b/benchmarks/libsodium/libsodium-secretstream_xchacha20poly1305.wasm differ
diff --git a/benchmarks/libsodium/libsodium-shorthash.input b/benchmarks/libsodium/libsodium-shorthash.input
new file mode 100644
index 00000000..04e5ff20
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-shorthash.input
@@ -0,0 +1 @@
+2289
diff --git a/benchmarks/libsodium/libsodium-shorthash.wasm b/benchmarks/libsodium/libsodium-shorthash.wasm
index d00100ca..548e79d6 100755
Binary files a/benchmarks/libsodium/libsodium-shorthash.wasm and b/benchmarks/libsodium/libsodium-shorthash.wasm differ
diff --git a/benchmarks/libsodium/libsodium-sign.input b/benchmarks/libsodium/libsodium-sign.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-sign.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-sign.wasm b/benchmarks/libsodium/libsodium-sign.wasm
index cdbe3d98..c2956481 100755
Binary files a/benchmarks/libsodium/libsodium-sign.wasm and b/benchmarks/libsodium/libsodium-sign.wasm differ
diff --git a/benchmarks/libsodium/libsodium-siphashx24.input b/benchmarks/libsodium/libsodium-siphashx24.input
new file mode 100644
index 00000000..ca9d3dab
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-siphashx24.input
@@ -0,0 +1 @@
+1869
diff --git a/benchmarks/libsodium/libsodium-siphashx24.wasm b/benchmarks/libsodium/libsodium-siphashx24.wasm
index 5fb01e81..adcb7a25 100755
Binary files a/benchmarks/libsodium/libsodium-siphashx24.wasm and b/benchmarks/libsodium/libsodium-siphashx24.wasm differ
diff --git a/benchmarks/libsodium/libsodium-sodium_core.input b/benchmarks/libsodium/libsodium-sodium_core.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-sodium_core.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-sodium_core.wasm b/benchmarks/libsodium/libsodium-sodium_core.wasm
index 57d81d15..38a43c83 100755
Binary files a/benchmarks/libsodium/libsodium-sodium_core.wasm and b/benchmarks/libsodium/libsodium-sodium_core.wasm differ
diff --git a/benchmarks/libsodium/libsodium-sodium_utils.input b/benchmarks/libsodium/libsodium-sodium_utils.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-sodium_utils.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-sodium_utils.wasm b/benchmarks/libsodium/libsodium-sodium_utils.wasm
index 27455c7a..39eb44ad 100755
Binary files a/benchmarks/libsodium/libsodium-sodium_utils.wasm and b/benchmarks/libsodium/libsodium-sodium_utils.wasm differ
diff --git a/benchmarks/libsodium/libsodium-sodium_utils2.wasm b/benchmarks/libsodium/libsodium-sodium_utils2.wasm
deleted file mode 100755
index 33bafd9f..00000000
Binary files a/benchmarks/libsodium/libsodium-sodium_utils2.wasm and /dev/null differ
diff --git a/benchmarks/libsodium/libsodium-sodium_utils3.wasm b/benchmarks/libsodium/libsodium-sodium_utils3.wasm
deleted file mode 100755
index 13a6d286..00000000
Binary files a/benchmarks/libsodium/libsodium-sodium_utils3.wasm and /dev/null differ
diff --git a/benchmarks/libsodium/libsodium-sodium_version.input b/benchmarks/libsodium/libsodium-sodium_version.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-sodium_version.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-sodium_version.wasm b/benchmarks/libsodium/libsodium-sodium_version.wasm
index 0158fd91..e0adadf7 100755
Binary files a/benchmarks/libsodium/libsodium-sodium_version.wasm and b/benchmarks/libsodium/libsodium-sodium_version.wasm differ
diff --git a/benchmarks/libsodium/libsodium-stream.input b/benchmarks/libsodium/libsodium-stream.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-stream.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-stream.wasm b/benchmarks/libsodium/libsodium-stream.wasm
index 640eee27..cc30a902 100755
Binary files a/benchmarks/libsodium/libsodium-stream.wasm and b/benchmarks/libsodium/libsodium-stream.wasm differ
diff --git a/benchmarks/libsodium/libsodium-stream2.input b/benchmarks/libsodium/libsodium-stream2.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-stream2.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-stream2.wasm b/benchmarks/libsodium/libsodium-stream2.wasm
index a3e471b3..66ee396d 100755
Binary files a/benchmarks/libsodium/libsodium-stream2.wasm and b/benchmarks/libsodium/libsodium-stream2.wasm differ
diff --git a/benchmarks/libsodium/libsodium-stream3.input b/benchmarks/libsodium/libsodium-stream3.input
new file mode 100644
index 00000000..01491692
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-stream3.input
@@ -0,0 +1 @@
+16892
diff --git a/benchmarks/libsodium/libsodium-stream3.wasm b/benchmarks/libsodium/libsodium-stream3.wasm
index e2d127fb..29e122a3 100755
Binary files a/benchmarks/libsodium/libsodium-stream3.wasm and b/benchmarks/libsodium/libsodium-stream3.wasm differ
diff --git a/benchmarks/libsodium/libsodium-stream4.input b/benchmarks/libsodium/libsodium-stream4.input
new file mode 100644
index 00000000..976d5de6
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-stream4.input
@@ -0,0 +1 @@
+8741
diff --git a/benchmarks/libsodium/libsodium-stream4.wasm b/benchmarks/libsodium/libsodium-stream4.wasm
index aeb29997..884d0f5d 100755
Binary files a/benchmarks/libsodium/libsodium-stream4.wasm and b/benchmarks/libsodium/libsodium-stream4.wasm differ
diff --git a/benchmarks/libsodium/libsodium-verify1.input b/benchmarks/libsodium/libsodium-verify1.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-verify1.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-verify1.wasm b/benchmarks/libsodium/libsodium-verify1.wasm
index e7425ea6..ad50dc21 100755
Binary files a/benchmarks/libsodium/libsodium-verify1.wasm and b/benchmarks/libsodium/libsodium-verify1.wasm differ
diff --git a/benchmarks/libsodium/libsodium-xchacha20.input b/benchmarks/libsodium/libsodium-xchacha20.input
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/benchmarks/libsodium/libsodium-xchacha20.input
@@ -0,0 +1 @@
+1
diff --git a/benchmarks/libsodium/libsodium-xchacha20.wasm b/benchmarks/libsodium/libsodium-xchacha20.wasm
index 79aab3f9..c9e0c7f0 100755
Binary files a/benchmarks/libsodium/libsodium-xchacha20.wasm and b/benchmarks/libsodium/libsodium-xchacha20.wasm differ
diff --git a/benchmarks/noop/Dockerfile b/benchmarks/noop/Dockerfile
index f6883aee..6f3b62b1 100644
--- a/benchmarks/noop/Dockerfile
+++ b/benchmarks/noop/Dockerfile
@@ -1,21 +1,33 @@
# This two-phase Dockerfile allows us to avoid re-downloading APT packages and wasi-sdk with every
# build.
+ARG WASI_SDK_VERSION=28
+
# First, retrieve wasi-sdk:
FROM ubuntu:24.04 AS builder
+ARG WASI_SDK_VERSION
WORKDIR /
RUN apt update && apt install -y wget
-# Download and extract wasi-sdk.
-RUN wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-28/wasi-sdk-28.0-x86_64-linux.tar.gz
-RUN tar xvf wasi-sdk-28.0-x86_64-linux.tar.gz
+# Download and extract wasi-sdk for the host architecture, then normalize the install path to
+# `/wasi-sdk` so that the stage below does not have to know the version or architecture.
+RUN ARCH=$(uname -m); \
+ case "$ARCH" in \
+ x86_64) WASI_ARCH=x86_64 ;; \
+ aarch64 | arm64) WASI_ARCH=arm64 ;; \
+ *) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; \
+ esac; \
+ TARBALL=wasi-sdk-${WASI_SDK_VERSION}.0-${WASI_ARCH}-linux.tar.gz; \
+ wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION}/$TARBALL; \
+ tar xvf $TARBALL; \
+ mv wasi-sdk-${WASI_SDK_VERSION}.0-${WASI_ARCH}-linux /wasi-sdk
# Second, compile the benchmark to Wasm.
FROM ubuntu:24.04
WORKDIR /
-COPY --from=builder /wasi-sdk-28.0-x86_64-linux /wasi-sdk/
+COPY --from=builder /wasi-sdk /wasi-sdk/
# Set common env vars.
ENV CC=/wasi-sdk/bin/clang
diff --git a/benchmarks/quicksort/Dockerfile b/benchmarks/quicksort/Dockerfile
index 108bf60f..f97be08a 100644
--- a/benchmarks/quicksort/Dockerfile
+++ b/benchmarks/quicksort/Dockerfile
@@ -1,21 +1,33 @@
# This two-phase Dockerfile allows us to avoid re-downloading APT packages and wasi-sdk with every
# build.
+ARG WASI_SDK_VERSION=28
+
# First, retrieve wasi-sdk:
FROM ubuntu:24.04 AS builder
+ARG WASI_SDK_VERSION
WORKDIR /
RUN apt update && apt install -y wget
-# Download and extract wasi-sdk.
-RUN wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-28/wasi-sdk-28.0-x86_64-linux.tar.gz
-RUN tar xvf wasi-sdk-28.0-x86_64-linux.tar.gz
+# Download and extract wasi-sdk for the host architecture, then normalize the install path to
+# `/wasi-sdk` so that the stage below does not have to know the version or architecture.
+RUN ARCH=$(uname -m); \
+ case "$ARCH" in \
+ x86_64) WASI_ARCH=x86_64 ;; \
+ aarch64 | arm64) WASI_ARCH=arm64 ;; \
+ *) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; \
+ esac; \
+ TARBALL=wasi-sdk-${WASI_SDK_VERSION}.0-${WASI_ARCH}-linux.tar.gz; \
+ wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION}/$TARBALL; \
+ tar xvf $TARBALL; \
+ mv wasi-sdk-${WASI_SDK_VERSION}.0-${WASI_ARCH}-linux /wasi-sdk
# Second, compile the benchmark to Wasm.
FROM ubuntu:24.04
WORKDIR /
-COPY --from=builder /wasi-sdk-28.0-x86_64-linux /wasi-sdk/
+COPY --from=builder /wasi-sdk /wasi-sdk/
# Set common env vars.
ENV CC=/wasi-sdk/bin/clang
diff --git a/benchmarks/quicksort/benchmark.wasm b/benchmarks/quicksort/benchmark.wasm
index ca389bac..f244b8cb 100755
Binary files a/benchmarks/quicksort/benchmark.wasm and b/benchmarks/quicksort/benchmark.wasm differ
diff --git a/benchmarks/quicksort/quicksort.c b/benchmarks/quicksort/quicksort.c
index 72e5775d..230f26de 100644
--- a/benchmarks/quicksort/quicksort.c
+++ b/benchmarks/quicksort/quicksort.c
@@ -170,30 +170,12 @@ void Quick (int run) {
printf("%d\n", sortlist[run + 1]);
}
-int read_int_from_file(const char *path)
-{
- char buf[64] = {0};
- int fd = open(path, O_RDONLY);
- assert(fd != -1);
-
- size_t m = 0, n = 0;
- do {
- m = read(fd, buf + n, sizeof(buf) - n - 1);
- assert((long)m >= 0);
- n += m;
- } while (m > 0 && n < sizeof(buf) - 1);
- assert(close(fd) == 0);
-
- buf[n] = '\0';
- return atoi(buf);
-}
-
int main()
{
int i;
/* The number of sort runs is read from disk so it can be tuned without
recompiling. */
- int runs = read_int_from_file("./default.input");
+ int runs = (int)bench_read_long("./default.input", 47);
bench_start();
for (i = 0; i < runs; i++) Quick(i);
bench_end();
diff --git a/benchmarks/quicksort/sightglass.h b/benchmarks/quicksort/sightglass.h
deleted file mode 100644
index a767e9e3..00000000
--- a/benchmarks/quicksort/sightglass.h
+++ /dev/null
@@ -1,37 +0,0 @@
-#ifndef sightglass_h
-#define sightglass_h 1
-
-/**
- * Call this function to indicate that recording should start. This call should be placed
- * immediately prior to the code to measure with sightglass-recorder. The attributes allow compilers
- * to generate the correct Wasm imports.
- */
-__attribute__((import_module("bench")))
-__attribute__((import_name("start")))
-void bench_start();
-
-/**
- * Call this function to indicate that recording should end. This call should be placed immediately
- * after the code to measure with sightglass-recorder. The attributes allow compilers to generate
- * the correct Wasm imports.
- */
-__attribute__((import_module("bench")))
-__attribute__((import_name("end")))
-void bench_end();
-
-/**
- * Call this function to prevent certain compiler-related optimizations related to knowing the value
- * of the passed variable.
- */
-#ifndef black_box
-static void _black_box(void *x)
-{
- (void)x;
-}
-static void (*volatile black_box)(void *x) = _black_box;
-#else
-void black_box(void *x);
-#endif
-#define BLACK_BOX(X) black_box((void *)&(X))
-
-#endif
diff --git a/benchmarks/quicksort/sightglass.h b/benchmarks/quicksort/sightglass.h
new file mode 120000
index 00000000..c4dfc720
--- /dev/null
+++ b/benchmarks/quicksort/sightglass.h
@@ -0,0 +1 @@
+../../include/sightglass.h
\ No newline at end of file
diff --git a/benchmarks/richards/Dockerfile b/benchmarks/richards/Dockerfile
index 40f7dac9..a3d21c53 100644
--- a/benchmarks/richards/Dockerfile
+++ b/benchmarks/richards/Dockerfile
@@ -1,21 +1,33 @@
# This two-phase Dockerfile allows us to avoid re-downloading APT packages and wasi-sdk with every
# build.
+ARG WASI_SDK_VERSION=28
+
# First, retrieve wasi-sdk:
FROM ubuntu:24.04 AS builder
+ARG WASI_SDK_VERSION
WORKDIR /
RUN apt update && apt install -y wget
-# Download and extract wasi-sdk.
-RUN wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-28/wasi-sdk-28.0-x86_64-linux.tar.gz
-RUN tar xvf wasi-sdk-28.0-x86_64-linux.tar.gz
+# Download and extract wasi-sdk for the host architecture, then normalize the install path to
+# `/wasi-sdk` so that the stage below does not have to know the version or architecture.
+RUN ARCH=$(uname -m); \
+ case "$ARCH" in \
+ x86_64) WASI_ARCH=x86_64 ;; \
+ aarch64 | arm64) WASI_ARCH=arm64 ;; \
+ *) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; \
+ esac; \
+ TARBALL=wasi-sdk-${WASI_SDK_VERSION}.0-${WASI_ARCH}-linux.tar.gz; \
+ wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION}/$TARBALL; \
+ tar xvf $TARBALL; \
+ mv wasi-sdk-${WASI_SDK_VERSION}.0-${WASI_ARCH}-linux /wasi-sdk
# Second, compile the benchmark to Wasm.
FROM ubuntu:24.04
WORKDIR /
-COPY --from=builder /wasi-sdk-28.0-x86_64-linux /wasi-sdk/
+COPY --from=builder /wasi-sdk /wasi-sdk/
# Set common env vars.
ENV CC=/wasi-sdk/bin/clang
diff --git a/benchmarks/richards/benchmark.wasm b/benchmarks/richards/benchmark.wasm
index 952fcdae..9a749d1d 100755
Binary files a/benchmarks/richards/benchmark.wasm and b/benchmarks/richards/benchmark.wasm differ
diff --git a/benchmarks/richards/richards.c b/benchmarks/richards/richards.c
index 00802930..5c62f69d 100644
--- a/benchmarks/richards/richards.c
+++ b/benchmarks/richards/richards.c
@@ -376,29 +376,11 @@ int getHoldcount()
return holdcount;
}
-int read_int_from_file(const char *path)
-{
- char buf[64] = {0};
- int fd = open(path, O_RDONLY);
- assert(fd != -1);
-
- size_t m = 0, n = 0;
- do {
- m = read(fd, buf + n, sizeof(buf) - n - 1);
- assert((long)m >= 0);
- n += m;
- } while (m > 0 && n < sizeof(buf) - 1);
- assert(close(fd) == 0);
-
- buf[n] = '\0';
- return atoi(buf);
-}
-
int main()
{
/* The workload size (the idle task's countdown) is read from disk so it
can be tuned without recompiling. */
- int count = read_int_from_file("./default.input");
+ int count = (int)bench_read_long("./default.input", 125000);
bench_start();
setup(count);
diff --git a/benchmarks/richards/sightglass.h b/benchmarks/richards/sightglass.h
deleted file mode 100644
index a767e9e3..00000000
--- a/benchmarks/richards/sightglass.h
+++ /dev/null
@@ -1,37 +0,0 @@
-#ifndef sightglass_h
-#define sightglass_h 1
-
-/**
- * Call this function to indicate that recording should start. This call should be placed
- * immediately prior to the code to measure with sightglass-recorder. The attributes allow compilers
- * to generate the correct Wasm imports.
- */
-__attribute__((import_module("bench")))
-__attribute__((import_name("start")))
-void bench_start();
-
-/**
- * Call this function to indicate that recording should end. This call should be placed immediately
- * after the code to measure with sightglass-recorder. The attributes allow compilers to generate
- * the correct Wasm imports.
- */
-__attribute__((import_module("bench")))
-__attribute__((import_name("end")))
-void bench_end();
-
-/**
- * Call this function to prevent certain compiler-related optimizations related to knowing the value
- * of the passed variable.
- */
-#ifndef black_box
-static void _black_box(void *x)
-{
- (void)x;
-}
-static void (*volatile black_box)(void *x) = _black_box;
-#else
-void black_box(void *x);
-#endif
-#define BLACK_BOX(X) black_box((void *)&(X))
-
-#endif
diff --git a/benchmarks/richards/sightglass.h b/benchmarks/richards/sightglass.h
new file mode 120000
index 00000000..c4dfc720
--- /dev/null
+++ b/benchmarks/richards/sightglass.h
@@ -0,0 +1 @@
+../../include/sightglass.h
\ No newline at end of file
diff --git a/benchmarks/shootout/Dockerfile b/benchmarks/shootout/Dockerfile
index 51b4e186..c9e26b0e 100644
--- a/benchmarks/shootout/Dockerfile
+++ b/benchmarks/shootout/Dockerfile
@@ -1,27 +1,38 @@
# This two-phase Dockerfile allows us to avoid re-downloading APT packages and
# wasi-sdk with every build.
-ARG WASI_SDK_VERSION=20
+ARG WASI_SDK_VERSION=28
# First, retrieve wasi-sdk:
-FROM ubuntu:22.04 AS builder
+FROM ubuntu:24.04 AS builder
ARG WASI_SDK_VERSION
WORKDIR /
RUN apt update && apt install -y wget
-RUN wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-$WASI_SDK_VERSION/wasi-sdk-$WASI_SDK_VERSION.0-linux.tar.gz
-RUN tar xvf wasi-sdk-${WASI_SDK_VERSION}.0-linux.tar.gz
+
+# Download and extract wasi-sdk for the host architecture, then normalize the install path to
+# `/wasi-sdk` so that the stage below does not have to know the version or architecture.
+RUN ARCH=$(uname -m); \
+ case "$ARCH" in \
+ x86_64) WASI_ARCH=x86_64 ;; \
+ aarch64 | arm64) WASI_ARCH=arm64 ;; \
+ *) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; \
+ esac; \
+ TARBALL=wasi-sdk-${WASI_SDK_VERSION}.0-${WASI_ARCH}-linux.tar.gz; \
+ wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION}/$TARBALL; \
+ tar xvf $TARBALL; \
+ mv wasi-sdk-${WASI_SDK_VERSION}.0-${WASI_ARCH}-linux /wasi-sdk
# Second, compile the benchmark to Wasm.
-FROM ubuntu:22.04
-ARG WASI_SDK_VERSION
-COPY --from=builder /wasi-sdk-${WASI_SDK_VERSION}.0 /wasi-sdk-${WASI_SDK_VERSION}.0/
+FROM ubuntu:24.04
+WORKDIR /
+COPY --from=builder /wasi-sdk /wasi-sdk/
# Set common env vars.
-ENV CC=/wasi-sdk-${WASI_SDK_VERSION}.0/bin/clang
-ENV CXX=/wasi-sdk-${WASI_SDK_VERSION}.0/bin/clang++
-ENV LD=/wasi-sdk-${WASI_SDK_VERSION}.0/bin/lld
-ENV CFLAGS=--sysroot=/wasi-sdk-${WASI_SDK_VERSION}.0/share/wasi-sysroot
-ENV CXXFLAGS=--sysroot=/wasi-sdk-${WASI_SDK_VERSION}.0/share/wasi-sysroot
-ENV PATH=$PATH:/wasi-sdk-${WASI_SDK_VERSION}.0
+ENV CC=/wasi-sdk/bin/clang
+ENV CXX=/wasi-sdk/bin/clang++
+ENV LD=/wasi-sdk/bin/lld
+ENV CFLAGS=--sysroot=/wasi-sdk/share/wasi-sysroot
+ENV CXXFLAGS=--sysroot=/wasi-sdk/share/wasi-sysroot
+ENV PATH=$PATH:/wasi-sdk
# Copy in the `src` directory.
ENV SRC=/usr/src/shootout
diff --git a/benchmarks/shootout/shootout-ackermann.repeat.input b/benchmarks/shootout/shootout-ackermann.repeat.input
new file mode 100644
index 00000000..b4de3947
--- /dev/null
+++ b/benchmarks/shootout/shootout-ackermann.repeat.input
@@ -0,0 +1 @@
+11
diff --git a/benchmarks/shootout/shootout-ackermann.wasm b/benchmarks/shootout/shootout-ackermann.wasm
index 53fbf3df..26285b18 100755
Binary files a/benchmarks/shootout/shootout-ackermann.wasm and b/benchmarks/shootout/shootout-ackermann.wasm differ
diff --git a/benchmarks/shootout/shootout-base64.iterations.input b/benchmarks/shootout/shootout-base64.iterations.input
new file mode 100644
index 00000000..4438e305
--- /dev/null
+++ b/benchmarks/shootout/shootout-base64.iterations.input
@@ -0,0 +1 @@
+293
diff --git a/benchmarks/shootout/shootout-base64.stdout.expected b/benchmarks/shootout/shootout-base64.stdout.expected
index 85f138d7..f2170326 100644
--- a/benchmarks/shootout/shootout-base64.stdout.expected
+++ b/benchmarks/shootout/shootout-base64.stdout.expected
@@ -1,2 +1,2 @@
-[base64] running with len = 1000 for 10000 iterations
+[base64] running with len = 1000 for 293 iterations
[base64] finished
diff --git a/benchmarks/shootout/shootout-base64.wasm b/benchmarks/shootout/shootout-base64.wasm
index d4249994..f91766f2 100755
Binary files a/benchmarks/shootout/shootout-base64.wasm and b/benchmarks/shootout/shootout-base64.wasm differ
diff --git a/benchmarks/shootout/shootout-ctype.iterations.input b/benchmarks/shootout/shootout-ctype.iterations.input
new file mode 100644
index 00000000..209e3ef4
--- /dev/null
+++ b/benchmarks/shootout/shootout-ctype.iterations.input
@@ -0,0 +1 @@
+20
diff --git a/benchmarks/shootout/shootout-ctype.stdout.expected b/benchmarks/shootout/shootout-ctype.stdout.expected
index 7585d185..35a8aa68 100644
--- a/benchmarks/shootout/shootout-ctype.stdout.expected
+++ b/benchmarks/shootout/shootout-ctype.stdout.expected
@@ -1,2 +1,2 @@
-[ctype] running with str_size = 50000 for 1000 iterations
+[ctype] running with str_size = 50000 for 20 iterations
[ctype] returned: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
diff --git a/benchmarks/shootout/shootout-ctype.wasm b/benchmarks/shootout/shootout-ctype.wasm
index 160513ba..496d8567 100755
Binary files a/benchmarks/shootout/shootout-ctype.wasm and b/benchmarks/shootout/shootout-ctype.wasm differ
diff --git a/benchmarks/shootout/shootout-ed25519.iterations.input b/benchmarks/shootout/shootout-ed25519.iterations.input
new file mode 100644
index 00000000..3c032078
--- /dev/null
+++ b/benchmarks/shootout/shootout-ed25519.iterations.input
@@ -0,0 +1 @@
+18
diff --git a/benchmarks/shootout/shootout-ed25519.wasm b/benchmarks/shootout/shootout-ed25519.wasm
index 2393d564..7d7fda49 100755
Binary files a/benchmarks/shootout/shootout-ed25519.wasm and b/benchmarks/shootout/shootout-ed25519.wasm differ
diff --git a/benchmarks/shootout/shootout-fib2.n.input b/benchmarks/shootout/shootout-fib2.n.input
new file mode 100644
index 00000000..f5c89552
--- /dev/null
+++ b/benchmarks/shootout/shootout-fib2.n.input
@@ -0,0 +1 @@
+32
diff --git a/benchmarks/shootout/shootout-fib2.stdout.expected b/benchmarks/shootout/shootout-fib2.stdout.expected
index d86350b7..6405d432 100644
--- a/benchmarks/shootout/shootout-fib2.stdout.expected
+++ b/benchmarks/shootout/shootout-fib2.stdout.expected
@@ -1,2 +1,2 @@
-[fib2] finding fibonacci number of: 42
-[fib2] returned: 433494437
+[fib2] finding fibonacci number of: 32
+[fib2] returned: 3524578
diff --git a/benchmarks/shootout/shootout-fib2.wasm b/benchmarks/shootout/shootout-fib2.wasm
index 71589cc6..495fface 100755
Binary files a/benchmarks/shootout/shootout-fib2.wasm and b/benchmarks/shootout/shootout-fib2.wasm differ
diff --git a/benchmarks/shootout/shootout-gimli.iterations.input b/benchmarks/shootout/shootout-gimli.iterations.input
new file mode 100644
index 00000000..da99673c
--- /dev/null
+++ b/benchmarks/shootout/shootout-gimli.iterations.input
@@ -0,0 +1 @@
+22000
diff --git a/benchmarks/shootout/shootout-gimli.wasm b/benchmarks/shootout/shootout-gimli.wasm
index 384c72ef..1d0431ea 100755
Binary files a/benchmarks/shootout/shootout-gimli.wasm and b/benchmarks/shootout/shootout-gimli.wasm differ
diff --git a/benchmarks/shootout/shootout-heapsort.iterations.input b/benchmarks/shootout/shootout-heapsort.iterations.input
new file mode 100644
index 00000000..8351c193
--- /dev/null
+++ b/benchmarks/shootout/shootout-heapsort.iterations.input
@@ -0,0 +1 @@
+14
diff --git a/benchmarks/shootout/shootout-heapsort.wasm b/benchmarks/shootout/shootout-heapsort.wasm
index 70747d58..b61c48c9 100755
Binary files a/benchmarks/shootout/shootout-heapsort.wasm and b/benchmarks/shootout/shootout-heapsort.wasm differ
diff --git a/benchmarks/shootout/shootout-keccak.iterations.input b/benchmarks/shootout/shootout-keccak.iterations.input
new file mode 100644
index 00000000..5caff40c
--- /dev/null
+++ b/benchmarks/shootout/shootout-keccak.iterations.input
@@ -0,0 +1 @@
+10000
diff --git a/benchmarks/shootout/shootout-keccak.wasm b/benchmarks/shootout/shootout-keccak.wasm
index 06d16602..391072d4 100755
Binary files a/benchmarks/shootout/shootout-keccak.wasm and b/benchmarks/shootout/shootout-keccak.wasm differ
diff --git a/benchmarks/shootout/shootout-matrix.iterations.input b/benchmarks/shootout/shootout-matrix.iterations.input
new file mode 100644
index 00000000..553a031b
--- /dev/null
+++ b/benchmarks/shootout/shootout-matrix.iterations.input
@@ -0,0 +1 @@
+15975
diff --git a/benchmarks/shootout/shootout-matrix.stdout.expected b/benchmarks/shootout/shootout-matrix.stdout.expected
index 1cd1a3cb..5ca84e2d 100644
--- a/benchmarks/shootout/shootout-matrix.stdout.expected
+++ b/benchmarks/shootout/shootout-matrix.stdout.expected
@@ -1,2 +1,2 @@
-[matrix] running matrix multiplication on 10 rows, 10 columns for 300000 iterations
+[matrix] running matrix multiplication on 10 rows, 10 columns for 15975 iterations
[matrix] returned 58115
diff --git a/benchmarks/shootout/shootout-matrix.wasm b/benchmarks/shootout/shootout-matrix.wasm
index ed404eee..7bd19bf9 100755
Binary files a/benchmarks/shootout/shootout-matrix.wasm and b/benchmarks/shootout/shootout-matrix.wasm differ
diff --git a/benchmarks/shootout/shootout-memmove.iterations.input b/benchmarks/shootout/shootout-memmove.iterations.input
new file mode 100644
index 00000000..cb1a40df
--- /dev/null
+++ b/benchmarks/shootout/shootout-memmove.iterations.input
@@ -0,0 +1 @@
+250
diff --git a/benchmarks/shootout/shootout-memmove.wasm b/benchmarks/shootout/shootout-memmove.wasm
index ff9cb1b3..04a512eb 100755
Binary files a/benchmarks/shootout/shootout-memmove.wasm and b/benchmarks/shootout/shootout-memmove.wasm differ
diff --git a/benchmarks/shootout/shootout-minicsv.iterations.input b/benchmarks/shootout/shootout-minicsv.iterations.input
new file mode 100644
index 00000000..9e703691
--- /dev/null
+++ b/benchmarks/shootout/shootout-minicsv.iterations.input
@@ -0,0 +1 @@
+15000
diff --git a/benchmarks/shootout/shootout-minicsv.wasm b/benchmarks/shootout/shootout-minicsv.wasm
index de933607..730ecd43 100755
Binary files a/benchmarks/shootout/shootout-minicsv.wasm and b/benchmarks/shootout/shootout-minicsv.wasm differ
diff --git a/benchmarks/shootout/shootout-nestedloop.length.input b/benchmarks/shootout/shootout-nestedloop.length.input
new file mode 100644
index 00000000..60d3b2f4
--- /dev/null
+++ b/benchmarks/shootout/shootout-nestedloop.length.input
@@ -0,0 +1 @@
+15
diff --git a/benchmarks/shootout/shootout-nestedloop.stdout.expected b/benchmarks/shootout/shootout-nestedloop.stdout.expected
index 882a43a1..2c5ca1f6 100644
--- a/benchmarks/shootout/shootout-nestedloop.stdout.expected
+++ b/benchmarks/shootout/shootout-nestedloop.stdout.expected
@@ -1,2 +1,2 @@
-[nestedloop] running 6 nested loops with 30 iterations each
-[nestedloop] returned 729000000
+[nestedloop] running 6 nested loops with 15 iterations each
+[nestedloop] returned 11390625
diff --git a/benchmarks/shootout/shootout-nestedloop.wasm b/benchmarks/shootout/shootout-nestedloop.wasm
index 6b81b2f2..36c13ab2 100755
Binary files a/benchmarks/shootout/shootout-nestedloop.wasm and b/benchmarks/shootout/shootout-nestedloop.wasm differ
diff --git a/benchmarks/shootout/shootout-random.length.input b/benchmarks/shootout/shootout-random.length.input
new file mode 100644
index 00000000..a1a4ce27
--- /dev/null
+++ b/benchmarks/shootout/shootout-random.length.input
@@ -0,0 +1 @@
+12900000
diff --git a/benchmarks/shootout/shootout-random.stdout.expected b/benchmarks/shootout/shootout-random.stdout.expected
index 9ac57f0b..82ae4504 100644
--- a/benchmarks/shootout/shootout-random.stdout.expected
+++ b/benchmarks/shootout/shootout-random.stdout.expected
@@ -1,2 +1,2 @@
-[random] generating random number in 40000000 iterations
-[random] returned 54.291695
+[random] generating random number in 12900000 iterations
+[random] returned 96.792838
diff --git a/benchmarks/shootout/shootout-random.wasm b/benchmarks/shootout/shootout-random.wasm
index 74862b70..b90cffc1 100755
Binary files a/benchmarks/shootout/shootout-random.wasm and b/benchmarks/shootout/shootout-random.wasm differ
diff --git a/benchmarks/shootout/shootout-ratelimit.iterations.input b/benchmarks/shootout/shootout-ratelimit.iterations.input
new file mode 100644
index 00000000..a92d2486
--- /dev/null
+++ b/benchmarks/shootout/shootout-ratelimit.iterations.input
@@ -0,0 +1 @@
+304000
diff --git a/benchmarks/shootout/shootout-ratelimit.stdout.expected b/benchmarks/shootout/shootout-ratelimit.stdout.expected
index e82bbfa0..058df38b 100644
--- a/benchmarks/shootout/shootout-ratelimit.stdout.expected
+++ b/benchmarks/shootout/shootout-ratelimit.stdout.expected
@@ -1,2 +1,2 @@
[ratelimit] start limiting
-[ratelimit] found 949528 hits
+[ratelimit] found 284148 hits
diff --git a/benchmarks/shootout/shootout-ratelimit.wasm b/benchmarks/shootout/shootout-ratelimit.wasm
index 3883144c..9435038a 100755
Binary files a/benchmarks/shootout/shootout-ratelimit.wasm and b/benchmarks/shootout/shootout-ratelimit.wasm differ
diff --git a/benchmarks/shootout/shootout-seqhash.arena-size.input b/benchmarks/shootout/shootout-seqhash.arena-size.input
new file mode 100644
index 00000000..d878b321
--- /dev/null
+++ b/benchmarks/shootout/shootout-seqhash.arena-size.input
@@ -0,0 +1 @@
+1100
diff --git a/benchmarks/shootout/shootout-seqhash.wasm b/benchmarks/shootout/shootout-seqhash.wasm
index bb9f6c98..7b85d54d 100755
Binary files a/benchmarks/shootout/shootout-seqhash.wasm and b/benchmarks/shootout/shootout-seqhash.wasm differ
diff --git a/benchmarks/shootout/shootout-sieve.length.input b/benchmarks/shootout/shootout-sieve.length.input
new file mode 100644
index 00000000..6d26270b
--- /dev/null
+++ b/benchmarks/shootout/shootout-sieve.length.input
@@ -0,0 +1 @@
+283
diff --git a/benchmarks/shootout/shootout-sieve.stdout.expected b/benchmarks/shootout/shootout-sieve.stdout.expected
index 7da2c1c1..aeefe1ac 100644
--- a/benchmarks/shootout/shootout-sieve.stdout.expected
+++ b/benchmarks/shootout/shootout-sieve.stdout.expected
@@ -1,2 +1,2 @@
-[sieve] running sieve with n = 17000
-[sieve] returned 17476000
+[sieve] running sieve with n = 283
+[sieve] returned 290924
diff --git a/benchmarks/shootout/shootout-sieve.wasm b/benchmarks/shootout/shootout-sieve.wasm
index 65a6b59e..9174229b 100755
Binary files a/benchmarks/shootout/shootout-sieve.wasm and b/benchmarks/shootout/shootout-sieve.wasm differ
diff --git a/benchmarks/shootout/shootout-switch.iterations.input b/benchmarks/shootout/shootout-switch.iterations.input
new file mode 100644
index 00000000..f599e28b
--- /dev/null
+++ b/benchmarks/shootout/shootout-switch.iterations.input
@@ -0,0 +1 @@
+10
diff --git a/benchmarks/shootout/shootout-switch.length.input b/benchmarks/shootout/shootout-switch.length.input
new file mode 100644
index 00000000..7f5b8d72
--- /dev/null
+++ b/benchmarks/shootout/shootout-switch.length.input
@@ -0,0 +1 @@
+3863
diff --git a/benchmarks/shootout/shootout-switch.stdout.expected b/benchmarks/shootout/shootout-switch.stdout.expected
index e9df3a81..b11ad4a9 100644
--- a/benchmarks/shootout/shootout-switch.stdout.expected
+++ b/benchmarks/shootout/shootout-switch.stdout.expected
@@ -1,2 +1,2 @@
-[switch] running switch statement for 1000 iterations on a 10000-byte string
+[switch] running switch statement for 10 iterations on a 3863-byte string
[switch] finished
\ No newline at end of file
diff --git a/benchmarks/shootout/shootout-switch.wasm b/benchmarks/shootout/shootout-switch.wasm
index c1c5110c..9b1ef024 100755
Binary files a/benchmarks/shootout/shootout-switch.wasm and b/benchmarks/shootout/shootout-switch.wasm differ
diff --git a/benchmarks/shootout/shootout-xblabla20.iterations.input b/benchmarks/shootout/shootout-xblabla20.iterations.input
new file mode 100644
index 00000000..e96a113a
--- /dev/null
+++ b/benchmarks/shootout/shootout-xblabla20.iterations.input
@@ -0,0 +1 @@
+5170
diff --git a/benchmarks/shootout/shootout-xblabla20.wasm b/benchmarks/shootout/shootout-xblabla20.wasm
index aa8c948e..68441f20 100755
Binary files a/benchmarks/shootout/shootout-xblabla20.wasm and b/benchmarks/shootout/shootout-xblabla20.wasm differ
diff --git a/benchmarks/shootout/shootout-xchacha20.iterations.input b/benchmarks/shootout/shootout-xchacha20.iterations.input
new file mode 100644
index 00000000..f03615a1
--- /dev/null
+++ b/benchmarks/shootout/shootout-xchacha20.iterations.input
@@ -0,0 +1 @@
+3115
diff --git a/benchmarks/shootout/shootout-xchacha20.wasm b/benchmarks/shootout/shootout-xchacha20.wasm
index 04bd5ea4..49f2f0bd 100755
Binary files a/benchmarks/shootout/shootout-xchacha20.wasm and b/benchmarks/shootout/shootout-xchacha20.wasm differ
diff --git a/benchmarks/shootout/src/ackermann.c b/benchmarks/shootout/src/ackermann.c
index 00bc9020..f0a1bb6c 100644
--- a/benchmarks/shootout/src/ackermann.c
+++ b/benchmarks/shootout/src/ackermann.c
@@ -18,32 +18,26 @@ int ackermann(int M, int N)
return ackermann(M - 1, ackermann(M, (N - 1)));
}
-int read_int_from_file(char* path) {
- char* buf [32] = { 0 };
-
- int fd = open(path, O_RDONLY);
- assert(fd != -1);
-
- size_t m = 0, n = 0;
- do {
- m = read(fd, (void*) &buf, sizeof(buf) - n - 1);
- assert(m >= 0);
- n += m;
- } while (m > 0);
- assert(close(fd) == 0);
-
- buf[n] = '\0';
- return atoi(&buf);
-}
-
int main()
{
- int M = read_int_from_file("./shootout-ackermann.m.input");
- int N = read_int_from_file("./shootout-ackermann.n.input");
+ int M = (int)bench_read_long("./shootout-ackermann.m.input", 3);
+ int N = (int)bench_read_long("./shootout-ackermann.n.input", 7);
+ /* `A(3, n)` grows by ~4x with each step of `n`, so no single `(M, N)` lands near the ~100M
+ instruction target; repeat the call instead. */
+ int repeat = (int) bench_read_long("./shootout-ackermann.repeat.input", 11);
printf("[ackermann] running with M = %d and N = %d\n", M, N);
+ int result = 0;
+ int i;
bench_start();
- int result = ackermann(M, N);
+ for (i = 0; i < repeat; i++)
+ {
+ /* Keep the compiler from hoisting this pure call out of the loop. */
+ BLACK_BOX(M);
+ BLACK_BOX(N);
+ result = ackermann(M, N);
+ BLACK_BOX(result);
+ }
bench_end();
printf("[ackermann] returned %d\n", result);
diff --git a/benchmarks/shootout/src/base64.c b/benchmarks/shootout/src/base64.c
index eb04b249..09f52c49 100644
--- a/benchmarks/shootout/src/base64.c
+++ b/benchmarks/shootout/src/base64.c
@@ -6,7 +6,9 @@
#include
#include
-#define ITERATIONS 10000
+/* Fallback for `./shootout-base64.iterations.input`, tuned so that this benchmark
+ executes ~100M Wasm instructions. */
+#define ITERATIONS 293
#define base64_ENCODED_LEN(BIN_LEN, VARIANT) \
(((BIN_LEN) / 3U) * 4U + \
@@ -277,10 +279,11 @@ int main()
BLACK_BOX(len);
- printf("[base64] running with len = %zu for %d iterations\n", len, ITERATIONS);
+ int iterations = (int) bench_read_long("./shootout-base64.iterations.input", ITERATIONS);
+ printf("[base64] running with len = %zu for %d iterations\n", len, iterations);
bench_start();
int i;
- for (i = 0; i < ITERATIONS; i++)
+ for (i = 0; i < iterations; i++)
{
BLACK_BOX(bin);
bin2base64(b64, b64_len, bin, len, 1);
diff --git a/benchmarks/shootout/src/ctype.c b/benchmarks/shootout/src/ctype.c
index d0fbfcf7..b87ee1d7 100644
--- a/benchmarks/shootout/src/ctype.c
+++ b/benchmarks/shootout/src/ctype.c
@@ -6,7 +6,9 @@
#include
#define STR_SIZE 50000
-#define ITERATIONS 1000
+/* Fallback for `./shootout-ctype.iterations.input`, tuned so that this benchmark
+ executes ~100M Wasm instructions. */
+#define ITERATIONS 20
int main()
{
@@ -21,9 +23,10 @@ int main()
memset(str, 'x', str_size);
str[str_size - 1U] = 0;
- printf("[ctype] running with str_size = %zu for %d iterations\n", str_size, ITERATIONS);
+ int iterations = (int) bench_read_long("./shootout-ctype.iterations.input", ITERATIONS);
+ printf("[ctype] running with str_size = %zu for %d iterations\n", str_size, iterations);
bench_start();
- for (i = 0; i < ITERATIONS; i++)
+ for (i = 0; i < iterations; i++)
{
BLACK_BOX(str);
for (j = 0U; j < str_size; j++)
diff --git a/benchmarks/shootout/src/ed25519.c b/benchmarks/shootout/src/ed25519.c
index 34750869..c021578d 100644
--- a/benchmarks/shootout/src/ed25519.c
+++ b/benchmarks/shootout/src/ed25519.c
@@ -4,7 +4,9 @@
#include
#include
-#define ITERATIONS 10000
+/* Fallback for `./shootout-ed25519.iterations.input`, tuned so that this benchmark
+ executes ~100M Wasm instructions. */
+#define ITERATIONS 18
#if defined(__SIZEOF_INT128__)
typedef unsigned __int128 uint128_t;
@@ -998,9 +1000,10 @@ int main()
unsigned char a[32] = { 0x42 };
BLACK_BOX(a);
+ int iterations = (int) bench_read_long("./shootout-ed25519.iterations.input", ITERATIONS);
bench_start();
int i;
- for (i = 0; i < ITERATIONS; i++) {
+ for (i = 0; i < iterations; i++) {
BLACK_BOX(p_s);
ge25519_p3 p;
ge25519_frombytes(&p, p_s);
diff --git a/benchmarks/shootout/src/fib2.c b/benchmarks/shootout/src/fib2.c
index a11bd242..d9d1d307 100644
--- a/benchmarks/shootout/src/fib2.c
+++ b/benchmarks/shootout/src/fib2.c
@@ -13,7 +13,8 @@ fib2(unsigned long n)
int main()
{
- int n = 42;
+ /* The fallback is tuned so that this benchmark executes ~100M Wasm instructions. */
+ int n = (int) bench_read_long("./shootout-fib2.n.input", 32);
printf("[fib2] finding fibonacci number of: %d\n", n);
bench_start();
diff --git a/benchmarks/shootout/src/gimli.c b/benchmarks/shootout/src/gimli.c
index ceb25e60..60d9076e 100644
--- a/benchmarks/shootout/src/gimli.c
+++ b/benchmarks/shootout/src/gimli.c
@@ -1,7 +1,9 @@
#include
#include
-#define ITERATIONS 10000
+/* Fallback for `./shootout-gimli.iterations.input`, tuned so that this benchmark
+ executes ~100M Wasm instructions. */
+#define ITERATIONS 22000
#define gimli_BLOCKBYTES 48
#define ROTL32(x, b) (uint32_t)(((x) << (b)) | ((x) >> (32 - (b))))
@@ -50,9 +52,10 @@ int main()
uint32_t state[gimli_BLOCKBYTES / 4] = { 0 };
BLACK_BOX(state);
+ int iterations = (int) bench_read_long("./shootout-gimli.iterations.input", ITERATIONS);
bench_start();
int i;
- for (i = 0; i < ITERATIONS; i++) {
+ for (i = 0; i < iterations; i++) {
gimli_core(state);
}
bench_end();
diff --git a/benchmarks/shootout/src/heapsort.c b/benchmarks/shootout/src/heapsort.c
index 4a00dd5d..a11ce857 100644
--- a/benchmarks/shootout/src/heapsort.c
+++ b/benchmarks/shootout/src/heapsort.c
@@ -4,7 +4,9 @@
#include
#define LENGTH 10000
-#define ITERATIONS 1000
+/* Fallback for `./shootout-heapsort.iterations.input`, tuned so that this benchmark
+ executes ~100M Wasm instructions. */
+#define ITERATIONS 14
#define IM 139968
#define IA 3877
@@ -62,9 +64,10 @@ int main()
double* ary = calloc(n + 1, sizeof(double));
double res;
+ int iterations = (int) bench_read_long("./shootout-heapsort.iterations.input", ITERATIONS);
bench_start();
int i, j;
- for (i = 0; i < ITERATIONS; i++) {
+ for (i = 0; i < iterations; i++) {
BLACK_BOX(ary);
BLACK_BOX(n);
for (j = 1; j <= n; j++) {
diff --git a/benchmarks/shootout/src/keccak.c b/benchmarks/shootout/src/keccak.c
index 58a05055..edca796f 100644
--- a/benchmarks/shootout/src/keccak.c
+++ b/benchmarks/shootout/src/keccak.c
@@ -2,6 +2,8 @@
#include
#include
+/* Fallback for `./shootout-keccak.iterations.input`, tuned so that this benchmark
+ executes ~100M Wasm instructions. */
#define ITERATIONS 10000
#define keccak_BLOCKBYTES 200
#define ROTL64(x, b) (uint32_t)(((x) << (b)) | ((x) >> (64 - (b))))
@@ -2217,9 +2219,10 @@ int main()
uint64_t state[keccak_BLOCKBYTES / 8] = { 0 };
BLACK_BOX(state);
+ int iterations = (int) bench_read_long("./shootout-keccak.iterations.input", ITERATIONS);
bench_start();
int i;
- for (i = 0; i < ITERATIONS; i++) {
+ for (i = 0; i < iterations; i++) {
keccak_core(state);
}
bench_end();
diff --git a/benchmarks/shootout/src/matrix.c b/benchmarks/shootout/src/matrix.c
index 351ff7c6..0dbb3710 100644
--- a/benchmarks/shootout/src/matrix.c
+++ b/benchmarks/shootout/src/matrix.c
@@ -3,7 +3,9 @@
#include
#include
-#define ITERATIONS 300000
+/* Fallback for `./shootout-matrix.iterations.input`, tuned so that this benchmark
+ executes ~100M Wasm instructions. */
+#define ITERATIONS 15975
#define SIZE 10
static int**
@@ -60,10 +62,11 @@ int main()
assert(m1 != NULL && m2 != NULL && mm != NULL);
// BENCHMARK
- printf("[matrix] running matrix multiplication on %d rows, %d columns for %d iterations\n", rows, cols, ITERATIONS);
+ int iterations = (int) bench_read_long("./shootout-matrix.iterations.input", ITERATIONS);
+ printf("[matrix] running matrix multiplication on %d rows, %d columns for %d iterations\n", rows, cols, iterations);
bench_start();
int i;
- for (i = 0; i < ITERATIONS; i++) {
+ for (i = 0; i < iterations; i++) {
mm = mmult(rows, cols, m1, m2, mm);
}
int res = mm[0][0] + mm[2][3] + mm[3][2] + mm[4][4];
diff --git a/benchmarks/shootout/src/memmove.c b/benchmarks/shootout/src/memmove.c
index ed160315..5a07bc5e 100644
--- a/benchmarks/shootout/src/memmove.c
+++ b/benchmarks/shootout/src/memmove.c
@@ -4,7 +4,9 @@
#include
#define STR_SIZE 10000
-#define ITERATIONS 10
+/* Fallback for `./shootout-memmove.iterations.input`, tuned so that this benchmark
+ executes ~100M Wasm instructions. */
+#define ITERATIONS 250
int main()
{
@@ -12,10 +14,11 @@ int main()
char* str = calloc(str_size, (size_t)1U);
assert(str != NULL);
+ int iterations = (int) bench_read_long("./shootout-memmove.iterations.input", ITERATIONS);
bench_start();
int i;
size_t j;
- for (i = 0; i < ITERATIONS; i++) {
+ for (i = 0; i < iterations; i++) {
BLACK_BOX(str);
for (j = (size_t)0U; j < str_size - (size_t)1U; j++) {
memmove(&str[j], str, str_size - j);
diff --git a/benchmarks/shootout/src/minicsv.c b/benchmarks/shootout/src/minicsv.c
index 4506d590..1042188d 100644
--- a/benchmarks/shootout/src/minicsv.c
+++ b/benchmarks/shootout/src/minicsv.c
@@ -5,7 +5,9 @@
#include
#include
-#define ITERATIONS 1000000
+/* Fallback for `./shootout-minicsv.iterations.input`, tuned so that this benchmark
+ executes ~100M Wasm instructions. */
+#define ITERATIONS 15000
#define MINICSV_DELIM ','
#define MINICSV_QUOTE '"'
@@ -113,8 +115,9 @@ int main()
size_t cols_count;
int i;
+ int iterations = (int) bench_read_long("./shootout-minicsv.iterations.input", ITERATIONS);
bench_start();
- for (i = 0; i < ITERATIONS; i++) {
+ for (i = 0; i < iterations; i++) {
char buf[] =
"first,line,has,\"comas,\"\"escaped\"\" characters\",and,\"multiples\r\nlines\"\r\n"
"second,line,\" has \",,empty,,,,columns\r\nremainder";
diff --git a/benchmarks/shootout/src/nestedloop.c b/benchmarks/shootout/src/nestedloop.c
index 64eb208d..badf2481 100644
--- a/benchmarks/shootout/src/nestedloop.c
+++ b/benchmarks/shootout/src/nestedloop.c
@@ -3,13 +3,18 @@
#include
#include
-#define LENGTH 30
+/* Fallback for `./shootout-nestedloop.length.input`, tuned so that this benchmark
+ executes ~100M Wasm instructions. */
+#define LENGTH 15
int main()
{
- int n = LENGTH;
+ int n = (int) bench_read_long("./shootout-nestedloop.length.input", LENGTH);
BLACK_BOX(n);
- int a, b, c, d, e, f, x = 0;
+ int a, b, c, d, e, f;
+ /* `x` is `volatile` so that the increment is a real load/store: otherwise the compiler
+ closes the whole loop nest into a single multiply and the benchmark measures nothing. */
+ volatile int x = 0;
BLACK_BOX(x);
printf("[nestedloop] running 6 nested loops with %d iterations each\n", n);
diff --git a/benchmarks/shootout/src/random.c b/benchmarks/shootout/src/random.c
index 42e72878..c8a562dd 100644
--- a/benchmarks/shootout/src/random.c
+++ b/benchmarks/shootout/src/random.c
@@ -2,7 +2,9 @@
#include
#include
-#define LENGTH 40000000
+/* Fallback for `./shootout-random.length.input`, tuned so that this benchmark
+ executes ~100M Wasm instructions. */
+#define LENGTH 12900000
#define IA 3877
#define IC 29573
#define IM 139968
@@ -20,7 +22,7 @@ int main()
long ia = IA;
long ic = IC;
long im = IM;
- int n = LENGTH;
+ int n = (int) bench_read_long("./shootout-random.length.input", LENGTH);
BLACK_BOX(n);
printf("[random] generating random number in %d iterations\n", n);
diff --git a/benchmarks/shootout/src/ratelimit.c b/benchmarks/shootout/src/ratelimit.c
index c2e02894..b9feb9f7 100644
--- a/benchmarks/shootout/src/ratelimit.c
+++ b/benchmarks/shootout/src/ratelimit.c
@@ -10,7 +10,9 @@
#define SLOTS_LEN 100000
#define PERIOD 1000
#define PEAK 10
-#define ITERATIONS 1000000
+/* Fallback for `./shootout-ratelimit.iterations.input`, tuned so that this benchmark
+ executes ~100M Wasm instructions. */
+#define ITERATIONS 304000
typedef struct RateLimiter_
{
@@ -145,9 +147,10 @@ int main()
uint64_t i;
int hits = 0;
+ uint64_t iterations = (uint64_t) bench_read_long("./shootout-ratelimit.iterations.input", ITERATIONS);
printf("[ratelimit] start limiting\n");
bench_start();
- for (i = (uint64_t)0U; i < ITERATIONS; i++)
+ for (i = (uint64_t)0U; i < iterations; i++)
{
memcpy(ip, &i, sizeof i);
BLACK_BOX(ip);
diff --git a/benchmarks/shootout/src/seqhash.c b/benchmarks/shootout/src/seqhash.c
index 080948ee..149dd693 100644
--- a/benchmarks/shootout/src/seqhash.c
+++ b/benchmarks/shootout/src/seqhash.c
@@ -7,7 +7,10 @@
#include
#include
-#define ARENA_SIZE ((size_t)1000000)
+/* Fallback for `./shootout-seqhash.arena-size.input`, tuned so that this benchmark executes ~100M
+ Wasm instructions. Each `hash_try()` call walks the whole arena, so the cost of solving a level
+ is proportional to this. */
+#define ARENA_SIZE ((size_t)1100)
#define LEVEL_FIRST 1
#define LEVEL_LAST 5
#define LEVELS (LEVEL_LAST - LEVEL_FIRST + 1)
@@ -224,7 +227,8 @@ hashseq_solve(HashSeqArena *arena, HashSeqSolution *solutions, const uint32_t su
int main()
{
- void *buf = calloc(ARENA_SIZE, (size_t)1U);
+ size_t arena_size = (size_t) bench_read_long("./shootout-seqhash.arena-size.input", ARENA_SIZE);
+ void *buf = calloc(arena_size, (size_t)1U);
assert(buf != NULL);
HashSeqArena arena;
HashSeqSolution solutions[LEVELS];
@@ -234,7 +238,9 @@ int main()
memcpy(suffix, buf, sizeof suffix);
BLACK_BOX(suffix);
- hashseq_arena_init(&arena, buf, ARENA_SIZE);
+ if (hashseq_arena_init(&arena, buf, arena_size) != 0) {
+ abort();
+ }
bench_start();
hashseq_solve(&arena, solutions, suffix, LEVEL_FIRST, LEVEL_LAST, HASH_ITERATIONS);
diff --git a/benchmarks/shootout/src/sieve.c b/benchmarks/shootout/src/sieve.c
index 926151c3..314308e9 100644
--- a/benchmarks/shootout/src/sieve.c
+++ b/benchmarks/shootout/src/sieve.c
@@ -4,13 +4,15 @@
#include
#include
-#define LENGTH 17000
+/* Fallback for `./shootout-sieve.length.input`, tuned so that this benchmark
+ executes ~100M Wasm instructions. */
+#define LENGTH 283
int main()
{
static char flags[8192 + 1];
unsigned long res;
- int n = LENGTH;
+ int n = (int) bench_read_long("./shootout-sieve.length.input", LENGTH);
long i, k;
int count;
diff --git a/benchmarks/shootout/src/switch.c b/benchmarks/shootout/src/switch.c
index 5912c3ed..f2405bdf 100644
--- a/benchmarks/shootout/src/switch.c
+++ b/benchmarks/shootout/src/switch.c
@@ -6,15 +6,18 @@
#include
#include
-#define ITERATIONS 1000
-#define LENGTH 10000
+/* Fallbacks for `./shootout-switch.iterations.input` and `./shootout-switch.length.input`,
+ tuned so that this benchmark executes ~100M Wasm instructions. */
+#define ITERATIONS 10
+#define LENGTH 3863
int main()
{
- size_t length = LENGTH;
+ size_t length = (size_t) bench_read_long("./shootout-switch.length.input", LENGTH);
+ int iterations = (int) bench_read_long("./shootout-switch.iterations.input", ITERATIONS);
uint32_t *x;
- x = malloc(LENGTH * sizeof *x);
+ x = malloc(length * sizeof *x);
assert(x != NULL);
size_t i;
@@ -23,10 +26,10 @@ int main()
x[i] = i;
}
- printf("[switch] running switch statement for %d iterations on a %zu-byte string\n", ITERATIONS, length);
+ printf("[switch] running switch statement for %d iterations on a %zu-byte string\n", iterations, length);
bench_start();
int j;
- for (j = 0; j < ITERATIONS; j++)
+ for (j = 0; j < iterations; j++)
{
BLACK_BOX(x);
BLACK_BOX(length);
diff --git a/benchmarks/shootout/src/xblabla20.c b/benchmarks/shootout/src/xblabla20.c
index cee61f02..e31ebe0a 100644
--- a/benchmarks/shootout/src/xblabla20.c
+++ b/benchmarks/shootout/src/xblabla20.c
@@ -6,7 +6,9 @@
#include
#include
-#define ITERATIONS 1000
+/* Fallback for `./shootout-xblabla20.iterations.input`, tuned so that this benchmark
+ executes ~100M Wasm instructions. */
+#define ITERATIONS 5170
#define BUF_SIZE 1000
#define ROTL64(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
@@ -215,9 +217,10 @@ int main()
BLACK_BOX(buf);
assert(BUF_SIZE >= xblabla20_KEYBYTES && BUF_SIZE >= xblabla20_NONCEBYTES);
+ int iterations = (int) bench_read_long("./shootout-xblabla20.iterations.input", ITERATIONS);
bench_start();
int i;
- for (i = 0; i < ITERATIONS; i++)
+ for (i = 0; i < iterations; i++)
{
xblabla20_xor(buf, buf, BUF_SIZE, buf, buf);
}
diff --git a/benchmarks/shootout/src/xchacha20.c b/benchmarks/shootout/src/xchacha20.c
index 8c9761df..ba144568 100644
--- a/benchmarks/shootout/src/xchacha20.c
+++ b/benchmarks/shootout/src/xchacha20.c
@@ -6,7 +6,9 @@
#include
#include
-#define ITERATIONS 1000
+/* Fallback for `./shootout-xchacha20.iterations.input`, tuned so that this benchmark
+ executes ~100M Wasm instructions. */
+#define ITERATIONS 3115
#define BUF_SIZE 1000
#define ROTL32(x, b) (uint32_t)(((x) << (b)) | ((x) >> (32 - (b))))
@@ -201,9 +203,10 @@ int main()
BLACK_BOX(buf);
assert(BUF_SIZE >= xchacha20_KEYBYTES && BUF_SIZE >= xchacha20_NONCEBYTES);
+ int iterations = (int) bench_read_long("./shootout-xchacha20.iterations.input", ITERATIONS);
bench_start();
int i;
- for (i = 0; i < ITERATIONS; i++)
+ for (i = 0; i < iterations; i++)
{
xchacha20_xor(buf, buf, BUF_SIZE, buf, buf);
}
diff --git a/benchmarks/simd.suite b/benchmarks/simd.suite
index 1da15398..ad93de15 100644
--- a/benchmarks/simd.suite
+++ b/benchmarks/simd.suite
@@ -74,7 +74,7 @@ libsodium/libsodium-secretbox7.wasm
libsodium/libsodium-secretbox8.wasm
libsodium/libsodium-secretbox_easy.wasm
libsodium/libsodium-secretbox_easy2.wasm
-libsodium/libsodium-secretstream.wasm
+libsodium/libsodium-secretstream_xchacha20poly1305.wasm
libsodium/libsodium-shorthash.wasm
libsodium/libsodium-sign.wasm
libsodium/libsodium-siphashx24.wasm
diff --git a/benchmarks/spidermonkey/README.md b/benchmarks/spidermonkey/README.md
new file mode 100644
index 00000000..6b226929
--- /dev/null
+++ b/benchmarks/spidermonkey/README.md
@@ -0,0 +1,37 @@
+# SpiderMonkey
+
+Three JavaScript workloads run on SpiderMonkey compiled to Wasm. `runtime.cpp` embeds the
+engine, evaluates the benchmark's JS from `js//`, then calls `main(input)` — and only
+that call sits between `bench_start()` and `bench_end()`. The `.input` file is read
+before the measured region and passed to `main` as a string.
+
+The JS sources are baked into the Wasm at build time (`build.sh` runs them through `xxd` into
+`js_files.h`), so changing anything under `js/` requires a rebuild. This directory is in
+`build-all.sh`'s `SKIPLIST` because that rebuild — which builds SpiderMonkey from source — is
+known to fail, so in practice the `*.input` files are the only tunable knob here.
+
+## Instruction counts
+
+`spidermonkey-regex` (~107M) and `spidermonkey-json` (~111M) sit in the ~100M band described in
+[../README.md].
+
+**`spidermonkey-markdown` cannot reach that band by resizing its input.** `marked.parse`
+compiles its inline-lexer regexes lazily on first use, and that one-time cost lands inside the
+measured region. Measured floor:
+
+| `spidermonkey-markdown.input` | instructions |
+| --- | --- |
+| `---\n` (4 B, a thematic break — no inline content, so no regex compilation) | 3.4M |
+| `hi\n` (3 B) | 247.6M |
+| 162 B | 252.4M |
+| 3,069 B (current) | 288.6M |
+| 22,620 B (the full CommonMark spec preamble, previously) | 852.8M |
+
+So any input with even one word of inline text costs ~248M, and the marginal cost is only
+~29k instructions/byte. The input is set to the first 3,069 bytes of the CommonMark spec —
+still real markdown (frontmatter, headings, links, inline code, prose paragraphs) and the
+closest practical approach to 100M, but ~2.9x over target.
+
+Moving the regex compilation out of the measured region would mean adding a warm-up
+`marked.parse(...)` at the top level of `js/markdown/main.js` (top-level JS is evaluated before
+`bench_start()`). That is the fix if this directory ever becomes rebuildable.
diff --git a/benchmarks/spidermonkey/sightglass.h b/benchmarks/spidermonkey/sightglass.h
deleted file mode 100644
index a767e9e3..00000000
--- a/benchmarks/spidermonkey/sightglass.h
+++ /dev/null
@@ -1,37 +0,0 @@
-#ifndef sightglass_h
-#define sightglass_h 1
-
-/**
- * Call this function to indicate that recording should start. This call should be placed
- * immediately prior to the code to measure with sightglass-recorder. The attributes allow compilers
- * to generate the correct Wasm imports.
- */
-__attribute__((import_module("bench")))
-__attribute__((import_name("start")))
-void bench_start();
-
-/**
- * Call this function to indicate that recording should end. This call should be placed immediately
- * after the code to measure with sightglass-recorder. The attributes allow compilers to generate
- * the correct Wasm imports.
- */
-__attribute__((import_module("bench")))
-__attribute__((import_name("end")))
-void bench_end();
-
-/**
- * Call this function to prevent certain compiler-related optimizations related to knowing the value
- * of the passed variable.
- */
-#ifndef black_box
-static void _black_box(void *x)
-{
- (void)x;
-}
-static void (*volatile black_box)(void *x) = _black_box;
-#else
-void black_box(void *x);
-#endif
-#define BLACK_BOX(X) black_box((void *)&(X))
-
-#endif
diff --git a/benchmarks/spidermonkey/sightglass.h b/benchmarks/spidermonkey/sightglass.h
new file mode 120000
index 00000000..c4dfc720
--- /dev/null
+++ b/benchmarks/spidermonkey/sightglass.h
@@ -0,0 +1 @@
+../../include/sightglass.h
\ No newline at end of file
diff --git a/benchmarks/spidermonkey/spidermonkey-markdown.input b/benchmarks/spidermonkey/spidermonkey-markdown.input
index 6a1474e0..f395cf74 100644
--- a/benchmarks/spidermonkey/spidermonkey-markdown.input
+++ b/benchmarks/spidermonkey/spidermonkey-markdown.input
@@ -100,767 +100,3 @@ to worry about indentation. But the Markdown version is much easier
to read. The nesting of list items is apparent to the eye in the
source, not just in the processed document.
-## Why is a spec needed?
-
-John Gruber's [canonical description of Markdown's
-syntax](http://daringfireball.net/projects/markdown/syntax)
-does not specify the syntax unambiguously. Here are some examples of
-questions it does not answer:
-
-1. How much indentation is needed for a sublist? The spec says that
- continuation paragraphs need to be indented four spaces, but is
- not fully explicit about sublists. It is natural to think that
- they, too, must be indented four spaces, but `Markdown.pl` does
- not require that. This is hardly a "corner case," and divergences
- between implementations on this issue often lead to surprises for
- users in real documents. (See [this comment by John
- Gruber](http://article.gmane.org/gmane.text.markdown.general/1997).)
-
-2. Is a blank line needed before a block quote or heading?
- Most implementations do not require the blank line. However,
- this can lead to unexpected results in hard-wrapped text, and
- also to ambiguities in parsing (note that some implementations
- put the heading inside the blockquote, while others do not).
- (John Gruber has also spoken [in favor of requiring the blank
- lines](http://article.gmane.org/gmane.text.markdown.general/2146).)
-
-3. Is a blank line needed before an indented code block?
- (`Markdown.pl` requires it, but this is not mentioned in the
- documentation, and some implementations do not require it.)
-
- ``` markdown
- paragraph
- code?
- ```
-
-4. What is the exact rule for determining when list items get
- wrapped in `` tags? Can a list be partially "loose" and partially
- "tight"? What should we do with a list like this?
-
- ``` markdown
- 1. one
-
- 2. two
- 3. three
- ```
-
- Or this?
-
- ``` markdown
- 1. one
- - a
-
- - b
- 2. two
- ```
-
- (There are some relevant comments by John Gruber
- [here](http://article.gmane.org/gmane.text.markdown.general/2554).)
-
-5. Can list markers be indented? Can ordered list markers be right-aligned?
-
- ``` markdown
- 8. item 1
- 9. item 2
- 10. item 2a
- ```
-
-6. Is this one list with a thematic break in its second item,
- or two lists separated by a thematic break?
-
- ``` markdown
- * a
- * * * * *
- * b
- ```
-
-7. When list markers change from numbers to bullets, do we have
- two lists or one? (The Markdown syntax description suggests two,
- but the perl scripts and many other implementations produce one.)
-
- ``` markdown
- 1. fee
- 2. fie
- - foe
- - fum
- ```
-
-8. What are the precedence rules for the markers of inline structure?
- For example, is the following a valid link, or does the code span
- take precedence ?
-
- ``` markdown
- [a backtick (`)](/url) and [another backtick (`)](/url).
- ```
-
-9. What are the precedence rules for markers of emphasis and strong
- emphasis? For example, how should the following be parsed?
-
- ``` markdown
- *foo *bar* baz*
- ```
-
-10. What are the precedence rules between block-level and inline-level
- structure? For example, how should the following be parsed?
-
- ``` markdown
- - `a long code span can contain a hyphen like this
- - and it can screw things up`
- ```
-
-11. Can list items include section headings? (`Markdown.pl` does not
- allow this, but does allow blockquotes to include headings.)
-
- ``` markdown
- - # Heading
- ```
-
-12. Can list items be empty?
-
- ``` markdown
- * a
- *
- * b
- ```
-
-13. Can link references be defined inside block quotes or list items?
-
- ``` markdown
- > Blockquote [foo].
- >
- > [foo]: /url
- ```
-
-14. If there are multiple definitions for the same reference, which takes
- precedence?
-
- ``` markdown
- [foo]: /url1
- [foo]: /url2
-
- [foo][]
- ```
-
-In the absence of a spec, early implementers consulted `Markdown.pl`
-to resolve these ambiguities. But `Markdown.pl` was quite buggy, and
-gave manifestly bad results in many cases, so it was not a
-satisfactory replacement for a spec.
-
-Because there is no unambiguous spec, implementations have diverged
-considerably. As a result, users are often surprised to find that
-a document that renders one way on one system (say, a GitHub wiki)
-renders differently on another (say, converting to docbook using
-pandoc). To make matters worse, because nothing in Markdown counts
-as a "syntax error," the divergence often isn't discovered right away.
-
-## About this document
-
-This document attempts to specify Markdown syntax unambiguously.
-It contains many examples with side-by-side Markdown and
-HTML. These are intended to double as conformance tests. An
-accompanying script `spec_tests.py` can be used to run the tests
-against any Markdown program:
-
- python test/spec_tests.py --spec spec.txt --program PROGRAM
-
-Since this document describes how Markdown is to be parsed into
-an abstract syntax tree, it would have made sense to use an abstract
-representation of the syntax tree instead of HTML. But HTML is capable
-of representing the structural distinctions we need to make, and the
-choice of HTML for the tests makes it possible to run the tests against
-an implementation without writing an abstract syntax tree renderer.
-
-Note that not every feature of the HTML samples is mandated by
-the spec. For example, the spec says what counts as a link
-destination, but it doesn't mandate that non-ASCII characters in
-the URL be percent-encoded. To use the automatic tests,
-implementers will need to provide a renderer that conforms to
-the expectations of the spec examples (percent-encoding
-non-ASCII characters in URLs). But a conforming implementation
-can use a different renderer and may choose not to
-percent-encode non-ASCII characters in URLs.
-
-This document is generated from a text file, `spec.txt`, written
-in Markdown with a small extension for the side-by-side tests.
-The script `tools/makespec.py` can be used to convert `spec.txt` into
-HTML or CommonMark (which can then be converted into other formats).
-
-In the examples, the `→` character is used to represent tabs.
-
-# Preliminaries
-
-## Characters and lines
-
-Any sequence of [characters] is a valid CommonMark
-document.
-
-A [character](@) is a Unicode code point. Although some
-code points (for example, combining accents) do not correspond to
-characters in an intuitive sense, all code points count as characters
-for purposes of this spec.
-
-This spec does not specify an encoding; it thinks of lines as composed
-of [characters] rather than bytes. A conforming parser may be limited
-to a certain encoding.
-
-A [line](@) is a sequence of zero or more [characters]
-other than line feed (`U+000A`) or carriage return (`U+000D`),
-followed by a [line ending] or by the end of file.
-
-A [line ending](@) is a line feed (`U+000A`), a carriage return
-(`U+000D`) not followed by a line feed, or a carriage return and a
-following line feed.
-
-A line containing no characters, or a line containing only spaces
-(`U+0020`) or tabs (`U+0009`), is called a [blank line](@).
-
-The following definitions of character classes will be used in this spec:
-
-A [Unicode whitespace character](@) is
-any code point in the Unicode `Zs` general category, or a tab (`U+0009`),
-line feed (`U+000A`), form feed (`U+000C`), or carriage return (`U+000D`).
-
-[Unicode whitespace](@) is a sequence of one or more
-[Unicode whitespace characters].
-
-A [tab](@) is `U+0009`.
-
-A [space](@) is `U+0020`.
-
-An [ASCII control character](@) is a character between `U+0000–1F` (both
-including) or `U+007F`.
-
-An [ASCII punctuation character](@)
-is `!`, `"`, `#`, `$`, `%`, `&`, `'`, `(`, `)`,
-`*`, `+`, `,`, `-`, `.`, `/` (U+0021–2F),
-`:`, `;`, `<`, `=`, `>`, `?`, `@` (U+003A–0040),
-`[`, `\`, `]`, `^`, `_`, `` ` `` (U+005B–0060),
-`{`, `|`, `}`, or `~` (U+007B–007E).
-
-A [Unicode punctuation character](@) is an [ASCII
-punctuation character] or anything in
-the general Unicode categories `Pc`, `Pd`, `Pe`, `Pf`, `Pi`, `Po`, or `Ps`.
-
-## Tabs
-
-Tabs in lines are not expanded to [spaces]. However,
-in contexts where spaces help to define block structure,
-tabs behave as if they were replaced by spaces with a tab stop
-of 4 characters.
-
-Thus, for example, a tab can be used instead of four spaces
-in an indented code block. (Note, however, that internal
-tabs are passed through as literal tabs, not expanded to
-spaces.)
-
-```````````````````````````````` example
-→foo→baz→→bim
-.
-
foo→baz→→bim
-
-````````````````````````````````
-
-```````````````````````````````` example
- →foo→baz→→bim
-.
-foo→baz→→bim
-
-````````````````````````````````
-
-```````````````````````````````` example
- a→a
- ὐ→a
-.
-a→a
-ὐ→a
-
-````````````````````````````````
-
-In the following example, a continuation paragraph of a list
-item is indented with a tab; this has exactly the same effect
-as indentation with four spaces would:
-
-```````````````````````````````` example
- - foo
-
-→bar
-.
-
-````````````````````````````````
-
-```````````````````````````````` example
-- foo
-
-→→bar
-.
-
-````````````````````````````````
-
-Normally the `>` that begins a block quote may be followed
-optionally by a space, which is not considered part of the
-content. In the following case `>` is followed by a tab,
-which is treated as if it were expanded into three spaces.
-Since one of these spaces is considered part of the
-delimiter, `foo` is considered to be indented six spaces
-inside the block quote context, so we get an indented
-code block starting with two spaces.
-
-```````````````````````````````` example
->→→foo
-.
-
- foo
-
-
-````````````````````````````````
-
-```````````````````````````````` example
--→→foo
-.
-
-````````````````````````````````
-
-
-```````````````````````````````` example
- foo
-→bar
-.
-foo
-bar
-
-````````````````````````````````
-
-```````````````````````````````` example
- - foo
- - bar
-→ - baz
-.
-
-````````````````````````````````
-
-```````````````````````````````` example
-#→Foo
-.
-Foo
-````````````````````````````````
-
-```````````````````````````````` example
-*→*→*→
-.
-
-````````````````````````````````
-
-
-## Insecure characters
-
-For security reasons, the Unicode character `U+0000` must be replaced
-with the REPLACEMENT CHARACTER (`U+FFFD`).
-
-
-## Backslash escapes
-
-Any ASCII punctuation character may be backslash-escaped:
-
-```````````````````````````````` example
-\!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~
-.
-!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
-````````````````````````````````
-
-
-Backslashes before other characters are treated as literal
-backslashes:
-
-```````````````````````````````` example
-\→\A\a\ \3\φ\«
-.
-\→\A\a\ \3\φ\«
-````````````````````````````````
-
-
-Escaped characters are treated as regular characters and do
-not have their usual Markdown meanings:
-
-```````````````````````````````` example
-\*not emphasized*
-\
not a tag
-\[not a link](/foo)
-\`not code`
-1\. not a list
-\* not a list
-\# not a heading
-\[foo]: /url "not a reference"
-\ö not a character entity
-.
-*not emphasized*
-<br/> not a tag
-[not a link](/foo)
-`not code`
-1. not a list
-* not a list
-# not a heading
-[foo]: /url "not a reference"
-ö not a character entity
-````````````````````````````````
-
-
-If a backslash is itself escaped, the following character is not:
-
-```````````````````````````````` example
-\\*emphasis*
-.
-\emphasis
-````````````````````````````````
-
-
-A backslash at the end of the line is a [hard line break]:
-
-```````````````````````````````` example
-foo\
-bar
-.
-foo
-bar
-````````````````````````````````
-
-
-Backslash escapes do not work in code blocks, code spans, autolinks, or
-raw HTML:
-
-```````````````````````````````` example
-`` \[\` ``
-.
-\[\`
-````````````````````````````````
-
-
-```````````````````````````````` example
- \[\]
-.
-\[\]
-
-````````````````````````````````
-
-
-```````````````````````````````` example
-~~~
-\[\]
-~~~
-.
-\[\]
-
-````````````````````````````````
-
-
-```````````````````````````````` example
-
-.
-http://example.com?find=\*
-````````````````````````````````
-
-
-```````````````````````````````` example
-
-.
-
-````````````````````````````````
-
-
-But they work in all other contexts, including URLs and link titles,
-link references, and [info strings] in [fenced code blocks]:
-
-```````````````````````````````` example
-[foo](/bar\* "ti\*tle")
-.
-foo
-````````````````````````````````
-
-
-```````````````````````````````` example
-[foo]
-
-[foo]: /bar\* "ti\*tle"
-.
-foo
-````````````````````````````````
-
-
-```````````````````````````````` example
-``` foo\+bar
-foo
-```
-.
-foo
-
-````````````````````````````````
-
-
-## Entity and numeric character references
-
-Valid HTML entity references and numeric character references
-can be used in place of the corresponding Unicode character,
-with the following exceptions:
-
-- Entity and character references are not recognized in code
- blocks and code spans.
-
-- Entity and character references cannot stand in place of
- special characters that define structural elements in
- CommonMark. For example, although `*` can be used
- in place of a literal `*` character, `*` cannot replace
- `*` in emphasis delimiters, bullet list markers, or thematic
- breaks.
-
-Conforming CommonMark parsers need not store information about
-whether a particular character was represented in the source
-using a Unicode character or an entity reference.
-
-[Entity references](@) consist of `&` + any of the valid
-HTML5 entity names + `;`. The
-document
-is used as an authoritative source for the valid entity
-references and their corresponding code points.
-
-```````````````````````````````` example
- & © Æ Ď
-¾ ℋ ⅆ
-∲ ≧̸
-.
- & © Æ Ď
-¾ ℋ ⅆ
-∲ ≧̸
-````````````````````````````````
-
-
-[Decimal numeric character
-references](@)
-consist of `` + a string of 1--7 arabic digits + `;`. A
-numeric character reference is parsed as the corresponding
-Unicode character. Invalid Unicode code points will be replaced by
-the REPLACEMENT CHARACTER (`U+FFFD`). For security reasons,
-the code point `U+0000` will also be replaced by `U+FFFD`.
-
-```````````````````````````````` example
-# Ӓ Ϡ
-.
-# Ӓ Ϡ �
-````````````````````````````````
-
-
-[Hexadecimal numeric character
-references](@) consist of `` +
-either `X` or `x` + a string of 1-6 hexadecimal digits + `;`.
-They too are parsed as the corresponding Unicode character (this
-time specified with a hexadecimal numeral instead of decimal).
-
-```````````````````````````````` example
-" ആ ಫ
-.
-" ആ ಫ
-````````````````````````````````
-
-
-Here are some nonentities:
-
-```````````````````````````````` example
-  &x;
-
-abcdef0;
-&ThisIsNotDefined; &hi?;
-.
-  &x; &#; &#x;
-�
-&#abcdef0;
-&ThisIsNotDefined; &hi?;
-````````````````````````````````
-
-
-Although HTML5 does accept some entity references
-without a trailing semicolon (such as `©`), these are not
-recognized here, because it makes the grammar too ambiguous:
-
-```````````````````````````````` example
-©
-.
-©
-````````````````````````````````
-
-
-Strings that are not on the list of HTML5 named entities are not
-recognized as entity references either:
-
-```````````````````````````````` example
-&MadeUpEntity;
-.
-&MadeUpEntity;
-````````````````````````````````
-
-
-Entity and numeric character references are recognized in any
-context besides code spans or code blocks, including
-URLs, [link titles], and [fenced code block][] [info strings]:
-
-```````````````````````````````` example
-
-.
-
-````````````````````````````````
-
-
-```````````````````````````````` example
-[foo](/föö "föö")
-.
-foo
-````````````````````````````````
-
-
-```````````````````````````````` example
-[foo]
-
-[foo]: /föö "föö"
-.
-foo
-````````````````````````````````
-
-
-```````````````````````````````` example
-``` föö
-foo
-```
-.
-foo
-
-````````````````````````````````
-
-
-Entity and numeric character references are treated as literal
-text in code spans and code blocks:
-
-```````````````````````````````` example
-`föö`
-.
-föö
-````````````````````````````````
-
-
-```````````````````````````````` example
- föfö
-.
-föfö
-
-````````````````````````````````
-
-
-Entity and numeric character references cannot be used
-in place of symbols indicating structure in CommonMark
-documents.
-
-```````````````````````````````` example
-*foo*
-*foo*
-.
-*foo*
-foo
-````````````````````````````````
-
-```````````````````````````````` example
-* foo
-
-* foo
-.
-* foo
-
-````````````````````````````````
-
-```````````````````````````````` example
-foo
bar
-.
-foo
-
-bar
-````````````````````````````````
-
-```````````````````````````````` example
- foo
-.
-→foo
-````````````````````````````````
-
-
-```````````````````````````````` example
-[a](url "tit")
-.
-[a](url "tit")
-````````````````````````````````
-
-
-
-# Blocks and inlines
-
-We can think of a document as a sequence of
-[blocks](@)---structural elements like paragraphs, block
-quotations, lists, headings, rules, and code blocks. Some blocks (like
-block quotes and list items) contain other blocks; others (like
-headings and paragraphs) contain [inline](@) content---text,
-links, emphasized text, images, code spans, and so on.
-
-## Precedence
-
-Indicators of block structure always take precedence over indicators
-of inline structure. So, for example, the following is a list with
-two items, not a list with one item containing a code span:
-
-```````````````````````````````` example
-- `one
-- two`
-.
-
-````````````````````````````````
-
-
-This means that parsing can proceed in two steps: first, the block
-structure of the document can be discerned; second, text lines inside
-paragraphs, headings, and other block constructs can be parsed for inline
-structure. The second step requires information about link reference
-definitions that will be available only at the end of the first
-step. Note that the first step requires processing lines in sequence,
-but the second can be parallelized, since the inline parsing of
-one block element does not affect the inline parsing of any other.
-
-## Container blocks and leaf blocks
-
-We can divide blocks into two types:
-[container blocks](#container-blocks),
-which can contain other blocks, and [leaf blocks](#leaf-blocks),
-which cannot.
diff --git a/benchmarks/spidermonkey/spidermonkey-markdown.stdout.expected b/benchmarks/spidermonkey/spidermonkey-markdown.stdout.expected
index 590cfa7f..ba505e0e 100644
--- a/benchmarks/spidermonkey/spidermonkey-markdown.stdout.expected
+++ b/benchmarks/spidermonkey/spidermonkey-markdown.stdout.expected
@@ -90,569 +90,4 @@ This paragraph belongs to item two of the outer list.
to worry about indentation. But the Markdown version is much easier
to read. The nesting of list items is apparent to the eye in the
source, not just in the processed document.
-Why is a spec needed?
-John Gruber's canonical description of Markdown's
-syntax
-does not specify the syntax unambiguously. Here are some examples of
-questions it does not answer:
-
-How much indentation is needed for a sublist? The spec says that
-continuation paragraphs need to be indented four spaces, but is
-not fully explicit about sublists. It is natural to think that
-they, too, must be indented four spaces, but Markdown.pl does
-not require that. This is hardly a "corner case," and divergences
-between implementations on this issue often lead to surprises for
-users in real documents. (See this comment by John
-Gruber.)
-
-Is a blank line needed before a block quote or heading?
-Most implementations do not require the blank line. However,
-this can lead to unexpected results in hard-wrapped text, and
-also to ambiguities in parsing (note that some implementations
-put the heading inside the blockquote, while others do not).
-(John Gruber has also spoken in favor of requiring the blank
-lines.)
-
-Is a blank line needed before an indented code block?
-(Markdown.pl requires it, but this is not mentioned in the
-documentation, and some implementations do not require it.)
-paragraph
- code?
-
-
-What is the exact rule for determining when list items get
-wrapped in <p> tags? Can a list be partially "loose" and partially
-"tight"? What should we do with a list like this?
-1. one
-
-2. two
-3. three
-
-Or this?
-1. one
- - a
-
- - b
-2. two
-
-(There are some relevant comments by John Gruber
-here.)
-
-Can list markers be indented? Can ordered list markers be right-aligned?
- 8. item 1
- 9. item 2
-10. item 2a
-
-
-Is this one list with a thematic break in its second item,
-or two lists separated by a thematic break?
-* a
-* * * * *
-* b
-
-
-When list markers change from numbers to bullets, do we have
-two lists or one? (The Markdown syntax description suggests two,
-but the perl scripts and many other implementations produce one.)
-1. fee
-2. fie
-- foe
-- fum
-
-
-What are the precedence rules for the markers of inline structure?
-For example, is the following a valid link, or does the code span
-take precedence ?
-[a backtick (`)](/url) and [another backtick (`)](/url).
-
-
-What are the precedence rules for markers of emphasis and strong
-emphasis? For example, how should the following be parsed?
-*foo *bar* baz*
-
-
-What are the precedence rules between block-level and inline-level
-structure? For example, how should the following be parsed?
-- `a long code span can contain a hyphen like this
- - and it can screw things up`
-
-
-Can list items include section headings? (Markdown.pl does not
-allow this, but does allow blockquotes to include headings.)
-- # Heading
-
-
-Can list items be empty?
-* a
-*
-* b
-
-
-Can link references be defined inside block quotes or list items?
-> Blockquote [foo].
->
-> [foo]: /url
-
-
-If there are multiple definitions for the same reference, which takes
-precedence?
-[foo]: /url1
-[foo]: /url2
-
-[foo][]
-
-
-
-In the absence of a spec, early implementers consulted Markdown.pl
-to resolve these ambiguities. But Markdown.pl was quite buggy, and
-gave manifestly bad results in many cases, so it was not a
-satisfactory replacement for a spec.
-Because there is no unambiguous spec, implementations have diverged
-considerably. As a result, users are often surprised to find that
-a document that renders one way on one system (say, a GitHub wiki)
-renders differently on another (say, converting to docbook using
-pandoc). To make matters worse, because nothing in Markdown counts
-as a "syntax error," the divergence often isn't discovered right away.
-About this document
-This document attempts to specify Markdown syntax unambiguously.
-It contains many examples with side-by-side Markdown and
-HTML. These are intended to double as conformance tests. An
-accompanying script spec_tests.py can be used to run the tests
-against any Markdown program:
-python test/spec_tests.py --spec spec.txt --program PROGRAM
-
-Since this document describes how Markdown is to be parsed into
-an abstract syntax tree, it would have made sense to use an abstract
-representation of the syntax tree instead of HTML. But HTML is capable
-of representing the structural distinctions we need to make, and the
-choice of HTML for the tests makes it possible to run the tests against
-an implementation without writing an abstract syntax tree renderer.
-Note that not every feature of the HTML samples is mandated by
-the spec. For example, the spec says what counts as a link
-destination, but it doesn't mandate that non-ASCII characters in
-the URL be percent-encoded. To use the automatic tests,
-implementers will need to provide a renderer that conforms to
-the expectations of the spec examples (percent-encoding
-non-ASCII characters in URLs). But a conforming implementation
-can use a different renderer and may choose not to
-percent-encode non-ASCII characters in URLs.
-This document is generated from a text file, spec.txt, written
-in Markdown with a small extension for the side-by-side tests.
-The script tools/makespec.py can be used to convert spec.txt into
-HTML or CommonMark (which can then be converted into other formats).
-In the examples, the → character is used to represent tabs.
-Preliminaries
-Characters and lines
-Any sequence of [characters] is a valid CommonMark
-document.
-A character is a Unicode code point. Although some
-code points (for example, combining accents) do not correspond to
-characters in an intuitive sense, all code points count as characters
-for purposes of this spec.
-This spec does not specify an encoding; it thinks of lines as composed
-of [characters] rather than bytes. A conforming parser may be limited
-to a certain encoding.
-A line is a sequence of zero or more [characters]
-other than line feed (U+000A) or carriage return (U+000D),
-followed by a [line ending] or by the end of file.
-A line ending is a line feed (U+000A), a carriage return
-(U+000D) not followed by a line feed, or a carriage return and a
-following line feed.
-A line containing no characters, or a line containing only spaces
-(U+0020) or tabs (U+0009), is called a blank line.
-The following definitions of character classes will be used in this spec:
-A Unicode whitespace character is
-any code point in the Unicode Zs general category, or a tab (U+0009),
-line feed (U+000A), form feed (U+000C), or carriage return (U+000D).
-Unicode whitespace is a sequence of one or more
-[Unicode whitespace characters].
-A tab is U+0009.
-A space is U+0020.
-An ASCII control character is a character between U+0000–1F (both
-including) or U+007F.
-An ASCII punctuation character
-is !, ", #, $, %, &, ', (, ),
-*, +, ,, -, ., / (U+0021–2F),
-:, ;, <, =, >, ?, @ (U+003A–0040),
-[, \, ], ^, _, ` (U+005B–0060),
-{, |, }, or ~ (U+007B–007E).
-A Unicode punctuation character is an [ASCII
-punctuation character] or anything in
-the general Unicode categories Pc, Pd, Pe, Pf, Pi, Po, or Ps.
-Tabs
-Tabs in lines are not expanded to [spaces]. However,
-in contexts where spaces help to define block structure,
-tabs behave as if they were replaced by spaces with a tab stop
-of 4 characters.
-Thus, for example, a tab can be used instead of four spaces
-in an indented code block. (Note, however, that internal
-tabs are passed through as literal tabs, not expanded to
-spaces.)
-→foo→baz→→bim
-.
-<pre><code>foo→baz→→bim
-</code></pre>
-
- →foo→baz→→bim
-.
-<pre><code>foo→baz→→bim
-</code></pre>
-
- a→a
- ὐ→a
-.
-<pre><code>a→a
-ὐ→a
-</code></pre>
-
-In the following example, a continuation paragraph of a list
-item is indented with a tab; this has exactly the same effect
-as indentation with four spaces would:
- - foo
-
-→bar
-.
-<ul>
-<li>
-<p>foo</p>
-<p>bar</p>
-</li>
-</ul>
-
-- foo
-
-→→bar
-.
-<ul>
-<li>
-<p>foo</p>
-<pre><code> bar
-</code></pre>
-</li>
-</ul>
-
-Normally the > that begins a block quote may be followed
-optionally by a space, which is not considered part of the
-content. In the following case > is followed by a tab,
-which is treated as if it were expanded into three spaces.
-Since one of these spaces is considered part of the
-delimiter, foo is considered to be indented six spaces
-inside the block quote context, so we get an indented
-code block starting with two spaces.
->→→foo
-.
-<blockquote>
-<pre><code> foo
-</code></pre>
-</blockquote>
-
--→→foo
-.
-<ul>
-<li>
-<pre><code> foo
-</code></pre>
-</li>
-</ul>
-
- foo
-→bar
-.
-<pre><code>foo
-bar
-</code></pre>
-
- - foo
- - bar
-→ - baz
-.
-<ul>
-<li>foo
-<ul>
-<li>bar
-<ul>
-<li>baz</li>
-</ul>
-</li>
-</ul>
-</li>
-</ul>
-
-#→Foo
-.
-<h1>Foo</h1>
-
-*→*→*→
-.
-<hr />
-
-Insecure characters
-For security reasons, the Unicode character U+0000 must be replaced
-with the REPLACEMENT CHARACTER (U+FFFD).
-Backslash escapes
-Any ASCII punctuation character may be backslash-escaped:
-\!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~
-.
-<p>!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~</p>
-
-Backslashes before other characters are treated as literal
-backslashes:
-\→\A\a\ \3\φ\«
-.
-<p>\→\A\a\ \3\φ\«</p>
-
-Escaped characters are treated as regular characters and do
-not have their usual Markdown meanings:
-\*not emphasized*
-\<br/> not a tag
-\[not a link](/foo)
-\`not code`
-1\. not a list
-\* not a list
-\# not a heading
-\[foo]: /url "not a reference"
-\ö not a character entity
-.
-<p>*not emphasized*
-<br/> not a tag
-[not a link](/foo)
-`not code`
-1. not a list
-* not a list
-# not a heading
-[foo]: /url "not a reference"
-&ouml; not a character entity</p>
-
-If a backslash is itself escaped, the following character is not:
-\\*emphasis*
-.
-<p>\<em>emphasis</em></p>
-
-A backslash at the end of the line is a [hard line break]:
-foo\
-bar
-.
-<p>foo<br />
-bar</p>
-
-Backslash escapes do not work in code blocks, code spans, autolinks, or
-raw HTML:
-`` \[\` ``
-.
-<p><code>\[\`</code></p>
-
- \[\]
-.
-<pre><code>\[\]
-</code></pre>
-
-~~~
-\[\]
-~~~
-.
-<pre><code>\[\]
-</code></pre>
-
-<http://example.com?find=\*>
-.
-<p><a href="http://example.com?find=%5C*">http://example.com?find=\*</a></p>
-
-<a href="/bar\/)">
-.
-<a href="/bar\/)">
-
-But they work in all other contexts, including URLs and link titles,
-link references, and [info strings] in [fenced code blocks]:
-[foo](/bar\* "ti\*tle")
-.
-<p><a href="/bar*" title="ti*tle">foo</a></p>
-
-[foo]
-
-[foo]: /bar\* "ti\*tle"
-.
-<p><a href="/bar*" title="ti*tle">foo</a></p>
-
-``` foo\+bar
-foo
-```
-.
-<pre><code class="language-foo+bar">foo
-</code></pre>
-
-Entity and numeric character references
-Valid HTML entity references and numeric character references
-can be used in place of the corresponding Unicode character,
-with the following exceptions:
-
-Entity and character references are not recognized in code
-blocks and code spans.
-
-Entity and character references cannot stand in place of
-special characters that define structural elements in
-CommonMark. For example, although * can be used
-in place of a literal * character, * cannot replace
-* in emphasis delimiters, bullet list markers, or thematic
-breaks.
-
-
-Conforming CommonMark parsers need not store information about
-whether a particular character was represented in the source
-using a Unicode character or an entity reference.
-Entity references consist of & + any of the valid
-HTML5 entity names + ;. The
-document https://html.spec.whatwg.org/entities.json
-is used as an authoritative source for the valid entity
-references and their corresponding code points.
- & © Æ Ď
-¾ ℋ ⅆ
-∲ ≧̸
-.
-<p> & © Æ Ď
-¾ ℋ ⅆ
-∲ ≧̸</p>
-
-Decimal numeric character
-references
-consist of &# + a string of 1--7 arabic digits + ;. A
-numeric character reference is parsed as the corresponding
-Unicode character. Invalid Unicode code points will be replaced by
-the REPLACEMENT CHARACTER (U+FFFD). For security reasons,
-the code point U+0000 will also be replaced by U+FFFD.
-# Ӓ Ϡ �
-.
-<p># Ӓ Ϡ �</p>
-
-Hexadecimal numeric character
-references consist of &# +
-either X or x + a string of 1-6 hexadecimal digits + ;.
-They too are parsed as the corresponding Unicode character (this
-time specified with a hexadecimal numeral instead of decimal).
-" ആ ಫ
-.
-<p>" ആ ಫ</p>
-
-Here are some nonentities:
-  &x; &#; &#x;
-�
-&#abcdef0;
-&ThisIsNotDefined; &hi?;
-.
-<p>&nbsp &x; &#; &#x;
-&#87654321;
-&#abcdef0;
-&ThisIsNotDefined; &hi?;</p>
-
-Although HTML5 does accept some entity references
-without a trailing semicolon (such as ©), these are not
-recognized here, because it makes the grammar too ambiguous:
-©
-.
-<p>&copy</p>
-
-Strings that are not on the list of HTML5 named entities are not
-recognized as entity references either:
-&MadeUpEntity;
-.
-<p>&MadeUpEntity;</p>
-
-Entity and numeric character references are recognized in any
-context besides code spans or code blocks, including
-URLs, [link titles], and [fenced code block][] [info strings]:
-<a href="öö.html">
-.
-<a href="öö.html">
-
-[foo](/föö "föö")
-.
-<p><a href="/f%C3%B6%C3%B6" title="föö">foo</a></p>
-
-[foo]
-
-[foo]: /föö "föö"
-.
-<p><a href="/f%C3%B6%C3%B6" title="föö">foo</a></p>
-
-``` föö
-foo
-```
-.
-<pre><code class="language-föö">foo
-</code></pre>
-
-Entity and numeric character references are treated as literal
-text in code spans and code blocks:
-`föö`
-.
-<p><code>f&ouml;&ouml;</code></p>
-
- föfö
-.
-<pre><code>f&ouml;f&ouml;
-</code></pre>
-
-Entity and numeric character references cannot be used
-in place of symbols indicating structure in CommonMark
-documents.
-*foo*
-*foo*
-.
-<p>*foo*
-<em>foo</em></p>
-
-* foo
-
-* foo
-.
-<p>* foo</p>
-<ul>
-<li>foo</li>
-</ul>
-
-foo bar
-.
-<p>foo
-
-bar</p>
-
-	foo
-.
-<p>→foo</p>
-
-[a](url "tit")
-.
-<p>[a](url "tit")</p>
-
-Blocks and inlines
-We can think of a document as a sequence of
-blocks---structural elements like paragraphs, block
-quotations, lists, headings, rules, and code blocks. Some blocks (like
-block quotes and list items) contain other blocks; others (like
-headings and paragraphs) contain inline content---text,
-links, emphasized text, images, code spans, and so on.
-Precedence
-Indicators of block structure always take precedence over indicators
-of inline structure. So, for example, the following is a list with
-two items, not a list with one item containing a code span:
-- `one
-- two`
-.
-<ul>
-<li>`one</li>
-<li>two`</li>
-</ul>
-
-This means that parsing can proceed in two steps: first, the block
-structure of the document can be discerned; second, text lines inside
-paragraphs, headings, and other block constructs can be parsed for inline
-structure. The second step requires information about link reference
-definitions that will be available only at the end of the first
-step. Note that the first step requires processing lines in sequence,
-but the second can be parallelized, since the inline parsing of
-one block element does not affect the inline parsing of any other.
-Container blocks and leaf blocks
-We can divide blocks into two types:
-container blocks,
-which can contain other blocks, and leaf blocks,
-which cannot.
All done!
diff --git a/benchmarks/sqlite3/README.md b/benchmarks/sqlite3/README.md
index 9851dde6..a9fcdf15 100644
--- a/benchmarks/sqlite3/README.md
+++ b/benchmarks/sqlite3/README.md
@@ -26,8 +26,23 @@ modified to:
- Add `"bench" "start"` and `"bench" "end"` imports for Sightglass timing.
- Add a `"_start"` export that calls `__wasm_call_ctors`, `bench.start`,
`wasm_main`, and `bench.end`.
-- Execute the sqlite3 benchmarks with `szTest=25` instead of `szTest=100`,
- bringing a default run under Sightglass from ~4 minutes to ~1 minute.
+- Execute the sqlite3 benchmarks with `szTest=1` instead of `szTest=100`.
+
+## Instruction count
+
+This benchmark executes ~459M Wasm instructions, which is above the ~100M that
+the rest of the corpus targets (see `benchmarks/README.md`).
+
+`szTest` is the only knob available: the C sources are not in this repository,
+so the size is patched directly in `sqlite3.wat` as the constant
+`i64.const 4294967297` (`= 2^32 + szTest`, sharing a 64-bit store with an
+adjacent field). It is already at its minimum of `1`, which brought the
+benchmark down from ~17.1G instructions at `szTest=25`.
+
+Going lower would require restricting which of speedtest1's test cases run,
+which would change what the benchmark measures, so this is left out of band
+deliberately. It is still well above `scripts/pca.R`'s
+`MIN_DYNAMIC_INST_COUNT`, so it continues to participate in the PCA.
## License
diff --git a/benchmarks/sqlite3/sqlite3.wasm b/benchmarks/sqlite3/sqlite3.wasm
index 8c9fafa4..5b9be74b 100644
Binary files a/benchmarks/sqlite3/sqlite3.wasm and b/benchmarks/sqlite3/sqlite3.wasm differ
diff --git a/benchmarks/sqlite3/sqlite3.wat b/benchmarks/sqlite3/sqlite3.wat
index b45ed089..6029b8f0 100644
--- a/benchmarks/sqlite3/sqlite3.wat
+++ b/benchmarks/sqlite3/sqlite3.wat
@@ -6404,7 +6404,7 @@
i32.const 67720
i32.store
i32.const 121324
- i64.const 4294967321
+ i64.const 4294967297
i64.store align=4
local.get 0
i32.const 2
diff --git a/benchmarks/tract-onnx-image-classification/README.md b/benchmarks/tract-onnx-image-classification/README.md
index b8c9be03..4e225875 100644
--- a/benchmarks/tract-onnx-image-classification/README.md
+++ b/benchmarks/tract-onnx-image-classification/README.md
@@ -5,3 +5,19 @@ benchmark the performance of float heavy computations.
Note that the classifier model is not included in the repo because it is large
and is instead downloaded if needed when running the `setup.sh` script.
+
+## Instruction count
+
+This benchmark executes ~6.6G Wasm instructions, far above the ~100M that the
+rest of the corpus targets (see `benchmarks/README.md`). It is the most expensive
+benchmark in the tree.
+
+The measured region is one forward pass of MobileNetV2, plus decoding and
+resizing `input.png`. Shrinking `input.png` to the model's native 224x224 (so
+that decode and resize become negligible) only brings it down to ~5.45G, so the
+inference itself accounts for ~83% of the cost. MobileNetV2's input shape is
+fixed at 224x224 by `assets/mobilenetv2-7.onnx`, so there is no workload knob
+short of swapping the model for a smaller one, which would change what this
+benchmark measures.
+
+It is therefore left out of band deliberately.
diff --git a/include/sightglass.h b/include/sightglass.h
index a767e9e3..04f5c494 100644
--- a/include/sightglass.h
+++ b/include/sightglass.h
@@ -1,6 +1,10 @@
#ifndef sightglass_h
#define sightglass_h 1
+#include
+#include
+#include
+
/**
* Call this function to indicate that recording should start. This call should be placed
* immediately prior to the code to measure with sightglass-recorder. The attributes allow compilers
@@ -34,4 +38,45 @@ void black_box(void *x);
#endif
#define BLACK_BOX(X) black_box((void *)&(X))
+/**
+ * Read a decimal integer -- typically a workload size -- from the file at `path`, returning
+ * `default_value` if the file is missing, empty, or does not start with a number.
+ *
+ * Benchmarks use this to keep their workload size in a sibling `*.input` file rather than in a
+ * compile-time constant, so that the benchmark can be retuned by editing that file instead of
+ * rebuilding the Wasm (see `benchmarks/README.md`). Sightglass preopens the directory containing
+ * the `.wasm` as the working directory, so `path` is normally of the form `"./default.input"`.
+ *
+ * Call this *before* `bench_start()` so the I/O is not measured.
+ */
+static inline long bench_read_long(const char *path, long default_value)
+{
+ char buf[64];
+ size_t n = 0;
+ ssize_t nread;
+ long value;
+ char *end;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ return default_value;
+ }
+ while (n + 1 < sizeof(buf)) {
+ nread = read(fd, buf + n, sizeof(buf) - n - 1);
+ if (nread <= 0) {
+ break;
+ }
+ n += (size_t)nread;
+ }
+ close(fd);
+
+ buf[n] = '\0';
+ value = strtol(buf, &end, 10);
+ if (end == buf) {
+ return default_value;
+ }
+ return value;
+}
+
#endif