Feature/volatile - #18
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces volatile variables (@v) as acquire/release-style synchronization objects across the interpreter and memory models, expands execution-graph provenance/conflict metadata, and adds a TikZ (.tex) execution-graph output option alongside the existing Graphviz (.dot) output. It also updates the Python test harness to support per-model expectation overrides embedded in example files.
Changes:
- Add parsing/AST support for
@volatileidentifiers and implement volatile acquire (read) / release (write) behavior in the interpreter and both linear/branching memory models. - Extend execution traces/graphs and conflict objects to carry source-event provenance; add TikZ execution-graph printing and CLI format selection.
- Add/adjust example programs and test harness logic to accommodate model-dependent accept/reject outcomes.
Reviewed changes
Copilot reviewed 39 out of 40 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| test_gitmem.py | Add per-model expectation overrides via // expect ... directives. |
| src/tikz.hh | Declare TikZ execution-graph printer. |
| src/tikz.cc | Implement TikZ execution-graph rendering. |
| src/thread_trace.hh | Add volatile read/write trace events and unlock g-predecessor tracking. |
| src/sync_state.hh | Add VolatileSyncState interface. |
| src/passes/statements.cc | Allow Volatile token in assignment/value forms. |
| src/passes/expressions.cc | Allow Volatile token as an expression operand. |
| src/parser.cc | Tokenize @name as Volatile. |
| src/memory_model.hh | Add volatile sync ops + model hooks; add global lock ordering flag. |
| src/linear/version_store.cc | Include source events in conflicts. |
| src/linear/memory_model.hh | Add volatile hooks; rename push to pullpush; global lock ordering true. |
| src/linear/memory_model.cc | Implement volatile acquire/release and new pull/push split. |
| src/lang.hh | Add Volatile token and grammar support. |
| src/interpreter.hh | Include TikZ/linear headers for execution-graph printing decisions. |
| src/interpreter.cc | Implement volatile eval + tracing; add global lock ordering predecessor; TikZ output path. |
| src/graphviz.hh | Add visitor hooks for volatile read/write nodes. |
| src/graphviz.cc | Render volatile read/write nodes and sync edges in Graphviz output. |
| src/graph.hh | Add volatile graph node types; extend unlock with conflict + g-predecessor. |
| src/gitmem.cc | Add --format option and adjust default output extension. |
| src/execution_state.hh | Add Volatile object storage and global last-g-push tracking. |
| src/execution_state.cc | Implement get_volatile; include volatiles in global-state equality. |
| src/conflict.hh | Add conflict source-event provenance plumbing. |
| src/branching/lazy/version_store.cc | Include source events in conflicts. |
| src/branching/eager/version_store.cc | Include source events in conflicts; add <queue> include. |
| src/branching/base_version_store.hh | Add VolatileState for branching volatile release tracking. |
| src/branching/base_memory_model.hh | Add volatile acquire/release hooks; create volatile state. |
| src/branching/base_memory_model.cc | Implement volatile acquire/release semantics for branching models. |
| examples/reject/semantics/linear/volatile_sync_conflict.gm | New volatile conflict example (linear reject). |
| examples/reject/semantics/linear/volatile_sync_conflict_two_vars.gm | New volatile conflict example (linear reject). |
| examples/reject/semantics/linear/volatile_sync_communicate_volatile.gm | New volatile communication example (linear reject). |
| examples/reject/semantics/branching/volatile_sync_conflict.gm | New volatile conflict example (branching reject; lazy override). |
| examples/reject/semantics/branching/volatile_sync_communicate_volatile.gm | New volatile communication example (branching reject). |
| examples/accept/semantics/linear/volatile.gm | New basic volatile example (linear accept). |
| examples/accept/semantics/linear/volatile_sync.gm | New volatile sync example (linear accept). |
| examples/accept/semantics/linear/volatile_sync_communicate.gm | New volatile sync communication example (linear accept). |
| examples/accept/semantics/branching/volatile.gm | New basic volatile example (branching accept). |
| examples/accept/semantics/branching/volatile_sync.gm | New volatile sync example (branching accept). |
| examples/accept/semantics/branching/volatile_sync_conflict_two_vars.gm | New two-volatile example (branching accept). |
| CMakeLists.txt | Add src/tikz.cc to build. |
| .gitignore | Ignore additional local/editor artifacts and .dot outputs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // In the linear model all unlocks push to the same g, so a lock on any | ||
| // variable follows the most recent unlock of ANY variable (not just this | ||
| // one). Use the global g-push predecessor when the per-lock one is absent. | ||
| auto lock_predecessor = (gctx.model->uses_global_lock_ordering() | ||
| && !lock.last_unlock_event) | ||
| ? gctx.last_g_push_event : lock.last_unlock_event; |
| if (output_path.empty()) { | ||
| std::string ext = (output_format == "tikz") ? ".tex" : ".dot"; | ||
| output_path = input_path.stem().replace_extension(ext); | ||
| } |
| #include "tikz.hh" | ||
| #include "overloaded.hh" | ||
| #include <fstream> | ||
| #include <unordered_map> | ||
| #include <map> | ||
| #include <algorithm> | ||
| #include <optional> | ||
| #include <cstdio> | ||
|
|
| auto is_sync_node = [](const Node* nd) -> bool { | ||
| return dynamic_cast<const Start*>(nd) || dynamic_cast<const End*>(nd) | ||
| || dynamic_cast<const Spawn*>(nd) || dynamic_cast<const Join*>(nd) | ||
| || dynamic_cast<const Lock*>(nd) || dynamic_cast<const Unlock*>(nd); | ||
| }; |
| } else if (auto* nd = dynamic_cast<const Write*>(n)) { | ||
| ev.label = "W(" + latex_escape(nd->var) + "$_{" + std::to_string(tid) + "}$) = " + std::to_string(nd->value); | ||
| } else if (auto* nd = dynamic_cast<const Read*>(n)) { | ||
| std::visit(overloaded{ | ||
| [&](const Read::SuccessfulRead& sr) { | ||
| ev.label = "R(" + latex_escape(nd->var) + "$_{" + std::to_string(tid) + "}$) = " + std::to_string(sr.value); | ||
| }, | ||
| [&](const Conflict&) { | ||
| ev.label = "R(" + latex_escape(nd->var) + "$_{" + std::to_string(tid) + "}$) = ?"; | ||
| ev.is_conflict = true; | ||
| } | ||
| }, nd->read_result); |
| } else if (dynamic_cast<const Start*>(ev.node)) { | ||
| // g: start pulls | ||
| if (has_g_lane) | ||
| f << "\\draw[link oneway] (laneG |- " << ev.name << ") -- (" << ev.name << ");\n"; | ||
| } else if (dynamic_cast<const End*>(ev.node)) { | ||
| // g: end pulls + pushes | ||
| if (has_g_lane) | ||
| f << "\\PullPush{(" << ev.name << ")}{(laneG |- " << ev.name << ")}\n"; | ||
| } |
| struct Unlock : Node { | ||
| const std::string var; | ||
| const std::optional<Conflict> conflict; | ||
| std::shared_ptr<const Node> g_predecessor; | ||
|
|
||
| Unlock(const std::string var) : var(var) {} | ||
| Unlock(const std::string var, std::optional<Conflict> conflict = std::nullopt, | ||
| std::shared_ptr<const Node> g_predecessor = nullptr) | ||
| : var(var), conflict(conflict), g_predecessor(g_predecessor) {} | ||
|
|
||
| void accept(Visitor *v) const override { v->visitUnlock(this); } |
EliasC
left a comment
There was a problem hiding this comment.
LGTM. Let's see if copilot finds anything
There was a problem hiding this comment.
Duplicate of examples/accept/semantics/linear/volatile_sync.gm
There was a problem hiding this comment.
Duplicate of examples/reject/semantics/linear/volatile_sync_conflict.gm
…te statements to allow for correct schuedling decisions in the model explorer. Correct some missing volatile wf trieste info, and render the dump correctly
…te temporaries to allow for the multiple scheduling points. This includes re-rerendering the original line and rendering the new line in the explorer output
No description provided.