1pub mod args;
2#[macro_use]
3pub mod arithmetic_ops;
4pub mod attributed_variables;
5pub mod code_walker;
6#[macro_use]
7pub mod loader;
8pub mod compile;
9pub mod config;
10pub mod copier;
11pub mod cycle_detection;
12pub mod disjuncts;
13pub mod dispatch;
14pub mod gc;
15pub mod heap;
16pub mod lib_machine;
17pub mod load_state;
18pub mod machine_errors;
19pub mod machine_indices;
20pub mod machine_state;
21pub mod machine_state_impl;
22pub mod mock_wam;
23pub mod partial_string;
24pub mod preprocessor;
25pub mod stack;
26pub mod streams;
27pub mod system_calls;
28pub mod term_stream;
29pub mod unify;
30
31use crate::arena::*;
32use crate::arithmetic::*;
33use crate::atom_table::*;
34#[cfg(feature = "ffi")]
35use crate::ffi::ForeignFunctionTable;
36use crate::forms::*;
37use crate::instructions::*;
38use crate::machine::args::*;
39use crate::machine::compile::*;
40use crate::machine::copier::*;
41use crate::machine::heap::*;
42use crate::machine::loader::*;
43use crate::machine::machine_errors::*;
44use crate::machine::machine_indices::*;
45use crate::machine::machine_state::*;
46use crate::machine::stack::*;
47use crate::machine::streams::*;
48use crate::offset_table::*;
49use crate::parser::ast::*;
50use crate::parser::dashu::{Integer, Rational};
51use crate::types::*;
52
53use indexmap::IndexMap;
54use lazy_static::lazy_static;
55use ordered_float::OrderedFloat;
56
57use rand::rngs::StdRng;
58use std::borrow::Cow;
59use std::cmp::Ordering;
60use std::env;
61use std::io::Read;
62use std::path::PathBuf;
63use std::sync::atomic::AtomicBool;
64use std::sync::OnceLock;
65
66lazy_static! {
67 pub static ref INTERRUPT: AtomicBool = AtomicBool::new(false);
68}
69
70#[derive(Debug)]
74pub struct Machine {
75 pub(super) machine_st: MachineState,
76 pub(super) indices: IndexStore,
77 pub(super) code: Code,
78 pub(super) user_input: Stream,
79 pub(super) user_output: Stream,
80 pub(super) user_error: Stream,
81 pub(super) load_contexts: Vec<LoadContext>,
82 #[cfg(feature = "ffi")]
83 pub(super) foreign_function_table: ForeignFunctionTable,
84 pub(super) rng: StdRng,
85}
86
87#[derive(Debug)]
88pub struct LoadContext {
89 pub(super) path: PathBuf,
90 pub(super) stream: Stream,
91 pub(super) module: Atom,
92}
93
94impl LoadContext {
95 #[inline]
96 fn new(path: &str, stream: Stream) -> Self {
97 let mut path_buf = PathBuf::from(path);
98
99 if path_buf.is_relative() {
100 let mut current_dir = current_dir();
101 current_dir.push(path_buf);
102 path_buf = current_dir;
103 }
104
105 LoadContext {
106 path: path_buf,
107 stream,
108 module: atom!("user"),
109 }
110 }
111}
112
113#[inline]
114fn current_dir() -> PathBuf {
115 if !cfg!(miri) {
116 env::current_dir().unwrap_or(PathBuf::from("./"))
117 } else {
118 PathBuf::from("./")
119 }
120}
121
122mod libraries {
123 use indexmap::IndexMap;
124 use std::sync::LazyLock;
125
126 static LIBRARIES: LazyLock<IndexMap<&'static str, &'static str>> = LazyLock::new(|| {
127 let mut m = IndexMap::new();
128
129 include!(concat!(env!("OUT_DIR"), "/libraries.rs"));
130
131 m
132 });
133
134 pub(crate) fn contains(name: &str) -> bool {
135 LIBRARIES.contains_key(name)
136 }
137
138 pub(crate) fn get(name: &str) -> Option<&'static str> {
139 LIBRARIES.get(name).copied()
140 }
141}
142
143pub static BREAK_FROM_DISPATCH_LOOP_LOC: usize = 0;
144pub static INSTALL_VERIFY_ATTR_INTERRUPT: usize = 1;
145pub static VERIFY_ATTR_INTERRUPT_LOC: usize = 2;
146pub static LIB_QUERY_SUCCESS: usize = 3;
147
148pub struct MachinePreludeView<'a> {
149 pub indices: &'a mut IndexStore,
150 pub code: &'a mut Code,
151 pub load_contexts: &'a mut Vec<LoadContext>,
152}
153
154pub(crate) fn import_builtin_impls(code_dir: &CodeDir, builtins: &mut Module) {
155 let keys = [
156 (atom!("@>"), 2),
157 (atom!("@<"), 2),
158 (atom!("@>="), 2),
159 (atom!("@=<"), 2),
160 (atom!("=="), 2),
161 (atom!("\\=="), 2),
162 (atom!(">"), 2),
163 (atom!("<"), 2),
164 (atom!(">="), 2),
165 (atom!("=<"), 2),
166 (atom!("=:="), 2),
167 (atom!("=\\="), 2),
168 (atom!("is"), 2),
169 (atom!("acyclic_term"), 1),
170 (atom!("arg"), 3),
171 (atom!("compare"), 3),
172 (atom!("copy_term"), 2),
173 (atom!("functor"), 3),
174 (atom!("ground"), 1),
175 (atom!("keysort"), 2),
176 (atom!("read"), 1),
177 (atom!("sort"), 2),
178 (atom!("$call"), 1),
179 (atom!("$call"), 2),
180 (atom!("$call"), 3),
181 (atom!("$call"), 4),
182 (atom!("$call"), 5),
183 (atom!("$call"), 6),
184 (atom!("$call"), 7),
185 (atom!("$call"), 8),
186 (atom!("$call"), 9),
187 (atom!("atom"), 1),
188 (atom!("atomic"), 1),
189 (atom!("compound"), 1),
190 (atom!("integer"), 1),
191 (atom!("number"), 1),
192 (atom!("rational"), 1),
193 (atom!("float"), 1),
194 (atom!("nonvar"), 1),
195 (atom!("var"), 1),
196 ];
197
198 for key in keys {
199 let idx = code_dir.get(&key).unwrap();
200 builtins.code_dir.insert(key, *idx);
201 builtins
202 .module_decl
203 .exports
204 .push(ModuleExport::PredicateKey(key));
205 }
206}
207
208#[inline]
209pub(crate) fn get_structure_index(value: HeapCellValue) -> Option<CodeIndex> {
210 read_heap_cell!(value,
211 (HeapCellValueTag::CodeIndexOffset, offset) => {
212 return Some(CodeIndex::from(offset));
213 }
214 _ => {
215 }
216 );
217
218 None
219}
220
221impl Machine {
222 #[inline]
223 fn prelude_view_and_machine_st(&mut self) -> (MachinePreludeView<'_>, &mut MachineState) {
224 (
225 MachinePreludeView {
226 indices: &mut self.indices,
227 code: &mut self.code,
228 load_contexts: &mut self.load_contexts,
229 },
230 &mut self.machine_st,
231 )
232 }
233
234 pub fn get_inference_count(&mut self) -> u64 {
236 self.machine_st
237 .cwil
238 .global_count
239 .clone()
240 .try_into()
241 .unwrap()
242 }
243
244 pub(crate) fn run_module_predicate(
250 &mut self,
251 module_name: Atom,
252 key: PredicateKey,
253 ) -> std::process::ExitCode {
254 if let Some(module) = self.indices.modules.get(&module_name) {
255 if let Some(code_idx) = module.code_dir.get(&key) {
256 let index_ptr = self
257 .machine_st
258 .arena
259 .code_index_tbl
260 .get_entry(code_idx.into());
261 let p = index_ptr.local().unwrap();
262
263 self.allocate_stub_choice_point();
265
266 self.machine_st.cp = BREAK_FROM_DISPATCH_LOOP_LOC;
267 self.machine_st.p = p;
268
269 return self.dispatch_loop();
270 }
271 }
272
273 unreachable!();
274 }
275
276 fn load_file(&mut self, path: &str, stream: Stream) {
277 self.machine_st.registers[1] = stream.into();
278 self.machine_st.registers[2] =
279 atom_as_cell!(AtomTable::build_with(&self.machine_st.atom_tbl, path));
280
281 self.run_module_predicate(atom!("loader"), (atom!("file_load"), 2));
282 }
283
284 fn load_top_level(&mut self, program: Cow<'static, str>) {
285 let mut path_buf = current_dir();
286
287 path_buf.push("src/toplevel.pl");
288
289 let path = path_buf.to_str().unwrap();
290 let toplevel_stream = match program {
291 Cow::Borrowed(s) => Stream::from_static_string(s, &mut self.machine_st.arena),
292 Cow::Owned(s) => Stream::from_owned_string(s, &mut self.machine_st.arena),
293 };
294
295 self.load_file(path, toplevel_stream);
296
297 if let Some(toplevel) = self.indices.modules.get(&atom!("$toplevel")) {
298 load_module(
299 &mut self.machine_st,
300 &mut self.indices.code_dir,
301 &mut self.indices.op_dir,
302 &mut self.indices.meta_predicates,
303 &CompilationTarget::User,
304 toplevel,
305 );
306 } else {
307 unreachable!()
308 }
309 }
310
311 fn load_special_forms(&mut self) {
312 let mut path_buf = current_dir();
313 path_buf.push("machine/attributed_variables.pl");
314
315 let stream = Stream::from_static_string(
316 include_str!("attributed_variables.pl"),
317 &mut self.machine_st.arena,
318 );
319
320 self.load_file(path_buf.to_str().unwrap(), stream);
321
322 let mut path_buf = current_dir();
323 path_buf.push("machine/project_attributes.pl");
324
325 let stream = Stream::from_static_string(
326 include_str!("project_attributes.pl"),
327 &mut self.machine_st.arena,
328 );
329
330 self.load_file(path_buf.to_str().unwrap(), stream);
331
332 if let Some(module) = self.indices.modules.get(&atom!("$atts")) {
333 if let Some(code_idx) = module.code_dir.get(&(atom!("driver"), 2)) {
334 let index_ptr = self
335 .machine_st
336 .arena
337 .code_index_tbl
338 .get_entry(code_idx.into());
339 self.machine_st.attr_var_init.verify_attrs_loc = index_ptr.local().unwrap();
340 }
341 }
342 }
343
344 pub(crate) fn configure_modules(&mut self) {
345 fn update_call_n_indices(
346 loader: &Module,
347 target_code_dir: &mut CodeDir,
348 arena: &mut Arena,
349 ) {
350 for arity in 1..66 {
351 let key = (atom!("call"), arity);
352
353 match loader.code_dir.get(&key).cloned() {
354 Some(src_code_index) => {
355 let code_index_tbl = &mut arena.code_index_tbl;
356
357 let target_code_index = target_code_dir.entry(key).or_insert_with(|| {
358 CodeIndex::new(IndexPtr::undefined(), code_index_tbl)
359 });
360
361 let src_code_ptr = code_index_tbl.get_entry(src_code_index.into());
362 target_code_index.set(code_index_tbl, src_code_ptr);
363 }
364 None => {
365 unreachable!();
366 }
367 }
368 }
369 }
370
371 if let Some(loader) = self.indices.modules.swap_remove(&atom!("loader")) {
372 if let Some(builtins) = self.indices.modules.get_mut(&atom!("builtins")) {
373 load_module(
376 &mut self.machine_st,
377 &mut builtins.code_dir,
378 &mut builtins.op_dir,
379 &mut builtins.meta_predicates,
380 &CompilationTarget::Module(atom!("builtins")),
381 &loader,
382 );
383
384 for export in &loader.module_decl.exports {
385 builtins.module_decl.exports.push(export.clone());
386 }
387
388 for arity in 10..66 {
389 builtins
390 .module_decl
391 .exports
392 .push(ModuleExport::PredicateKey((atom!("call"), arity)));
393 }
394 }
395
396 for (_, target_module) in self.indices.modules.iter_mut() {
397 update_call_n_indices(
398 &loader,
399 &mut target_module.code_dir,
400 &mut self.machine_st.arena,
401 );
402 }
403
404 update_call_n_indices(
405 &loader,
406 &mut self.indices.code_dir,
407 &mut self.machine_st.arena,
408 );
409
410 self.indices.modules.insert(atom!("loader"), loader);
411 } else {
412 unreachable!()
413 }
414 }
415
416 pub(crate) fn add_impls_to_indices(&mut self) {
417 let impls_offset = self.code.len() + 4;
418
419 self.code.extend(vec![
420 Instruction::BreakFromDispatchLoop,
421 Instruction::InstallVerifyAttr,
422 Instruction::VerifyAttrInterrupt(0),
423 Instruction::BreakFromDispatchLoop, Instruction::ExecuteTermGreaterThan,
425 Instruction::ExecuteTermLessThan,
426 Instruction::ExecuteTermGreaterThanOrEqual,
427 Instruction::ExecuteTermLessThanOrEqual,
428 Instruction::ExecuteTermEqual,
429 Instruction::ExecuteTermNotEqual,
430 Instruction::ExecuteNumberGreaterThan(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
431 Instruction::ExecuteNumberLessThan(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
432 Instruction::ExecuteNumberGreaterThanOrEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
433 Instruction::ExecuteNumberLessThanOrEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
434 Instruction::ExecuteNumberEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
435 Instruction::ExecuteNumberNotEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
436 Instruction::ExecuteIs(temp_v!(1), ar_reg!(temp_v!(2))),
437 Instruction::ExecuteAcyclicTerm,
438 Instruction::ExecuteArg,
439 Instruction::ExecuteCompare,
440 Instruction::ExecuteCopyTerm,
441 Instruction::ExecuteFunctor,
442 Instruction::ExecuteGround,
443 Instruction::ExecuteKeySort,
444 Instruction::ExecuteSort,
445 Instruction::ExecuteN(1),
446 Instruction::ExecuteN(2),
447 Instruction::ExecuteN(3),
448 Instruction::ExecuteN(4),
449 Instruction::ExecuteN(5),
450 Instruction::ExecuteN(6),
451 Instruction::ExecuteN(7),
452 Instruction::ExecuteN(8),
453 Instruction::ExecuteN(9),
454 Instruction::ExecuteIsAtom(temp_v!(1)),
455 Instruction::ExecuteIsAtomic(temp_v!(1)),
456 Instruction::ExecuteIsCompound(temp_v!(1)),
457 Instruction::ExecuteIsInteger(temp_v!(1)),
458 Instruction::ExecuteIsNumber(temp_v!(1)),
459 Instruction::ExecuteIsRational(temp_v!(1)),
460 Instruction::ExecuteIsFloat(temp_v!(1)),
461 Instruction::ExecuteIsNonVar(temp_v!(1)),
462 Instruction::ExecuteIsVar(temp_v!(1)),
463 ]);
464
465 for (p, instr) in self.code[impls_offset..].iter().enumerate() {
466 let key = instr.to_name_and_arity();
467 self.indices.code_dir.insert(
468 key,
469 CodeIndex::new(
470 IndexPtr::index(p + impls_offset),
471 &mut self.machine_st.arena.code_index_tbl,
472 ),
473 );
474 }
475 }
476
477 pub(crate) fn configure_streams(&mut self) {
481 self.indices
482 .set_stream(atom!("user_input"), self.user_input);
483 self.indices
484 .set_stream(atom!("user_output"), self.user_output);
485 self.indices
486 .set_stream(atom!("user_error"), self.user_error);
487
488 let mut null_options = StreamOptions::default();
489 null_options.set_alias_to_atom_opt(Some(atom!("null_stream")));
490
491 self.indices
492 .set_stream(atom!("null_stream"), Stream::Null(null_options));
493 }
494
495 #[inline(always)]
496 pub(crate) fn run_verify_attr_interrupt(&mut self, arity: usize) {
497 let p = self.machine_st.attr_var_init.verify_attrs_loc;
498 step_or_resource_error!(
499 self.machine_st,
500 self.machine_st.verify_attr_interrupt(p, arity)
501 );
502 }
503
504 fn next_clause_applicable(&mut self, mut offset: usize) -> bool {
505 loop {
506 match &self.code[offset] {
507 Instruction::IndexingCode(indexing_lines) => {
508 let mut oip = 0;
509 let mut cell = empty_list_as_cell!();
510
511 loop {
512 let indexing_code_ptr = match &indexing_lines[oip] {
513 &IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(
514 arg,
515 v,
516 c,
517 l,
518 s,
519 )) => {
520 cell = self.deref_register(arg);
521 self.machine_st
522 .select_switch_on_term_index(cell, v, c, l, s)
523 }
524 IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(hm)) => {
525 hm.get(&cell).cloned().unwrap_or(IndexingCodePtr::Fail)
527 }
528 IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(hm)) => {
529 self.machine_st.select_switch_on_structure_index(cell, hm)
530 }
531 _ => {
532 offset += 1;
533 break;
534 }
535 };
536
537 match indexing_code_ptr {
538 IndexingCodePtr::External(_) | IndexingCodePtr::DynamicExternal(_) => {
539 offset += 1;
540 break;
541 }
542 IndexingCodePtr::Internal(i) => oip += i,
543 IndexingCodePtr::Fail => return false,
544 }
545 }
546 }
547 &Instruction::GetConstant(Level::Shallow, lit, RegType::Temp(t)) => {
548 let cell = self.deref_register(t);
549
550 if cell.is_var() {
551 offset += 1;
552 } else {
553 unify!(self.machine_st, cell, lit);
554
555 if self.machine_st.fail {
556 self.machine_st.fail = false;
557 return false;
558 } else {
559 offset += 1;
560 }
561 }
562 }
563 &Instruction::GetList(Level::Shallow, RegType::Temp(t)) => {
564 let cell = self.deref_register(t);
565
566 read_heap_cell!(cell,
567 (HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc) => {offset += 1;
569 }
570 (HeapCellValueTag::Str, s) => {
571 let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s]).get_name_and_arity();
572
573 if name == atom!(".") && arity == 2 {
574 offset += 1;
575 } else {
576 return false;
577 }
578 }
579 (HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => {
580 offset += 1;
581 }
582 _ => {
583 return false;
584 }
585 );
586 }
587 &Instruction::GetStructure(Level::Shallow, name, arity, RegType::Temp(t)) => {
588 let cell = self.deref_register(t);
589
590 read_heap_cell!(cell,
591 (HeapCellValueTag::Str, s) => {
592 if (name, arity) == cell_as_atom_cell!(self.machine_st.heap[s]).get_name_and_arity() {
593 offset += 1;
594 } else {
595 return false;
596 }
597 }
598 (HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => {
599 offset += 1;
600 }
601 _ => {
602 return false;
603 }
604 );
605 }
606 &Instruction::GetPartialString(Level::Shallow, ref string, RegType::Temp(t)) => {
607 let cell = self.deref_register(t);
608
609 read_heap_cell!(cell,
610 (HeapCellValueTag::PStrLoc, pstr_loc) => {
611 let heap_slice = &self.machine_st.heap.as_slice()[pstr_loc ..];
612
613 match compare_pstr_slices(heap_slice, string.as_bytes()) {
614 PStrSegmentCmpResult::Continue(..) => offset += 1,
615 _ => return false,
616 }
617 }
618 (HeapCellValueTag::Lis) => {
619 offset += 1;
620 }
621 (HeapCellValueTag::Str, s) => {
622 let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s])
623 .get_name_and_arity();
624
625 if name == atom!(".") && arity == 2 {
626 offset += 1;
627 } else {
628 return false;
629 }
630 }
631 (HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => {
632 offset += 1;
633 }
634 _ => {
635 return false;
636 }
637 );
638 }
639 Instruction::GetConstant(..)
640 | Instruction::GetList(..)
641 | Instruction::GetStructure(..)
642 | Instruction::GetPartialString(..)
643 | &Instruction::UnifyVoid(..)
644 | &Instruction::UnifyConstant(..)
645 | &Instruction::GetVariable(..)
646 | &Instruction::GetValue(..)
647 | &Instruction::UnifyVariable(..)
648 | &Instruction::UnifyValue(..)
649 | &Instruction::UnifyLocalValue(..) => {
650 offset += 1;
651 }
652 _ => {
653 break;
654 }
655 }
656 }
657
658 true
659 }
660
661 fn next_applicable_clause(&mut self, mut offset: usize) -> Option<usize> {
662 while !self.next_clause_applicable(self.machine_st.p + offset + 1) {
663 match &self.code[self.machine_st.p + offset] {
664 &Instruction::DefaultRetryMeElse(o)
665 | &Instruction::RetryMeElse(o)
666 | &Instruction::DynamicElse(.., NextOrFail::Next(o))
667 | &Instruction::DynamicInternalElse(.., NextOrFail::Next(o)) => offset += o,
668 _ => {
669 return None;
670 }
671 }
672 }
673
674 Some(offset)
675 }
676
677 fn next_inner_applicable_clause(&mut self) -> Option<u32> {
678 let mut inner_offset = 1u32;
679
680 loop {
681 match &self.code[self.machine_st.p] {
682 Instruction::IndexingCode(indexing_lines) => {
683 match &indexing_lines[self.machine_st.oip as usize] {
684 IndexingLine::IndexedChoice(indexed_choice) => {
685 match &indexed_choice[(self.machine_st.iip + inner_offset) as usize] {
686 &IndexedChoiceInstruction::Retry(o)
687 | &IndexedChoiceInstruction::DefaultRetry(o) => {
688 if self.next_clause_applicable(self.machine_st.p + o) {
689 return Some(inner_offset);
690 }
691
692 inner_offset += 1;
693 }
694 &IndexedChoiceInstruction::Trust(o)
695 | &IndexedChoiceInstruction::DefaultTrust(o) => {
696 return if self.next_clause_applicable(self.machine_st.p + o) {
697 Some(inner_offset)
698 } else {
699 None
700 };
701 }
702 _ => unreachable!(),
703 }
704 }
705 IndexingLine::DynamicIndexedChoice(indexed_choice) => {
706 let idx = (self.machine_st.iip + inner_offset) as usize;
707 let o = indexed_choice[idx];
708
709 if idx + 1 == indexed_choice.len() {
710 return if self.next_clause_applicable(self.machine_st.p + o) {
711 Some(inner_offset)
712 } else {
713 None
714 };
715 } else {
716 if self.next_clause_applicable(self.machine_st.p + o) {
717 return Some(inner_offset);
718 }
719
720 inner_offset += 1;
721 }
722 }
723 _ => unreachable!(),
724 }
725 }
726 _ => unreachable!(),
727 }
728 }
729 }
730
731 #[inline(always)]
732 pub(super) fn try_me_else(&mut self, offset: usize) {
733 if let Some(offset) = self.next_applicable_clause(offset) {
734 let n = self.machine_st.num_of_args;
735 let b = self.machine_st.stack.allocate_or_frame(n);
736 let or_frame = self.machine_st.stack.index_or_frame_mut(b);
737
738 or_frame.prelude.num_cells = n;
739 or_frame.prelude.e = self.machine_st.e;
740 or_frame.prelude.cp = self.machine_st.cp;
741 or_frame.prelude.b = self.machine_st.b;
742 or_frame.prelude.bp = self.machine_st.p + offset;
743 or_frame.prelude.boip = 0;
744 or_frame.prelude.biip = 0;
745 or_frame.prelude.tr = self.machine_st.tr;
746 or_frame.prelude.h = self.machine_st.heap.cell_len();
747 or_frame.prelude.b0 = self.machine_st.b0;
748 or_frame.prelude.attr_var_queue_len =
749 self.machine_st.attr_var_init.attr_var_queue.len();
750
751 self.machine_st.b = b;
752
753 for i in 0..n {
754 or_frame[i] = self.machine_st.registers[i + 1];
755 }
756
757 self.machine_st.hb = self.machine_st.heap.cell_len();
758 }
759
760 self.machine_st.p += 1;
761 }
762
763 #[inline(always)]
764 pub(super) fn indexed_try(&mut self, offset: usize) {
765 if let Some(iip_offset) = self.next_inner_applicable_clause() {
766 let n = self.machine_st.num_of_args;
767 let b = self.machine_st.stack.allocate_or_frame(n);
768 let or_frame = self.machine_st.stack.index_or_frame_mut(b);
769
770 or_frame.prelude.num_cells = n;
771 or_frame.prelude.e = self.machine_st.e;
772 or_frame.prelude.cp = self.machine_st.cp;
773 or_frame.prelude.b = self.machine_st.b;
774 or_frame.prelude.bp = self.machine_st.p;
775 or_frame.prelude.boip = self.machine_st.oip;
776 or_frame.prelude.biip = self.machine_st.iip + iip_offset; or_frame.prelude.tr = self.machine_st.tr;
778 or_frame.prelude.h = self.machine_st.heap.cell_len();
779 or_frame.prelude.b0 = self.machine_st.b0;
780 or_frame.prelude.attr_var_queue_len =
781 self.machine_st.attr_var_init.attr_var_queue.len();
782
783 self.machine_st.b = b;
784
785 for i in 0..n {
786 or_frame[i] = self.machine_st.registers[i + 1];
787 }
788
789 self.machine_st.hb = self.machine_st.heap.cell_len();
790
791 }
794
795 self.machine_st.p += offset;
796 }
797
798 #[inline(always)]
799 fn retry_me_else(&mut self, offset: usize) {
800 let b = self.machine_st.b;
801 let or_frame = self.machine_st.stack.index_or_frame_mut(b);
802 let n = or_frame.prelude.num_cells;
803
804 let old_tr = or_frame.prelude.tr;
805 let curr_tr = self.machine_st.tr;
806
807 for i in 0..n {
808 self.machine_st.registers[i + 1] = or_frame[i];
809 }
810
811 self.unwind_trail(old_tr, curr_tr);
812
813 if let Some(offset) = self.next_applicable_clause(offset) {
814 let or_frame = self.machine_st.stack.index_or_frame_mut(b);
815
816 self.machine_st.num_of_args = n;
817 self.machine_st.e = or_frame.prelude.e;
818 self.machine_st.cp = or_frame.prelude.cp;
819
820 or_frame.prelude.bp = self.machine_st.p + offset;
821
822 let target_h = or_frame.prelude.h;
823 let attr_var_queue_len = or_frame.prelude.attr_var_queue_len;
824
825 self.machine_st.tr = or_frame.prelude.tr;
826 self.reset_attr_var_state(attr_var_queue_len);
827
828 self.machine_st.hb = target_h;
829
830 self.machine_st.trail.truncate(self.machine_st.tr);
831 self.machine_st.heap.truncate(target_h);
832
833 self.machine_st.p += 1;
834 } else {
835 self.trust_me_epilogue();
836 }
837 }
838
839 #[inline(always)]
840 fn retry(&mut self, offset: usize) {
841 let b = self.machine_st.b;
842 let or_frame = self.machine_st.stack.index_or_frame_mut(b);
843 let n = or_frame.prelude.num_cells;
844
845 let old_tr = or_frame.prelude.tr;
846 let curr_tr = self.machine_st.tr;
847
848 for i in 0..n {
849 self.machine_st.registers[i + 1] = or_frame[i];
850 }
851
852 self.unwind_trail(old_tr, curr_tr);
853
854 if let Some(iip_offset) = self.next_inner_applicable_clause() {
855 let or_frame = self.machine_st.stack.index_or_frame_mut(b);
856
857 self.machine_st.num_of_args = n;
858 self.machine_st.e = or_frame.prelude.e;
859 self.machine_st.cp = or_frame.prelude.cp;
860
861 or_frame.prelude.biip += iip_offset;
862
863 let target_h = or_frame.prelude.h;
864 let attr_var_queue_len = or_frame.prelude.attr_var_queue_len;
865
866 self.machine_st.tr = or_frame.prelude.tr;
867 self.machine_st.trail.truncate(self.machine_st.tr);
868
869 self.reset_attr_var_state(attr_var_queue_len);
870
871 self.machine_st.hb = target_h;
872 self.machine_st.p += offset;
873
874 self.machine_st.heap.truncate(target_h);
875
876 } else {
893 self.trust_epilogue(offset);
894 }
895 }
896
897 #[inline(always)]
898 fn trust(&mut self, offset: usize) {
899 let b = self.machine_st.b;
900 let or_frame = self.machine_st.stack.index_or_frame(b);
901 let n = or_frame.prelude.num_cells;
902
903 let old_tr = or_frame.prelude.tr;
904 let curr_tr = self.machine_st.tr;
905
906 for i in 0..n {
907 self.machine_st.registers[i + 1] = or_frame[i];
908 }
909
910 self.unwind_trail(old_tr, curr_tr);
911 self.trust_epilogue(offset);
912 }
913
914 #[inline(always)]
915 fn trust_epilogue(&mut self, offset: usize) {
916 let b = self.machine_st.b;
917 let or_frame = self.machine_st.stack.index_or_frame(b);
918 let n = or_frame.prelude.num_cells;
919
920 self.machine_st.num_of_args = n;
921 self.machine_st.e = or_frame.prelude.e;
922 self.machine_st.cp = or_frame.prelude.cp;
923
924 let target_h = or_frame.prelude.h;
925
926 self.machine_st.tr = or_frame.prelude.tr;
927 self.machine_st.trail.truncate(self.machine_st.tr);
928
929 self.machine_st.b = or_frame.prelude.b;
930
931 self.reset_attr_var_state(or_frame.prelude.attr_var_queue_len);
932
933 self.machine_st.hb = target_h;
934 self.machine_st.p += offset;
935
936 self.machine_st.stack.truncate(b);
937 self.machine_st.heap.truncate(target_h);
938
939 self.machine_st.oip = 0;
940 self.machine_st.iip = 0;
941 }
942
943 #[inline(always)]
944 fn trust_me(&mut self) {
945 let b = self.machine_st.b;
946 let or_frame = self.machine_st.stack.index_or_frame(b);
947 let n = or_frame.prelude.num_cells;
948
949 for i in 0..n {
950 self.machine_st.registers[i + 1] = or_frame[i];
951 }
952
953 let old_tr = or_frame.prelude.tr;
954 let curr_tr = self.machine_st.tr;
955
956 self.unwind_trail(old_tr, curr_tr);
957
958 self.trust_me_epilogue();
959 }
960
961 #[inline(always)]
962 fn trust_me_epilogue(&mut self) {
963 let b = self.machine_st.b;
964 let or_frame = self.machine_st.stack.index_or_frame(b);
965 let n = or_frame.prelude.num_cells;
966
967 self.machine_st.num_of_args = n;
968 self.machine_st.e = or_frame.prelude.e;
969 self.machine_st.cp = or_frame.prelude.cp;
970
971 let target_h = or_frame.prelude.h;
972
973 self.machine_st.tr = or_frame.prelude.tr;
974 self.machine_st.b = or_frame.prelude.b;
975
976 self.reset_attr_var_state(or_frame.prelude.attr_var_queue_len);
977
978 self.machine_st.hb = target_h;
979 self.machine_st.p += 1;
980
981 self.machine_st.trail.truncate(self.machine_st.tr);
982 self.machine_st.stack.truncate(b);
983 self.machine_st.heap.truncate(target_h);
984 }
985
986 #[inline(always)]
987 fn undefined_procedure(&mut self, name: Atom, arity: usize) -> CallResult {
988 match self.machine_st.flags.unknown {
989 Unknown::Error => Err(self.machine_st.throw_undefined_error(name, arity)),
990 Unknown::Fail => {
991 self.machine_st.fail = true;
992 Ok(())
993 }
994 Unknown::Warn => {
995 println!(
996 "% Warning: predicate {}/{} is undefined",
997 name.as_str(),
998 arity
999 );
1000 self.machine_st.fail = true;
1001 Ok(())
1002 }
1003 }
1004 }
1005
1006 #[inline(always)]
1007 fn try_call(&mut self, name: Atom, arity: usize, idx: IndexPtr) -> CallResult {
1008 let compiled_tl_index = idx.p() as usize;
1009
1010 match idx.tag() {
1011 IndexPtrTag::DynamicUndefined => {
1012 self.machine_st.fail = true;
1013 }
1014 IndexPtrTag::Undefined => {
1015 return self.undefined_procedure(name, arity);
1016 }
1017 IndexPtrTag::DynamicIndex => {
1018 self.machine_st.dynamic_mode = FirstOrNext::First;
1019 self.machine_st.call_at_index(arity, compiled_tl_index);
1020 }
1021 IndexPtrTag::Index => {
1022 self.machine_st.call_at_index(arity, compiled_tl_index);
1023 }
1024 }
1025
1026 Ok(())
1027 }
1028
1029 #[inline(always)]
1030 fn try_execute(&mut self, name: Atom, arity: usize, idx: IndexPtr) -> CallResult {
1031 let compiled_tl_index = idx.p() as usize;
1032
1033 match idx.tag() {
1034 IndexPtrTag::DynamicUndefined => {
1035 self.machine_st.fail = true;
1036 }
1037 IndexPtrTag::Undefined => {
1038 return self.undefined_procedure(name, arity);
1039 }
1040 IndexPtrTag::DynamicIndex => {
1041 self.machine_st.dynamic_mode = FirstOrNext::First;
1042 self.machine_st.execute_at_index(arity, compiled_tl_index);
1043 }
1044 IndexPtrTag::Index => self.machine_st.execute_at_index(arity, compiled_tl_index),
1045 }
1046
1047 Ok(())
1048 }
1049
1050 #[inline(always)]
1051 fn call_clause(&mut self, module_name: Atom, key: PredicateKey) -> CallResult {
1052 let (name, arity) = key;
1053
1054 if module_name == atom!("user") {
1055 if let Some(idx) = self.indices.code_dir.get(&(name, arity)).cloned() {
1056 let index_ptr = self.machine_st.arena.code_index_tbl.get_entry(idx.into());
1057 self.try_call(name, arity, index_ptr)
1058 } else {
1059 Err(self.machine_st.throw_undefined_error(name, arity))
1060 }
1061 } else if let Some(module) = self.indices.modules.get(&module_name) {
1062 if let Some(idx) = module.code_dir.get(&(name, arity)).cloned() {
1063 let index_ptr = self.machine_st.arena.code_index_tbl.get_entry(idx.into());
1064 self.try_call(name, arity, index_ptr)
1065 } else {
1066 self.undefined_procedure(name, arity)
1067 }
1068 } else {
1069 let stub = functor_stub(name, arity);
1070 let err = self
1071 .machine_st
1072 .existence_error(ExistenceError::QualifiedProcedure {
1073 module_name,
1074 name,
1075 arity,
1076 });
1077
1078 Err(self.machine_st.error_form(err, stub))
1079 }
1080 }
1081
1082 #[inline(always)]
1083 fn execute_clause(&mut self, module_name: Atom, key: PredicateKey) -> CallResult {
1084 let (name, arity) = key;
1085
1086 if module_name == atom!("user") {
1087 if let Some(offset) = self.indices.code_dir.get(&(name, arity)).cloned() {
1088 let index_ptr = self
1089 .machine_st
1090 .arena
1091 .code_index_tbl
1092 .get_entry(offset.into());
1093 self.try_execute(name, arity, index_ptr)
1094 } else {
1095 self.undefined_procedure(name, arity)
1096 }
1097 } else if let Some(module) = self.indices.modules.get(&module_name) {
1098 if let Some(offset) = module.code_dir.get(&(name, arity)).cloned() {
1099 let index_ptr = self
1100 .machine_st
1101 .arena
1102 .code_index_tbl
1103 .get_entry(offset.into());
1104 self.try_execute(name, arity, index_ptr)
1105 } else {
1106 self.undefined_procedure(name, arity)
1107 }
1108 } else {
1109 let stub = functor_stub(name, arity);
1110 let err = self
1111 .machine_st
1112 .existence_error(ExistenceError::QualifiedProcedure {
1113 module_name,
1114 name,
1115 arity,
1116 });
1117
1118 Err(self.machine_st.error_form(err, stub))
1119 }
1120 }
1121
1122 #[inline(always)]
1123 fn call_n(&mut self, module_name: Atom, arity: usize) -> CallResult {
1124 let key = self.machine_st.setup_call_n(arity)?;
1125 self.call_clause(module_name, key)
1126 }
1127
1128 #[inline(always)]
1129 fn execute_n(&mut self, module_name: Atom, arity: usize) -> CallResult {
1130 let key = self.machine_st.setup_call_n(arity)?;
1131 self.execute_clause(module_name, key)
1132 }
1133
1134 #[inline(always)]
1135 fn run_cleaners(&mut self) -> bool {
1136 static CLEANER_INIT: OnceLock<(usize, usize)> = OnceLock::new();
1137
1138 let (r_c_w_h, r_c_wo_h) = *CLEANER_INIT.get_or_init(|| {
1139 let r_c_w_h_atom = atom!("run_cleaners_with_handling");
1140 let r_c_wo_h_atom = atom!("run_cleaners_without_handling");
1141 let iso_ext = atom!("iso_ext");
1142
1143 let r_c_w_h = self
1144 .indices
1145 .get_predicate_code_index(r_c_w_h_atom, 0, iso_ext)
1146 .and_then(|code_idx| {
1147 self.machine_st
1148 .arena
1149 .code_index_tbl
1150 .get_entry(code_idx.into())
1151 .local()
1152 })
1153 .unwrap();
1154 let r_c_wo_h = self
1155 .indices
1156 .get_predicate_code_index(r_c_wo_h_atom, 1, iso_ext)
1157 .and_then(|code_idx| {
1158 self.machine_st
1159 .arena
1160 .code_index_tbl
1161 .get_entry(code_idx.into())
1162 .local()
1163 })
1164 .unwrap();
1165 (r_c_w_h, r_c_wo_h)
1166 });
1167
1168 if let Some(&(_, b_cutoff, prev_block)) = self.machine_st.cont_pts.last() {
1169 if self.machine_st.b < b_cutoff {
1170 let (idx, arity) = if self.machine_st.effective_block() > prev_block {
1171 (r_c_w_h, 0)
1172 } else {
1173 self.machine_st.registers[1] = fixnum_as_cell!(
1174 unsafe { Fixnum::build_with_unchecked(b_cutoff as i64) }
1176 );
1177
1178 (r_c_wo_h, 1)
1179 };
1180
1181 self.machine_st.call_at_index(arity, idx);
1182 return true;
1183 }
1184 }
1185
1186 false
1187 }
1188
1189 pub(super) fn unwind_trail(&mut self, a1: usize, a2: usize) {
1190 for i in (a1..a2).rev() {
1194 let h = self.machine_st.trail[i].get_value() as usize;
1195
1196 match self.machine_st.trail[i].get_tag() {
1197 TrailEntryTag::TrailedHeapVar => {
1198 self.machine_st.heap[h] = heap_loc_as_cell!(h);
1199 }
1200 TrailEntryTag::TrailedStackVar => {
1201 self.machine_st.stack[h] = stack_loc_as_cell!(h);
1202 }
1203 TrailEntryTag::TrailedAttrVar => {
1204 self.machine_st.heap[h] = attr_var_as_cell!(h);
1205 }
1206 TrailEntryTag::TrailedAttrVarListLink => {
1207 let l = self.machine_st.trail[i + 1].get_value() as usize;
1208
1209 if l < self.machine_st.hb {
1210 if h == l {
1211 self.machine_st.heap[h] = heap_loc_as_cell!(h);
1212 } else {
1213 read_heap_cell!(self.machine_st.heap[l],
1214 (HeapCellValueTag::Var) => {
1215 self.machine_st.heap[h] = list_loc_as_cell!(l);
1216 }
1217 _ => {
1218 self.machine_st.heap[h] = heap_loc_as_cell!(l);
1219 }
1220 );
1221 }
1222 } else {
1223 self.machine_st.heap[h] = heap_loc_as_cell!(h);
1224 }
1225 }
1226 TrailEntryTag::TrailedBlackboardEntry => {
1227 let key = Atom::from(h as u64);
1228
1229 match self.indices.global_variables.get_mut(&key) {
1230 Some((_, ref mut loc)) => *loc = None,
1231 None => unreachable!(),
1232 }
1233 }
1234 TrailEntryTag::TrailedBlackboardOffset => {
1235 let key = Atom::from(h as u64);
1236 let value_cell = HeapCellValue::from(u64::from(self.machine_st.trail[i + 1]));
1237
1238 match self.indices.global_variables.get_mut(&key) {
1239 Some((_, ref mut loc)) => *loc = Some(value_cell),
1240 None => unreachable!(),
1241 }
1242 }
1243 TrailEntryTag::TrailedAttachedValue => {}
1244 }
1245 }
1246 }
1247}
1248
1249#[cfg(test)]
1250mod tests {
1251 use super::config::*;
1252 use super::*;
1253
1254 #[test]
1255 #[cfg_attr(miri, ignore)]
1256 fn test_run_module_predicate_throw() {
1257 let mut machine = MachineBuilder::default()
1258 .with_toplevel(
1259 r#"
1260 :- module('$toplevel', []).
1261 repl :- throw(kaboom).
1262 "#,
1263 )
1264 .build();
1265
1266 machine.run_module_predicate(atom!("$toplevel"), (atom!("repl"), 0));
1267 }
1268}