flowlog.c)This document summarizes the main internal components of the Flowlog
engine in flowlog.c.
Navigation: flowlog.c uses
SECTION NN and SUBSECTION NN.N headings. When
you want to jump to the implementation of a topic described here, search
for the corresponding section heading. The full section map lives in DEVELOPMENT.md.
Code references (section markers in flowlog.c):
pl_program containing (SECTION
10 and SECTION 05.4):
op/3) stored as
pl_op_overridesThe parser supports ISO term syntax, quoted atoms, lists, operators,
and the ISO read_term/3 / write_term/3 option
set used by tests.
Operator parsing follows a small disambiguation rule that matters for some control constructs (SUBSECTIONS 10.4/10.5):
Op(...) (no whitespace) is parsed as a functor
call.Op (...) (with whitespace) is parsed as a
prefix operator applied to a grouped term/goal.For example, \+ (A -> B) parses as negation of an
if-then goal, while \+(A -> B) is treated as a functor
call and requires extra parentheses around A -> B.
Program loading is deterministic (SECTION 25):
pl_program.Flowlog represents terms as a tagged union (SUBSECTION 05.2):
PACKED_STRINGS.md)
(SECTION 11)Flowlog uses a two-tier integer representation to keep small arithmetic fast while supporting unbounded ISO integers:
pl_term as
SLO (machine word).SLO, values
promote to pl_bigint (heap-allocated, base-2^32
limbs).-1/0/+1) and canonicalized back to small ints when they
fit.This makes current_prolog_flag(bounded,false) true
without penalizing common small-integer code paths. For the full layout
and algorithm notes, see ARITHMETIC.md.
Bindings live in a persistent environment structure
(pl_env) with parent links (copy-on-write style) so
backtracking can cheaply fork environments (SECTION 14).
To keep variable dereferencing fast even with parent-linked environments, each clause-instantiation frame records the “introduced scope”, and variable lookup stops once that scope is reached (older frames cannot contain bindings for a scope that did not yet exist). The unifier also opportunistically caches deep ancestor bindings into the current env frame to reduce repeated parent-chain scans in pure recursive workloads (SECTION 14).
Flowlog also maintains a small amount of call metadata (predicate indicator, depth, flags) used for (SECTION 06):
occurs_check flag) (SECTIONS 14/15).bagof/3 / setof/3 groupingFlowlog provides three execution engines that share the same parser,
program representation, built-ins, error model, and indexing (SECTIONS
22/23/24). The default engine is wamvm (SECTION 25).
interp /
tree)The interpreter is a direct solver (closer to a “tree-walking” engine than a compilation pipeline) (SECTION 22):
pl_program containing
predicates and their clauses (plus operator overrides and flags)
(SECTIONS 10/25 and SECTION 05.4).This engine supports the full feature set, including (SECTIONS 13/20/14):
assert*/1,
retract/1, …)wam)The WAM-lite engine is a fast fallback/alternative engine and is intended to reduce per-goal overhead for common pure/static ISO code (SECTION 23).
In broad terms it:
The interpreter remains available as a reference engine and for debugging, but the WAM-lite engine aims to execute the full feature set directly (including dynamic database predicates, tabling, and occurs-check).
To detect what engine actually ran a query from within Prolog, use (SECTIONS 22/23/24):
?- current_prolog_flag(flowlog_engine_active, Engine).wamvm)wamvm is the default engine mode and runs user-defined
clause bodies on a small bytecode VM (see WAM_ROADMAP.md) (SECTIONS
18/24; default in SECTION 25).
In broad terms:
CALL and PROCEED) (SECTION
18).;/2, multi-clause predicates, and a few key
generators such as select1/3) (SECTION 24).wam (SECTIONS 05/11/12/13/14/15).Performance notes:
wamvm keeps
select1/3 nondeterminism inside the VM (as a choicepoint)
in the non-OR-par path, avoiding per-alternative continuation
reconstruction (SECTION 24).select1/3 chunks are executed via
an internal generator goal ($select1_range/5) so each chunk
runs as a single VM invocation, and chunk jobs reuse the
already-compiled WAMVM program code (no per-chunk rebuild) (SECTION
24).Fallbacks:
flowlog_engine=wamvm, Flowlog first checks whether
the loaded program/query is supported by the VM (SECTION 24).FLOWLOG_WAMVM_REQUIRE_VM=1 is set,
in which case it errors instead). This includes tabling
(:- table/1) and any goals not yet supported by the VM’s
conservative support check (SECTION 24 and SECTION 20).FLOWLOG_PROFILE_WAMVM=1 to print snapshot/call counters at
the end of a query (SECTION 24).Conformance note:
FLOWLOG_WAMVM_REQUIRE_VM=1). A dedicated target exists to
keep this true: make inria-wamvm-vmonly.Flowlog uses multiple indexing layers (SECTION 13):
Indexing is designed to be safe under dynamic database updates by using epoching and snapshots (SECTION 13).
Indexing is a performance feature, not a semantic one:
ISO database predicates (including asserta/1,
assertz/1, retract/1, abolish/1,
clause/2) are implemented with (SECTION 13):
Flowlog supports call-by-variant tabling for declared predicates (SECTION 20):
:- table(path/2).Internally, each tabled predicate maintains:
Tabling runs under both engines. While tabling is active, OR-parallelism is currently disabled to keep the table state single-threaded (SECTION 20 and SECTION 21). Tabled predicates are also treated as AND-par unsafe (they can create observable search/answer effects) (SECTION 20 and SECTION 23).
OR-parallelism runs independent alternative ranges concurrently (SECTION 21):
Cancellation:
I/O:
AND-parallelism is used for limited “safe” cases (SECTION 23):
Flowlog performs a conservative analysis and only parallelizes when it can preserve ISO-visible semantics (SECTION 23).
The REPL is implemented directly in flowlog.c (SECTION 26):
termios raw mode (SUBSECTION 26.2).~/.flowlog_history by default
(configurable via FLOWLOG_HISTORY_FILE) (SUBSECTION
26.2).SIGINFO) (SUBSECTION
04.3 and SECTION 06).