Skip to main content

plg_runtime/
abi.rs

1//! The C-ABI surface generated code calls into (`plg_rt_*`).
2//!
3//! Every function here matches a declaration emitted by plgc codegen;
4//! the pairing is documented in docs/design/RUNTIME_ABI.md and protected
5//! by the compiler↔runtime version sync in crates/compiler/build.rs.
6//!
7//! Safety: all `m` pointers originate from `plg_rt_init` and live for
8//! the whole process; generated code is single-threaded.
9
10use crate::cell;
11use crate::machine::{ContFn, Machine};
12
13#[inline]
14fn mref<'a>(m: *mut Machine) -> &'a mut Machine {
15    unsafe { &mut *m }
16}
17
18/// Bump the step counter. Returns 0 when the limit is exceeded (the
19/// uncatchable resource error is set; generated code returns 0).
20#[unsafe(no_mangle)]
21pub extern "C" fn plg_rt_step(m: *mut Machine) -> i32 {
22    mref(m).step() as i32
23}
24
25/// Fresh unbound variable; returns its REF word.
26#[unsafe(no_mangle)]
27pub extern "C" fn plg_rt_new_var(m: *mut Machine) -> u64 {
28    mref(m).new_var()
29}
30
31/// Allocate an `n`-cell continuation frame; returns the base index.
32#[unsafe(no_mangle)]
33pub extern "C" fn plg_rt_frame_alloc(m: *mut Machine, n: u32) -> u64 {
34    mref(m).frame_alloc(n as usize) as u64
35}
36
37#[unsafe(no_mangle)]
38pub extern "C" fn plg_rt_frame_set(m: *mut Machine, base: u64, i: u32, w: u64) {
39    mref(m).heap[base as usize + i as usize] = w;
40}
41
42#[unsafe(no_mangle)]
43pub extern "C" fn plg_rt_frame_get(m: *mut Machine, base: u64, i: u32) -> u64 {
44    mref(m).heap[base as usize + i as usize]
45}
46
47#[unsafe(no_mangle)]
48pub extern "C" fn plg_rt_areg_get(m: *mut Machine, i: u32) -> u64 {
49    mref(m).areg[i as usize]
50}
51
52#[unsafe(no_mangle)]
53pub extern "C" fn plg_rt_areg_set(m: *mut Machine, i: u32, w: u64) {
54    mref(m).areg[i as usize] = w;
55}
56
57#[unsafe(no_mangle)]
58pub extern "C" fn plg_rt_breg_set(m: *mut Machine, i: u32, w: u64) {
59    mref(m).breg[i as usize] = w;
60}
61
62/// Build a compound from breg[0..arity]; returns its STR word.
63#[unsafe(no_mangle)]
64pub extern "C" fn plg_rt_put_struct(m: *mut Machine, functor: u32, arity: u32) -> u64 {
65    let m = mref(m);
66    let idx = m.heap.len();
67    m.heap.push(cell::pack_functor(functor, arity));
68    for i in 0..arity as usize {
69        let w = m.breg[i];
70        m.heap.push(w);
71    }
72    cell::make(cell::TAG_STR, idx as u64)
73}
74
75#[unsafe(no_mangle)]
76pub extern "C" fn plg_rt_put_list(m: *mut Machine, head: u64, tail: u64) -> u64 {
77    let m = mref(m);
78    let idx = m.heap.len();
79    m.heap.push(head);
80    m.heap.push(tail);
81    cell::make(cell::TAG_LST, idx as u64)
82}
83
84#[unsafe(no_mangle)]
85pub extern "C" fn plg_rt_put_float(m: *mut Machine, bits: u64) -> u64 {
86    let m = mref(m);
87    let idx = m.heap.len();
88    m.heap.push(bits);
89    cell::make(cell::TAG_FLT, idx as u64)
90}
91
92/// Box an i64 outside the i61 immediate range (BIG cell).
93#[unsafe(no_mangle)]
94pub extern "C" fn plg_rt_put_big(m: *mut Machine, value: i64) -> u64 {
95    let m = mref(m);
96    let idx = m.heap.len();
97    m.heap.push(value as u64);
98    cell::make(cell::TAG_BIG, idx as u64)
99}
100
101/// Generic unification; returns 1 on success.
102#[unsafe(no_mangle)]
103pub extern "C" fn plg_rt_unify(m: *mut Machine, a: u64, b: u64) -> i32 {
104    crate::unify::unify(mref(m), a, b) as i32
105}
106
107/// Set the success continuation (k_fn passed as a raw pointer-sized
108/// integer in IR; the transmute re-types it).
109#[unsafe(no_mangle)]
110pub extern "C" fn plg_rt_set_k(m: *mut Machine, k_fn: u64, k_env: u64) {
111    let m = mref(m);
112    m.k_fn = unsafe { std::mem::transmute::<usize, ContFn>(k_fn as usize) };
113    m.k_env = k_env;
114}
115
116#[unsafe(no_mangle)]
117pub extern "C" fn plg_rt_k_fn(m: *mut Machine) -> u64 {
118    mref(m).k_fn as usize as u64
119}
120
121#[unsafe(no_mangle)]
122pub extern "C" fn plg_rt_k_env(m: *mut Machine) -> u64 {
123    mref(m).k_env
124}
125
126/// Push a choice point whose retry re-enters generated code.
127#[unsafe(no_mangle)]
128pub extern "C" fn plg_rt_push_cp(m: *mut Machine, retry: u64, env: u64) {
129    let m = mref(m);
130    let retry = unsafe { std::mem::transmute::<usize, ContFn>(retry as usize) };
131    m.push_cp(retry, env);
132}
133
134/// Always-fail predicate body: the target for `:- dynamic` predicates
135/// with no clauses (silent-fail linter contract).
136#[unsafe(no_mangle)]
137pub extern "C" fn plg_rt_pred_fail(_m: *mut Machine, _env: u64) -> i32 {
138    0
139}
140
141/// Raise existence_error(procedure, F/A) — the compiled stub for body
142/// goals that reference a predicate with no clauses (and the runtime
143/// path for unknown query goals shares the message shape).
144#[unsafe(no_mangle)]
145pub extern "C" fn plg_rt_existence_error(
146    m: *mut Machine,
147    functor: u32,
148    arity: u32,
149    site_id: u32,
150) -> i32 {
151    let _site = crate::machine::ErrorSiteGuard::enter(m, site_id);
152    let m = mref(m);
153    let name = m.atoms.resolve(functor).to_string();
154    crate::errors::existence_procedure(m, &name, arity);
155    0
156}
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161    use plg_shared::StringInterner;
162
163    #[test]
164    fn put_struct_consumes_breg() {
165        let mut m = Machine::new(StringInterner::new(), Vec::new());
166        let mp = &mut *m as *mut Machine;
167        plg_rt_breg_set(mp, 0, cell::make_atom(1));
168        plg_rt_breg_set(mp, 1, cell::make_int(7));
169        let w = plg_rt_put_struct(mp, 9, 2);
170        assert_eq!(cell::tag_of(w), cell::TAG_STR);
171        let idx = cell::payload(w) as usize;
172        assert_eq!(cell::unpack_functor(m.heap[idx]), (9, 2));
173        assert_eq!(m.heap[idx + 1], cell::make_atom(1));
174        assert_eq!(m.heap[idx + 2], cell::make_int(7));
175    }
176
177    #[test]
178    fn frames_roundtrip() {
179        let mut m = Machine::new(StringInterner::new(), Vec::new());
180        let mp = &mut *m as *mut Machine;
181        let f = plg_rt_frame_alloc(mp, 3);
182        plg_rt_frame_set(mp, f, 2, 99);
183        assert_eq!(plg_rt_frame_get(mp, f, 2), 99);
184    }
185
186    #[test]
187    fn k_roundtrips_through_u64() {
188        let mut m = Machine::new(StringInterner::new(), Vec::new());
189        let mp = &mut *m as *mut Machine;
190        let k = plg_rt_pred_fail as *const () as usize as u64;
191        plg_rt_set_k(mp, k, 42);
192        assert_eq!(plg_rt_k_fn(mp), k);
193        assert_eq!(plg_rt_k_env(mp), 42);
194    }
195}