diff --git a/divine/vm/context.hpp b/divine/vm/context.hpp
deleted file mode 100644
index 9ef097262f37ef701e70eb39c5fbfd9475fac444..0000000000000000000000000000000000000000
--- a/divine/vm/context.hpp
+++ /dev/null
@@ -1,592 +0,0 @@
-// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*-
-
-/*
- * (c) 2016 Petr RoÄŤkai <code@fixp.eu>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#pragma once
-
-#include <divine/vm/loops.hpp>
-#include <divine/vm/frame.hpp>
-#include <divine/vm/value.hpp>
-#include <divine/vm/memory.hpp>
-#include <divine/vm/types.hpp>
-#include <divine/vm/divm.h>
-
-#include <brick-data>
-#include <brick-types>
-
-#include <unordered_set>
-
-namespace llvm { class Value; }
-
-/* The context of a computation, takes care of the following aspects:
- *  - storing and interacting with control registers (ctx_ctlreg)
- *  - interaction with the heap (ctx_heap)
- *  - debug mode and tracing (ctx_trace, ctx_debug)
- *  - taking and restoring snapshots of the computation (ctx_snapshot)
- *  - hold on to the program -- harvard architecture (ctx_program)
- *  - keeping track of faults (ctx_fault)
- *  - cache the internal pointers of 'important' objects (ctx_ptr2i)
- *
- * There are some dependencies:
- *   - ctx_ctlreg, ctx_trace and ctx_program have no further dependencies
- *   - ctx_heap needs to interact with the active frame and hence needs ctx_ctlreg
- *   - ctx_ptr2i needs registers and heap
- *   - ctx_debug needs to look at control flags and snapshot/rollback the heap: ctx_ctlreg & ctx_heap
- *     plus it needs to invalidate the ptr2i cache
- *   - ctx_snapshot needs to invalidate the ptr2i cache
- *   - ctx_fault needs to ctx_program and ctx_ctlreg to construct the fault handler frame */
-
-namespace divine::vm
-{
-    struct TraceSchedChoice { value::Pointer list; };
-
-    using Location = _VM_Operand::Location;
-    using PtrRegister = GenericPointer;
-    using IntRegister = uint64_t;
-
-    template< typename... Ts >
-    struct ctx_mod : Ts... {};
-
-    /* state of a computation */
-    struct State
-    {
-        std::array< PtrRegister, _VM_CR_PtrCount > ptr;
-        std::array< PtrRegister, _VM_CR_UserCount > user;
-        IntRegister flags = 0, objid_shuffle = 0;
-        uint32_t instruction_counter = 0;
-    };
-
-    struct ctx_ctlreg
-    {
-        State _state; /* TODO make this a ref */
-        PtrRegister _pc;
-        PtrRegister _fault_handler, _state_ptr, _scheduler;
-
-        void set( _VM_ControlRegister r, uint64_t v )
-        {
-            ASSERT( r == _VM_CR_Flags || r == _VM_CR_ObjIdShuffle );
-            if ( r == _VM_CR_Flags )
-                _state.flags = v;
-            else
-                _state.objid_shuffle = v;
-        }
-
-        PtrRegister &_ref_ptr( _VM_ControlRegister r )
-        {
-            ASSERT( r != _VM_CR_ObjIdShuffle && r != _VM_CR_Flags );
-
-            if ( r < _VM_CR_PtrCount )
-                return _state.ptr[ r ];
-            else if ( r < _VM_CR_PtrCount + _VM_CR_UserCount )
-                return _state.user[ r - _VM_CR_User1 ];
-            else if ( r == _VM_CR_Scheduler )
-                return _scheduler;
-            else if ( r == _VM_CR_FaultHandler )
-                return _fault_handler;
-            else if ( r == _VM_CR_State )
-                return _state_ptr;
-            else if ( r == _VM_CR_PC )
-                return _pc;
-            else __builtin_unreachable();
-        }
-
-        PtrRegister _ref_ptr( _VM_ControlRegister r ) const
-        {
-            return const_cast< ctx_ctlreg * >( this )->_ref_ptr( r );
-        }
-
-        void set( _VM_ControlRegister r, GenericPointer v )
-        {
-            _ref_ptr( r ) = v;
-        }
-
-        PtrRegister get_ptr( _VM_ControlRegister r ) const
-        {
-            return _ref_ptr( r );
-        }
-
-        IntRegister get_int( _VM_ControlRegister r ) const
-        {
-            if ( r == _VM_CR_Flags )
-                return _state.flags;
-            if ( r == _VM_CR_ObjIdShuffle )
-                return _state.objid_shuffle;
-            __builtin_unreachable();
-        }
-
-        uint32_t instruction_count() { return _state.instruction_counter; }
-        void instruction_count( uint32_t v ) { _state.instruction_counter = v; }
-
-        void pc( PtrRegister pc ) { _pc = pc; }
-
-        PtrRegister pc() { return _pc; }
-        PtrRegister frame() { return _state.ptr[ _VM_CR_Frame ]; }
-        PtrRegister globals() { return _state.ptr[ _VM_CR_Globals ]; }
-        PtrRegister constants() { return _state.ptr[ _VM_CR_Constants ]; }
-        IntRegister &objid_shuffle() { return _state.objid_shuffle; }
-
-        PtrRegister fault_handler() { return _fault_handler; }
-        PtrRegister state_ptr() { return _state_ptr; }
-        PtrRegister scheduler() { return _scheduler; }
-
-        bool in_kernel() { return _state.flags & _VM_CF_KernelMode; }
-
-        uint64_t flags() { return _state.flags; }
-        bool flags_any( uint64_t f ) { return _state.flags & f; }
-        bool flags_all( uint64_t f ) { return ( _state.flags & f ) == f; }
-        void flags_set( uint64_t clear, uint64_t set )
-        {
-            _state.flags &= ~clear;
-            _state.flags |=  set;
-        }
-    };
-
-    template< typename heap_t, typename next >
-    struct ctx_heap : next
-    {
-        using Heap = heap_t;
-        heap_t _heap;
-
-        heap_t &heap() { return _heap; }
-        const heap_t &heap() const { return _heap; }
-
-        void heap( const heap_t &h )
-        {
-            _heap = h;
-            if ( !this->frame().null() )
-                load_pc();
-        }
-
-        auto sync_pc()
-        {
-            if ( this->frame().null() )
-                return typename heap_t::Internal();
-            auto loc = this->heap().loc( this->frame(), ptr2i( Location( _VM_CR_Frame ) ) );
-            return this->heap().write( loc, PointerV( this->pc() ) );
-        }
-
-        void load_pc()
-        {
-            PointerV pc;
-            this->heap().read( this->frame(), pc );
-            this->pc( pc.cooked() );
-        }
-
-        typename Heap::Internal ptr2i( Location l )
-        {
-            return this->heap().ptr2i( this->get_ptr( _VM_ControlRegister( l ) ) );
-        }
-
-        void ptr2i( Location, typename Heap::Internal ) {}
-        void flush_ptr2i() {}
-    };
-
-    struct ctx_trace
-    {
-        bool enter_debug() { return false; }
-        void leave_debug() {}
-        bool debug_allowed() { return false; }
-        bool debug_mode() { return false; }
-
-        /* an interface for debug mode implementation */
-        void debug_save() {}
-        void debug_restore() {}
-
-        void entered( CodePointer ) {}
-        void left( CodePointer ) {}
-
-        virtual std::string fault_str() { return "(no info)"; }
-        virtual void fault_clear() {}
-        virtual void doublefault() {}
-        virtual void fault( Fault, HeapPointer, CodePointer ) {}
-        virtual void trace( TraceText ) {}
-        virtual void trace( TraceFault ) {}
-        virtual void trace( TraceDebugPersist ) {}
-        virtual void trace( TraceSchedInfo ) {}
-        virtual void trace( TraceSchedChoice ) {}
-        virtual void trace( TraceTaskID ) {}
-        virtual void trace( TraceStateType ) {}
-        virtual void trace( TraceTypeAlias ) {}
-        virtual void trace( TraceInfo ) {}
-        virtual void trace( TraceAssume ) {}
-        virtual void trace( TraceLeakCheck ) {}
-        virtual void trace( std::string ) {}
-        virtual ~ctx_trace() {}
-    };
-
-    template< typename program_t >
-    struct ctx_program
-    {
-        using Program = program_t;
-        Program *_program;
-        Program &program() { return *_program; }
-        void program( Program &p ) { _program = &p; }
-    };
-
-    template< typename next >
-    struct ctx_choose_0 : next
-    {
-        template< typename I >
-        int choose( int, I, I ) { return 0; }
-    };
-
-    template< typename heap, typename program >
-    struct ctx_base
-    {
-        template< typename >
-        struct module : ctx_heap< heap, ctx_mod< ctx_trace, ctx_ctlreg, ctx_program< program > > >
-        {
-            static constexpr const bool uses_ptr2i = false;
-            static constexpr const bool has_debug_mode = false;
-        };
-    };
-
-    template< typename next >
-    struct track_nothing : next
-    {
-        static_assert( !next::has_debug_mode );
-
-        bool test_loop( CodePointer, int )
-        {
-            return !this->flags_all( _VM_CF_IgnoreLoop );
-        }
-
-        bool test_crit( CodePointer, GenericPointer, int, int )
-        {
-            return !this->flags_all( _VM_CF_IgnoreCrit );
-        }
-
-        bool track_test( Interrupt::Type, CodePointer ) { return true; }
-        void reset_interrupted() {}
-    };
-
-    /* Implementation of debug mode. Include in the context stack when you want
-     * dbg.call to be actually performed. Not including ctx_debug in the context
-     * saves considerable resources (especially computation time). */
-
-    template< typename next >
-    struct ctx_debug : next
-    {
-        State _debug_state;
-        PtrRegister _debug_pc;
-
-        int _debug_depth = 0;
-        bool _debug_allowed = false;
-        std::vector< Interrupt > _interrupts;
-
-        static constexpr const bool uses_ptr2i = true;
-        static constexpr const bool has_debug_mode = true;
-
-        bool track_test( Interrupt::Type t, CodePointer pc )
-        {
-            _interrupts.push_back( Interrupt{ t, this->_state.instruction_counter, pc } );
-            return true;
-        }
-
-        bool debug_allowed() { return _debug_allowed; }
-        bool debug_mode() { return this->flags_any( _VM_CF_DebugMode ); }
-        void enable_debug() { _debug_allowed = true; }
-
-        bool test_loop( CodePointer p, int i )
-        {
-            return !debug_mode() && next::test_loop( p, i );
-        }
-
-        bool test_crit( CodePointer p, GenericPointer a, int s, int t )
-        {
-            return !debug_mode() && next::test_crit( p, a, s, t );
-        }
-
-        bool enter_debug();
-        void leave_debug();
-
-        void debug_save();
-        void debug_restore();
-
-        using next::set;
-
-        void set( _VM_ControlRegister r, uint64_t v )
-        {
-            next::set( r, v );
-            if ( _debug_depth )
-                ASSERT( debug_mode() );
-        }
-
-        void entered( CodePointer f )
-        {
-            if ( debug_mode() )
-                ++ _debug_depth;
-            else
-                next::entered( f );
-        }
-
-        void left( CodePointer f )
-        {
-            if ( debug_mode() )
-            {
-                ASSERT_LEQ( 1, _debug_depth );
-                -- _debug_depth;
-                if ( !_debug_depth )
-                    leave_debug();
-            }
-            else
-                next::left( f );
-        }
-
-        std::vector< HeapPointer > _debug_persist;
-        typename next::Heap::Pool _debug_pool;
-        typename next::Heap::Snapshot _debug_snap;
-
-        using next::trace;
-
-        virtual void trace( TraceDebugPersist t )
-        {
-            if ( t.ptr.type() == PointerType::Weak )
-                _debug_persist.push_back( t.ptr );
-            else
-                {
-                    this->trace( "FAULT: cannot persist a non-weak object " +
-                                 brick::string::fmt( t.ptr ) );
-                    this->fault( _VM_F_Memory, this->frame(), this->pc() );
-                }
-        }
-    };
-
-    template< typename next >
-    struct ctx_ptr2i : next
-    {
-        using Heap = typename next::Heap;
-        using HeapInternal = typename Heap::Internal;
-        using Snapshot = typename Heap::Snapshot;
-
-        static_assert( !next::uses_ptr2i );
-        std::array< HeapInternal, _VM_CR_PtrCount > _ptr2i;
-
-        using next::set;
-
-        void set( _VM_ControlRegister r, GenericPointer v )
-        {
-            if ( r < _VM_CR_PtrCount )
-                _ptr2i[ r ] = v.null() ? HeapInternal() : this->heap().ptr2i( v );
-            next::set( r, v );
-        }
-
-        HeapInternal ptr2i( Location l )
-        {
-            ASSERT_LT( l, _VM_Operand::Invalid );
-            ASSERT_EQ( next::ptr2i( l ), _ptr2i[ l ] );
-            return _ptr2i[ l ];
-        }
-
-        void ptr2i( Location l, HeapInternal i )
-        {
-            if ( i )
-                _ptr2i[ l ] = i;
-            else
-                flush_ptr2i();
-        }
-
-        void flush_ptr2i()
-        {
-            for ( int i = 0; i < _VM_CR_PtrCount; ++i )
-                _ptr2i[ i ] = next::ptr2i( Location( i ) );
-        }
-
-        auto sync_pc()
-        {
-            auto newfr = next::sync_pc();
-            ptr2i( Location( _VM_CR_Frame ), newfr );
-            return newfr;
-        }
-
-        void load_pc()
-        {
-            PointerV pc;
-            auto loc = this->heap().loc( this->frame(), ptr2i( Location( _VM_CR_Frame ) ) );
-            this->heap().read( loc, pc );
-            this->_pc = pc.cooked();
-        }
-    };
-
-    template< typename next >
-    struct ctx_snapshot : next
-    {
-        using Heap = typename next::Heap;
-        using Snapshot = typename Heap::Snapshot;
-
-        static constexpr const bool uses_ptr2i = true;
-
-        Snapshot snapshot( typename Heap::Pool &p )
-        {
-            this->sync_pc();
-            auto rv = this->heap().snapshot( p );
-            this->flush_ptr2i();
-            return rv;
-        }
-
-        using next::trace;
-        virtual void trace( TraceText tt ) { trace( this->heap().read_string( tt.text ) ); }
-    };
-
-    template< typename next >
-    struct ctx_fault : next
-    {
-        std::string _fault;
-
-        using next::trace;
-        void trace( TraceFault tt ) override { _fault = tt.string; }
-
-        void fault_clear() override { _fault.clear(); }
-        std::string fault_str() override { return _fault; }
-
-        void doublefault() override
-        {
-            this->flags_set( 0, _VM_CF_Error | _VM_CF_Stop );
-        }
-
-        void fault( Fault f, HeapPointer frame, CodePointer pc ) override
-        {
-            auto fh = this->fault_handler();
-            if ( this->debug_mode() )
-            {
-                this->trace( "W: " + this->fault_str() + " in debug mode (abandoned)" );
-                this->fault_clear();
-                this->_debug_depth = 0; /* short-circuit */
-                this->leave_debug();
-            }
-            else if ( fh.null() )
-            {
-                this->trace( "FATAL: no fault handler installed" );
-                doublefault();
-            }
-            else
-                make_frame( *this, fh, PointerV( this->frame() ),
-                            value::Int< 32 >( f ), PointerV( frame ), PointerV( pc ) );
-        }
-    };
-
-    template< typename next >
-    struct ctx_legacy : next
-    {
-        using Program = typename next::Program;
-        using Heap = typename next::Heap;
-        using Pool = typename Heap::Pool;
-        using PointerV = value::Pointer;
-        using Location = typename Program::Slot::Location;
-        using Snapshot = typename Heap::Snapshot;
-        using next::_heap;
-
-        static constexpr const bool uses_ptr2i = true;
-
-        using next::trace;
-        virtual void trace( TraceLeakCheck ) override;
-
-        bool _track_mem = false;
-
-        using MemMap = brick::data::IntervalSet< GenericPointer >;
-
-        void track_memory( bool b ) { _track_mem = b; }
-
-        template< typename Ctx >
-        void load( const Ctx &ctx )
-        {
-            clear();
-            for ( int i = 0; i < _VM_CR_Last; ++i )
-            {
-                auto cr = _VM_ControlRegister( i );
-                if ( cr == _VM_CR_Flags || cr == _VM_CR_ObjIdShuffle )
-                    this->set( cr, ctx.get_int( cr ) );
-                else
-                    this->set( cr, ctx.get_ptr( cr ) );
-            }
-            ASSERT( !this->debug_mode() );
-            this->_heap = ctx.heap();
-        }
-
-        virtual void clear()
-        {
-            this->_fault.clear();
-            this->_interrupts.clear();
-            this->reset_interrupted();
-            this->flush_ptr2i();
-            this->set( _VM_CR_User1, GenericPointer() );
-            this->set( _VM_CR_User2, GenericPointer() );
-            this->set( _VM_CR_User3, GenericPointer() );
-            this->set( _VM_CR_User4, GenericPointer() );
-            this->set( _VM_CR_ObjIdShuffle, 0 );
-            this->_state.instruction_counter = 0;
-        }
-
-        void load( Pool &p, Snapshot snap ) { _heap.restore( p, snap ); clear(); }
-        void reset() { _heap.reset(); clear(); }
-
-        HeapPointer frame() { return next::frame(); }
-        HeapPointer globals() { return next::globals(); }
-        HeapPointer constants() { return next::constants(); }
-
-        bool test_loop( CodePointer pc, int ctr )
-        {
-            if ( this->flags_all( _VM_CF_IgnoreLoop ) || this->debug_mode() )
-                return false;
-
-            if ( next::test_loop( pc, ctr ) )
-                return this->track_test( Interrupt::Cfl, pc );
-            else
-                return false;
-        }
-
-        void count_instruction()
-        {
-            if ( !this->debug_mode() )
-                ++ this->_state.instruction_counter;
-        }
-    };
-
-    template< template< typename > class mod >
-    struct m
-    {
-        template < typename next >
-        using module = mod< next >;
-    };
-
-    template< typename... > struct compose {};
-
-    template< typename head, typename... tail >
-    struct compose< head, tail... > : head::template module< compose< tail... > >
-    {
-    };
-
-    template<>
-    struct compose<> {};
-
-    template< typename Program, typename Heap >
-    struct Context : compose< m< ctx_legacy >, m< ctx_fault >, m< ctx_debug >,
-                              m< track_loops >, m< track_nothing >, m< ctx_choose_0 >,
-                              m< ctx_snapshot >, m< ctx_ptr2i >, ctx_base< Heap, Program > > {};
-
-    template< typename program_t, typename heap >
-    struct ctx_const : compose< m< ctx_choose_0 >, m< track_nothing >, ctx_base< heap, program_t > >
-    {
-        void setup( int gds, int cds )
-        {
-            this->set( _VM_CR_Constants, this->heap().make( cds ).cooked() );
-            if ( gds )
-                this->set( _VM_CR_Globals, this->heap().make( gds ).cooked() );
-        }
-    };
-
-}
diff --git a/divine/vm/ctx-base.hpp b/divine/vm/ctx-base.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0cdd3193b5caf619c4f804ef79e5fb8024e12bd5
--- /dev/null
+++ b/divine/vm/ctx-base.hpp
@@ -0,0 +1,278 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+
+/*
+ * (c) 2016 Petr RoÄŤkai <code@fixp.eu>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#pragma once
+
+#include <divine/vm/value.hpp>
+#include <divine/vm/memory.hpp>
+#include <divine/vm/types.hpp>
+#include <divine/vm/divm.h>
+
+/* The context of a computation, takes care of the following aspects:
+ *  - storing and interacting with control registers (ctx_ctlreg)
+ *  - interaction with the heap (ctx_heap)
+ *  - debug mode and tracing (ctx_trace, ctx_debug)
+ *  - taking and restoring snapshots of the computation (ctx_snapshot)
+ *  - hold on to the program -- harvard architecture (ctx_program)
+ *  - keeping track of faults (ctx_fault)
+ *  - cache the internal pointers of 'important' objects (ctx_ptr2i)
+ *
+ * There are some dependencies:
+ *   - ctx_ctlreg, ctx_trace and ctx_program have no further dependencies
+ *   - ctx_heap needs to interact with the active frame and hence needs ctx_ctlreg
+ *   - ctx_ptr2i needs registers and heap
+ *   - ctx_debug needs to look at control flags and snapshot/rollback the heap: ctx_ctlreg & ctx_heap
+ *     plus it needs to invalidate the ptr2i cache
+ *   - ctx_snapshot needs to invalidate the ptr2i cache
+ *   - ctx_fault needs to ctx_program and ctx_ctlreg to construct the fault handler frame */
+
+namespace divine::vm
+{
+    struct TraceSchedChoice { value::Pointer list; };
+
+    using Location = _VM_Operand::Location;
+    using PtrRegister = GenericPointer;
+    using IntRegister = uint64_t;
+
+    template< typename... > struct compose {};
+
+    template< typename head, typename... tail >
+    struct compose< head, tail... >
+    {
+        template< typename next >
+        struct module : head::template module< typename compose< tail... >::template module< next > >
+        {};
+    };
+
+    template<>
+    struct compose<>
+    {
+        template< typename next >
+        struct module : next {};
+    };
+
+    struct empty {};
+
+    template< typename... comps >
+    using compose_stack = typename compose< comps... >::template module< empty >;
+}
+
+namespace divine::vm::ctx
+{
+    template< template< typename > class mod >
+    struct m
+    {
+        template < typename next >
+        using module = mod< next >;
+    };
+
+    /* state of a computation */
+    struct State
+    {
+        std::array< PtrRegister, _VM_CR_PtrCount > ptr;
+        std::array< PtrRegister, _VM_CR_UserCount > user;
+        IntRegister flags = 0, objid_shuffle = 0;
+        uint32_t instruction_counter = 0;
+    };
+
+    struct ctx_ctlreg
+    {
+        State _state; /* TODO make this a ref */
+        PtrRegister _pc;
+        PtrRegister _fault_handler, _state_ptr, _scheduler;
+
+        void set( _VM_ControlRegister r, uint64_t v )
+        {
+            ASSERT( r == _VM_CR_Flags || r == _VM_CR_ObjIdShuffle );
+            if ( r == _VM_CR_Flags )
+                _state.flags = v;
+            else
+                _state.objid_shuffle = v;
+        }
+
+        PtrRegister &_ref_ptr( _VM_ControlRegister r )
+        {
+            ASSERT( r != _VM_CR_ObjIdShuffle && r != _VM_CR_Flags );
+
+            if ( r < _VM_CR_PtrCount )
+                return _state.ptr[ r ];
+            else if ( r < _VM_CR_PtrCount + _VM_CR_UserCount )
+                return _state.user[ r - _VM_CR_User1 ];
+            else if ( r == _VM_CR_Scheduler )
+                return _scheduler;
+            else if ( r == _VM_CR_FaultHandler )
+                return _fault_handler;
+            else if ( r == _VM_CR_State )
+                return _state_ptr;
+            else if ( r == _VM_CR_PC )
+                return _pc;
+            else __builtin_unreachable();
+        }
+
+        PtrRegister _ref_ptr( _VM_ControlRegister r ) const
+        {
+            return const_cast< ctx_ctlreg * >( this )->_ref_ptr( r );
+        }
+
+        void set( _VM_ControlRegister r, GenericPointer v )
+        {
+            _ref_ptr( r ) = v;
+        }
+
+        PtrRegister get_ptr( _VM_ControlRegister r ) const
+        {
+            return _ref_ptr( r );
+        }
+
+        IntRegister get_int( _VM_ControlRegister r ) const
+        {
+            if ( r == _VM_CR_Flags )
+                return _state.flags;
+            if ( r == _VM_CR_ObjIdShuffle )
+                return _state.objid_shuffle;
+            __builtin_unreachable();
+        }
+
+        uint32_t instruction_count() { return _state.instruction_counter; }
+        void instruction_count( uint32_t v ) { _state.instruction_counter = v; }
+
+        void pc( PtrRegister pc ) { _pc = pc; }
+
+        PtrRegister pc() { return _pc; }
+        PtrRegister frame() { return _state.ptr[ _VM_CR_Frame ]; }
+        PtrRegister globals() { return _state.ptr[ _VM_CR_Globals ]; }
+        PtrRegister constants() { return _state.ptr[ _VM_CR_Constants ]; }
+        IntRegister &objid_shuffle() { return _state.objid_shuffle; }
+
+        PtrRegister fault_handler() { return _fault_handler; }
+        PtrRegister state_ptr() { return _state_ptr; }
+        PtrRegister scheduler() { return _scheduler; }
+
+        bool in_kernel() { return _state.flags & _VM_CF_KernelMode; }
+
+        uint64_t flags() { return _state.flags; }
+        bool flags_any( uint64_t f ) { return _state.flags & f; }
+        bool flags_all( uint64_t f ) { return ( _state.flags & f ) == f; }
+        void flags_set( uint64_t clear, uint64_t set )
+        {
+            _state.flags &= ~clear;
+            _state.flags |=  set;
+        }
+    };
+
+    template< typename heap_t, typename next >
+    struct ctx_heap : next
+    {
+        using Heap = heap_t;
+        heap_t _heap;
+
+        heap_t &heap() { return _heap; }
+        const heap_t &heap() const { return _heap; }
+
+        void heap( const heap_t &h )
+        {
+            _heap = h;
+            if ( !this->frame().null() )
+                load_pc();
+        }
+
+        auto sync_pc()
+        {
+            if ( this->frame().null() )
+                return typename heap_t::Internal();
+            auto loc = this->heap().loc( this->frame(), ptr2i( Location( _VM_CR_Frame ) ) );
+            return this->heap().write( loc, PointerV( this->pc() ) );
+        }
+
+        void load_pc()
+        {
+            PointerV pc;
+            this->heap().read( this->frame(), pc );
+            this->pc( pc.cooked() );
+        }
+
+        typename Heap::Internal ptr2i( Location l )
+        {
+            return this->heap().ptr2i( this->get_ptr( _VM_ControlRegister( l ) ) );
+        }
+
+        void ptr2i( Location, typename Heap::Internal ) {}
+        void flush_ptr2i() {}
+    };
+
+    struct ctx_trace
+    {
+        bool enter_debug() { return false; }
+        void leave_debug() {}
+        bool debug_allowed() { return false; }
+        bool debug_mode() { return false; }
+
+        /* an interface for debug mode implementation */
+        void debug_save() {}
+        void debug_restore() {}
+
+        void entered( CodePointer ) {}
+        void left( CodePointer ) {}
+
+        virtual std::string fault_str() { return "(no info)"; }
+        virtual void fault_clear() {}
+        virtual void doublefault() {}
+        virtual void fault( Fault, HeapPointer, CodePointer ) {}
+        virtual void trace( TraceText ) {}
+        virtual void trace( TraceFault ) {}
+        virtual void trace( TraceDebugPersist ) {}
+        virtual void trace( TraceSchedInfo ) {}
+        virtual void trace( TraceSchedChoice ) {}
+        virtual void trace( TraceTaskID ) {}
+        virtual void trace( TraceStateType ) {}
+        virtual void trace( TraceTypeAlias ) {}
+        virtual void trace( TraceInfo ) {}
+        virtual void trace( TraceAssume ) {}
+        virtual void trace( TraceLeakCheck ) {}
+        virtual void trace( std::string ) {}
+        virtual ~ctx_trace() {}
+    };
+
+    template< typename program_t >
+    struct ctx_program
+    {
+        using Program = program_t;
+        Program *_program;
+        Program &program() { return *_program; }
+        void program( Program &p ) { _program = &p; }
+    };
+
+    struct ctx_choose_0
+    {
+        template< typename I >
+        int choose( int, I, I ) { return 0; }
+    };
+
+    template< typename prog, typename heap >
+    struct base
+    {
+        struct basement : ctx_choose_0, ctx_trace, ctx_ctlreg, ctx_program< prog > {};
+
+        template< typename >
+        struct module : ctx_heap< heap, basement >
+        {
+            static constexpr const bool uses_ptr2i = false;
+            static constexpr const bool has_debug_mode = false;
+        };
+    };
+}
diff --git a/divine/vm/ctx-debug.hpp b/divine/vm/ctx-debug.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4ee4dd56fdc57ea7d52a2f2eda5b6fff3ab7f796
--- /dev/null
+++ b/divine/vm/ctx-debug.hpp
@@ -0,0 +1,116 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+
+/*
+ * (c) 2016 Petr RoÄŤkai <code@fixp.eu>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#pragma once
+
+namespace divine::vm::ctx
+{
+    /* Implementation of debug mode. Include in the context stack when you want
+     * dbg.call to be actually performed. Not including ctx_debug in the context
+     * saves considerable resources (especially computation time). */
+
+    template< typename next >
+    struct debug_i : next
+    {
+        State _debug_state;
+        PtrRegister _debug_pc;
+
+        int _debug_depth = 0;
+        bool _debug_allowed = false;
+        std::vector< Interrupt > _interrupts;
+
+        static constexpr const bool uses_ptr2i = true;
+        static constexpr const bool has_debug_mode = true;
+
+        bool track_test( Interrupt::Type t, CodePointer pc )
+        {
+            _interrupts.push_back( Interrupt{ t, this->_state.instruction_counter, pc } );
+            return true;
+        }
+
+        bool debug_allowed() { return _debug_allowed; }
+        bool debug_mode() { return this->flags_any( _VM_CF_DebugMode ); }
+        void enable_debug() { _debug_allowed = true; }
+
+        bool test_loop( CodePointer p, int i )
+        {
+            return !debug_mode() && next::test_loop( p, i );
+        }
+
+        bool test_crit( CodePointer p, GenericPointer a, int s, int t )
+        {
+            return !debug_mode() && next::test_crit( p, a, s, t );
+        }
+
+        bool enter_debug();
+        void leave_debug();
+
+        void debug_save();
+        void debug_restore();
+
+        using next::set;
+
+        void set( _VM_ControlRegister r, uint64_t v )
+        {
+            next::set( r, v );
+            if ( _debug_depth )
+                ASSERT( debug_mode() );
+        }
+
+        void entered( CodePointer f )
+        {
+            if ( debug_mode() )
+                ++ _debug_depth;
+            else
+                next::entered( f );
+        }
+
+        void left( CodePointer f )
+        {
+            if ( debug_mode() )
+            {
+                ASSERT_LEQ( 1, _debug_depth );
+                -- _debug_depth;
+                if ( !_debug_depth )
+                    leave_debug();
+            }
+            else
+                next::left( f );
+        }
+
+        std::vector< HeapPointer > _debug_persist;
+        typename next::Heap::Pool _debug_pool;
+        typename next::Heap::Snapshot _debug_snap;
+
+        using next::trace;
+
+        virtual void trace( TraceDebugPersist t )
+        {
+            if ( t.ptr.type() == PointerType::Weak )
+                _debug_persist.push_back( t.ptr );
+            else
+                {
+                    this->trace( "FAULT: cannot persist a non-weak object " +
+                                 brick::string::fmt( t.ptr ) );
+                    this->fault( _VM_F_Memory, this->frame(), this->pc() );
+                }
+        }
+    };
+
+    using debug = m< debug_i >;
+}
diff --git a/divine/vm/context.tpp b/divine/vm/ctx-debug.tpp
similarity index 94%
rename from divine/vm/context.tpp
rename to divine/vm/ctx-debug.tpp
index 0243ab9dc16692f33b5b56d6ccc7a4dc2aee2adc..a7853f01d445b9759658c432e56ebe25efd59499 100644
--- a/divine/vm/context.tpp
+++ b/divine/vm/ctx-debug.tpp
@@ -21,11 +21,11 @@
 #include <divine/vm/memory.tpp>
 #include <divine/vm/program.hpp>
 
-namespace divine::vm
+namespace divine::vm::ctx
 {
 
     template< typename next >
-    void ctx_debug< next >::debug_save()
+    void debug_i< next >::debug_save()
     {
         if ( this->heap().can_snapshot() )
         {
@@ -35,7 +35,7 @@ namespace divine::vm
     }
 
     template< typename next >
-    void ctx_debug< next >::debug_restore()
+    void debug_i< next >::debug_restore()
     {
         if ( !this->heap().can_snapshot() )
             return;
@@ -63,7 +63,7 @@ namespace divine::vm
     }
 
     template< typename next >
-    void ctx_legacy< next >::trace( TraceLeakCheck )
+    void legacy_i< next >::trace( TraceLeakCheck )
     {
         bool flagged = false;
         auto leak = [&]( HeapPointer ptr )
@@ -96,7 +96,7 @@ namespace divine::vm
     }
 
     template< typename next >
-    bool ctx_debug< next >::enter_debug()
+    bool debug_i< next >::enter_debug()
     {
         if ( !debug_allowed() )
             -- this->_state.instruction_counter; /* dbg.call does not count */
@@ -117,7 +117,7 @@ namespace divine::vm
     }
 
     template< typename next >
-    void ctx_debug< next >::leave_debug()
+    void debug_i< next >::leave_debug()
     {
         ASSERT( debug_allowed() );
         ASSERT( debug_mode() );
diff --git a/divine/vm/ctx-fault.hpp b/divine/vm/ctx-fault.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..af1a60687c53d2dd93e020bf630ac5e5f94ff6bc
--- /dev/null
+++ b/divine/vm/ctx-fault.hpp
@@ -0,0 +1,61 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+
+/*
+ * (c) 2016 Petr RoÄŤkai <code@fixp.eu>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#pragma once
+
+namespace divine::vm::ctx
+{
+    template< typename next >
+    struct fault_i : next
+    {
+        std::string _fault;
+
+        using next::trace;
+        void trace( TraceFault tt ) override { _fault = tt.string; }
+
+        void fault_clear() override { _fault.clear(); }
+        std::string fault_str() override { return _fault; }
+
+        void doublefault() override
+        {
+            this->flags_set( 0, _VM_CF_Error | _VM_CF_Stop );
+        }
+
+        void fault( Fault f, HeapPointer frame, CodePointer pc ) override
+        {
+            auto fh = this->fault_handler();
+            if ( this->debug_mode() )
+            {
+                this->trace( "W: " + this->fault_str() + " in debug mode (abandoned)" );
+                this->fault_clear();
+                this->_debug_depth = 0; /* short-circuit */
+                this->leave_debug();
+            }
+            else if ( fh.null() )
+            {
+                this->trace( "FATAL: no fault handler installed" );
+                doublefault();
+            }
+            else
+                make_frame( *this, fh, PointerV( this->frame() ),
+                            value::Int< 32 >( f ), PointerV( frame ), PointerV( pc ) );
+        }
+    };
+
+    using fault = m< fault_i >;
+}
diff --git a/divine/vm/frame.hpp b/divine/vm/ctx-frame.hpp
similarity index 100%
rename from divine/vm/frame.hpp
rename to divine/vm/ctx-frame.hpp
diff --git a/divine/vm/ctx-legacy.hpp b/divine/vm/ctx-legacy.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e49bfecc7750babe607424109921c7234461394c
--- /dev/null
+++ b/divine/vm/ctx-legacy.hpp
@@ -0,0 +1,102 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+
+/*
+ * (c) 2016 Petr RoÄŤkai <code@fixp.eu>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#pragma once
+#include <brick-data>
+
+namespace divine::vm::ctx
+{
+    template< typename next >
+    struct legacy_i : next
+    {
+        using Program = typename next::Program;
+        using Heap = typename next::Heap;
+        using Pool = typename Heap::Pool;
+        using PointerV = value::Pointer;
+        using Location = typename Program::Slot::Location;
+        using Snapshot = typename Heap::Snapshot;
+        using next::_heap;
+
+        static constexpr const bool uses_ptr2i = true;
+
+        using next::trace;
+        virtual void trace( TraceLeakCheck ) override;
+
+        bool _track_mem = false;
+
+        using MemMap = brick::data::IntervalSet< GenericPointer >;
+
+        void track_memory( bool b ) { _track_mem = b; }
+
+        template< typename Ctx >
+        void load( const Ctx &ctx )
+        {
+            clear();
+            for ( int i = 0; i < _VM_CR_Last; ++i )
+            {
+                auto cr = _VM_ControlRegister( i );
+                if ( cr == _VM_CR_Flags || cr == _VM_CR_ObjIdShuffle )
+                    this->set( cr, ctx.get_int( cr ) );
+                else
+                    this->set( cr, ctx.get_ptr( cr ) );
+            }
+            ASSERT( !this->debug_mode() );
+            this->_heap = ctx.heap();
+        }
+
+        virtual void clear()
+        {
+            this->_fault.clear();
+            this->_interrupts.clear();
+            this->reset_interrupted();
+            this->flush_ptr2i();
+            this->set( _VM_CR_User1, GenericPointer() );
+            this->set( _VM_CR_User2, GenericPointer() );
+            this->set( _VM_CR_User3, GenericPointer() );
+            this->set( _VM_CR_User4, GenericPointer() );
+            this->set( _VM_CR_ObjIdShuffle, 0 );
+            this->_state.instruction_counter = 0;
+        }
+
+        void load( Pool &p, Snapshot snap ) { _heap.restore( p, snap ); clear(); }
+        void reset() { _heap.reset(); clear(); }
+
+        HeapPointer frame() { return next::frame(); }
+        HeapPointer globals() { return next::globals(); }
+        HeapPointer constants() { return next::constants(); }
+
+        bool test_loop( CodePointer pc, int ctr )
+        {
+            if ( this->flags_all( _VM_CF_IgnoreLoop ) || this->debug_mode() )
+                return false;
+
+            if ( next::test_loop( pc, ctr ) )
+                return this->track_test( Interrupt::Cfl, pc );
+            else
+                return false;
+        }
+
+        void count_instruction()
+        {
+            if ( !this->debug_mode() )
+                ++ this->_state.instruction_counter;
+        }
+    };
+
+    using legacy = m< legacy_i >;
+}
diff --git a/divine/vm/ctx-memory.hpp b/divine/vm/ctx-memory.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7b370e59fb2cdb288f31edb36cc021d9bbacf3a5
--- /dev/null
+++ b/divine/vm/ctx-memory.hpp
@@ -0,0 +1,101 @@
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+
+/*
+ * (c) 2016 Petr RoÄŤkai <code@fixp.eu>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#pragma once
+
+namespace divine::vm::ctx
+{
+    template< typename next >
+    struct ptr2i_i : next
+    {
+        using Heap = typename next::Heap;
+        using HeapInternal = typename Heap::Internal;
+        using Snapshot = typename Heap::Snapshot;
+
+        static_assert( !next::uses_ptr2i );
+        std::array< HeapInternal, _VM_CR_PtrCount > _ptr2i;
+
+        using next::set;
+
+        void set( _VM_ControlRegister r, GenericPointer v )
+        {
+            if ( r < _VM_CR_PtrCount )
+                _ptr2i[ r ] = v.null() ? HeapInternal() : this->heap().ptr2i( v );
+            next::set( r, v );
+        }
+
+        HeapInternal ptr2i( Location l )
+        {
+            ASSERT_LT( l, _VM_Operand::Invalid );
+            ASSERT_EQ( next::ptr2i( l ), _ptr2i[ l ] );
+            return _ptr2i[ l ];
+        }
+
+        void ptr2i( Location l, HeapInternal i )
+        {
+            if ( i )
+                _ptr2i[ l ] = i;
+            else
+                flush_ptr2i();
+        }
+
+        void flush_ptr2i()
+        {
+            for ( int i = 0; i < _VM_CR_PtrCount; ++i )
+                _ptr2i[ i ] = next::ptr2i( Location( i ) );
+        }
+
+        auto sync_pc()
+        {
+            auto newfr = next::sync_pc();
+            ptr2i( Location( _VM_CR_Frame ), newfr );
+            return newfr;
+        }
+
+        void load_pc()
+        {
+            PointerV pc;
+            auto loc = this->heap().loc( this->frame(), ptr2i( Location( _VM_CR_Frame ) ) );
+            this->heap().read( loc, pc );
+            this->_pc = pc.cooked();
+        }
+    };
+
+    template< typename next >
+    struct snapshot_i : next
+    {
+        using Heap = typename next::Heap;
+        using Snapshot = typename Heap::Snapshot;
+
+        static constexpr const bool uses_ptr2i = true;
+
+        Snapshot snapshot( typename Heap::Pool &p )
+        {
+            this->sync_pc();
+            auto rv = this->heap().snapshot( p );
+            this->flush_ptr2i();
+            return rv;
+        }
+
+        using next::trace;
+        virtual void trace( TraceText tt ) { trace( this->heap().read_string( tt.text ) ); }
+    };
+
+    using ptr2i = m< ptr2i_i >;
+    using snapshot = m< snapshot_i >;
+}
diff --git a/divine/vm/loops.hpp b/divine/vm/ctx-track.hpp
similarity index 73%
rename from divine/vm/loops.hpp
rename to divine/vm/ctx-track.hpp
index f2bc5c71e7ce4601eb5a1b79e1866887c5418213..a4de28f432e48840ca372533880ca18bd5b5c428 100644
--- a/divine/vm/loops.hpp
+++ b/divine/vm/ctx-track.hpp
@@ -21,10 +21,29 @@
 #include <divine/vm/pointer.hpp>
 #include <unordered_set>
 
-namespace divine::vm
+namespace divine::vm::ctx
 {
     template< typename next >
-    struct track_loops : next
+    struct track_nothing_i : next
+    {
+        static_assert( !next::has_debug_mode );
+
+        bool test_loop( CodePointer, int )
+        {
+            return !this->flags_all( _VM_CF_IgnoreLoop );
+        }
+
+        bool test_crit( CodePointer, GenericPointer, int, int )
+        {
+            return !this->flags_all( _VM_CF_IgnoreCrit );
+        }
+
+        bool track_test( Interrupt::Type, CodePointer ) { return true; }
+        void reset_interrupted() {}
+    };
+
+    template< typename next >
+    struct track_loops_i : next
     {
         static_assert( !next::has_debug_mode );
 
@@ -61,4 +80,7 @@ namespace divine::vm
             loops.emplace_back();
         }
     };
+
+    using track_nothing = m< track_nothing_i >;
+    using track_loops = m< track_loops_i >;
 }
diff --git a/divine/vm/eval.tpp b/divine/vm/eval.tpp
index 5c313eb5a764019eee9506b358f832c41bab1f1c..2996241093e08d66b1990f60c98d6726a57b373c 100644
--- a/divine/vm/eval.tpp
+++ b/divine/vm/eval.tpp
@@ -21,7 +21,7 @@
 #include <divine/vm/eval.hpp>
 #include <divine/vm/memory.tpp>
 #include <divine/vm/opnames.hpp>
-#include <divine/vm/context.tpp>
+#include <divine/vm/ctx-debug.tpp>
 #include <brick-tuple>
 
 namespace divine::vm