@amritk/nish-x86_64-linux 0.26.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/INSTALL.md CHANGED
@@ -254,7 +254,8 @@ routes are
254
254
  # on the musl host, with its own clang and the runtime from this repository
255
255
  clang app.ll runtime/runtime.c runtime/runtime-os.c runtime/runtime-parallel.c \
256
256
  runtime/runtime-cross.c runtime/runtime-host.c runtime/runtime-net.c \
257
- runtime/runtime-simd.c runtime/runtime-aes.c runtime/runtime-term.c runtime/runtime-bytes.c -lm -o app
257
+ runtime/runtime-simd.c runtime/runtime-aes.c runtime/runtime-sha256.c runtime/runtime-term.c \
258
+ runtime/runtime-bytes.c runtime/runtime-checksum.c runtime/runtime-sandbox.c -lm -o app
258
259
  ```
259
260
 
260
261
  `--link`ing inside the container is the mistake to avoid: it shells out to the
@@ -390,7 +391,7 @@ it into `build/seed/`; `NISH_BOOTSTRAP=<path>` names one you already have and
390
391
  skips the download. §2a has what the build does with it.
391
392
 
392
393
  The published package ships `bin/` (the `nish` command, which is a launcher),
393
- `runtime/` (the C runtime — ten translation units, the wasm subset and their
394
+ `runtime/` (the C runtime — thirteen translation units, the wasm subset and their
394
395
  header — and `runtime/sqlite/`, SQLite's vendored source),
395
396
  `scripts/build.sh` (the link pipeline), `std/` (the standard library) and
396
397
  `docs/AI.md` (the rules card). The
@@ -403,12 +404,13 @@ working directory — and the binary runs from inside its own platform package
403
404
  for exactly that reason.
404
405
 
405
406
  Building the IR yourself rather than through `--link` means naming the runtime
406
- on the `clang` line, and it is ten files:
407
+ on the `clang` line, and it is thirteen files:
407
408
 
408
409
  ```bash
409
410
  clang app.ll runtime/runtime.c runtime/runtime-os.c runtime/runtime-parallel.c \
410
411
  runtime/runtime-cross.c runtime/runtime-host.c runtime/runtime-net.c \
411
- runtime/runtime-simd.c runtime/runtime-aes.c runtime/runtime-term.c runtime/runtime-bytes.c -lm -o app
412
+ runtime/runtime-simd.c runtime/runtime-aes.c runtime/runtime-sha256.c runtime/runtime-term.c \
413
+ runtime/runtime-bytes.c runtime/runtime-checksum.c runtime/runtime-sandbox.c -lm -o app
412
414
  ```
413
415
 
414
416
  `runtime.c` is the part every program touches — the arena, strings, arrays,
@@ -421,14 +423,17 @@ the strings and arrays a task answers, a channel sends or a `Mutex` keeps out
421
423
  of the arena they were made in, `runtime-host.c` holds the wall clock,
422
424
  entropy, file times and signals, `runtime-net.c` the sockets of `nish:net`,
423
425
  `runtime-simd.c` and `runtime-aes.c` the vector text search and AES-GCM,
424
- `runtime-term.c` the stdin reads and terminal calls of `nish:term`, and
426
+ `runtime-sha256.c` the SHA-256 block kernel `nish/crypto/sha256` hands its
427
+ blocks to on a CPU with the SHA instructions (`NISH_SHA256=soft` keeps the
428
+ portable path), `runtime-term.c` the stdin reads and terminal calls of `nish:term`,
425
429
  `runtime-bytes.c` the byte-class runs of `nish/bytes`, which `nish/json` steps
426
- over.
430
+ over, `runtime-checksum.c` the CRC-32 kernels of `nish/compress/checksum`,
431
+ and `runtime-sandbox.c` the confinement a `--sandbox` build applies.
427
432
  They are separate so that each carries its own measured size ceiling
428
433
  ([docs/wp7-runtime.md](wp7-runtime.md)); nothing in the core calls into the
429
434
  others, so a line that names only `runtime.c` and `runtime-os.c` still links a
430
- program that uses none of the other eight. `scripts/build.sh` compiles the other
431
- eight beside any
435
+ program that uses none of the other nine. `scripts/build.sh` compiles the other
436
+ nine beside any
432
437
  `runtime.c` it is handed, so a build that goes through it — every `--link`,
433
438
  and every `--profile` recipe in these documents — needs to name only the one.
434
439
 
package/bin/nish CHANGED
Binary file
package/docs/AI.md CHANGED
@@ -2181,8 +2181,9 @@ also watches the signal fd. None of `spawn`, `kill`, `waitPid` may be called in
2181
2181
  a task or a parallel body; the two host facts may. `availableParallelism()` (`i32`, at least 1, no `--threads` needed) and
2182
2182
  `totalMemory()` (`i64` bytes, or `-1`) size a program to the machine. None of
2183
2183
  the five exists on a wasm target, and `spawn` and `waitPid` throw under Node.
2184
- `setNice(n)` sets the nice value (clamped to -20..19; lowering it unprivileged
2185
- is `-13`) and `setIdlePriority()` moves the program to Linux's `SCHED_IDLE`
2184
+ `setNice(n)` sets the nice value, and it is lower-only even as root: `n` takes
2185
+ 0..19 (`-22` outside it) and a value below the current one is `-13`, with no
2186
+ system call either way. `setIdlePriority()` moves the program to Linux's `SCHED_IDLE`
2186
2187
  (`-95` on Darwin); each answers 0 or a negative errno. Call them at the top of
2187
2188
  `main`, never in a task. An unprivileged process can never leave `SCHED_IDLE`
2188
2189
  and an idle one can starve on a saturated machine, so keep a normal-priority
@@ -2201,6 +2202,10 @@ wait for it to be writable, then `connectResult(fd)` is 0 or the failure. Buffer
2201
2202
  are `u8[]` only; an address is 18 bytes of one (IPv4 as `::ffff:a.b.c.d`, then
2202
2203
  the port big-endian); hosts are numeric literals; to reach a name, resolve it first with
2203
2204
  `nish/net/dns` (`DnsClient` on your loop, or the blocking `resolveHost`).
2205
+ To fetch a URL from a CLI, skip all of this: `nish/net/http-client`'s
2206
+ `httpFetch(new HttpRequest("GET", url), new HttpOptions())` blocks until the
2207
+ response's head is in and answers it with `body.readAll(limit)`, or null on a
2208
+ failure (an `HttpClient` keeps the connection and says why in `error`).
2204
2209
  `netRead` / `netWrite` panic on a range outside the buffer.
2205
2210
  `tcpListenFlags(host, port, backlog, flags)` is `tcpListen` with flag 1
2206
2211
  `SO_REUSEPORT`, so several listeners, in one process or many, share a port (Linux
@@ -2614,7 +2619,8 @@ negative `DEFLATE_ERR_*` / `GZIP_ERR_*` code for any malformed stream, so test
2614
2619
  it before reading `out`. `GzipWriter` and `GzipReader` stream. On HTTP/1.1,
2615
2620
  `conn.respondEncoded(status, names, values, body, off, len, enc,
2616
2621
  conn.acceptEncoding())` with an `HttpCompressor` from `nish/net/http-compress`
2617
- negotiates, compresses and sets the fields. Do not compress a secret beside
2622
+ negotiates, compresses when it pays (`minLength`, `minSavingPercent`,
2623
+ `segmentMode`; `enc.decision` says why not) and sets the fields. Do not compress a secret beside
2618
2624
  text the client chose (BREACH). (No block, as for `nish/text`;
2619
2625
  `tests/link/std_compress` is its program, and the user guide's
2620
2626
  [compression page](guide/using-nish/compression.md) its walk-through.)
@@ -2654,13 +2660,13 @@ a `PgBatcher` built from a `PgBatchConfig` (`name`, `select` reading the key as
2654
2660
  queue is full, or for a text key too long for `maxBytes`, with no room left in
2655
2661
  `queueBytes`, with a NUL or not UTF-8 — and `take(conn)`
2656
2662
  binds the waiting ones, each key once, as one execution of its `sql` over
2657
- `unnest($1) with ordinality`, answering a run handle or -1. Hand that
2663
+ `unnest($1) with ordinality`, answering a run handle or -1. Set `binary` in the `PgBatchConfig` and each batch comes in binary: `take` opens its bind with `bindStartBinary`, and `row` reads the routing ordinal with `binaryInt` (`binary.failed` is saved and restored, so the caller's own decoding is untouched). Hand that
2658
2664
  execution's `PG_ROW` to `row(run, conn)`, `PG_COMPLETE` to `end(run)`, an error
2659
2665
  or `PG_LOST` to `fail(run)` (on a spent handle these do nothing and count in
2660
2666
  `stray`), and after each call `next(run)` until
2661
2667
  `PG_BATCH_NONE`: `PG_BATCH_ROW` (the connection's row answers request `tag`),
2662
2668
  then one of `PG_BATCH_DONE`, `PG_BATCH_MISSING` or `PG_BATCH_FAILED` per
2663
- request. It reads no clock: when to take is the loop's choice. (No block;
2669
+ request. It reads no clock: when to take is the loop's choice, or a `PgBatchClock`'s (`PgBatchClockConfig`: `minCount`, `factor` × the moving average of batch sizes, `maxWaitUs`, the idle bypass `lightCount`; defaults are a fixed window), which `takeDue(conn, clock, nowUs, pool.inFlight())` consults after `clock.arrived(nowUs)` per add; `PgBatchTurns` wakes one collecting connection only when its batch is due. (No block;
2664
2670
  `tests/postgres/live`'s `batch` scenario runs it against a server, and the user
2665
2671
  guide's [PostgreSQL page](guide/using-nish/postgres.md#batching-keyed-reads)
2666
2672
  its walk-through.)
@@ -2817,6 +2823,22 @@ const settings = (): string => readFileSync("settings.txt");
2817
2823
  export const main = (): i32 => settings().length;
2818
2824
  ```
2819
2825
 
2826
+ The policy judges the source; `--sandbox` (Linux, `--link` and `nish run`)
2827
+ makes the kernel hold the running binary to the same set. Before any C
2828
+ constructor runs, it withholds every file, process and socket operation `main`
2829
+ does not reach, C code it links included. A file operation it refuses fails
2830
+ like a missing permission; a refused process or socket call ends the program
2831
+ with `SIGSYS` (status 159); a kernel that cannot apply it exits 126 before
2832
+ `main` unless built with `--sandbox=best-effort`. A darwin target, a library
2833
+ with no `main` and `--emit-napi` refuse the flag
2834
+ ([LANGUAGE.md](LANGUAGE.md#the-sandbox)). Only under `--sandbox` may an
2835
+ `--allow` list scope `fs.read` or `fs.write` to a directory
2836
+ (`--allow fs.read=/srv/data`): the directory must exist at build time, and
2837
+ the kernel grants the capability beneath it alone — except that a `main`
2838
+ reaching `process.spawn` can still read every file beneath `/`, so that
2839
+ `exec` can load a program. Any other scope, a scope
2840
+ in `--deny` and a scope without `--sandbox` are usage errors, exit 2.
2841
+
2820
2842
  ## Before you say it compiles
2821
2843
 
2822
2844
  Run it. `nish file.ts --json` is one command and it is the only proof.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amritk/nish-x86_64-linux",
3
- "version": "0.26.0",
3
+ "version": "0.28.0",
4
4
  "description": "The nish native compiler for Linux on x86_64",
5
5
  "license": "MIT",
6
6
  "os": [
package/runtime/core.d.ts CHANGED
@@ -7,3 +7,50 @@ export * from "./core/host.js";
7
7
  export * from "./core/numbers.js";
8
8
  export * from "./core/result.js";
9
9
  export * from "./core/strings.js";
10
+
11
+ // `nish:simd`: what `--emit-ts` writes for a program's `import ... from "nish:simd"`.
12
+ import type { f32, f64, i32, u16, u32, u8 } from "./core/numbers.js";
13
+
14
+ /**
15
+ * A sixteen-byte vector of lanes. Opaque: TypeScript refuses `+` on one, as
16
+ * `nish` does, but lets `===` and `<` through, which `nish` refuses. Under
17
+ * Node it is a typed array made once and never written after.
18
+ */
19
+ export interface u8x16 { readonly __nishSimd: "u8x16"; readonly __lane: u8 }
20
+ export interface u16x8 { readonly __nishSimd: "u16x8"; readonly __lane: u16 }
21
+ export interface i32x4 { readonly __nishSimd: "i32x4"; readonly __lane: i32 }
22
+ export interface u32x4 { readonly __nishSimd: "u32x4"; readonly __lane: u32 }
23
+ export interface f32x4 { readonly __nishSimd: "f32x4"; readonly __lane: f32 }
24
+ export interface f64x2 { readonly __nishSimd: "f64x2"; readonly __lane: f64 }
25
+ /** Any of the six. */
26
+ export type SimdVector = u8x16 | u16x8 | i32x4 | u32x4 | f32x4 | f64x2;
27
+ /** One value per lane, lane 0 first. */
28
+ export declare function u8x16(
29
+ a0: u8, a1: u8, a2: u8, a3: u8, a4: u8, a5: u8, a6: u8, a7: u8,
30
+ a8: u8, a9: u8, a10: u8, a11: u8, a12: u8, a13: u8, a14: u8, a15: u8
31
+ ): u8x16;
32
+ export declare function u16x8(a0: u16, a1: u16, a2: u16, a3: u16, a4: u16, a5: u16, a6: u16, a7: u16): u16x8;
33
+ export declare function i32x4(a0: i32, a1: i32, a2: i32, a3: i32): i32x4;
34
+ export declare function u32x4(a0: u32, a1: u32, a2: u32, a3: u32): u32x4;
35
+ export declare function f32x4(a0: f32, a1: f32, a2: f32, a3: f32): f32x4;
36
+ export declare function f64x2(a0: f64, a1: f64): f64x2;
37
+ /** `x` in every lane. */
38
+ export declare function splatU8x16(x: u8): u8x16;
39
+ export declare function splatU16x8(x: u16): u16x8;
40
+ export declare function splatI32x4(x: i32): i32x4;
41
+ export declare function splatU32x4(x: u32): u32x4;
42
+ export declare function splatF32x4(x: f32): f32x4;
43
+ export declare function splatF64x2(x: f64): f64x2;
44
+ /** `xs[i]` to `xs[i + N - 1]`, bounds-checked once for the whole vector. */
45
+ export declare function loadU8x16(xs: readonly u8[], i: i32): u8x16;
46
+ export declare function loadU16x8(xs: readonly u16[], i: i32): u16x8;
47
+ export declare function loadI32x4(xs: readonly i32[], i: i32): i32x4;
48
+ export declare function loadU32x4(xs: readonly u32[], i: i32): u32x4;
49
+ export declare function loadF32x4(xs: readonly f32[], i: i32): f32x4;
50
+ export declare function loadF64x2(xs: readonly f64[], i: i32): f64x2;
51
+ /** The lanes of `v` into `xs[i]` to `xs[i + N - 1]`, checked once. */
52
+ export declare function store<V extends SimdVector>(v: V, xs: V["__lane"][], i: i32): void;
53
+ /** Lane `k`, an integer literal in range. */
54
+ export declare function extract<V extends SimdVector>(v: V, k: i32): V["__lane"];
55
+ /** A new vector: `v` with lane `k` set to `x`. */
56
+ export declare function replace<V extends SimdVector>(v: V, k: i32, x: V["__lane"]): V;
package/runtime/core.mjs CHANGED
@@ -48,8 +48,75 @@
48
48
  *
49
49
  * The rewrite rules that call these helpers are listed in docs/wp13-differential.md.
50
50
  */
51
+ import { panicIndex, toIndex } from "./core/checks.mjs";
52
+
51
53
  export * from "./core/arrays.mjs";
52
54
  export * from "./core/host.mjs";
53
55
  export * from "./core/numbers.mjs";
54
56
  export * from "./core/result.mjs";
55
57
  export * from "./core/strings.mjs";
58
+
59
+ // ---- `nish:simd` ------------------------------------------------------------
60
+ // A vector is a fresh typed array of its lanes, made once and never written
61
+ // after: `replace` copies, so a vector held in two places stays one value, as
62
+ // the native `<N x T>` register is. The array type does the lane arithmetic a
63
+ // native lane does: a `u8` lane wraps through `Uint8Array`, an `f32` lane rounds
64
+ // through `Float32Array`. `load…` and `store` check the whole vector once, and
65
+ // panic with the native words: the first index, and the array's length.
66
+
67
+ const simdMake = (Lanes) => (...lanes) => new Lanes(lanes);
68
+ const simdSplat = (Lanes) => (x) => new Lanes(16 / Lanes.BYTES_PER_ELEMENT).fill(x);
69
+ // The first index of `count` lanes at `i` in `xs`, or the native panic naming
70
+ // a lane that is out of range: the first when it is, else the last. A negative
71
+ // index prints as the unsigned 64-bit number the native compare saw.
72
+ const simdAt = (xs, i, count) => {
73
+ const k = toIndex(i);
74
+ if (k < 0) panicIndex(BigInt.asUintN(64, BigInt(k)), xs.length);
75
+ if (k >= xs.length) panicIndex(k, xs.length);
76
+ if (k + count > xs.length) panicIndex(k + count - 1, xs.length);
77
+ return k;
78
+ };
79
+ const simdLoad = (Lanes) => (xs, i) => {
80
+ const count = 16 / Lanes.BYTES_PER_ELEMENT;
81
+ const k = simdAt(xs, i, count);
82
+ const out = new Lanes(count);
83
+ for (let j = 0; j < count; j++) out[j] = xs[k + j];
84
+ return out;
85
+ };
86
+
87
+ export const u8x16 = simdMake(Uint8Array);
88
+ export const u16x8 = simdMake(Uint16Array);
89
+ export const i32x4 = simdMake(Int32Array);
90
+ export const u32x4 = simdMake(Uint32Array);
91
+ export const f32x4 = simdMake(Float32Array);
92
+ export const f64x2 = simdMake(Float64Array);
93
+ export const splatU8x16 = simdSplat(Uint8Array);
94
+ export const splatU16x8 = simdSplat(Uint16Array);
95
+ export const splatI32x4 = simdSplat(Int32Array);
96
+ export const splatU32x4 = simdSplat(Uint32Array);
97
+ export const splatF32x4 = simdSplat(Float32Array);
98
+ export const splatF64x2 = simdSplat(Float64Array);
99
+ export const loadU8x16 = simdLoad(Uint8Array);
100
+ export const loadU16x8 = simdLoad(Uint16Array);
101
+ export const loadI32x4 = simdLoad(Int32Array);
102
+ export const loadU32x4 = simdLoad(Uint32Array);
103
+ export const loadF32x4 = simdLoad(Float32Array);
104
+ export const loadF64x2 = simdLoad(Float64Array);
105
+
106
+ /** `store(v, xs, i)`: the lanes into `xs[i]` to `xs[i + N - 1]`, checked once. */
107
+ export function store(v, xs, i) {
108
+ const k = simdAt(xs, i, v.length);
109
+ for (let j = 0; j < v.length; j++) xs[k + j] = v[j];
110
+ }
111
+
112
+ /** `extract(v, k)`: lane `k`, a constant the checker held in range. */
113
+ export function extract(v, k) {
114
+ return v[k];
115
+ }
116
+
117
+ /** `replace(v, k, x)`: a new vector, `v` with lane `k` set to `x`. */
118
+ export function replace(v, k, x) {
119
+ const out = v.slice();
120
+ out[k] = x;
121
+ return out;
122
+ }
package/runtime/nish.d.ts CHANGED
@@ -356,10 +356,10 @@ declare function availableParallelism(): i32;
356
356
  /** Bytes of physical memory, or -1 when the host does not say. */
357
357
  declare function totalMemory(): i64;
358
358
  /**
359
- * Set this process's nice value to `n`: 0, or a negative errno. The kernel
360
- * clamps `n` to -20..19, and lowering it below the current value needs the
361
- * privilege to (-13 without it on Linux). Children started afterwards inherit
362
- * it. Throws under Node.
359
+ * Set this process's nice value to `n`, which must be 0..19: 0, or a negative
360
+ * errno. Lower-only, even as root: `n` outside 0..19 is -22 (EINVAL), and `n`
361
+ * below the current value is -13 (EACCES), both with no system call. Children
362
+ * started afterwards inherit it. Throws under Node.
363
363
  */
364
364
  declare function setNice(n: i32): i32;
365
365
  /**
@@ -588,7 +588,7 @@ declare module "nish:process" {
588
588
  export function availableParallelism(): i32;
589
589
  /** Bytes of physical memory, or -1. */
590
590
  export function totalMemory(): i64;
591
- /** Set this process's nice value (clamped to -20..19): 0, or a negative errno. */
591
+ /** Set this process's nice value, 0..19 only: 0, -22 outside it, or -13 below the current value (lower-only, even as root). */
592
592
  export function setNice(n: i32): i32;
593
593
  /**
594
594
  * Move this process to `SCHED_IDLE`: 0, a negative errno, or -95 off Linux.
@@ -698,6 +698,55 @@ declare module "nish:unsafe" {
698
698
  export function readCString(p: CPtr): string;
699
699
  }
700
700
 
701
+ // `nish:simd` (docs/LANGUAGE.md -> Vectors): the six sixteen-byte vector
702
+ // types and the calls that make, load, store and take one apart. Under Node,
703
+ // `runtime/nish.mjs` answers the import with `runtime/core.mjs`'s names.
704
+ declare module "nish:simd" {
705
+ /**
706
+ * A sixteen-byte vector of lanes. Opaque: TypeScript refuses `+` on one, as
707
+ * `nish` does, but lets `===` and `<` through, which `nish` refuses. Under
708
+ * Node it is a typed array made once and never written after.
709
+ */
710
+ export interface u8x16 { readonly __nishSimd: "u8x16"; readonly __lane: u8 }
711
+ export interface u16x8 { readonly __nishSimd: "u16x8"; readonly __lane: u16 }
712
+ export interface i32x4 { readonly __nishSimd: "i32x4"; readonly __lane: i32 }
713
+ export interface u32x4 { readonly __nishSimd: "u32x4"; readonly __lane: u32 }
714
+ export interface f32x4 { readonly __nishSimd: "f32x4"; readonly __lane: f32 }
715
+ export interface f64x2 { readonly __nishSimd: "f64x2"; readonly __lane: f64 }
716
+ /** Any of the six. */
717
+ export type SimdVector = u8x16 | u16x8 | i32x4 | u32x4 | f32x4 | f64x2;
718
+ /** One value per lane, lane 0 first. */
719
+ export function u8x16(
720
+ a0: u8, a1: u8, a2: u8, a3: u8, a4: u8, a5: u8, a6: u8, a7: u8,
721
+ a8: u8, a9: u8, a10: u8, a11: u8, a12: u8, a13: u8, a14: u8, a15: u8
722
+ ): u8x16;
723
+ export function u16x8(a0: u16, a1: u16, a2: u16, a3: u16, a4: u16, a5: u16, a6: u16, a7: u16): u16x8;
724
+ export function i32x4(a0: i32, a1: i32, a2: i32, a3: i32): i32x4;
725
+ export function u32x4(a0: u32, a1: u32, a2: u32, a3: u32): u32x4;
726
+ export function f32x4(a0: f32, a1: f32, a2: f32, a3: f32): f32x4;
727
+ export function f64x2(a0: f64, a1: f64): f64x2;
728
+ /** `x` in every lane. */
729
+ export function splatU8x16(x: u8): u8x16;
730
+ export function splatU16x8(x: u16): u16x8;
731
+ export function splatI32x4(x: i32): i32x4;
732
+ export function splatU32x4(x: u32): u32x4;
733
+ export function splatF32x4(x: f32): f32x4;
734
+ export function splatF64x2(x: f64): f64x2;
735
+ /** `xs[i]` to `xs[i + N - 1]`, bounds-checked once for the whole vector. */
736
+ export function loadU8x16(xs: readonly u8[], i: i32): u8x16;
737
+ export function loadU16x8(xs: readonly u16[], i: i32): u16x8;
738
+ export function loadI32x4(xs: readonly i32[], i: i32): i32x4;
739
+ export function loadU32x4(xs: readonly u32[], i: i32): u32x4;
740
+ export function loadF32x4(xs: readonly f32[], i: i32): f32x4;
741
+ export function loadF64x2(xs: readonly f64[], i: i32): f64x2;
742
+ /** The lanes of `v` into `xs[i]` to `xs[i + N - 1]`, checked once. */
743
+ export function store<V extends SimdVector>(v: V, xs: V["__lane"][], i: i32): void;
744
+ /** Lane `k`, an integer literal in range. */
745
+ export function extract<V extends SimdVector>(v: V, k: i32): V["__lane"];
746
+ /** A new vector: `v` with lane `k` set to `x`. */
747
+ export function replace<V extends SimdVector>(v: V, k: i32, x: V["__lane"]): V;
748
+ }
749
+
701
750
  declare module "nish:io" {
702
751
  /** `s` to stdout with no trailing newline and no conversion. */
703
752
  export function write(s: string): void;
package/runtime/nish.h CHANGED
@@ -9,14 +9,17 @@
9
9
  * functions, the directory and subprocess calls, `nish_getenv`, the monotonic
10
10
  * clock, `nish_platform` / `nish_arch` — and runtime-parallel.c, runtime-host.c,
11
11
  * runtime-net.c, runtime-simd.c (the byte searches that read a vector at a
12
- * time), runtime-aes.c (AES-GCM on the CPU's AES instructions) and
13
- * runtime-term.c (stdin and the terminal, `nish:term`) split off
12
+ * time), runtime-aes.c (AES-GCM on the CPU's AES instructions),
13
+ * runtime-sha256.c (SHA-256 on the CPU's SHA instructions), runtime-checksum.c
14
+ * (CRC-32 on its carry-less multiply or CRC instructions) and runtime-term.c
15
+ * (stdin and the terminal, `nish:term`) split off
14
16
  * the same way since. They are separate so that each carries its own measured
15
17
  * code-size ceiling (docs/wp7-runtime.md, "Runtime additions and budget"); a
16
18
  * host links all of them, which `scripts/build.sh` does for you when handed
17
- * runtime.c, runtime-aes.c included: the compiler calls its kernels from
18
- * std/crypto/aes.ts, so `scripts/build.sh` pairs it and `src/run-cache.ts`
19
- * caches it like the others. `tests/run.js` checks that every runtime function
19
+ * runtime.c, runtime-aes.c, runtime-sha256.c and runtime-checksum.c included: the
20
+ * compiler calls their kernels from std/crypto/aes.ts, std/crypto/sha256.ts and
21
+ * std/compress/checksum.ts, so `scripts/build.sh` pairs each of them with
22
+ * runtime.c and `src/run-cache.ts` caches them like the others. `tests/run.js` checks that every runtime function
20
23
  * declared in runtime.ts has a prototype in this file.
21
24
  *
22
25
  * Headers generated by `nish --emit-header` include this one, so keep
@@ -282,6 +285,20 @@ nish_array *nish_alloc_array(uint64_t elem_size, uint64_t len);
282
285
  Variable-time: never call it on a secret. */
283
286
  int64_t nish_regex_scan(const nish_str *s, const nish_array *masks, int64_t depth, int64_t off, int64_t width,
284
287
  int64_t from);
288
+ /* `Math.floor`, `Math.ceil` and `Math.trunc`, and the floor `Math.round`
289
+ takes (#943): each answers as C's `floor`, `ceil` and `trunc` do, -0, the
290
+ infinities and NaN included. A module for x86-64 calls them and registers a
291
+ constructor in `@llvm.global_ctors` that passes `nish_round_init` libm's
292
+ `floor`, `ceil` and `trunc`; it asks the CPU for SSE4.1 once before `main`
293
+ and keeps them on a machine without it, or under `NISH_SIMD=scalar`. Each
294
+ is otherwise SSE4.1's `roundsd`, so the constructor must run first. The
295
+ runtime references no libm symbol: the module that rounds does. They live
296
+ in runtime/runtime-simd.c; runtime-wasm.c has the wasm32 ones for host IR
297
+ that `scripts/build.sh --profile wasm` links. */
298
+ void nish_round_init(double (*floor_fn)(double), double (*ceil_fn)(double), double (*trunc_fn)(double));
299
+ double nish_floor(double x);
300
+ double nish_ceil(double x);
301
+ double nish_trunc(double x);
285
302
  /* No array grows past 2^31 - 1 elements and no string past 2^31 - 1 bytes:
286
303
  * `nish_array_grow`, `nish_alloc_array` and `nish_str_concat` fail as an
287
304
  * allocation does (`nish: out of memory`, exit 1; a trap on wasm) rather than
@@ -334,6 +351,40 @@ void nish_aes_ctr(const nish_array *rk, int32_t rounds, const nish_array *j0, co
334
351
  `rk` instead, and they are the same H. 0 <= length <= data.len. */
335
352
  void nish_aes_ghash(nish_array *y, uint64_t hHi, uint64_t hLo, const nish_array *rk, const nish_array *data,
336
353
  int32_t length);
354
+ /* ---- SHA-256 on the CPU's SHA instructions --------------------------------
355
+ * runtime/runtime-sha256.c: SHA-NI on x86-64, the ARMv8 SHA-256 instructions
356
+ * on AArch64. The compiler calls the kernel in place of std/crypto/sha256.ts's
357
+ * private `sha256Compress`, behind `nish_sha256_hw`, and calls the Nish
358
+ * function itself when either answers 0. Constant time in the state and the
359
+ * block. */
360
+ /* 1 when this process runs the kernel, 0 when it runs the Nish code. Resolved
361
+ on the first call and fixed from then on: `NISH_SHA256=soft` pins 0; unset,
362
+ `hw` or any other value is the best this machine has, read on that call from
363
+ CPUID (leaf 7's SHA, leaf 1's SSE4.1 and SSSE3), AArch64 Linux's AT_HWCAP
364
+ (SHA2) or macOS's `hw.optional.arm.FEAT_SHA256`. Always 0 on other targets,
365
+ wasm32 among them. */
366
+ int32_t nish_sha256_hw(void);
367
+ /* FIPS 180-4 §6.2.2 on the 64 bytes at src[at..at + 64) (u8[]), into the hash
368
+ value h[0..8) (u32[]); 1 when it ran. Answers 0 and writes nothing unless
369
+ h.len >= 8, w.len >= 64 and 0 <= at <= src.len - 64, `sha256Compress`'s
370
+ guard, so the caller's fallback panics as the Nish function does. `w` (u32[],
371
+ the message schedule the Nish code fills) is neither read nor written. */
372
+ int32_t nish_sha256_compress(nish_array *h, const nish_array *w, const nish_array *src, int32_t at);
373
+ /* ---- CRC-32 on the CPU's carry-less multiply or CRC instructions ---------
374
+ * runtime/runtime-checksum.c: PCLMULQDQ folding on x86-64, the ARMv8 CRC32
375
+ * instructions on AArch64. The compiler calls the kernel in
376
+ * place of std/compress/checksum.ts's private `crc32Walk`, behind
377
+ * `nish_crc32_hw`, and calls the Nish walker itself when that answers 0. */
378
+ /* 1 when this process runs the kernel, 0 when it runs the Nish tables.
379
+ Resolved on the first call and fixed from then on: `NISH_CRC32=soft` pins 0;
380
+ unset or any other value is the best this machine has, read on that call
381
+ from CPUID leaf 1 (PCLMULQDQ, SSE4.1), AArch64's AT_HWCAP (CRC32) or
382
+ Darwin's hw.optional sysctls. Always 0 on other targets, wasm32 among them. */
383
+ int32_t nish_crc32_hw(void);
384
+ /* The CRC-32 (ISO 3309, RFC 1952) `crc` extended over buf[off..off+len)
385
+ (u8[]), conditioned as zlib's crc32(): 0 starts a message, and the answer
386
+ continues one. The caller has checked 0 <= off <= off + len <= buf.len. */
387
+ uint32_t nish_crc32_update(uint32_t crc, const nish_array *buf, int32_t off, int32_t len);
337
388
 
338
389
  /* `secureZero(bytes)` (#385): sets every byte of `bytes` to zero through
339
390
  * `volatile` stores, out of line, so that neither `-O2` nor `-flto` can drop
@@ -480,9 +531,9 @@ nish_array *nish_wait_pid(int32_t pid, int32_t flags);
480
531
  * page size; Darwin: `hw.memsize`), or -1 where the host does not say. */
481
532
  int64_t nish_total_memory(void);
482
533
  /* `setNice(n)`: this process's nice value set to `n` (`setpriority`), 0 or a
483
- * negative errno. The kernel clamps `n` to -20..19; lowering it below the
484
- * current value without the privilege is -EACCES (or -EPERM). -ENOSYS under
485
- * WASI. */
534
+ * negative errno. Lower-only, as root too: `n` takes 0..19, and anything else
535
+ * is -22 (-EINVAL) with no system call; `n` below the current value is -13
536
+ * (-EACCES) with none either. -ENOSYS under WASI. */
486
537
  int32_t nish_set_nice(int32_t n);
487
538
  /* `setIdlePriority()`: this process moved to `SCHED_IDLE` (Linux), 0 or a
488
539
  * negative errno; -95 (Linux's EOPNOTSUPP) elsewhere, -ENOSYS under WASI. An
@@ -727,6 +778,14 @@ int64_t nish_cpu_count(void);
727
778
  * rounded up, and `NISH_NUM_THREADS` when it is set to a positive number. At
728
779
  * least 1; 1 under WASI. Read each call (runtime-os.c). */
729
780
  int64_t nish_cpu_online(void);
781
+ /* WP37 `--sandbox` (runtime-sandbox.c): `nish_sandbox` confines the process to
782
+ the capability mask `caps` from `.preinit_array`, `mode` 1 strict and 2
783
+ best-effort, and `nish_sandbox_check` refuses to run at the top of `main`
784
+ when it never ran. The directory lists are a later stage's and are null.
785
+ `nish_cpu_prime` caches the pool's CPU count while /sys is still readable. */
786
+ void nish_sandbox(int32_t caps, int32_t mode, const char *read_dirs, const char *write_dirs);
787
+ void nish_sandbox_check(void);
788
+ void nish_cpu_prime(void);
730
789
  void nish_parallel_range(nish_par_body body, void *ctx, int64_t len, int64_t grain);
731
790
  /* Keeps every later region and join on threads created and joined per call,
732
791
  * as before the pool existed, so no parked worker outlives the code it would
package/runtime/nish.mjs CHANGED
@@ -314,6 +314,9 @@ provide("arena", shim.arena);
314
314
  // zero-fills it. `nish:unsafe` and `nish:term` are each answered by a module
315
315
  // that hands back the globals installed above: its functions are globals of this module either
316
316
  // way, and the import is what the compiler requires a program to write.
317
+ // `nish:simd` is `runtime/core.mjs`, whose vectors are typed arrays made once
318
+ // and never written after, so `--emit-ts` output and a program run as it was
319
+ // written share one implementation.
317
320
  const UNSAFE_EXPORTS = ["uncheckedGet", "uncheckedSet", "wrappingAdd", "wrappingSub", "wrappingMul"];
318
321
  const unsafeSource = UNSAFE_EXPORTS.map((name) => `export const ${name} = globalThis.${name};`).join("\n");
319
322
  const TERM_EXPORTS = ["readStdin", "waitStdin", "isTerminal", "terminalSize", "setRawMode", "setTerminalRestore"];
@@ -326,6 +329,9 @@ registerHooks({
326
329
  if (specifier === "nish:unsafe") {
327
330
  return { url: `data:text/javascript,${encodeURIComponent(unsafeSource)}`, shortCircuit: true };
328
331
  }
332
+ if (specifier === "nish:simd") {
333
+ return next(new URL("./core.mjs", import.meta.url).href, context);
334
+ }
329
335
  if (specifier === "nish:term") {
330
336
  return { url: `data:text/javascript,${encodeURIComponent(termSource)}`, shortCircuit: true };
331
337
  }