@amritk/nish 0.10.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/LICENSE +21 -0
- package/README.md +553 -0
- package/bin/launcher.js +186 -0
- package/bin/nish +23 -0
- package/bin/packaging.js +220 -0
- package/docs/AI.md +1076 -0
- package/docs/INSTALL.md +468 -0
- package/llms.txt +49 -0
- package/package.json +87 -0
- package/runtime/nish.d.ts +290 -0
- package/runtime/nish.h +340 -0
- package/runtime/nish.mjs +143 -0
- package/runtime/runtime.c +1184 -0
- package/runtime/runtime_os.c +351 -0
- package/runtime/runtime_parallel.c +156 -0
- package/runtime/runtime_wasm.c +99 -0
- package/runtime/shim.mjs +672 -0
- package/scripts/bootstrap.sh +357 -0
- package/scripts/build.sh +279 -0
- package/scripts/changelog-gen.mjs +528 -0
- package/scripts/changelog-section.sh +28 -0
- package/scripts/ci-profile.mjs +187 -0
- package/scripts/codes-registry.js +73 -0
- package/scripts/gen-diagnostic-codes.mjs +199 -0
- package/scripts/gen-pow5-tables.py +45 -0
- package/scripts/nish-compiler.sh +17 -0
- package/scripts/platform-package.mjs +91 -0
- package/scripts/postinstall.mjs +133 -0
- package/scripts/size-report.sh +72 -0
- package/scripts/smoke.sh +94 -0
- package/scripts/verify-binaries.sh +213 -0
- package/std/README.md +185 -0
- package/std/json.ts +402 -0
- package/std/pair.ts +28 -0
- package/std/testing.ts +347 -0
- package/std/text.ts +193 -0
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Build the self-hosted compiler: `self/`, compiled by `self/`.
|
|
3
|
+
#
|
|
4
|
+
# scripts/bootstrap.sh [-o <exe>] [--stages 1|2|3] [--profile speed|size|debug]
|
|
5
|
+
# [--work <dir>] [--verify] [--quiet]
|
|
6
|
+
#
|
|
7
|
+
# The chain is the one docs/wp14-selfhost.md §1 defines, with the seed as a
|
|
8
|
+
# parameter rather than a fixture (docs/wp19-stage0-retirement.md §3, G3):
|
|
9
|
+
#
|
|
10
|
+
# seed whatever compiles stage1 NISH_BOOTSTRAP, or build/seed
|
|
11
|
+
# stage1 self/, built by the seed
|
|
12
|
+
# stage2 self/, built by stage1 the default output
|
|
13
|
+
# stage3 self/, built by stage2 --verify only
|
|
14
|
+
#
|
|
15
|
+
# NISH_BOOTSTRAP=<path> names the seed, the way GOROOT_BOOTSTRAP names the Go
|
|
16
|
+
# that builds Go. It is either a released `nish`, executed directly, or a Node
|
|
17
|
+
# entry point (`.js`, `.mjs`, `.cjs`) wrapping one, executed as `node <path>`:
|
|
18
|
+
#
|
|
19
|
+
# NISH_BOOTSTRAP=~/nish-0.6.0-linux-x86_64 scripts/bootstrap.sh --verify
|
|
20
|
+
#
|
|
21
|
+
# Unset, the seed is build/seed/bin/nish, the last release as
|
|
22
|
+
# `scripts/fetch-seed.sh` unpacks it, and this runs that script first. When the
|
|
23
|
+
# fetch fails it stops and says so: a compiler has to come from somewhere, and
|
|
24
|
+
# the only place left is a release.
|
|
25
|
+
#
|
|
26
|
+
# stage2 is what this installs, because it is the first binary in the chain
|
|
27
|
+
# that no part of the seed emitted: the seed built the compiler that built it,
|
|
28
|
+
# and `--verify` is what says the two agree. `--stages 1` stops at the seed's
|
|
29
|
+
# own output, which is enough to *use* the self-hosted compiler and half the
|
|
30
|
+
# wait.
|
|
31
|
+
#
|
|
32
|
+
# --verify runs the equalities the proof is made of, byte for byte:
|
|
33
|
+
#
|
|
34
|
+
# IR(seed, self/) == IR(stage1, self/) reported, never asserted
|
|
35
|
+
# IR(stage1, self/) == IR(stage2, self/) the fixed point: self-hosted
|
|
36
|
+
# stage3 == stage2 as files, on ELF and on Mach-O
|
|
37
|
+
# alike; see below for what the
|
|
38
|
+
# Mach-O half took
|
|
39
|
+
#
|
|
40
|
+
# The last two are properties of the working tree and of nothing else: whatever
|
|
41
|
+
# built stage1, the compiler `self/` describes has to agree with itself and
|
|
42
|
+
# then reproduce itself. Both are asserted whatever the seed is.
|
|
43
|
+
#
|
|
44
|
+
# The third is a raw byte comparison, and on Mach-O two links of the same input
|
|
45
|
+
# did not produce the same bytes -- measured, at identical size, with every IR
|
|
46
|
+
# equality green (WP19 R2). WHICH bytes is measured too now, and so is what they
|
|
47
|
+
# vary with: LC_UUID, stable across two links to one output path and differing
|
|
48
|
+
# on a link to another, plus on arm64 the one code-directory slot that hashes
|
|
49
|
+
# the page it sits on. That is why `link_stage` below builds every comparable
|
|
50
|
+
# stage at one path, and with it the comparison holds as raw bytes on Mach-O as
|
|
51
|
+
# well -- measured end to end, whatever the UUID turns out to be a function of.
|
|
52
|
+
# Under that, on Darwin only, the script
|
|
53
|
+
# still asserts the size, strips what it can, and reports anything left over as
|
|
54
|
+
# *unattributed* rather than as a compiler difference -- narrower than raw bytes
|
|
55
|
+
# and wider than an exemption, which would accept a stage3 that is a different
|
|
56
|
+
# compiler. The comparison and the reasoning live in
|
|
57
|
+
# `scripts/verify-binaries.sh` -- a script rather than a block in here so that
|
|
58
|
+
# `tests/run.js` can drive both platforms' branches -- and its header has the
|
|
59
|
+
# measurement, byte offsets and all.
|
|
60
|
+
#
|
|
61
|
+
# The first one is reported and not asserted. With a released seed it is one
|
|
62
|
+
# implementation at two points in time: the IR HEAD emits for `self/` is the
|
|
63
|
+
# IR the last release emitted for it. Nothing in that sentence is about
|
|
64
|
+
# bootstrapping. It is a freeze on codegen between releases, and it fails on
|
|
65
|
+
# exactly the changes a release cycle exists to carry: the first improvement to
|
|
66
|
+
# land broke it, when a flow-sensitive bounds analysis proved 46 of `self/`'s
|
|
67
|
+
# 1,206 index checks redundant and 20 of 56 modules "differed" because the
|
|
68
|
+
# optimisation worked. So the difference is reported, and the report is
|
|
69
|
+
# information about this release rather than a verdict on the bootstrap.
|
|
70
|
+
#
|
|
71
|
+
# While `src/` existed, a stage0 seed made the same comparison the second half
|
|
72
|
+
# of Wheeler's diverse double-compiling, and it was asserted there
|
|
73
|
+
# (docs/wp19-stage0-retirement.md §1, G6). That claim went with `src/`.
|
|
74
|
+
#
|
|
75
|
+
# What the seeded run buys is not that equality. The rolling freeze — "a
|
|
76
|
+
# construct added in 0.N cannot be used by `self/` until 0.(N+1)" — is enforced
|
|
77
|
+
# by stage1 being built at all: a `self/` that reaches for something the seed
|
|
78
|
+
# has never heard of does not compile, does not link, and never gets as far as
|
|
79
|
+
# a comparison. That failure is loud here whatever the seed is, and it is the
|
|
80
|
+
# whole of what the seeded run proves about the freeze
|
|
81
|
+
# (docs/wp19-stage0-retirement.md §3, G3).
|
|
82
|
+
#
|
|
83
|
+
# `node tests/self/bootstrap.js` is the same check with the suite's reporting,
|
|
84
|
+
# and is what CI runs; this script is how the compiler gets built for use.
|
|
85
|
+
#
|
|
86
|
+
# The result is a complete compiler: it plans its own output, makes the
|
|
87
|
+
# directories and links through `scripts/build.sh` itself, so nothing has to
|
|
88
|
+
# stand between it and a build (docs/wp14-selfhost.md §7a).
|
|
89
|
+
#
|
|
90
|
+
# Needs a runnable seed, and clang + lld on PATH for the links
|
|
91
|
+
# (docs/INSTALL.md).
|
|
92
|
+
set -euo pipefail
|
|
93
|
+
|
|
94
|
+
# Read the seed before the `cd` below moves us: a relative NISH_BOOTSTRAP is
|
|
95
|
+
# relative to the directory the caller typed it in, not to the repository root.
|
|
96
|
+
seed_given="${NISH_BOOTSTRAP:-}"
|
|
97
|
+
case "$seed_given" in
|
|
98
|
+
""|/*) ;;
|
|
99
|
+
*) seed_given="$PWD/$seed_given" ;;
|
|
100
|
+
esac
|
|
101
|
+
|
|
102
|
+
cd "$(dirname "$0")/.."
|
|
103
|
+
|
|
104
|
+
out=build/nish
|
|
105
|
+
work=build/selfhost
|
|
106
|
+
profile=speed
|
|
107
|
+
stages=2
|
|
108
|
+
verify=0
|
|
109
|
+
quiet=0
|
|
110
|
+
|
|
111
|
+
usage() {
|
|
112
|
+
cat <<'EOF'
|
|
113
|
+
usage: scripts/bootstrap.sh [-o <exe>] [--stages 1|2|3] [--profile speed|size|debug]
|
|
114
|
+
[--work <dir>] [--verify] [--quiet]
|
|
115
|
+
|
|
116
|
+
Builds the self-hosted compiler. The seed builds stage1, stage1 builds stage2
|
|
117
|
+
(the default output), stage2 builds stage3. --verify compares the IR each stage
|
|
118
|
+
emits for self/ and the stage2/stage3 binaries, byte for byte. The result is
|
|
119
|
+
the command line itself: -o, --link, --profile and the rest.
|
|
120
|
+
|
|
121
|
+
IR(seed) == IR(stage1) asks whether codegen has changed since the seed was
|
|
122
|
+
built, so it is reported and not asserted; IR(stage1) == IR(stage2) and
|
|
123
|
+
stage3 == stage2 are asserted whatever the seed is.
|
|
124
|
+
|
|
125
|
+
The seed is NISH_BOOTSTRAP=<path> when it is set — a released `nish` binary, or
|
|
126
|
+
a .js/.mjs entry point run under node — and build/seed/bin/nish, the release
|
|
127
|
+
scripts/fetch-seed.sh unpacks, when it is not:
|
|
128
|
+
|
|
129
|
+
scripts/bootstrap.sh
|
|
130
|
+
NISH_BOOTSTRAP=~/nish-0.6.0-linux-x86_64 scripts/bootstrap.sh --verify
|
|
131
|
+
EOF
|
|
132
|
+
exit "${1:-2}"
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
while [ $# -gt 0 ]; do
|
|
136
|
+
case "$1" in
|
|
137
|
+
-o|--output) out="${2:-}"; [ -n "$out" ] || usage; shift 2 ;;
|
|
138
|
+
--work) work="${2:-}"; [ -n "$work" ] || usage; shift 2 ;;
|
|
139
|
+
--profile) profile="${2:-}"; [ -n "$profile" ] || usage; shift 2 ;;
|
|
140
|
+
--stages) stages="${2:-}"; shift 2 ;;
|
|
141
|
+
--verify) verify=1; shift ;;
|
|
142
|
+
--quiet) quiet=1; shift ;;
|
|
143
|
+
-h|--help) usage 0 ;;
|
|
144
|
+
*) echo "bootstrap: unknown argument \`$1\`" >&2; usage ;;
|
|
145
|
+
esac
|
|
146
|
+
done
|
|
147
|
+
|
|
148
|
+
case "$stages" in
|
|
149
|
+
1|2|3) ;;
|
|
150
|
+
*) echo "bootstrap: --stages takes 1, 2 or 3 (got \`$stages\`)" >&2; exit 2 ;;
|
|
151
|
+
esac
|
|
152
|
+
# `--stages` says which compiler to install; verifying the fixed point needs
|
|
153
|
+
# the stage that closes it built as well, whether or not it is the one kept.
|
|
154
|
+
install=$stages
|
|
155
|
+
build_to=$stages
|
|
156
|
+
[ "$verify" -eq 1 ] && build_to=3
|
|
157
|
+
|
|
158
|
+
# The published npm package ships bin/, runtime/, scripts/ and std/ but not
|
|
159
|
+
# self/ -- and, since 0.6.0, no compiler of its own at all -- so say which file
|
|
160
|
+
# is missing rather than failing inside the compiler.
|
|
161
|
+
if [ ! -f self/compile.ts ]; then
|
|
162
|
+
echo "bootstrap: self/compile.ts is missing; run this from a checkout of the repository" >&2
|
|
163
|
+
exit 3
|
|
164
|
+
fi
|
|
165
|
+
|
|
166
|
+
# One place that knows how to invoke the seed, so every stage below reads the
|
|
167
|
+
# same whichever kind of seed ran.
|
|
168
|
+
run_seed() {
|
|
169
|
+
if [ "$seed_kind" = node ]; then
|
|
170
|
+
node "$seed" "$@"
|
|
171
|
+
else
|
|
172
|
+
"$seed" "$@"
|
|
173
|
+
fi
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
seed_die() {
|
|
177
|
+
echo "bootstrap: $seed_origin $seed $1" >&2
|
|
178
|
+
echo "bootstrap: the seed is a released \`nish\` binary, or a .js/.mjs entry point run under node" >&2
|
|
179
|
+
exit 3
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
# Unset: the release scripts/fetch-seed.sh leaves in build/seed, fetched
|
|
183
|
+
# first when it is not there (a no-op, without the network, when it is).
|
|
184
|
+
if [ -z "$seed_given" ]; then
|
|
185
|
+
if ! sh scripts/fetch-seed.sh >&2 || [ ! -e build/seed/bin/nish ]; then
|
|
186
|
+
echo "bootstrap: no seed: NISH_BOOTSTRAP is unset and scripts/fetch-seed.sh could not" >&2
|
|
187
|
+
echo "bootstrap: fetch the last release into build/seed; set NISH_BOOTSTRAP=<nish>" >&2
|
|
188
|
+
exit 3
|
|
189
|
+
fi
|
|
190
|
+
seed_given="$PWD/build/seed/bin/nish"
|
|
191
|
+
seed_origin="the released seed"
|
|
192
|
+
else
|
|
193
|
+
seed_origin="NISH_BOOTSTRAP seed"
|
|
194
|
+
fi
|
|
195
|
+
seed=$seed_given
|
|
196
|
+
# The kind is decided by the extension, and deliberately not by the
|
|
197
|
+
# executable bit or by sniffing the bytes. The bit describes the download
|
|
198
|
+
# rather than the file — a binary unpacked from a release tarball or pulled
|
|
199
|
+
# out of a CI artifact can arrive without +x —
|
|
200
|
+
# whereas the suffix is the one thing whoever built the seed chose. Anything
|
|
201
|
+
# with no suffix is a binary, which is how a released `nish` arrives.
|
|
202
|
+
case "$seed" in
|
|
203
|
+
*.js|*.mjs|*.cjs) seed_kind=node; seed_label="$seed_origin (node $seed)" ;;
|
|
204
|
+
*) seed_kind=native; seed_label="$seed_origin ($seed)" ;;
|
|
205
|
+
esac
|
|
206
|
+
[ -e "$seed" ] || seed_die "does not exist"
|
|
207
|
+
[ -f "$seed" ] || seed_die "is not a file"
|
|
208
|
+
if [ "$seed_kind" = native ]; then
|
|
209
|
+
[ -x "$seed" ] || seed_die "is not executable"
|
|
210
|
+
else
|
|
211
|
+
[ -r "$seed" ] || seed_die "is not readable"
|
|
212
|
+
command -v node >/dev/null 2>&1 || seed_die "needs node on PATH, which is not there"
|
|
213
|
+
fi
|
|
214
|
+
# A seed that cannot answer `--version` cannot compile self/ either — a
|
|
215
|
+
# binary built for another platform, a .js that is not a compiler — and
|
|
216
|
+
# finding that out here names the variable and the path the caller set, where
|
|
217
|
+
# finding it out in the stage1 link names a temporary file three stages deep.
|
|
218
|
+
run_seed --version >/dev/null 2>&1 || seed_die "is not runnable (\`--version\` failed)"
|
|
219
|
+
if ! command -v clang >/dev/null 2>&1 && [ -z "${CC:-}" ]; then
|
|
220
|
+
echo "bootstrap: needs clang on PATH to link the stages (see docs/INSTALL.md)" >&2
|
|
221
|
+
exit 3
|
|
222
|
+
fi
|
|
223
|
+
|
|
224
|
+
say() { [ "$quiet" -eq 1 ] || printf '%s\n' "$*"; }
|
|
225
|
+
|
|
226
|
+
# `a` and `b` hold one `.ll` per module of `self/`. Both the module set and
|
|
227
|
+
# every byte of every module must match: a stage that emitted one module fewer
|
|
228
|
+
# has not agreed about the rest.
|
|
229
|
+
compare_ir() {
|
|
230
|
+
local a="$1" b="$2" label="$3" name
|
|
231
|
+
if ! diff <(cd "$a" && ls ./*.ll) <(cd "$b" && ls ./*.ll) >/dev/null; then
|
|
232
|
+
echo "bootstrap: $label: the two stages emitted different module sets" >&2
|
|
233
|
+
exit 1
|
|
234
|
+
fi
|
|
235
|
+
for name in "$a"/*.ll; do
|
|
236
|
+
if ! cmp -s "$name" "$b/$(basename "$name")"; then
|
|
237
|
+
echo "bootstrap: $label: $(basename "$name") differs" >&2
|
|
238
|
+
diff "$name" "$b/$(basename "$name")" | head -n 20 >&2
|
|
239
|
+
exit 1
|
|
240
|
+
fi
|
|
241
|
+
done
|
|
242
|
+
say " $label: $(ls "$a"/*.ll | wc -l | tr -d ' ') modules identical"
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
# IR(seed) vs IR(stage1), reported and never asserted. What it measures is
|
|
246
|
+
# whether codegen has moved since the seed was built, which is a fact about
|
|
247
|
+
# this release rather than a property of the bootstrap — the header says why.
|
|
248
|
+
# Nothing in here exits.
|
|
249
|
+
survey_ir() {
|
|
250
|
+
local a="$1" b="$2" total=0 differing=0 name
|
|
251
|
+
if ! diff <(cd "$a" && ls ./*.ll) <(cd "$b" && ls ./*.ll) >/dev/null; then
|
|
252
|
+
say " note: IR(seed) vs IR(stage1): the two emitted different module sets."
|
|
253
|
+
say " Not a bootstrap failure: this comparison is not asserted"
|
|
254
|
+
say " (see the header of this script)."
|
|
255
|
+
return 0
|
|
256
|
+
fi
|
|
257
|
+
for name in "$a"/*.ll; do
|
|
258
|
+
total=$((total + 1))
|
|
259
|
+
cmp -s "$name" "$b/$(basename "$name")" || differing=$((differing + 1))
|
|
260
|
+
done
|
|
261
|
+
if [ "$differing" -eq 0 ]; then
|
|
262
|
+
say " note: IR(seed) vs IR(stage1): all $total modules identical."
|
|
263
|
+
say " Not asserted: codegen simply has not moved for self/ since"
|
|
264
|
+
say " the seed was built."
|
|
265
|
+
else
|
|
266
|
+
say " note: IR(seed) vs IR(stage1): $differing of $total modules differ."
|
|
267
|
+
say " Not a bootstrap failure. Codegen has moved for self/ since the"
|
|
268
|
+
say " seed was built, which is what a release carries. See the header"
|
|
269
|
+
say " of this script and docs/wp19-stage0-retirement.md §3, G3."
|
|
270
|
+
fi
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
# Build one stage with another, at a path every stage shares, and move it into
|
|
274
|
+
# place afterwards.
|
|
275
|
+
#
|
|
276
|
+
# The shared path is what makes `stage3 == stage2` hold on Mach-O, and it is
|
|
277
|
+
# measured rather than tidy-mindedness: LC_UUID is stable across two links to
|
|
278
|
+
# one output path and differs on a link to another. Two links of one input to
|
|
279
|
+
# one path produce one UUID and byte-identical files; the same two to `.../one`
|
|
280
|
+
# and `.../two` differ in exactly those sixteen bytes. So stage2 at
|
|
281
|
+
# `$work/stage2` and stage3 at `$work/stage3` gave two different UUIDs to two
|
|
282
|
+
# compilers that had agreed about every other byte, and the comparison could
|
|
283
|
+
# only ever report that as unattributed. The output path is the leading reason
|
|
284
|
+
# and the probe cannot rule out an invocation counter -- docs/wp10-ci.md#ci-matrix
|
|
285
|
+
# has that, the offsets, and the remedy that did not work -- but the shared path
|
|
286
|
+
# was measured to work end to end either way, and build.sh's Darwin branch says
|
|
287
|
+
# why the flag that drops the load command is not the answer.
|
|
288
|
+
#
|
|
289
|
+
# It costs one rename per stage and weakens nothing: the comparison is still a
|
|
290
|
+
# raw `cmp` of the two files.
|
|
291
|
+
#
|
|
292
|
+
# stage1 is left out because nothing compares it to a binary -- `IR(seed) ==
|
|
293
|
+
# IR(stage1)` compares the .ll files its build wrote, and those are the same
|
|
294
|
+
# bytes wherever the executable landed.
|
|
295
|
+
link_stage() {
|
|
296
|
+
local by="$1" name="$2"
|
|
297
|
+
rm -rf "$work/stage" "$work/stage.modules"
|
|
298
|
+
"$by" self/compile.ts --link "$work/stage" --profile "$profile" >/dev/null
|
|
299
|
+
rm -rf "$work/$name" "$work/$name.modules"
|
|
300
|
+
mv "$work/stage" "$work/$name"
|
|
301
|
+
mv "$work/stage.modules" "$work/$name.modules"
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
mkdir -p "$work"
|
|
305
|
+
outdir=$(dirname "$out")
|
|
306
|
+
[ "$outdir" = "" ] || mkdir -p "$outdir"
|
|
307
|
+
|
|
308
|
+
# Every stage is built the same way, by the stage before it, with one
|
|
309
|
+
# `--link`: the compiler plans the output, makes `<exe>.modules/` and runs
|
|
310
|
+
# `scripts/build.sh` itself (docs/wp14-selfhost.md §7a). That leaves each
|
|
311
|
+
# stage's IR in `<exe>.modules/`, which is where the equalities read it, and
|
|
312
|
+
# means the chain exercises the same driver a user does.
|
|
313
|
+
say "stage1: self/ compiled by $seed_label"
|
|
314
|
+
rm -rf "$work/stage1.modules"
|
|
315
|
+
# This is the step the rolling freeze is enforced by, so it says so when it
|
|
316
|
+
# fails rather than leaving a reader with the compiler's own diagnostic and no
|
|
317
|
+
# idea which rule it just met.
|
|
318
|
+
if ! run_seed self/compile.ts --link "$work/stage1" --profile "$profile" >/dev/null; then
|
|
319
|
+
echo "bootstrap: $seed_label could not build stage1 from self/ (its output is above)" >&2
|
|
320
|
+
echo "bootstrap: if it refused the source, that is the freeze doing its job: a construct" >&2
|
|
321
|
+
echo "bootstrap: added in 0.N cannot be used by self/ until 0.(N+1) (docs/wp12-release.md," >&2
|
|
322
|
+
echo "bootstrap: \"The bootstrap seed\"; docs/wp19-stage0-retirement.md §3, G3)" >&2
|
|
323
|
+
exit 1
|
|
324
|
+
fi
|
|
325
|
+
|
|
326
|
+
if [ "$build_to" -ge 2 ]; then
|
|
327
|
+
say "stage2: self/ compiled by stage1"
|
|
328
|
+
link_stage "$work/stage1" stage2
|
|
329
|
+
# The seed equality, reported and not asserted.
|
|
330
|
+
[ "$verify" -eq 1 ] && survey_ir "$work/stage1.modules" "$work/stage2.modules"
|
|
331
|
+
fi
|
|
332
|
+
|
|
333
|
+
if [ "$build_to" -ge 3 ]; then
|
|
334
|
+
say "stage3: self/ compiled by stage2"
|
|
335
|
+
link_stage "$work/stage2" stage3
|
|
336
|
+
[ "$verify" -eq 1 ] && compare_ir "$work/stage2.modules" "$work/stage3.modules" "IR(stage1) == IR(stage2)"
|
|
337
|
+
# `stage3 == stage2`, in scripts/verify-binaries.sh: byte-identical, which
|
|
338
|
+
# both stages being linked at one path is what buys on Mach-O as well. Under
|
|
339
|
+
# that, on Darwin, it still asserts the size, strips debug information where
|
|
340
|
+
# a tool can strip it, and fails anything left over as *unattributed* -- a
|
|
341
|
+
# net now rather than the arm that decides a darwin row. The comparison lives
|
|
342
|
+
# in a script of its own so that tests/run.js can drive every branch of it
|
|
343
|
+
# from one machine -- its header is where the reasoning is, and NISH_UNAME_S
|
|
344
|
+
# is how the suite asks for the other platform's branch.
|
|
345
|
+
if [ "$verify" -eq 1 ]; then
|
|
346
|
+
if verdict="$(bash scripts/verify-binaries.sh "$work/stage2" "$work/stage3" 2>&1)"; then
|
|
347
|
+
printf '%s\n' "$verdict" | while IFS= read -r line; do say " $line"; done
|
|
348
|
+
else
|
|
349
|
+
printf '%s\n' "$verdict" >&2
|
|
350
|
+
exit 1
|
|
351
|
+
fi
|
|
352
|
+
fi
|
|
353
|
+
fi
|
|
354
|
+
|
|
355
|
+
cp "$work/stage$install" "$out"
|
|
356
|
+
say "bootstrap: wrote $out ($(wc -c < "$out" | tr -d ' ') bytes, stage$install, $profile profile)"
|
|
357
|
+
say " it takes -o <file.ll>, -o <dir>/, --link <exe> and --profile"
|
package/scripts/build.sh
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Build an Nish .ll module (plus optional C sources) into a native binary.
|
|
3
|
+
#
|
|
4
|
+
# scripts/build.sh <module.ll> [more .ll/.c files...] -o <out> [--profile debug|speed|size|wasm]
|
|
5
|
+
#
|
|
6
|
+
# The C runtime is three translation units and is named as one: an input
|
|
7
|
+
# <dir>/runtime.c also compiles <dir>/runtime_os.c, the half that wraps the
|
|
8
|
+
# system calls (files, directories, subprocesses, the environment, the clock),
|
|
9
|
+
# and <dir>/runtime_parallel.c, the half that divides a range of work across
|
|
10
|
+
# threads. Each of those files says why they are compiled and measured apart.
|
|
11
|
+
#
|
|
12
|
+
# Profiles:
|
|
13
|
+
# debug clang defaults: no optimisation, symbols kept. The "before" number.
|
|
14
|
+
# speed -O3 + LTO + section GC + strip. Rust `--release` equivalent.
|
|
15
|
+
# size -Oz + LTO + section GC + strip + no unwind tables. Rust
|
|
16
|
+
# `opt-level="z"`, `panic="abort"`, `strip=true` equivalent.
|
|
17
|
+
# wasm wasm32 freestanding module exporting every non-internal function;
|
|
18
|
+
# load it from Node. Add runtime/runtime_wasm.c to the inputs when a
|
|
19
|
+
# function uses arrays (the arena and the array cold paths, no libc);
|
|
20
|
+
# strings and I/O still need a WASI runtime and are not available.
|
|
21
|
+
# wasm wasm32 freestanding module exporting every non-internal function
|
|
22
|
+
# (for modules that do not use the C runtime); load it from Node.
|
|
23
|
+
# wasi wasm32-wasi command module: runtime.c linked against wasi-libc, so
|
|
24
|
+
# string programs run under any WASI host (`_start` runs `main`;
|
|
25
|
+
# `node examples/wasi-host.mjs app.wasm args...`). Needs a WASI
|
|
26
|
+
# sysroot: WASI_SYSROOT=<dir>, or /usr/lib/wasi-sysroot,
|
|
27
|
+
# /opt/wasi-sdk/share/wasi-sysroot, /usr/share/wasi-sysroot.
|
|
28
|
+
# napi Node addon (<out>.node): the speed flags plus -shared -fPIC, built
|
|
29
|
+
# against the Node headers next to `node` (override: NODE_INCLUDE=<dir
|
|
30
|
+
# containing node_api.h>). Inputs: <modules.ll> runtime/runtime.c and
|
|
31
|
+
# the shim from `nish --emit-napi`.
|
|
32
|
+
#
|
|
33
|
+
# Profile-guided optimisation (WP9), for the speed, size and napi profiles:
|
|
34
|
+
# --pgo-generate instrumented build (-fprofile-generate); running the
|
|
35
|
+
# binary writes default_*.profraw into the current
|
|
36
|
+
# directory (or $LLVM_PROFILE_FILE)
|
|
37
|
+
# --pgo-use <profdata> optimise with a merged profile (-fprofile-use=<file>)
|
|
38
|
+
# Recipe:
|
|
39
|
+
# scripts/build.sh app.ll runtime/runtime.c -o app.instr --profile speed --pgo-generate
|
|
40
|
+
# ./app.instr <typical input> # one or more training runs
|
|
41
|
+
# llvm-profdata merge -o app.profdata default_*.profraw
|
|
42
|
+
# scripts/build.sh app.ll runtime/runtime.c -o app --profile speed --pgo-use app.profdata
|
|
43
|
+
# The instrumented link needs the compiler-rt profile runtime (Ubuntu:
|
|
44
|
+
# libclang-rt-<ver>-dev; it ships with Apple clang and Homebrew llvm).
|
|
45
|
+
# docs/wp9-optimisation.md reports what PGO buys on the benchmark suite.
|
|
46
|
+
#
|
|
47
|
+
# Threads (WP20 T0): `--threads` compiles every input with -DNISH_THREADS, which
|
|
48
|
+
# makes the arena and the RNG seed in runtime/runtime.c thread-local. Pass it
|
|
49
|
+
# exactly when the IR was compiled with `nish --threads` (`nish --threads
|
|
50
|
+
# --link` does it for you): compiled modules reference `@nish_arena` as a
|
|
51
|
+
# thread-local global, and ELF will not link that against a non-TLS definition,
|
|
52
|
+
# so a half-threaded build fails at the link rather than at run time.
|
|
53
|
+
#
|
|
54
|
+
# Debug info (WP10): `-g` compiles every input with -g and skips the strip
|
|
55
|
+
# step of the speed/size/napi profiles, so the DWARF that `nish -g`
|
|
56
|
+
# put in the .ll (line table, variables) reaches the binary. `nish
|
|
57
|
+
# --link -g` passes it through automatically.
|
|
58
|
+
#
|
|
59
|
+
# Works on Linux (clang + lld preferred, GNU ld tolerated) and macOS (Apple ld64
|
|
60
|
+
# or Homebrew llvm). Set CC to pick a compiler (default: clang on PATH).
|
|
61
|
+
set -euo pipefail
|
|
62
|
+
|
|
63
|
+
profile=speed
|
|
64
|
+
out=""
|
|
65
|
+
inputs=()
|
|
66
|
+
pgo=() # -fprofile-generate / -fprofile-use=<file>
|
|
67
|
+
debug=0 # -g: keep DWARF (nish -g emits it in the IR; runtime.c gets it here)
|
|
68
|
+
threads=0 # --threads: -DNISH_THREADS, the thread-local arena (WP20 T0)
|
|
69
|
+
while [ $# -gt 0 ]; do
|
|
70
|
+
case "$1" in
|
|
71
|
+
-o) out="$2"; shift 2 ;;
|
|
72
|
+
--profile) profile="$2"; shift 2 ;;
|
|
73
|
+
-g) debug=1; shift ;;
|
|
74
|
+
--threads) threads=1; shift ;;
|
|
75
|
+
--pgo-generate) pgo=(-fprofile-generate); shift ;;
|
|
76
|
+
--pgo-use)
|
|
77
|
+
[ -f "$2" ] || { echo "error: --pgo-use: profile '$2' not found (run the instrumented binary, then llvm-profdata merge)" >&2; exit 2; }
|
|
78
|
+
pgo=("-fprofile-use=$2"); shift 2 ;;
|
|
79
|
+
-h|--help) sed -n '2,41p' "$0"; exit 0 ;;
|
|
80
|
+
*) inputs+=("$1"); shift ;;
|
|
81
|
+
esac
|
|
82
|
+
done
|
|
83
|
+
[ ${#inputs[@]} -gt 0 ] || { echo "error: no input files" >&2; exit 2; }
|
|
84
|
+
[ -n "$out" ] || { echo "error: -o <out> is required" >&2; exit 2; }
|
|
85
|
+
|
|
86
|
+
# The runtime is three translation units, and a caller names one: whoever passes
|
|
87
|
+
# <dir>/runtime.c gets <dir>/runtime_os.c and <dir>/runtime_parallel.c compiled
|
|
88
|
+
# 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
|
|
91
|
+
# would otherwise leak: `nish --link` builds its command line in
|
|
92
|
+
# self/compile.ts, the published package's recipe in every document and
|
|
93
|
+
# README names runtime.c, and a user's own clang line does too. Pairing them
|
|
94
|
+
# here keeps every one of those correct, and keeps "the runtime" one thing to
|
|
95
|
+
# name from the outside. A caller that names one itself is left alone, because
|
|
96
|
+
# naming one object twice is a duplicate-symbol error.
|
|
97
|
+
for i in ${inputs[@]+"${inputs[@]}"}; do
|
|
98
|
+
case "$i" in
|
|
99
|
+
*/runtime.c|runtime.c)
|
|
100
|
+
for half in runtime_os.c runtime_parallel.c; do
|
|
101
|
+
side="${i%runtime.c}$half"
|
|
102
|
+
have=0
|
|
103
|
+
for j in "${inputs[@]}"; do
|
|
104
|
+
if [ "$j" = "$side" ]; then have=1; fi
|
|
105
|
+
done
|
|
106
|
+
if [ "$have" = 0 ] && [ -f "$side" ]; then inputs+=("$side"); fi
|
|
107
|
+
done ;;
|
|
108
|
+
esac
|
|
109
|
+
done
|
|
110
|
+
|
|
111
|
+
CC=${CC:-clang}
|
|
112
|
+
common=(-Wno-override-module) # our IR is target-neutral; clang fills the triple in
|
|
113
|
+
elf=() # flags that only make sense for ELF targets
|
|
114
|
+
|
|
115
|
+
# Platform-specific dead-stripping, symbol stripping and LTO linker selection.
|
|
116
|
+
case "$(uname -s)" in
|
|
117
|
+
Darwin)
|
|
118
|
+
# ld64: -dead_strip is the --gc-sections equivalent, -x drops local symbols
|
|
119
|
+
# (-s is deprecated on macOS). No -fuse-ld=lld: ld64 does LTO natively.
|
|
120
|
+
#
|
|
121
|
+
# NOT -no_uuid, however tempting it looks from the reproducibility side.
|
|
122
|
+
# LC_UUID is what made two links of one input differ here -- measured, on
|
|
123
|
+
# both darwin rows (docs/wp10-ci.md#ci-matrix) -- and dropping it does make
|
|
124
|
+
# them identical, and the binary then does not run: on arm64 dyld refuses
|
|
125
|
+
# an image with no LC_UUID outright, `missing LC_UUID load command`
|
|
126
|
+
# followed by SIGABRT, so the stage1 that linked went on to abort the
|
|
127
|
+
# moment the bootstrap ran it. Measured too, on the run after the one that
|
|
128
|
+
# named the UUID. An unloadable compiler is worse than any reproducibility,
|
|
129
|
+
# so the UUID stays. The ELF branch's --build-id=none below is the flag
|
|
130
|
+
# this would have been; ELF has no loader that insists on one.
|
|
131
|
+
#
|
|
132
|
+
# It costs nothing, because the UUID was measured stable across two links
|
|
133
|
+
# of one input to one output path, and differing on a link to another. That
|
|
134
|
+
# is what scripts/bootstrap.sh links every comparable stage at one path for,
|
|
135
|
+
# and it is why `stage3 == stage2` holds on Mach-O with the load command in.
|
|
136
|
+
gc=(-Wl,-dead_strip); strip_flag=(-Wl,-x) ;;
|
|
137
|
+
*)
|
|
138
|
+
gc=(-Wl,--gc-sections -Wl,--as-needed -Wl,-O2 -Wl,--build-id=none); strip_flag=(-s)
|
|
139
|
+
elf=(-fno-plt)
|
|
140
|
+
# GNU ld needs the gold plugin for LTO; prefer lld when clang can find it.
|
|
141
|
+
if command -v ld.lld >/dev/null 2>&1; then common+=(-fuse-ld=lld); fi ;;
|
|
142
|
+
esac
|
|
143
|
+
|
|
144
|
+
# -g: compile everything with debug info and never strip, whatever the profile,
|
|
145
|
+
# so the line table nish emitted survives into the binary.
|
|
146
|
+
if [ "$debug" = 1 ]; then common+=(-g); strip_flag=(); fi
|
|
147
|
+
|
|
148
|
+
# --threads: the storage class of the arena is ABI, so every input is compiled
|
|
149
|
+
# with the same macro -- C runtime and generated N-API shim alike.
|
|
150
|
+
#
|
|
151
|
+
# -ftls-model=initial-exec names, for the C side, the model the IR already
|
|
152
|
+
# names. Without it a -fPIC build (the napi profile) reaches the arena through
|
|
153
|
+
# a __tls_get_addr call, so the two halves of one inlined allocator would use
|
|
154
|
+
# two different models; it is also smaller (4,759 bytes of runtime.c `.text*`
|
|
155
|
+
# against 4,820 at -Oz -fPIC) and free where the model was local-exec anyway.
|
|
156
|
+
#
|
|
157
|
+
# `tls` is the same pair again for the wasm and wasi profiles, which build their
|
|
158
|
+
# own command line instead of using `common`; it is empty on every ordinary
|
|
159
|
+
# build, hence the bash 3.2 expansion spelling explained below.
|
|
160
|
+
#
|
|
161
|
+
# -pthread is for runtime_parallel.c, the translation unit that divides a range
|
|
162
|
+
# of work across threads: it is compiled in either configuration and only spawns
|
|
163
|
+
# under this macro, so this is the build where the flag has to be on the command
|
|
164
|
+
# line. On a current glibc the library half is already inside libc and the link
|
|
165
|
+
# would succeed without it; passing it is what makes that an implementation
|
|
166
|
+
# detail rather than something the build depends on.
|
|
167
|
+
#
|
|
168
|
+
# It is deliberately in `common` and not in `tls`: `tls` is the wasm and wasi
|
|
169
|
+
# command lines, which have no threads to link against, and where
|
|
170
|
+
# runtime_parallel.c compiles to its sequential fallback because it tests
|
|
171
|
+
# __wasi__ and __wasm__ as well as the macro.
|
|
172
|
+
tls=()
|
|
173
|
+
if [ "$threads" = 1 ]; then
|
|
174
|
+
common+=(-DNISH_THREADS=1 -ftls-model=initial-exec -pthread)
|
|
175
|
+
tls=(-DNISH_THREADS=1 -ftls-model=initial-exec)
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
# The ${arr[@]+"${arr[@]}"} spelling below is not a style tic: macOS ships bash
|
|
179
|
+
# 3.2 (Apple will not ship GPLv3), where expanding an empty array as "${arr[@]}"
|
|
180
|
+
# under `set -u` is a fatal "unbound variable" -- bash 4.4 made it legal, which is
|
|
181
|
+
# why Linux never noticed. `pgo`, `elf`, `strip_flag` and `libs` are all empty on
|
|
182
|
+
# ordinary builds, so please do not simplify these back.
|
|
183
|
+
case "$profile" in
|
|
184
|
+
debug)
|
|
185
|
+
"$CC" "${common[@]}" "${inputs[@]}" -lm -o "$out" ;;
|
|
186
|
+
speed)
|
|
187
|
+
"$CC" "${common[@]}" -O3 -flto -DNDEBUG ${pgo[@]+"${pgo[@]}"} \
|
|
188
|
+
-ffunction-sections -fdata-sections -fomit-frame-pointer \
|
|
189
|
+
-fno-asynchronous-unwind-tables -fno-unwind-tables ${elf[@]+"${elf[@]}"} \
|
|
190
|
+
"${gc[@]}" ${strip_flag[@]+"${strip_flag[@]}"} "${inputs[@]}" -lm -o "$out" ;;
|
|
191
|
+
size)
|
|
192
|
+
"$CC" "${common[@]}" -Oz -flto -DNDEBUG ${pgo[@]+"${pgo[@]}"} \
|
|
193
|
+
-ffunction-sections -fdata-sections -fomit-frame-pointer \
|
|
194
|
+
-fno-asynchronous-unwind-tables -fno-unwind-tables ${elf[@]+"${elf[@]}"} \
|
|
195
|
+
-fno-stack-protector -fvisibility=hidden \
|
|
196
|
+
"${gc[@]}" ${strip_flag[@]+"${strip_flag[@]}"} "${inputs[@]}" -lm -o "$out" ;;
|
|
197
|
+
wasm)
|
|
198
|
+
# clang resolves wasm-ld next to its own binary first, then on PATH; ask it
|
|
199
|
+
# rather than probing PATH so a Homebrew llvm without a PATH entry still works.
|
|
200
|
+
wasm_ld=$("$CC" -print-prog-name=wasm-ld)
|
|
201
|
+
if [ ! -x "$wasm_ld" ]; then
|
|
202
|
+
echo "error: the wasm profile needs wasm-ld (install lld; on macOS: brew install llvm@18)" >&2
|
|
203
|
+
exit 2
|
|
204
|
+
fi
|
|
205
|
+
# -mbulk-memory lowers llvm.memset/memcpy (`new Array<T>(n)`, `push` growth) to the
|
|
206
|
+
# memory.fill/memory.copy instructions instead of libc calls the freestanding link lacks.
|
|
207
|
+
"$CC" -Wno-override-module --target=wasm32-unknown-unknown -Oz -nostdlib -mbulk-memory \
|
|
208
|
+
${tls[@]+"${tls[@]}"} \
|
|
209
|
+
-Wl,--no-entry -Wl,--export-all -Wl,--strip-all -Wl,--gc-sections \
|
|
210
|
+
"${inputs[@]}" -o "$out" ;;
|
|
211
|
+
wasi)
|
|
212
|
+
sysroot=${WASI_SYSROOT:-}
|
|
213
|
+
for d in /usr/lib/wasi-sysroot /opt/wasi-sdk/share/wasi-sysroot /usr/share/wasi-sysroot; do
|
|
214
|
+
[ -n "$sysroot" ] || { [ -d "$d" ] && sysroot=$d; }
|
|
215
|
+
done
|
|
216
|
+
if [ -z "$sysroot" ] || [ ! -d "$sysroot/lib" ]; then
|
|
217
|
+
echo "error: the wasi profile needs a WASI sysroot (wasi-libc headers and libc.a) and none was found." >&2
|
|
218
|
+
echo " Install wasi-sdk's sysroot (https://github.com/WebAssembly/wasi-sdk/releases: wasi-sysroot-<ver>.tar.gz," >&2
|
|
219
|
+
echo " or apt install wasi-libc) and set WASI_SYSROOT=<dir> if it is not in one of the default locations" >&2
|
|
220
|
+
echo " (/usr/lib/wasi-sysroot, /opt/wasi-sdk/share/wasi-sysroot, /usr/share/wasi-sysroot). See docs/INSTALL.md." >&2
|
|
221
|
+
exit 2
|
|
222
|
+
fi
|
|
223
|
+
wasm_ld=$("$CC" -print-prog-name=wasm-ld)
|
|
224
|
+
if [ ! -x "$wasm_ld" ]; then
|
|
225
|
+
echo "error: the wasi profile needs wasm-ld (install lld; on macOS: brew install llvm@18)" >&2
|
|
226
|
+
exit 2
|
|
227
|
+
fi
|
|
228
|
+
# clang links compiler-rt's wasm32 builtins from its resource dir (wasi-sdk and Debian's
|
|
229
|
+
# libclang-rt-<ver>-dev-wasm32 put it there); wasi-libc's strtoll needs its __multi3.
|
|
230
|
+
# Otherwise look for wasi-sdk's separate libclang_rt.builtins-wasm32-wasi-<ver>.tar.gz
|
|
231
|
+
# unpacked next to the sysroot, or WASI_BUILTINS=<file>, and name libc explicitly
|
|
232
|
+
# (wasi-libc's libc.a includes libm) so clang stops looking for the archive itself.
|
|
233
|
+
libs=()
|
|
234
|
+
if [ ! -f "$("$CC" -print-resource-dir)/lib/wasi/libclang_rt.builtins-wasm32.a" ]; then
|
|
235
|
+
for f in "${WASI_BUILTINS:-}" "$sysroot/lib/wasm32-wasi/libclang_rt.builtins-wasm32.a" \
|
|
236
|
+
"$sysroot/lib/libclang_rt.builtins-wasm32.a" "$sysroot"/../libclang_rt.builtins-wasm32*/libclang_rt.builtins-wasm32.a; do
|
|
237
|
+
if [ -n "$f" ] && [ -f "$f" ]; then libs=(-nodefaultlibs -lc "$f"); break; fi
|
|
238
|
+
done
|
|
239
|
+
if [ ${#libs[@]} -eq 0 ]; then
|
|
240
|
+
echo "error: the wasi profile needs compiler-rt's wasm32 builtins (libclang_rt.builtins-wasm32.a) and clang has none." >&2
|
|
241
|
+
echo " Install libclang-rt-<ver>-dev-wasm32, or unpack wasi-sdk's libclang_rt.builtins-wasm32-wasi-<ver>.tar.gz" >&2
|
|
242
|
+
echo " into $sysroot/lib/wasm32-wasi/ or point WASI_BUILTINS at the .a file. See docs/INSTALL.md." >&2
|
|
243
|
+
exit 2
|
|
244
|
+
fi
|
|
245
|
+
fi
|
|
246
|
+
"$CC" -Wno-override-module --target=wasm32-wasi --sysroot="$sysroot" -Oz -DNDEBUG \
|
|
247
|
+
${tls[@]+"${tls[@]}"} \
|
|
248
|
+
-ffunction-sections -fdata-sections -Wl,--gc-sections -Wl,--strip-all \
|
|
249
|
+
"${inputs[@]}" ${libs[@]+"${libs[@]}"} -o "$out" ;;
|
|
250
|
+
napi)
|
|
251
|
+
# Node ships its C headers next to the binary: <prefix>/bin/node and
|
|
252
|
+
# <prefix>/include/node/node_api.h (official tarballs, nvm, fnm, volta).
|
|
253
|
+
# Distro packages put them in libnode-dev; NODE_INCLUDE overrides the guess.
|
|
254
|
+
node_bin=${NODE:-node}
|
|
255
|
+
node_inc=${NODE_INCLUDE:-}
|
|
256
|
+
if [ -z "$node_inc" ]; then
|
|
257
|
+
node_inc=$("$node_bin" -p "require('path').dirname(process.execPath) + '/../include/node'" 2>/dev/null || true)
|
|
258
|
+
fi
|
|
259
|
+
if [ -z "$node_inc" ] || [ ! -f "$node_inc/node_api.h" ]; then
|
|
260
|
+
echo "error: the napi profile needs the Node headers, but ${node_inc:-<node not found>}/node_api.h does not exist." >&2
|
|
261
|
+
echo " Install Node from nodejs.org/nvm (they ship include/node), or apt install libnode-dev and" >&2
|
|
262
|
+
echo " set NODE_INCLUDE=/usr/include/node (any directory that contains node_api.h)." >&2
|
|
263
|
+
exit 2
|
|
264
|
+
fi
|
|
265
|
+
shared=(-shared -fPIC)
|
|
266
|
+
# macOS: the napi_* symbols come from the node binary at load time, so the
|
|
267
|
+
# linker must not insist on resolving them. ELF shared objects allow this.
|
|
268
|
+
case "$(uname -s)" in Darwin) shared+=(-Wl,-undefined,dynamic_lookup) ;; esac
|
|
269
|
+
# runtime/nish.h is the public ABI header the generated shim includes.
|
|
270
|
+
runtime_inc="$(cd "$(dirname "$0")/../runtime" && pwd)"
|
|
271
|
+
"$CC" "${common[@]}" -O3 -flto -DNDEBUG ${pgo[@]+"${pgo[@]}"} -I"$node_inc" -I"$runtime_inc" \
|
|
272
|
+
-ffunction-sections -fdata-sections -fomit-frame-pointer \
|
|
273
|
+
-fno-asynchronous-unwind-tables -fno-unwind-tables ${elf[@]+"${elf[@]}"} \
|
|
274
|
+
"${shared[@]}" "${gc[@]}" ${strip_flag[@]+"${strip_flag[@]}"} "${inputs[@]}" -lm -o "$out" ;;
|
|
275
|
+
*) echo "error: unknown profile '$profile'" >&2; exit 2 ;;
|
|
276
|
+
esac
|
|
277
|
+
|
|
278
|
+
# `wc -c` pads with spaces on macOS; strip them so the number is clean.
|
|
279
|
+
printf '%s: %s bytes (%s)\n' "$out" "$(wc -c < "$out" | tr -d ' ')" "$profile"
|