@amritk/nish-x86_64-linux 0.17.0 → 0.20.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.
Files changed (61) hide show
  1. package/INSTALL.md +43 -25
  2. package/bin/nish +0 -0
  3. package/package.json +2 -2
  4. package/runtime/nish.d.ts +55 -5
  5. package/runtime/nish.h +231 -20
  6. package/runtime/nish.mjs +16 -0
  7. package/runtime/runtime-aes.c +490 -0
  8. package/runtime/runtime-cross.c +366 -0
  9. package/runtime/runtime-host.c +7 -6
  10. package/runtime/runtime-net.c +93 -34
  11. package/runtime/runtime-os.c +213 -15
  12. package/runtime/runtime-parallel.c +590 -107
  13. package/runtime/runtime-simd.c +203 -0
  14. package/runtime/runtime-wasm.c +16 -3
  15. package/runtime/runtime.c +89 -23
  16. package/runtime/shim.mjs +71 -1
  17. package/scripts/build.sh +225 -20
  18. package/std/README.md +86 -55
  19. package/std/bytes.ts +163 -0
  20. package/std/crypto/LICENSE-libsecp256k1 +19 -0
  21. package/std/crypto/aes.ts +238 -76
  22. package/std/crypto/chacha20poly1305.ts +43 -9
  23. package/std/crypto/ed25519.ts +840 -0
  24. package/std/crypto/hkdf.ts +304 -12
  25. package/std/crypto/hmac.ts +245 -53
  26. package/std/crypto/p256.ts +1376 -42
  27. package/std/crypto/x25519.ts +55 -37
  28. package/std/crypto/x509-random.ts +71 -0
  29. package/std/crypto/x509.ts +9 -9
  30. package/std/json-tokenizer.ts +1296 -0
  31. package/std/json-typed.ts +1014 -0
  32. package/std/json-value.ts +1163 -0
  33. package/std/json.ts +1412 -70
  34. package/std/net/buffer-pool.ts +151 -0
  35. package/std/net/hpack.ts +416 -112
  36. package/std/net/http-fields.ts +93 -0
  37. package/std/net/http1-server.ts +417 -55
  38. package/std/net/http1.ts +31 -6
  39. package/std/net/http2-tls.ts +112 -24
  40. package/std/net/http2.ts +333 -75
  41. package/std/net/http3-server.ts +231 -45
  42. package/std/net/http3.ts +187 -28
  43. package/std/net/prefork.ts +412 -0
  44. package/std/net/quic-conn-params.ts +86 -16
  45. package/std/net/quic-listener.ts +576 -219
  46. package/std/net/quic-packet.ts +443 -58
  47. package/std/net/quic-recovery.ts +361 -30
  48. package/std/net/quic-stream.ts +434 -28
  49. package/std/net/quic.ts +922 -206
  50. package/std/net/router.ts +676 -0
  51. package/std/net/tls/codec.ts +426 -27
  52. package/std/net/tls/record-server.ts +478 -132
  53. package/std/net/tls/record.ts +133 -42
  54. package/std/net/tls/schedule.ts +464 -55
  55. package/std/net/tls/ticket.ts +610 -0
  56. package/std/net/tls-tcp.ts +204 -57
  57. package/std/net/tls.ts +1132 -305
  58. package/std/net/url.ts +571 -0
  59. package/std/random.ts +94 -0
  60. package/std/text.ts +77 -0
  61. package/std/threads.ts +438 -14
package/scripts/build.sh CHANGED
@@ -3,12 +3,15 @@
3
3
  #
4
4
  # scripts/build.sh <module.ll> [more .ll/.c files...] -o <out> [--profile debug|speed|size|wasm|wasi|napi]
5
5
  #
6
- # The C runtime is five translation units and is named as one: an input
6
+ # The C runtime is eight translation units and is named as one: an input
7
7
  # <dir>/runtime.c also compiles <dir>/runtime-os.c, the half that wraps the
8
8
  # system calls (files, directories, subprocesses, the environment, the clock),
9
9
  # <dir>/runtime-parallel.c, the half that divides a range of work across
10
- # threads, <dir>/runtime-host.c, the wall clock, entropy, file times and
11
- # signals, and <dir>/runtime-net.c, the sockets of `nish:net`. Each of those
10
+ # threads, <dir>/runtime-cross.c, the copies that carry a string or an array
11
+ # from one thread's arena to another's, <dir>/runtime-host.c, the wall clock, entropy, file times and
12
+ # signals, <dir>/runtime-net.c, the sockets of `nish:net`,
13
+ # <dir>/runtime-simd.c, the byte searches that read a vector at a time, and
14
+ # <dir>/runtime-aes.c, AES-GCM on the CPU's AES instructions. Each of those
12
15
  # files says why they are compiled and measured apart.
13
16
  #
14
17
  # Profiles:
@@ -56,6 +59,29 @@
56
59
  # put in the .ll (line table, variables) reaches the binary. `nish
57
60
  # --link -g` passes it through automatically.
58
61
  #
62
+ # CPU level (WP38 S2): `--cpu x86-64-v3` compiles and links every input --
63
+ # the module, the runtime and any other C -- with -march=x86-64-v3
64
+ # -ffp-contract=off, so the code generator may use AVX2, BMI2 and the other
65
+ # v3 instructions and still forms no fused multiply-add the IR did not write.
66
+ # It is the one level taken; any other value is refused with exit 2, and so are
67
+ # the wasm and wasi profiles, which are not x86-64. A binary built with it
68
+ # stops with SIGILL on a CPU without AVX2. `nish --link --cpu x86-64-v3`
69
+ # passes it through.
70
+ #
71
+ # The runtime compiled once (the debug, speed and size profiles only; `nish
72
+ # --link` and `nish run` use these through their runtime cache, src/compile.ts):
73
+ # --runtime-objects <dir> compile the runtime.c input and its seven siblings,
74
+ # one $CC -c each and all at once, into
75
+ # <dir>/<unit>.o with the profile's compile flags,
76
+ # and link nothing. speed and size store LTO bitcode,
77
+ # so the link can still inline the runtime into the
78
+ # program. No -o; prints nothing on success.
79
+ # --runtime-from <dir> link against <dir>/<unit>.o in place of a
80
+ # runtime.c input, after compiling every other input
81
+ # with its own $CC -c, all at once. The link line is
82
+ # the one the profile always uses.
83
+ # Both refuse the wasm, wasi and napi profiles and --pgo-*, with exit 2.
84
+ #
59
85
  # Works on Linux (clang + lld preferred, GNU ld tolerated) and macOS (Apple ld64
60
86
  # or Homebrew llvm). Set CC to pick a compiler (default: clang on PATH).
61
87
  set -euo pipefail
@@ -66,38 +92,88 @@ inputs=()
66
92
  pgo=() # -fprofile-generate / -fprofile-use=<file>
67
93
  debug=0 # -g: keep DWARF (nish -g emits it in the IR; runtime.c gets it here)
68
94
  threads=0 # --threads: -DNISH_THREADS, the thread-local arena (WP20 T0)
95
+ objects_to="" # --runtime-objects <dir>: compile the runtime alone, into <dir>
96
+ objects_from="" # --runtime-from <dir>: link against the runtime compiled there
97
+ cpu="" # --cpu x86-64-v3: the one CPU level above the baseline (WP38 S2)
69
98
  while [ $# -gt 0 ]; do
70
99
  case "$1" in
71
100
  -o) out="$2"; shift 2 ;;
72
101
  --profile) profile="$2"; shift 2 ;;
73
102
  -g) debug=1; shift ;;
74
103
  --threads) threads=1; shift ;;
104
+ --cpu)
105
+ [ -n "${2-}" ] || { echo "error: --cpu needs a level; the one taken is x86-64-v3" >&2; exit 2; }
106
+ cpu="$2"; shift 2 ;;
107
+ --runtime-objects) objects_to="$2"; shift 2 ;;
108
+ --runtime-from) objects_from="$2"; shift 2 ;;
75
109
  --pgo-generate) pgo=(-fprofile-generate); shift ;;
76
110
  --pgo-use)
77
111
  [ -f "$2" ] || { echo "error: --pgo-use: profile '$2' not found (run the instrumented binary, then llvm-profdata merge)" >&2; exit 2; }
78
112
  pgo=("-fprofile-use=$2"); shift 2 ;;
79
- -h|--help) sed -n '2,41p' "$0"; exit 0 ;;
113
+ -h|--help) sed -n '2,85p' "$0"; exit 0 ;;
80
114
  *) inputs+=("$1"); shift ;;
81
115
  esac
82
116
  done
83
117
  [ ${#inputs[@]} -gt 0 ] || { echo "error: no input files" >&2; exit 2; }
84
- [ -n "$out" ] || { echo "error: -o <out> is required" >&2; exit 2; }
118
+ if [ -n "$cpu" ]; then
119
+ [ "$cpu" = x86-64-v3 ] || { echo "error: --cpu takes x86-64-v3, not '$cpu'" >&2; exit 2; }
120
+ case "$profile" in
121
+ wasm|wasi) echo "error: --cpu x86-64-v3 is an x86-64 level, so the $profile profile does not take it" >&2; exit 2 ;;
122
+ esac
123
+ fi
124
+ # wasm-ld exports a wasm module's linear memory as `memory`, so under the wasm
125
+ # profile's `--export-all` a function of that name makes a module Node refuses
126
+ # to compile ("Duplicate export name 'memory'", #643). The wasi module exports
127
+ # only `memory` and `_start`, but the name is reserved there too, so a program
128
+ # that links for wasi also links for wasm. The compiler refuses one (NL3038)
129
+ # when it knows the build is wasm, but IR compiled for the host with `-o` alone
130
+ # reaches these profiles without telling it, so the IR is read here too.
131
+ case "$profile" in
132
+ wasm|wasi)
133
+ for i in ${inputs[@]+"${inputs[@]}"}; do
134
+ case "$i" in
135
+ *.ll)
136
+ # An `internal` or `private` definition is not exported, so it is left alone.
137
+ if grep -E '^define [^@]*@memory\(' "$i" | grep -Evq '^define (internal|private) '; then
138
+ echo "error: $i defines a function named \`memory\`, the name the $profile profile's module exports its linear memory under; rename the function" >&2
139
+ exit 2
140
+ fi ;;
141
+ esac
142
+ done ;;
143
+ esac
144
+ if [ -n "$objects_to" ] || [ -n "$objects_from" ]; then
145
+ # A cached runtime is the profile's ordinary compile, so it exists only for
146
+ # the profiles that compile the runtime with nothing but these flags.
147
+ case "$profile" in
148
+ debug|speed|size) ;;
149
+ *) echo "error: --runtime-objects and --runtime-from take the debug, speed and size profiles, not '$profile'" >&2; exit 2 ;;
150
+ esac
151
+ [ ${#pgo[@]} -eq 0 ] || { echo "error: --runtime-objects and --runtime-from do not take --pgo-*" >&2; exit 2; }
152
+ [ -z "$objects_to" ] || [ -z "$objects_from" ] || { echo "error: --runtime-objects and --runtime-from are two different builds" >&2; exit 2; }
153
+ fi
154
+ [ -n "$out" ] || [ -n "$objects_to" ] || { echo "error: -o <out> is required" >&2; exit 2; }
85
155
 
86
- # The runtime is five translation units, and a caller names one: whoever passes
156
+ # The runtime is eight translation units, and a caller names one: whoever passes
87
157
  # <dir>/runtime.c gets <dir>/runtime-os.c, <dir>/runtime-parallel.c,
88
- # <dir>/runtime-host.c and <dir>/runtime-net.c compiled beside it. They were one file until the operating-system half was split out
89
- # for its own size budget, and the parallel half followed for the same reason
90
- # (each file's header comment says why), and a link line is where those splits
158
+ # <dir>/runtime-cross.c, <dir>/runtime-host.c, <dir>/runtime-net.c, <dir>/runtime-simd.c and
159
+ # <dir>/runtime-aes.c compiled beside it, on every profile, wasi included:
160
+ # runtime-simd.c takes its scalar path there unless the build adds -msimd128,
161
+ # and runtime-aes.c compiles to an answer of 0 and stubs that nothing calls,
162
+ # because the compiler emits no call to its kernels for wasm32. They were one file until the
163
+ # operating-system half was split out for its own size budget, and the parallel
164
+ # half followed for the same reason (each file's header comment says why), and
165
+ # a link line is where those splits
91
166
  # would otherwise leak: `nish --link` builds its command line in
92
167
  # src/compile.ts, the published package's recipe in every document and
93
168
  # README names runtime.c, and a user's own clang line does too. Pairing them
94
169
  # here keeps every one of those correct, and keeps "the runtime" one thing to
95
170
  # name from the outside. A caller that names one itself is left alone, because
96
171
  # naming one object twice is a duplicate-symbol error.
172
+ halves="runtime-os.c runtime-parallel.c runtime-cross.c runtime-host.c runtime-net.c runtime-simd.c runtime-aes.c"
97
173
  for i in ${inputs[@]+"${inputs[@]}"}; do
98
174
  case "$i" in
99
175
  */runtime.c|runtime.c)
100
- for half in runtime-os.c runtime-parallel.c runtime-host.c runtime-net.c; do
176
+ for half in $halves; do
101
177
  side="${i%runtime.c}$half"
102
178
  have=0
103
179
  for j in "${inputs[@]}"; do
@@ -173,12 +249,130 @@ if [ "$debug" = 1 ]; then common+=(-g); strip_flag=(); fi
173
249
  # command lines, which have no threads to link against, and where
174
250
  # runtime-parallel.c compiles to its sequential fallback because it tests
175
251
  # __wasi__ and __wasm__ as well as the macro.
252
+ # --cpu: every unit at the one level, so the module, the runtime and the link
253
+ # (LTO generates code there) agree. -ffp-contract=off keeps the meaning fixed:
254
+ # v3 has FMA, and contracting a*b+c into one rounding would print a different
255
+ # float than the baseline build (docs/LANGUAGE.md, "The CPU level").
256
+ if [ -n "$cpu" ]; then common+=(-march=x86-64-v3 -ffp-contract=off); fi
257
+
176
258
  tls=()
177
259
  if [ "$threads" = 1 ]; then
178
260
  common+=(-DNISH_THREADS=1 -ftls-model=initial-exec -pthread)
179
261
  tls=(-DNISH_THREADS=1 -ftls-model=initial-exec)
180
262
  fi
181
263
 
264
+ # What the speed and size profiles compile every input with, beside `common`.
265
+ # One list per profile, because the runtime cache below compiles with exactly
266
+ # what the one-command link does, and two copies of a flag set drift.
267
+ optimise=()
268
+ case "$profile" in
269
+ speed)
270
+ optimise=(-O3 -flto -DNDEBUG ${pgo[@]+"${pgo[@]}"}
271
+ -ffunction-sections -fdata-sections -fomit-frame-pointer
272
+ -fno-asynchronous-unwind-tables -fno-unwind-tables ${elf[@]+"${elf[@]}"}) ;;
273
+ size)
274
+ optimise=(-Oz -flto -DNDEBUG ${pgo[@]+"${pgo[@]}"}
275
+ -ffunction-sections -fdata-sections -fomit-frame-pointer
276
+ -fno-asynchronous-unwind-tables -fno-unwind-tables ${elf[@]+"${elf[@]}"}
277
+ -fno-stack-protector -fvisibility=hidden) ;;
278
+ esac
279
+
280
+ # --runtime-objects and --runtime-from: the one-command link split in two, with
281
+ # every compile it held run at once. A `$CC -c` takes `common` and `optimise`,
282
+ # which is what the one command compiles each input with -- less
283
+ # -fuse-ld=lld, which only names the linker -- so a runtime unit compiles to the
284
+ # same object either way: native code for debug, -flto bitcode for speed and
285
+ # size, which the link still optimises together with the program. The link
286
+ # takes the same line the profile always uses, with objects for sources.
287
+ if [ -n "$objects_to" ] || [ -n "$objects_from" ]; then
288
+ compile=()
289
+ for flag in "${common[@]}"; do
290
+ case "$flag" in -fuse-ld=*) ;; *) compile+=("$flag") ;; esac
291
+ done
292
+ compile+=(${optimise[@]+"${optimise[@]}"})
293
+ # Waits for every job, not just the last, and fails when any of them did.
294
+ # bash 3.2 has no `wait -n`, so the jobs are waited for in order.
295
+ pids=()
296
+ join_jobs() {
297
+ local failed=0 pid
298
+ for pid in ${pids[@]+"${pids[@]}"}; do
299
+ wait "$pid" || failed=1
300
+ done
301
+ pids=()
302
+ return "$failed"
303
+ }
304
+ # The compiles are this script's children, so a signal that stops it stops
305
+ # them too, before anything they write into is removed. A finished script
306
+ # has waited for every one of them, so there is nothing left to stop.
307
+ stop_jobs() {
308
+ if [ ${#pids[@]} -gt 0 ]; then
309
+ kill "${pids[@]}" 2>/dev/null || true
310
+ wait "${pids[@]}" 2>/dev/null || true
311
+ pids=()
312
+ fi
313
+ }
314
+ work=""
315
+ trap 'stop_jobs; [ -z "$work" ] || rm -rf -- "$work"' EXIT
316
+ trap 'exit 130' INT
317
+ trap 'exit 143' TERM
318
+ fi
319
+
320
+ if [ -n "$objects_to" ]; then
321
+ runtime=""
322
+ for i in "${inputs[@]}"; do
323
+ case "$i" in
324
+ */runtime.c|runtime.c) runtime="$i" ;;
325
+ */runtime-*.c|runtime-*.c) ;; # its siblings, added above
326
+ *) echo "error: --runtime-objects compiles the runtime alone, not $i" >&2; exit 2 ;;
327
+ esac
328
+ done
329
+ [ -n "$runtime" ] || { echo "error: --runtime-objects needs runtime/runtime.c" >&2; exit 2; }
330
+ mkdir -p -- "$objects_to"
331
+ objects_to=$(cd -- "$objects_to" && pwd)
332
+ # Each unit is compiled from inside its own directory, by its bare name, so
333
+ # an object holds no trace of where the compile was started: -g's DWARF names
334
+ # the file `runtime.c` in the runtime's own directory, whichever directory
335
+ # the first link that compiled it was run in.
336
+ runtime_dir=$(dirname -- "$runtime")
337
+ # A CC named by a relative path is named from here, not from there.
338
+ case "$CC" in /*) ;; */*) CC="$(pwd)/$CC" ;; esac
339
+ for unit in runtime.c $halves; do
340
+ (cd -- "$runtime_dir" && exec "$CC" "${compile[@]}" -c "$unit" -o "$objects_to/${unit%.c}.o") &
341
+ pids+=("$!")
342
+ done
343
+ join_jobs || { echo "error: the runtime did not compile" >&2; exit 1; }
344
+ exit 0
345
+ fi
346
+
347
+ if [ -n "$objects_from" ]; then
348
+ for unit in runtime.c $halves; do
349
+ [ -f "$objects_from/${unit%.c}.o" ] || { echo "error: --runtime-from: $objects_from/${unit%.c}.o is missing" >&2; exit 2; }
350
+ done
351
+ # The program's own objects are this link's alone, so they go in a private
352
+ # directory of its own and go with it.
353
+ work=$(mktemp -d "${TMPDIR:-/tmp}/nish-link.XXXXXX")
354
+ objects=()
355
+ n=0
356
+ for i in "${inputs[@]}"; do
357
+ case "$i" in */runtime.c|runtime.c) echo "error: --runtime-from links the runtime in $objects_from, so $i is not an input" >&2; exit 2 ;; esac
358
+ n=$((n + 1))
359
+ "$CC" "${compile[@]}" -c "$i" -o "$work/$n.o" &
360
+ pids+=("$!")
361
+ objects+=("$work/$n.o")
362
+ done
363
+ join_jobs || { echo "error: a module did not compile" >&2; exit 1; }
364
+ # In the order the one-command link names them: the inputs, then runtime.c
365
+ # and its siblings.
366
+ for unit in runtime.c $halves; do
367
+ objects+=("$objects_from/${unit%.c}.o")
368
+ done
369
+ inputs=("${objects[@]}")
370
+ # clang runs dsymutil itself when one command compiles and links with -g on
371
+ # macOS; a link of objects does not, so it is run here, while the program's
372
+ # objects the debug map points at are still there.
373
+ if [ "$debug" = 1 ] && [ "$(uname -s)" = Darwin ]; then dsym=1; else dsym=0; fi
374
+ fi
375
+
182
376
  # The ${arr[@]+"${arr[@]}"} spelling below is not a style tic: macOS ships bash
183
377
  # 3.2 (Apple will not ship GPLv3), where expanding an empty array as "${arr[@]}"
184
378
  # under `set -u` is a fatal "unbound variable" -- bash 4.4 made it legal, which is
@@ -187,16 +381,8 @@ fi
187
381
  case "$profile" in
188
382
  debug)
189
383
  "$CC" "${common[@]}" "${inputs[@]}" -lm -o "$out" ;;
190
- speed)
191
- "$CC" "${common[@]}" -O3 -flto -DNDEBUG ${pgo[@]+"${pgo[@]}"} \
192
- -ffunction-sections -fdata-sections -fomit-frame-pointer \
193
- -fno-asynchronous-unwind-tables -fno-unwind-tables ${elf[@]+"${elf[@]}"} \
194
- "${gc[@]}" ${strip_flag[@]+"${strip_flag[@]}"} "${inputs[@]}" -lm -o "$out" ;;
195
- size)
196
- "$CC" "${common[@]}" -Oz -flto -DNDEBUG ${pgo[@]+"${pgo[@]}"} \
197
- -ffunction-sections -fdata-sections -fomit-frame-pointer \
198
- -fno-asynchronous-unwind-tables -fno-unwind-tables ${elf[@]+"${elf[@]}"} \
199
- -fno-stack-protector -fvisibility=hidden \
384
+ speed|size)
385
+ "$CC" "${common[@]}" "${optimise[@]}" \
200
386
  "${gc[@]}" ${strip_flag[@]+"${strip_flag[@]}"} "${inputs[@]}" -lm -o "$out" ;;
201
387
  wasm)
202
388
  # clang resolves wasm-ld next to its own binary first, then on PATH; ask it
@@ -206,6 +392,23 @@ case "$profile" in
206
392
  echo "error: the wasm profile needs wasm-ld (install lld; on macOS: brew install llvm@18)" >&2
207
393
  exit 2
208
394
  fi
395
+ # IR compiled for the host rather than with `--target wasm32` calls std/crypto/aes.ts's
396
+ # AES kernels behind `nish_aes_hw` (src/kernels.ts). runtime-aes.c's wasm32 build
397
+ # answers 0 there, so the bitsliced code runs, and it is added only for such IR:
398
+ # every other module links the bytes it always did.
399
+ # A caller that names runtime-aes.c itself is left alone, as `halves` leaves it:
400
+ # one object named twice is a duplicate-symbol error.
401
+ aes_named=0
402
+ aes_wanted=0
403
+ for i in ${inputs[@]+"${inputs[@]}"}; do
404
+ case "$i" in
405
+ */runtime-aes.c|runtime-aes.c) aes_named=1 ;;
406
+ *.ll) if grep -q '@nish_aes_hw(' "$i"; then aes_wanted=1; fi ;;
407
+ esac
408
+ done
409
+ if [ "$aes_wanted" = 1 ] && [ "$aes_named" = 0 ]; then
410
+ inputs+=("$(cd "$(dirname "$0")/../runtime" && pwd)/runtime-aes.c")
411
+ fi
209
412
  # -mbulk-memory lowers llvm.memset/memcpy (`new Array<T>(n)`, `push` growth) to the
210
413
  # memory.fill/memory.copy instructions instead of libc calls the freestanding link lacks.
211
414
  "$CC" -Wno-override-module --target=wasm32-unknown-unknown -Oz -nostdlib -mbulk-memory \
@@ -280,5 +483,7 @@ case "$profile" in
280
483
  *) echo "error: unknown profile '$profile'" >&2; exit 2 ;;
281
484
  esac
282
485
 
486
+ if [ "${dsym:-0}" = 1 ]; then "$("$CC" -print-prog-name=dsymutil)" "$out"; fi
487
+
283
488
  # `wc -c` pads with spaces on macOS; strip them so the number is clean.
284
489
  printf '%s: %s bytes (%s)\n' "$out" "$(wc -c < "$out" | tr -d ' ')" "$profile"