1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//! Names and ids the compile boundary agrees on with the island.
//!
//! Everything here is a *convention*, not a translation: how a user-defined
//! function's LLVM symbol is spelled, how an `rt_llvm_call` operation is
//! numbered, which builtin methods bypass the generic `assoc` path, and the
//! two trait-object id tables a translation is handed. `compiler.rs`'s own
//! Lisp source mangles and re-derives these same strings and numbers, so a
//! change on either side has to be matched on the other — which is exactly
//! why they live in one small module of their own rather than inside the
//! translator that happens to use them most.
//!
//! Extracted from `ast_bridge` when that translator was deleted (Phase 2
//! Stage C): none of this ever touched the typed AST, so none of it went with
//! it.
use HashMap;
use ;
use cratePath;
use ConcreteNames;
/// The two trait-object id tables a translation reads, interned by `Interp`
/// *before* translation starts: both are baked into the emitted IR as
/// constants (`(dyn-new id ...)`, `(dyn-upcast trait-id ...)`), so there is
/// nothing to resolve lazily mid-translation, exactly like [`Ctx::globals`].
///
/// One parameter rather than two because every translation entry point and
/// every nested-scope hand-off needs both, and they are interned together
/// (`Interp::register_dyn_box`).
/// A process-wide counter for synthesizing unique LLVM symbol names for
/// anonymous functions — every `lambda`/`fnref`-forwarding-wrapper/
/// immediately-invoked-lambda gets one (labels/closures Stage 4), since none
/// of those have a name from user source the way a `labels` def or `defun`
/// does. Process-wide (not per-translation-pass or per-outer-function) so
/// two lambdas compiled into the same shared AOT module — even from two
/// *different* `defun`s — can never collide, with no mangling scheme to get
/// right.
static LAMBDA_COUNTER: AtomicU64 = new;
/// The LLVM symbol a call to `path` must name: the `typelisp-rt` shim, when
/// `path` is a free builtin with no typelisp body
/// ([`crate::compile::externs::builtin_shim`]), and otherwise the mangled
/// name of the compiled function.
///
/// The one place that choice is made. `core_bridge` writes the answer into
/// the node it hands the island, which looks the name up and calls it —
/// there is no second derivation on the island side to disagree with this
/// one.
/// The LLVM symbol `target`'s compiled body is defined under — the same
/// string a *call site* emits for it (`core_bridge::translate_call`'s
/// [`callee_symbol_name`], `translate_assoc`'s [`ConcreteNames::method_symbol`]),
/// so a body and the calls to it can't be declared under two different names.
///
/// One function for every place that builds or loads a precompiled library —
/// the generators in [`crate::compile::bootstrap`] and
/// [`crate::compile::prelude_bootstrap`], the JIT and `compile-file` — so they
/// cannot come to spell a definition two ways.
/// `Vector<T>`'s builtin methods that have no compiled `defmethod` body and so
/// must be lowered to a dedicated `vector-op` node (`translate_vector_method`)
/// rather than routed through the generic `assoc` path. Deliberately *not*
/// `iter`: that one is a real prelude `defmethod` (`vector-iter::new`), a
/// normal compiled method, so it stays on the `assoc` path. `HashTable<K,V>`
/// shares the `new`/`get`/`set` names but a different `type_name`, so the
/// [`path_is_builtin`] guard at the call site keeps them apart.
pub const VECTOR_BUILTIN_METHODS: = ;
/// `HashTable<K,V>`'s builtin methods lowered to a `hashtable-op` node
/// (`translate_hashtable_method` -> the `rt_hashtable_*` family) rather than
/// the generic `assoc` path — every one except `iter`, `get`, `set`,
/// `remove`, `maphash` and `size`, which are genuine prelude `defmethod`s.
///
/// `get`/`set`/`remove` were here once, as three whole-lookup shims. They
/// could not stay: looking a key up means hashing it and comparing it, and
/// both of those are the key type's own `sxhash`/`equals` — typelisp
/// methods a Rust shim cannot call. The bucket primitives take the hash the
/// prelude computed and an index it found, which is the part this layer can
/// answer on its own.
pub const HASHTABLE_BUILTIN_METHODS: = ;
/// The stable operation id compiled code passes as `rt_llvm_call`'s first
/// argument: FNV-1a over `"type-key::method"`, folded into a tagged-`Sexpr`
/// integer's 61-bit signed payload. A *hash*, not a table index, so the id
/// embedded in a committed compiler-island bitcode artifact (interp-closure
/// removal Stage 3) survives methods being added to or removed from the
/// table in a later build — an index would silently shift. Collisions are
/// checked once at table construction (`interp`'s `llvm_op_table`), which
/// panics rather than dispatching two methods through one id.
///
/// The `<< 3 >> 3` fold is load-bearing, not cosmetic: this id is baked into
/// the `llvm-op` node as a `Sexpr` `Int`, and the *compiled* island reads
/// `Sexpr` ints back in the 3-bit-tagged representation (`raw >> 3`), which
/// would silently drop the top 3 bits of a full-width hash — so a
/// natively-compiled `rt_llvm_call` would pass a truncated id the table (keyed
/// on the full hash) has no entry for, aborting at runtime. Folding here means
/// the id already fits 61 bits, so it round-trips identically through the
/// tagged compiled path and the interpreter's full-width `Value::Int`, and the
/// table (built from this same function) keys on exactly what compiled code
/// passes. (A change to this fold requires regenerating `compiler_island.bc`,
/// whose own native-scope ops carry ids baked by it — the freshness test
/// catches a stale artifact.)