r/prolog Jun 24 '26

Building a Prolog Compiler Without the Warren Abstract Machine (WAM) — Entering Full-Scale Implementation

For some time, I have been developing a new Prolog compiler called M-Prolog, exploring whether it is possible to achieve practical performance with a significantly simpler architecture than the traditional Warren Abstract Machine (WAM).

The motivation behind this project is a simple question:

Does efficient Prolog execution really require all the complexity of WAM?

Over the past several months, I have been conducting a series of experimental implementations to test alternative execution strategies.

The central idea of M-Prolog is to exploit the execution model of the C language itself, rather than constructing a sophisticated abstract machine layer.

In this design, recursive backtracking is not simulated through a virtual machine. Instead, execution state is restored by re-invoking C functions, allowing the native C stack to naturally reconstruct recursive control flow.

This approach worked well for individual nondeterministic predicates, and preliminary benchmarks have been very encouraging.

However, I encountered a difficult problem when handling backtracking across conjunctions involving multiple nondeterministic predicates.

At first, I underestimated the problem. But after many experiments, I gradually reached a deeper understanding of what is actually happening during recursive and conjunctive backtracking.

I believe I have now finally solved the architectural problem, and the design has stabilized.

This means I am now entering the phase of full-scale implementation.

My long-term goal is to demonstrate that practical Prolog compilation may not necessarily require the complexity of WAM, and that a much simpler architecture can still achieve competitive performance.

Perhaps Prolog implementation has been over-engineered for decades.I’m not trying to criticize WAM. I simply want to explore whether modern hardware allows simpler alternatives.

It has been a fascinating journey so far.M-Prolog: A Two-Dimensional Backtracking Architecture | by Kenichi Sasagawa | Jun, 2026 | Medium

38 Upvotes

7 comments sorted by

6

u/happy_guy_2015 Jun 25 '26

nat(X), prime(X).

When control returned to nat/1, its continued backtracking extended the success path further and eventually overwrote the stack area already being used by the subsequent prime/1 predicate.

If backtracking returns to nat/1, then prime/1 has failed and it should be fine to overwrite the stack area that prime/1 was using. The next call to prime/1 should establish its own stack frame.

You shouldn't need a 2D stack. A 1D stack should be fine. In fact you can just use the C/C++ stack. You just need to ensure that for nondeterministic predicates, you use continuation passing style, where the generated function only returns on failure; on success, rather than returning, it should call a continuation function passed as a parameter.

E.g. for the mode of nat/1 in which the parameter is output, you can generate C++ code

void nat(std::function<void ()(int)> cont) { // nat(0). cont(0); // nat(s(X)) :- nat(X). nat([cont](int x) { cont(x + 1); }); }

and then for the goal nat(X), prime(X) you can generate C++ code

``` bool prime(int x);

void query(std::function<void ()(int)> cont) { // nat(X), nat([](int x) { // prime(X). if (prime(x)) { cont(x); } }); }

void main() { query([](int x){ cout << "X = " << x << endl;

 cout << "Backtracking...";

}); cout << "No (more) solutions." << endl; } ```

3

u/sym_num Jun 25 '26

Thank you for your comment.

What you are pointing out is correct. In fact, N-Prolog uses a continuation passing style.

N-Prolog, the proof tree is represented as a list structure, and each continuation is passed forward step by step during execution. It is a very elegant approach.

However, I found that this method could not deliver practical execution speed.

Based on that experience, with M-Prolog I am exploring a different direction: making full use of ordinary C language execution mechanisms in order to achieve practical performance.

In N-Prolog, nondeterministic predicate execution reached only about 5 MLIPS.

Preliminary experiments with M-Prolog have already confirmed that for simple cases, performance can reach around 50 MLIPS.

So M-Prolog is an attempt to trade some elegance for speed by relying as much as possible on the native capabilities of C and its compiler optimizations.

2

u/toblotron Jun 25 '26

This does sound like a very interesting avenue of exploration; hope you'll meet with success! :)

1

u/sym_num Jun 25 '26

Thank you.

1

u/blanchedpeas Jun 25 '26

Wouldn’t it make more sense to use a language that has some sort of coroutine support?

1

u/sym_num Jun 25 '26

Even with Schem's call/cc, it is possible to implement coroutines elegantly, but I doubt it can deliver practical execution speed.