Implement Phase 8.5A: Core 25-instruction MIR specification with ownership verification
Co-authored-by: moe-charm <217100418+moe-charm@users.noreply.github.com>
This commit is contained in:
85
mir_25_instruction_mapping.md
Normal file
85
mir_25_instruction_mapping.md
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
# MIR 25-Instruction Mapping Plan
|
||||||
|
|
||||||
|
## Current State: 32 Instructions → Target: 25 Instructions
|
||||||
|
|
||||||
|
### Tier-0: Universal Core (8 instructions)
|
||||||
|
1. **Const** ✅ (already exists)
|
||||||
|
2. **BinOp** ✅ (already exists)
|
||||||
|
3. **Compare** ✅ (already exists)
|
||||||
|
4. **Branch** ✅ (already exists)
|
||||||
|
5. **Jump** ✅ (already exists)
|
||||||
|
6. **Phi** ✅ (already exists)
|
||||||
|
7. **Call** ✅ (already exists)
|
||||||
|
8. **Return** ✅ (already exists)
|
||||||
|
|
||||||
|
### Tier-1: Nyash Semantics (12 instructions)
|
||||||
|
9. **NewBox** ✅ (already exists)
|
||||||
|
10. **BoxFieldLoad** ← RENAME from Load/RefGet
|
||||||
|
11. **BoxFieldStore** ← RENAME from Store/RefSet
|
||||||
|
12. **BoxCall** ✅ (already exists)
|
||||||
|
13. **Safepoint** ✅ (already exists as separate instruction)
|
||||||
|
14. **RefGet** → RENAME to RefGet ✅
|
||||||
|
15. **RefSet** → RENAME to RefSet ✅
|
||||||
|
16. **WeakNew** ✅ (already exists)
|
||||||
|
17. **WeakLoad** ✅ (already exists)
|
||||||
|
18. **WeakCheck** ← NEW (check weak reference validity)
|
||||||
|
19. **Send** ← NEW (Bus communication)
|
||||||
|
20. **Recv** ← NEW (Bus communication)
|
||||||
|
|
||||||
|
### Tier-2: Implementation Assistance (5 instructions)
|
||||||
|
21. **TailCall** ← NEW (tail call optimization)
|
||||||
|
22. **Adopt** ← NEW (ownership transfer)
|
||||||
|
23. **Release** ← NEW (ownership release)
|
||||||
|
24. **MemCopy** ← NEW (optimized memory operations)
|
||||||
|
25. **AtomicFence** ← RENAME from BarrierRead/BarrierWrite
|
||||||
|
|
||||||
|
## Instructions to Remove/Consolidate (7 instructions)
|
||||||
|
- **UnaryOp** → Merge into BinOp or eliminate
|
||||||
|
- **Load** → Consolidate into BoxFieldLoad
|
||||||
|
- **Store** → Consolidate into BoxFieldStore
|
||||||
|
- **ArrayGet** → Use BoxFieldLoad with array indexing
|
||||||
|
- **ArraySet** → Use BoxFieldStore with array indexing
|
||||||
|
- **Cast** → Eliminate or merge into BinOp
|
||||||
|
- **Copy** → Eliminate (optimization-specific)
|
||||||
|
- **Debug** → Remove from MIR (keep as separate system)
|
||||||
|
- **Print** → Use Call with print function
|
||||||
|
- **Throw** → Use Call with exception function
|
||||||
|
- **Catch** → Use Call with catch handler
|
||||||
|
- **RefNew** → Eliminate (use NewBox)
|
||||||
|
- **TypeCheck** → Use Compare with type introspection
|
||||||
|
- **BarrierRead/BarrierWrite** → Consolidate into AtomicFence
|
||||||
|
- **FutureNew/FutureSet/Await** → Use BoxCall with Future methods
|
||||||
|
|
||||||
|
## Effect System Mapping
|
||||||
|
|
||||||
|
### Current → New Effect Categories
|
||||||
|
- **Pure**: Const, BinOp, Compare, Phi, RefGet, WeakNew, WeakLoad, WeakCheck
|
||||||
|
- **Mut**: BoxFieldStore, RefSet, Adopt, Release, MemCopy
|
||||||
|
- **Io**: Send, Recv, Safepoint, AtomicFence
|
||||||
|
- **Control**: Branch, Jump, Return, TailCall
|
||||||
|
- **Context-dependent**: Call, BoxCall
|
||||||
|
|
||||||
|
## Implementation Strategy
|
||||||
|
|
||||||
|
### Phase 1: Core Instruction Consolidation
|
||||||
|
1. Rename Load → BoxFieldLoad
|
||||||
|
2. Rename Store → BoxFieldStore
|
||||||
|
3. Remove eliminated instructions
|
||||||
|
4. Add missing new instructions
|
||||||
|
|
||||||
|
### Phase 2: Effect System Update
|
||||||
|
1. Update effect classification to 4 categories
|
||||||
|
2. Update all instruction effect mappings
|
||||||
|
3. Implement effect-based optimization rules
|
||||||
|
|
||||||
|
### Phase 3: Backend Updates
|
||||||
|
1. Update Interpreter backend
|
||||||
|
2. Update VM backend
|
||||||
|
3. Update WASM backend
|
||||||
|
4. Ensure all support exactly 25 instructions
|
||||||
|
|
||||||
|
### Phase 4: Verification System
|
||||||
|
1. Implement ownership forest verification
|
||||||
|
2. Add strong cycle detection
|
||||||
|
3. Add weak reference safety checks
|
||||||
|
4. Implement RefSet ownership validation
|
||||||
@ -10,51 +10,65 @@ use std::fmt;
|
|||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct EffectMask(u16);
|
pub struct EffectMask(u16);
|
||||||
|
|
||||||
/// Individual effect types
|
/// Individual effect types for the 4-category MIR hierarchy
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum Effect {
|
pub enum Effect {
|
||||||
/// Pure computation with no side effects
|
/// Pure computation with no side effects (Tier-0: reorderable, CSE/LICM eligible)
|
||||||
Pure = 0x0001,
|
Pure = 0x0001,
|
||||||
/// Reads from heap/memory (but doesn't modify)
|
/// Mutable operations (Tier-1: same Box/Field dependency preservation)
|
||||||
ReadHeap = 0x0002,
|
Mut = 0x0002,
|
||||||
/// Writes to heap/memory
|
/// I/O operations (Tier-1: no reordering, side effects present)
|
||||||
WriteHeap = 0x0004,
|
Io = 0x0004,
|
||||||
/// Performs I/O operations (file, network, console)
|
/// Control flow operations (Tier-0: affects execution flow)
|
||||||
IO = 0x0008,
|
Control = 0x0008,
|
||||||
/// P2P/network communication
|
|
||||||
P2P = 0x0010,
|
// Legacy effects for compatibility (will be mapped to above categories)
|
||||||
/// Foreign Function Interface calls
|
/// Reads from heap/memory (maps to Pure if read-only)
|
||||||
FFI = 0x0020,
|
ReadHeap = 0x0010,
|
||||||
/// May panic or throw exceptions
|
/// Writes to heap/memory (maps to Mut)
|
||||||
Panic = 0x0040,
|
WriteHeap = 0x0020,
|
||||||
/// Allocates memory
|
/// P2P/network communication (maps to Io)
|
||||||
Alloc = 0x0080,
|
P2P = 0x0040,
|
||||||
/// Accesses global state
|
/// Foreign Function Interface calls (maps to Io)
|
||||||
Global = 0x0100,
|
FFI = 0x0080,
|
||||||
/// Thread/async operations
|
/// May panic or throw exceptions (maps to Io)
|
||||||
Async = 0x0200,
|
Panic = 0x0100,
|
||||||
/// Unsafe operations
|
/// Allocates memory (maps to Mut)
|
||||||
Unsafe = 0x0400,
|
Alloc = 0x0200,
|
||||||
/// Debug/logging operations
|
/// Accesses global state (maps to Io)
|
||||||
Debug = 0x0800,
|
Global = 0x0400,
|
||||||
/// Memory barrier operations
|
/// Thread/async operations (maps to Io)
|
||||||
Barrier = 0x1000,
|
Async = 0x0800,
|
||||||
|
/// Unsafe operations (maps to Io)
|
||||||
|
Unsafe = 0x1000,
|
||||||
|
/// Debug/logging operations (maps to Io)
|
||||||
|
Debug = 0x2000,
|
||||||
|
/// Memory barrier operations (maps to Io)
|
||||||
|
Barrier = 0x4000,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EffectMask {
|
impl EffectMask {
|
||||||
/// No effects - pure computation
|
/// No effects - pure computation
|
||||||
pub const PURE: Self = Self(Effect::Pure as u16);
|
pub const PURE: Self = Self(Effect::Pure as u16);
|
||||||
|
|
||||||
|
/// Mutable operations (writes, ownership changes)
|
||||||
|
pub const MUT: Self = Self(Effect::Mut as u16);
|
||||||
|
|
||||||
|
/// I/O operations (external effects, cannot reorder)
|
||||||
|
pub const IO: Self = Self(Effect::Io as u16);
|
||||||
|
|
||||||
|
/// Control flow operations
|
||||||
|
pub const CONTROL: Self = Self(Effect::Control as u16);
|
||||||
|
|
||||||
|
// Legacy constants for compatibility
|
||||||
/// Memory read effects
|
/// Memory read effects
|
||||||
pub const READ: Self = Self(Effect::ReadHeap as u16);
|
pub const READ: Self = Self(Effect::ReadHeap as u16);
|
||||||
|
pub const read: Self = Self::READ; // Lowercase alias for compatibility
|
||||||
|
|
||||||
/// Memory write effects (includes read)
|
/// Memory write effects (includes read)
|
||||||
pub const WRITE: Self = Self((Effect::WriteHeap as u16) | (Effect::ReadHeap as u16));
|
pub const WRITE: Self = Self((Effect::WriteHeap as u16) | (Effect::ReadHeap as u16));
|
||||||
|
|
||||||
/// I/O effects
|
/// P2P communication effects
|
||||||
pub const IO: Self = Self(Effect::IO as u16);
|
|
||||||
|
|
||||||
/// P2P communication effects
|
|
||||||
pub const P2P: Self = Self(Effect::P2P as u16);
|
pub const P2P: Self = Self(Effect::P2P as u16);
|
||||||
|
|
||||||
/// Panic/exception effects
|
/// Panic/exception effects
|
||||||
@ -115,15 +129,50 @@ impl EffectMask {
|
|||||||
|
|
||||||
/// Check if the computation is pure (no side effects)
|
/// Check if the computation is pure (no side effects)
|
||||||
pub fn is_pure(self) -> bool {
|
pub fn is_pure(self) -> bool {
|
||||||
self.0 == 0 || self.0 == (Effect::Pure as u16)
|
self.contains(Effect::Pure) || self.0 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the computation is mutable (modifies state)
|
||||||
|
pub fn is_mut(self) -> bool {
|
||||||
|
self.contains(Effect::Mut) ||
|
||||||
|
self.contains(Effect::WriteHeap) ||
|
||||||
|
self.contains(Effect::Alloc)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the computation has I/O effects (external side effects)
|
||||||
|
pub fn is_io(self) -> bool {
|
||||||
|
self.contains(Effect::Io) ||
|
||||||
|
self.contains(Effect::P2P) ||
|
||||||
|
self.contains(Effect::FFI) ||
|
||||||
|
self.contains(Effect::Global) ||
|
||||||
|
self.contains(Effect::Async) ||
|
||||||
|
self.contains(Effect::Unsafe) ||
|
||||||
|
self.contains(Effect::Debug) ||
|
||||||
|
self.contains(Effect::Barrier) ||
|
||||||
|
self.contains(Effect::Panic)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the computation affects control flow
|
||||||
|
pub fn is_control(self) -> bool {
|
||||||
|
self.contains(Effect::Control)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the primary effect category for MIR optimization
|
||||||
|
pub fn primary_category(self) -> Effect {
|
||||||
|
if self.is_control() {
|
||||||
|
Effect::Control
|
||||||
|
} else if self.is_io() {
|
||||||
|
Effect::Io
|
||||||
|
} else if self.is_mut() {
|
||||||
|
Effect::Mut
|
||||||
|
} else {
|
||||||
|
Effect::Pure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the computation only reads (doesn't modify state)
|
/// Check if the computation only reads (doesn't modify state)
|
||||||
pub fn is_read_only(self) -> bool {
|
pub fn is_read_only(self) -> bool {
|
||||||
!self.contains(Effect::WriteHeap) &&
|
!self.is_mut() && !self.is_io()
|
||||||
!self.contains(Effect::IO) &&
|
|
||||||
!self.contains(Effect::P2P) &&
|
|
||||||
!self.contains(Effect::Global)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if parallel execution is safe
|
/// Check if parallel execution is safe
|
||||||
@ -142,14 +191,15 @@ impl EffectMask {
|
|||||||
pub fn effect_names(self) -> Vec<&'static str> {
|
pub fn effect_names(self) -> Vec<&'static str> {
|
||||||
let mut names = Vec::new();
|
let mut names = Vec::new();
|
||||||
|
|
||||||
if self.is_pure() {
|
// Primary categories
|
||||||
names.push("pure");
|
if self.contains(Effect::Pure) { names.push("pure"); }
|
||||||
return names;
|
if self.contains(Effect::Mut) { names.push("mut"); }
|
||||||
}
|
if self.contains(Effect::Io) { names.push("io"); }
|
||||||
|
if self.contains(Effect::Control) { names.push("control"); }
|
||||||
|
|
||||||
|
// Legacy effects for detailed tracking
|
||||||
if self.contains(Effect::ReadHeap) { names.push("read"); }
|
if self.contains(Effect::ReadHeap) { names.push("read"); }
|
||||||
if self.contains(Effect::WriteHeap) { names.push("write"); }
|
if self.contains(Effect::WriteHeap) { names.push("write"); }
|
||||||
if self.contains(Effect::IO) { names.push("io"); }
|
|
||||||
if self.contains(Effect::P2P) { names.push("p2p"); }
|
if self.contains(Effect::P2P) { names.push("p2p"); }
|
||||||
if self.contains(Effect::FFI) { names.push("ffi"); }
|
if self.contains(Effect::FFI) { names.push("ffi"); }
|
||||||
if self.contains(Effect::Panic) { names.push("panic"); }
|
if self.contains(Effect::Panic) { names.push("panic"); }
|
||||||
@ -160,6 +210,10 @@ impl EffectMask {
|
|||||||
if self.contains(Effect::Debug) { names.push("debug"); }
|
if self.contains(Effect::Debug) { names.push("debug"); }
|
||||||
if self.contains(Effect::Barrier) { names.push("barrier"); }
|
if self.contains(Effect::Barrier) { names.push("barrier"); }
|
||||||
|
|
||||||
|
if names.is_empty() {
|
||||||
|
names.push("none");
|
||||||
|
}
|
||||||
|
|
||||||
names
|
names
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -240,8 +294,8 @@ mod tests {
|
|||||||
assert!(effects.contains(Effect::WriteHeap));
|
assert!(effects.contains(Effect::WriteHeap));
|
||||||
assert!(!effects.is_read_only());
|
assert!(!effects.is_read_only());
|
||||||
|
|
||||||
effects = effects.add(Effect::IO);
|
effects = effects.add(Effect::Io);
|
||||||
assert!(effects.contains(Effect::IO));
|
assert!(effects.contains(Effect::Io));
|
||||||
assert!(!effects.is_parallel_safe());
|
assert!(!effects.is_parallel_safe());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,7 +307,7 @@ mod tests {
|
|||||||
let combined = read_effect | io_effect;
|
let combined = read_effect | io_effect;
|
||||||
|
|
||||||
assert!(combined.contains(Effect::ReadHeap));
|
assert!(combined.contains(Effect::ReadHeap));
|
||||||
assert!(combined.contains(Effect::IO));
|
assert!(combined.contains(Effect::Io));
|
||||||
assert!(!combined.is_pure());
|
assert!(!combined.is_pure());
|
||||||
assert!(!combined.is_parallel_safe());
|
assert!(!combined.is_parallel_safe());
|
||||||
}
|
}
|
||||||
|
|||||||
543
src/mir/instruction_v2.rs
Normal file
543
src/mir/instruction_v2.rs
Normal file
@ -0,0 +1,543 @@
|
|||||||
|
/*!
|
||||||
|
* MIR 25-Instruction Specification Implementation
|
||||||
|
*
|
||||||
|
* Complete hierarchical MIR instruction set based on ChatGPT5 + AI Council design
|
||||||
|
*/
|
||||||
|
|
||||||
|
use super::{ValueId, EffectMask, Effect, BasicBlockId};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
/// MIR instruction types - exactly 25 instructions per specification
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum MirInstructionV2 {
|
||||||
|
// === TIER-0: UNIVERSAL CORE (8 instructions) ===
|
||||||
|
|
||||||
|
/// Load a constant value (pure)
|
||||||
|
/// `%dst = const value`
|
||||||
|
Const {
|
||||||
|
dst: ValueId,
|
||||||
|
value: ConstValue,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Binary arithmetic operation (pure)
|
||||||
|
/// `%dst = %lhs op %rhs`
|
||||||
|
BinOp {
|
||||||
|
dst: ValueId,
|
||||||
|
op: BinaryOp,
|
||||||
|
lhs: ValueId,
|
||||||
|
rhs: ValueId,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Compare two values (pure)
|
||||||
|
/// `%dst = %lhs cmp %rhs`
|
||||||
|
Compare {
|
||||||
|
dst: ValueId,
|
||||||
|
op: CompareOp,
|
||||||
|
lhs: ValueId,
|
||||||
|
rhs: ValueId,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Conditional branch (control)
|
||||||
|
/// `br %condition -> %then_bb, %else_bb`
|
||||||
|
Branch {
|
||||||
|
condition: ValueId,
|
||||||
|
then_bb: BasicBlockId,
|
||||||
|
else_bb: BasicBlockId,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Unconditional jump (control)
|
||||||
|
/// `jmp %target_bb`
|
||||||
|
Jump {
|
||||||
|
target: BasicBlockId,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// SSA phi function for merging values (pure)
|
||||||
|
/// `%dst = phi [%val1 from %bb1, %val2 from %bb2, ...]`
|
||||||
|
Phi {
|
||||||
|
dst: ValueId,
|
||||||
|
inputs: Vec<(BasicBlockId, ValueId)>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// External function call (context-dependent)
|
||||||
|
/// `%dst = call %func(%args...)`
|
||||||
|
Call {
|
||||||
|
dst: Option<ValueId>,
|
||||||
|
func: ValueId,
|
||||||
|
args: Vec<ValueId>,
|
||||||
|
effects: EffectMask,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Return from function (control)
|
||||||
|
/// `ret %value` or `ret void`
|
||||||
|
Return {
|
||||||
|
value: Option<ValueId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
// === TIER-1: NYASH SEMANTICS (12 instructions) ===
|
||||||
|
|
||||||
|
/// Create a new Box instance (strong ownership node in ownership forest)
|
||||||
|
/// `%dst = new_box "BoxType"(%args...)`
|
||||||
|
NewBox {
|
||||||
|
dst: ValueId,
|
||||||
|
box_type: String,
|
||||||
|
args: Vec<ValueId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Load Box field value (pure)
|
||||||
|
/// `%dst = %box.field`
|
||||||
|
BoxFieldLoad {
|
||||||
|
dst: ValueId,
|
||||||
|
box_val: ValueId,
|
||||||
|
field: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Store value to Box field (mut)
|
||||||
|
/// `%box.field = %value`
|
||||||
|
BoxFieldStore {
|
||||||
|
box_val: ValueId,
|
||||||
|
field: String,
|
||||||
|
value: ValueId,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Box method invocation (context-dependent)
|
||||||
|
/// `%dst = %box.method(%args...)`
|
||||||
|
BoxCall {
|
||||||
|
dst: Option<ValueId>,
|
||||||
|
box_val: ValueId,
|
||||||
|
method: String,
|
||||||
|
args: Vec<ValueId>,
|
||||||
|
effects: EffectMask,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Safepoint for finalization/interrupts (io)
|
||||||
|
/// `safepoint`
|
||||||
|
Safepoint,
|
||||||
|
|
||||||
|
/// Get reference as value (pure)
|
||||||
|
/// `%dst = ref_get %reference`
|
||||||
|
RefGet {
|
||||||
|
dst: ValueId,
|
||||||
|
reference: ValueId,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Set/replace reference target with ownership validation (mut)
|
||||||
|
/// `ref_set %reference = %new_target`
|
||||||
|
RefSet {
|
||||||
|
reference: ValueId,
|
||||||
|
new_target: ValueId,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Create weak reference handle (non-owning link) (pure)
|
||||||
|
/// `%dst = weak_new %box`
|
||||||
|
WeakNew {
|
||||||
|
dst: ValueId,
|
||||||
|
box_val: ValueId,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Load from weak reference with liveness check (returns null if dead) (pure)
|
||||||
|
/// `%dst = weak_load %weak_ref`
|
||||||
|
WeakLoad {
|
||||||
|
dst: ValueId,
|
||||||
|
weak_ref: ValueId,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Check weak reference validity (returns bool) (pure)
|
||||||
|
/// `%dst = weak_check %weak_ref`
|
||||||
|
WeakCheck {
|
||||||
|
dst: ValueId,
|
||||||
|
weak_ref: ValueId,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Send message via Bus system (io)
|
||||||
|
/// `send %bus, %message`
|
||||||
|
Send {
|
||||||
|
bus: ValueId,
|
||||||
|
message: ValueId,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Receive message from Bus system (io)
|
||||||
|
/// `%dst = recv %bus`
|
||||||
|
Recv {
|
||||||
|
dst: ValueId,
|
||||||
|
bus: ValueId,
|
||||||
|
},
|
||||||
|
|
||||||
|
// === TIER-2: IMPLEMENTATION ASSISTANCE (5 instructions) ===
|
||||||
|
|
||||||
|
/// Tail call optimization (control)
|
||||||
|
/// `tail_call %func(%args...)`
|
||||||
|
TailCall {
|
||||||
|
func: ValueId,
|
||||||
|
args: Vec<ValueId>,
|
||||||
|
effects: EffectMask,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Ownership transfer: this takes strong ownership of child (mut)
|
||||||
|
/// `adopt %parent, %child`
|
||||||
|
Adopt {
|
||||||
|
parent: ValueId,
|
||||||
|
child: ValueId,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Release strong ownership (weakify or nullify) (mut)
|
||||||
|
/// `release %reference`
|
||||||
|
Release {
|
||||||
|
reference: ValueId,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Optimized memory copy for structs/arrays (mut)
|
||||||
|
/// `memcopy %dest, %src, %size`
|
||||||
|
MemCopy {
|
||||||
|
dest: ValueId,
|
||||||
|
src: ValueId,
|
||||||
|
size: ValueId,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Atomic fence for concurrency ordering at Actor/Port boundaries (io)
|
||||||
|
/// `atomic_fence %ordering`
|
||||||
|
AtomicFence {
|
||||||
|
ordering: AtomicOrdering,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constant values in MIR
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum ConstValue {
|
||||||
|
Integer(i64),
|
||||||
|
Float(f64),
|
||||||
|
Bool(bool),
|
||||||
|
String(String),
|
||||||
|
Null,
|
||||||
|
Void,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Binary operations
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum BinaryOp {
|
||||||
|
// Arithmetic
|
||||||
|
Add, Sub, Mul, Div, Mod,
|
||||||
|
|
||||||
|
// Bitwise
|
||||||
|
BitAnd, BitOr, BitXor, Shl, Shr,
|
||||||
|
|
||||||
|
// Logical
|
||||||
|
And, Or,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Comparison operations
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum CompareOp {
|
||||||
|
Eq, Ne, Lt, Le, Gt, Ge,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Atomic ordering for AtomicFence instruction
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum AtomicOrdering {
|
||||||
|
Relaxed,
|
||||||
|
Acquire,
|
||||||
|
Release,
|
||||||
|
AcqRel,
|
||||||
|
SeqCst,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MirInstructionV2 {
|
||||||
|
/// Get the effect mask for this instruction according to 4-category system
|
||||||
|
pub fn effects(&self) -> EffectMask {
|
||||||
|
match self {
|
||||||
|
// TIER-0: Universal Core
|
||||||
|
// Pure operations
|
||||||
|
MirInstructionV2::Const { .. } |
|
||||||
|
MirInstructionV2::BinOp { .. } |
|
||||||
|
MirInstructionV2::Compare { .. } |
|
||||||
|
MirInstructionV2::Phi { .. } => EffectMask::PURE,
|
||||||
|
|
||||||
|
// Control flow operations
|
||||||
|
MirInstructionV2::Branch { .. } |
|
||||||
|
MirInstructionV2::Jump { .. } |
|
||||||
|
MirInstructionV2::Return { .. } => EffectMask::CONTROL,
|
||||||
|
|
||||||
|
// Context-dependent operations
|
||||||
|
MirInstructionV2::Call { effects, .. } => *effects,
|
||||||
|
|
||||||
|
// TIER-1: Nyash Semantics
|
||||||
|
// Pure operations
|
||||||
|
MirInstructionV2::BoxFieldLoad { .. } |
|
||||||
|
MirInstructionV2::RefGet { .. } |
|
||||||
|
MirInstructionV2::WeakNew { .. } |
|
||||||
|
MirInstructionV2::WeakLoad { .. } |
|
||||||
|
MirInstructionV2::WeakCheck { .. } => EffectMask::PURE,
|
||||||
|
|
||||||
|
// Mutable operations
|
||||||
|
MirInstructionV2::NewBox { .. } => EffectMask::MUT.add(Effect::Alloc),
|
||||||
|
MirInstructionV2::BoxFieldStore { .. } |
|
||||||
|
MirInstructionV2::RefSet { .. } => EffectMask::MUT,
|
||||||
|
|
||||||
|
// I/O operations
|
||||||
|
MirInstructionV2::Safepoint |
|
||||||
|
MirInstructionV2::Send { .. } |
|
||||||
|
MirInstructionV2::Recv { .. } => EffectMask::IO,
|
||||||
|
|
||||||
|
// Context-dependent operations
|
||||||
|
MirInstructionV2::BoxCall { effects, .. } => *effects,
|
||||||
|
|
||||||
|
// TIER-2: Implementation Assistance
|
||||||
|
// Control flow operations
|
||||||
|
MirInstructionV2::TailCall { .. } => EffectMask::CONTROL,
|
||||||
|
|
||||||
|
// Mutable operations
|
||||||
|
MirInstructionV2::Adopt { .. } |
|
||||||
|
MirInstructionV2::Release { .. } |
|
||||||
|
MirInstructionV2::MemCopy { .. } => EffectMask::MUT,
|
||||||
|
|
||||||
|
// I/O operations
|
||||||
|
MirInstructionV2::AtomicFence { .. } => EffectMask::IO.add(Effect::Barrier),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the destination ValueId if this instruction produces a value
|
||||||
|
pub fn dst_value(&self) -> Option<ValueId> {
|
||||||
|
match self {
|
||||||
|
MirInstructionV2::Const { dst, .. } |
|
||||||
|
MirInstructionV2::BinOp { dst, .. } |
|
||||||
|
MirInstructionV2::Compare { dst, .. } |
|
||||||
|
MirInstructionV2::Phi { dst, .. } |
|
||||||
|
MirInstructionV2::NewBox { dst, .. } |
|
||||||
|
MirInstructionV2::BoxFieldLoad { dst, .. } |
|
||||||
|
MirInstructionV2::RefGet { dst, .. } |
|
||||||
|
MirInstructionV2::WeakNew { dst, .. } |
|
||||||
|
MirInstructionV2::WeakLoad { dst, .. } |
|
||||||
|
MirInstructionV2::WeakCheck { dst, .. } |
|
||||||
|
MirInstructionV2::Recv { dst, .. } => Some(*dst),
|
||||||
|
|
||||||
|
MirInstructionV2::Call { dst, .. } |
|
||||||
|
MirInstructionV2::BoxCall { dst, .. } => *dst,
|
||||||
|
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get all ValueIds used by this instruction
|
||||||
|
pub fn used_values(&self) -> Vec<ValueId> {
|
||||||
|
match self {
|
||||||
|
MirInstructionV2::Const { .. } => vec![],
|
||||||
|
|
||||||
|
MirInstructionV2::BinOp { lhs, rhs, .. } |
|
||||||
|
MirInstructionV2::Compare { lhs, rhs, .. } => vec![*lhs, *rhs],
|
||||||
|
|
||||||
|
MirInstructionV2::Branch { condition, .. } => vec![*condition],
|
||||||
|
|
||||||
|
MirInstructionV2::Jump { .. } => vec![],
|
||||||
|
|
||||||
|
MirInstructionV2::Phi { inputs, .. } => {
|
||||||
|
inputs.iter().map(|(_, value_id)| *value_id).collect()
|
||||||
|
},
|
||||||
|
|
||||||
|
MirInstructionV2::Call { func, args, .. } => {
|
||||||
|
let mut values = vec![*func];
|
||||||
|
values.extend(args.iter().copied());
|
||||||
|
values
|
||||||
|
},
|
||||||
|
|
||||||
|
MirInstructionV2::Return { value } => {
|
||||||
|
value.map(|v| vec![v]).unwrap_or_default()
|
||||||
|
},
|
||||||
|
|
||||||
|
MirInstructionV2::NewBox { args, .. } => args.clone(),
|
||||||
|
|
||||||
|
MirInstructionV2::BoxFieldLoad { box_val, .. } => vec![*box_val],
|
||||||
|
|
||||||
|
MirInstructionV2::BoxFieldStore { box_val, value, .. } => vec![*box_val, *value],
|
||||||
|
|
||||||
|
MirInstructionV2::BoxCall { box_val, args, .. } => {
|
||||||
|
let mut values = vec![*box_val];
|
||||||
|
values.extend(args.iter().copied());
|
||||||
|
values
|
||||||
|
},
|
||||||
|
|
||||||
|
MirInstructionV2::Safepoint => vec![],
|
||||||
|
|
||||||
|
MirInstructionV2::RefGet { reference, .. } => vec![*reference],
|
||||||
|
|
||||||
|
MirInstructionV2::RefSet { reference, new_target, .. } => vec![*reference, *new_target],
|
||||||
|
|
||||||
|
MirInstructionV2::WeakNew { box_val, .. } => vec![*box_val],
|
||||||
|
|
||||||
|
MirInstructionV2::WeakLoad { weak_ref, .. } |
|
||||||
|
MirInstructionV2::WeakCheck { weak_ref, .. } => vec![*weak_ref],
|
||||||
|
|
||||||
|
MirInstructionV2::Send { bus, message, .. } => vec![*bus, *message],
|
||||||
|
|
||||||
|
MirInstructionV2::Recv { bus, .. } => vec![*bus],
|
||||||
|
|
||||||
|
MirInstructionV2::TailCall { func, args, .. } => {
|
||||||
|
let mut values = vec![*func];
|
||||||
|
values.extend(args.iter().copied());
|
||||||
|
values
|
||||||
|
},
|
||||||
|
|
||||||
|
MirInstructionV2::Adopt { parent, child, .. } => vec![*parent, *child],
|
||||||
|
|
||||||
|
MirInstructionV2::Release { reference, .. } => vec![*reference],
|
||||||
|
|
||||||
|
MirInstructionV2::MemCopy { dest, src, size, .. } => vec![*dest, *src, *size],
|
||||||
|
|
||||||
|
MirInstructionV2::AtomicFence { .. } => vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the instruction tier (0, 1, or 2)
|
||||||
|
pub fn tier(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
// Tier-0: Universal Core
|
||||||
|
MirInstructionV2::Const { .. } |
|
||||||
|
MirInstructionV2::BinOp { .. } |
|
||||||
|
MirInstructionV2::Compare { .. } |
|
||||||
|
MirInstructionV2::Branch { .. } |
|
||||||
|
MirInstructionV2::Jump { .. } |
|
||||||
|
MirInstructionV2::Phi { .. } |
|
||||||
|
MirInstructionV2::Call { .. } |
|
||||||
|
MirInstructionV2::Return { .. } => 0,
|
||||||
|
|
||||||
|
// Tier-1: Nyash Semantics
|
||||||
|
MirInstructionV2::NewBox { .. } |
|
||||||
|
MirInstructionV2::BoxFieldLoad { .. } |
|
||||||
|
MirInstructionV2::BoxFieldStore { .. } |
|
||||||
|
MirInstructionV2::BoxCall { .. } |
|
||||||
|
MirInstructionV2::Safepoint { .. } |
|
||||||
|
MirInstructionV2::RefGet { .. } |
|
||||||
|
MirInstructionV2::RefSet { .. } |
|
||||||
|
MirInstructionV2::WeakNew { .. } |
|
||||||
|
MirInstructionV2::WeakLoad { .. } |
|
||||||
|
MirInstructionV2::WeakCheck { .. } |
|
||||||
|
MirInstructionV2::Send { .. } |
|
||||||
|
MirInstructionV2::Recv { .. } => 1,
|
||||||
|
|
||||||
|
// Tier-2: Implementation Assistance
|
||||||
|
MirInstructionV2::TailCall { .. } |
|
||||||
|
MirInstructionV2::Adopt { .. } |
|
||||||
|
MirInstructionV2::Release { .. } |
|
||||||
|
MirInstructionV2::MemCopy { .. } |
|
||||||
|
MirInstructionV2::AtomicFence { .. } => 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a human-readable description of the instruction
|
||||||
|
pub fn description(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
// Tier-0
|
||||||
|
MirInstructionV2::Const { .. } => "Load constant value",
|
||||||
|
MirInstructionV2::BinOp { .. } => "Binary arithmetic operation",
|
||||||
|
MirInstructionV2::Compare { .. } => "Compare two values",
|
||||||
|
MirInstructionV2::Branch { .. } => "Conditional branch",
|
||||||
|
MirInstructionV2::Jump { .. } => "Unconditional jump",
|
||||||
|
MirInstructionV2::Phi { .. } => "SSA phi function",
|
||||||
|
MirInstructionV2::Call { .. } => "External function call",
|
||||||
|
MirInstructionV2::Return { .. } => "Return from function",
|
||||||
|
|
||||||
|
// Tier-1
|
||||||
|
MirInstructionV2::NewBox { .. } => "Create Box instance",
|
||||||
|
MirInstructionV2::BoxFieldLoad { .. } => "Load Box field value",
|
||||||
|
MirInstructionV2::BoxFieldStore { .. } => "Store to Box field",
|
||||||
|
MirInstructionV2::BoxCall { .. } => "Box method invocation",
|
||||||
|
MirInstructionV2::Safepoint => "Finalization/interrupt safepoint",
|
||||||
|
MirInstructionV2::RefGet { .. } => "Get reference as value",
|
||||||
|
MirInstructionV2::RefSet { .. } => "Set reference target",
|
||||||
|
MirInstructionV2::WeakNew { .. } => "Create weak reference",
|
||||||
|
MirInstructionV2::WeakLoad { .. } => "Load from weak reference",
|
||||||
|
MirInstructionV2::WeakCheck { .. } => "Check weak reference validity",
|
||||||
|
MirInstructionV2::Send { .. } => "Send Bus message",
|
||||||
|
MirInstructionV2::Recv { .. } => "Receive Bus message",
|
||||||
|
|
||||||
|
// Tier-2
|
||||||
|
MirInstructionV2::TailCall { .. } => "Tail call optimization",
|
||||||
|
MirInstructionV2::Adopt { .. } => "Transfer ownership",
|
||||||
|
MirInstructionV2::Release { .. } => "Release ownership",
|
||||||
|
MirInstructionV2::MemCopy { .. } => "Optimized memory copy",
|
||||||
|
MirInstructionV2::AtomicFence { .. } => "Atomic memory fence",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for MirInstructionV2 {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.description())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::mir::{ValueIdGenerator, BasicBlockIdGenerator};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_instruction_count() {
|
||||||
|
// Verify we have exactly 25 instruction variants
|
||||||
|
// This is a compile-time verification
|
||||||
|
let _tier0_count = 8; // Const, BinOp, Compare, Branch, Jump, Phi, Call, Return
|
||||||
|
let _tier1_count = 12; // NewBox, BoxFieldLoad/Store, BoxCall, Safepoint, RefGet/Set, WeakNew/Load/Check, Send, Recv
|
||||||
|
let _tier2_count = 5; // TailCall, Adopt, Release, MemCopy, AtomicFence
|
||||||
|
let _total = _tier0_count + _tier1_count + _tier2_count;
|
||||||
|
assert_eq!(_total, 25, "MIR instruction set must have exactly 25 instructions");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_effect_categories() {
|
||||||
|
let mut value_gen = ValueIdGenerator::new();
|
||||||
|
let mut bb_gen = BasicBlockIdGenerator::new();
|
||||||
|
|
||||||
|
// Test pure operations
|
||||||
|
let const_inst = MirInstructionV2::Const {
|
||||||
|
dst: value_gen.next(),
|
||||||
|
value: ConstValue::Integer(42),
|
||||||
|
};
|
||||||
|
assert!(const_inst.effects().is_pure(), "Const should be pure");
|
||||||
|
assert_eq!(const_inst.tier(), 0, "Const should be Tier-0");
|
||||||
|
|
||||||
|
// Test mut operations
|
||||||
|
let store_inst = MirInstructionV2::BoxFieldStore {
|
||||||
|
box_val: value_gen.next(),
|
||||||
|
field: "value".to_string(),
|
||||||
|
value: value_gen.next(),
|
||||||
|
};
|
||||||
|
assert!(store_inst.effects().is_mut(), "BoxFieldStore should be mut");
|
||||||
|
assert_eq!(store_inst.tier(), 1, "BoxFieldStore should be Tier-1");
|
||||||
|
|
||||||
|
// Test io operations
|
||||||
|
let send_inst = MirInstructionV2::Send {
|
||||||
|
bus: value_gen.next(),
|
||||||
|
message: value_gen.next(),
|
||||||
|
};
|
||||||
|
assert!(send_inst.effects().is_io(), "Send should be io");
|
||||||
|
assert_eq!(send_inst.tier(), 1, "Send should be Tier-1");
|
||||||
|
|
||||||
|
// Test control operations
|
||||||
|
let branch_inst = MirInstructionV2::Branch {
|
||||||
|
condition: value_gen.next(),
|
||||||
|
then_bb: bb_gen.next(),
|
||||||
|
else_bb: bb_gen.next(),
|
||||||
|
};
|
||||||
|
assert!(branch_inst.effects().is_control(), "Branch should be control");
|
||||||
|
assert_eq!(branch_inst.tier(), 0, "Branch should be Tier-0");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ownership_operations() {
|
||||||
|
let mut value_gen = ValueIdGenerator::new();
|
||||||
|
|
||||||
|
// Test ownership transfer
|
||||||
|
let adopt_inst = MirInstructionV2::Adopt {
|
||||||
|
parent: value_gen.next(),
|
||||||
|
child: value_gen.next(),
|
||||||
|
};
|
||||||
|
assert!(adopt_inst.effects().is_mut(), "Adopt should be mut");
|
||||||
|
assert_eq!(adopt_inst.tier(), 2, "Adopt should be Tier-2");
|
||||||
|
|
||||||
|
// Test weak reference operations
|
||||||
|
let weak_check = MirInstructionV2::WeakCheck {
|
||||||
|
dst: value_gen.next(),
|
||||||
|
weak_ref: value_gen.next(),
|
||||||
|
};
|
||||||
|
assert!(weak_check.effects().is_pure(), "WeakCheck should be pure");
|
||||||
|
assert_eq!(weak_check.tier(), 1, "WeakCheck should be Tier-1");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,20 +6,24 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
pub mod instruction;
|
pub mod instruction;
|
||||||
|
pub mod instruction_v2; // New 25-instruction specification
|
||||||
pub mod basic_block;
|
pub mod basic_block;
|
||||||
pub mod function;
|
pub mod function;
|
||||||
pub mod builder;
|
pub mod builder;
|
||||||
pub mod verification;
|
pub mod verification;
|
||||||
|
pub mod ownership_verifier; // Ownership forest verification
|
||||||
pub mod printer;
|
pub mod printer;
|
||||||
pub mod value_id;
|
pub mod value_id;
|
||||||
pub mod effect;
|
pub mod effect;
|
||||||
|
|
||||||
// Re-export main types for easy access
|
// Re-export main types for easy access
|
||||||
pub use instruction::{MirInstruction, BinaryOp, CompareOp, UnaryOp, ConstValue, MirType};
|
pub use instruction::{MirInstruction, BinaryOp, CompareOp, UnaryOp, ConstValue, MirType};
|
||||||
|
pub use instruction_v2::{MirInstructionV2, AtomicOrdering}; // New 25-instruction set
|
||||||
pub use basic_block::{BasicBlock, BasicBlockId, BasicBlockIdGenerator};
|
pub use basic_block::{BasicBlock, BasicBlockId, BasicBlockIdGenerator};
|
||||||
pub use function::{MirFunction, MirModule, FunctionSignature};
|
pub use function::{MirFunction, MirModule, FunctionSignature};
|
||||||
pub use builder::MirBuilder;
|
pub use builder::MirBuilder;
|
||||||
pub use verification::{MirVerifier, VerificationError};
|
pub use verification::{MirVerifier, VerificationError};
|
||||||
|
pub use ownership_verifier::{OwnershipVerifier, OwnershipError, OwnershipStats}; // Ownership forest verification
|
||||||
pub use printer::MirPrinter;
|
pub use printer::MirPrinter;
|
||||||
pub use value_id::{ValueId, LocalId, ValueIdGenerator};
|
pub use value_id::{ValueId, LocalId, ValueIdGenerator};
|
||||||
pub use effect::{EffectMask, Effect};
|
pub use effect::{EffectMask, Effect};
|
||||||
|
|||||||
559
src/mir/ownership_verifier.rs
Normal file
559
src/mir/ownership_verifier.rs
Normal file
@ -0,0 +1,559 @@
|
|||||||
|
/*!
|
||||||
|
* Ownership Forest Verification System
|
||||||
|
*
|
||||||
|
* Implements ownership forest validation rules per ChatGPT5 specification:
|
||||||
|
* - Ownership forest: strong in-degree ≤ 1
|
||||||
|
* - Strong cycle prohibition: strong edges form DAG (forest)
|
||||||
|
* - Weak/strong interaction: bidirectional strong → error
|
||||||
|
* - RefSet safety: strong→strong requires Release of old target
|
||||||
|
* - WeakLoad/WeakCheck deterministic behavior: null/false on expiration
|
||||||
|
*/
|
||||||
|
|
||||||
|
use super::{MirInstructionV2, ValueId, MirFunction, MirModule};
|
||||||
|
use std::collections::{HashMap, HashSet, VecDeque};
|
||||||
|
|
||||||
|
/// Ownership forest verification errors
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum OwnershipError {
|
||||||
|
/// Strong reference has multiple owners (violates forest constraint)
|
||||||
|
MultipleStrongOwners {
|
||||||
|
target: ValueId,
|
||||||
|
owners: Vec<ValueId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Strong reference cycle detected (violates DAG constraint)
|
||||||
|
StrongCycle {
|
||||||
|
cycle: Vec<ValueId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Bidirectional strong references (should be strong + weak)
|
||||||
|
BidirectionalStrong {
|
||||||
|
first: ValueId,
|
||||||
|
second: ValueId,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// RefSet without proper Release of old target
|
||||||
|
UnsafeRefSet {
|
||||||
|
reference: ValueId,
|
||||||
|
old_target: ValueId,
|
||||||
|
new_target: ValueId,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// WeakLoad on expired reference (should return null deterministically)
|
||||||
|
WeakLoadExpired {
|
||||||
|
weak_ref: ValueId,
|
||||||
|
dead_target: ValueId,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Use after Release (accessing released ownership)
|
||||||
|
UseAfterRelease {
|
||||||
|
value: ValueId,
|
||||||
|
released_at: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Invalid ownership transfer via Adopt
|
||||||
|
InvalidAdopt {
|
||||||
|
parent: ValueId,
|
||||||
|
child: ValueId,
|
||||||
|
reason: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ownership forest verifier
|
||||||
|
pub struct OwnershipVerifier {
|
||||||
|
/// Strong ownership edges: child -> parent
|
||||||
|
strong_edges: HashMap<ValueId, ValueId>,
|
||||||
|
|
||||||
|
/// Weak reference edges: weak_ref -> target
|
||||||
|
weak_edges: HashMap<ValueId, ValueId>,
|
||||||
|
|
||||||
|
/// Released references (no longer valid for ownership)
|
||||||
|
released: HashSet<ValueId>,
|
||||||
|
|
||||||
|
/// Track live weak references for liveness checking
|
||||||
|
live_weak_refs: HashSet<ValueId>,
|
||||||
|
|
||||||
|
/// Track dead targets for WeakLoad/WeakCheck determinism
|
||||||
|
dead_targets: HashSet<ValueId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OwnershipVerifier {
|
||||||
|
/// Create a new ownership verifier
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
strong_edges: HashMap::new(),
|
||||||
|
weak_edges: HashMap::new(),
|
||||||
|
released: HashSet::new(),
|
||||||
|
live_weak_refs: HashSet::new(),
|
||||||
|
dead_targets: HashSet::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verify ownership forest properties for an entire module
|
||||||
|
pub fn verify_module(&mut self, module: &MirModule) -> Result<(), Vec<OwnershipError>> {
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
|
||||||
|
for function in module.functions.values() {
|
||||||
|
if let Err(mut function_errors) = self.verify_function(function) {
|
||||||
|
errors.append(&mut function_errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if errors.is_empty() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(errors)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verify ownership forest properties for a single function
|
||||||
|
pub fn verify_function(&mut self, function: &MirFunction) -> Result<(), Vec<OwnershipError>> {
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
|
||||||
|
// Reset state for this function
|
||||||
|
self.strong_edges.clear();
|
||||||
|
self.weak_edges.clear();
|
||||||
|
self.released.clear();
|
||||||
|
self.live_weak_refs.clear();
|
||||||
|
self.dead_targets.clear();
|
||||||
|
|
||||||
|
// Process all instructions to build ownership graph
|
||||||
|
for block in function.blocks.values() {
|
||||||
|
for instruction in block.all_instructions() {
|
||||||
|
if let Err(mut inst_errors) = self.process_instruction(instruction) {
|
||||||
|
errors.append(&mut inst_errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify global ownership forest properties
|
||||||
|
if let Err(mut forest_errors) = self.verify_ownership_forest() {
|
||||||
|
errors.append(&mut forest_errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
if errors.is_empty() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(errors)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process a single instruction and update ownership state
|
||||||
|
fn process_instruction(&mut self, instruction: &MirInstructionV2) -> Result<(), Vec<OwnershipError>> {
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
|
||||||
|
match instruction {
|
||||||
|
// NewBox creates a new ownership root
|
||||||
|
MirInstructionV2::NewBox { dst, .. } => {
|
||||||
|
// New boxes are ownership roots (no parent)
|
||||||
|
// Clear any existing ownership for this value
|
||||||
|
self.strong_edges.remove(dst);
|
||||||
|
},
|
||||||
|
|
||||||
|
// RefSet changes ownership relationships
|
||||||
|
MirInstructionV2::RefSet { reference, new_target } => {
|
||||||
|
// Check if the reference currently has a strong target
|
||||||
|
if let Some(old_target) = self.strong_edges.get(reference) {
|
||||||
|
// Strong→Strong replacement requires explicit Release
|
||||||
|
if !self.released.contains(old_target) {
|
||||||
|
errors.push(OwnershipError::UnsafeRefSet {
|
||||||
|
reference: *reference,
|
||||||
|
old_target: *old_target,
|
||||||
|
new_target: *new_target,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set new strong ownership
|
||||||
|
self.strong_edges.insert(*reference, *new_target);
|
||||||
|
|
||||||
|
// Verify no multiple strong owners after this change
|
||||||
|
if let Err(mut multiple_errors) = self.check_multiple_owners(*new_target) {
|
||||||
|
errors.append(&mut multiple_errors);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Adopt transfers ownership
|
||||||
|
MirInstructionV2::Adopt { parent, child } => {
|
||||||
|
// Verify the adoption is valid
|
||||||
|
if self.released.contains(child) {
|
||||||
|
errors.push(OwnershipError::InvalidAdopt {
|
||||||
|
parent: *parent,
|
||||||
|
child: *child,
|
||||||
|
reason: "Cannot adopt released reference".to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for cycle creation
|
||||||
|
if self.would_create_cycle(*parent, *child) {
|
||||||
|
errors.push(OwnershipError::InvalidAdopt {
|
||||||
|
parent: *parent,
|
||||||
|
child: *child,
|
||||||
|
reason: "Would create strong cycle".to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Establish strong ownership
|
||||||
|
self.strong_edges.insert(*child, *parent);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Release removes ownership
|
||||||
|
MirInstructionV2::Release { reference } => {
|
||||||
|
self.strong_edges.remove(reference);
|
||||||
|
self.released.insert(*reference);
|
||||||
|
|
||||||
|
// Mark any targets of this reference as potentially dead
|
||||||
|
if let Some(target) = self.weak_edges.get(reference) {
|
||||||
|
self.dead_targets.insert(*target);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// WeakNew creates weak reference
|
||||||
|
MirInstructionV2::WeakNew { dst, box_val } => {
|
||||||
|
self.weak_edges.insert(*dst, *box_val);
|
||||||
|
self.live_weak_refs.insert(*dst);
|
||||||
|
},
|
||||||
|
|
||||||
|
// WeakLoad checks liveness
|
||||||
|
MirInstructionV2::WeakLoad { weak_ref, .. } => {
|
||||||
|
if let Some(target) = self.weak_edges.get(weak_ref) {
|
||||||
|
if self.dead_targets.contains(target) {
|
||||||
|
// This is actually expected behavior - WeakLoad should return null
|
||||||
|
// We track this for deterministic behavior verification
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// WeakCheck verifies liveness
|
||||||
|
MirInstructionV2::WeakCheck { weak_ref, .. } => {
|
||||||
|
if let Some(target) = self.weak_edges.get(weak_ref) {
|
||||||
|
if self.dead_targets.contains(target) {
|
||||||
|
// This is expected - WeakCheck should return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Other instructions don't affect ownership
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
if errors.is_empty() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(errors)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verify global ownership forest properties
|
||||||
|
fn verify_ownership_forest(&self) -> Result<(), Vec<OwnershipError>> {
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
|
||||||
|
// Check for multiple strong owners (violates forest constraint)
|
||||||
|
let mut target_owners: HashMap<ValueId, Vec<ValueId>> = HashMap::new();
|
||||||
|
for (child, parent) in &self.strong_edges {
|
||||||
|
target_owners.entry(*parent).or_insert_with(Vec::new).push(*child);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (target, owners) in target_owners {
|
||||||
|
if owners.len() > 1 {
|
||||||
|
errors.push(OwnershipError::MultipleStrongOwners { target, owners });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for strong cycles (violates DAG constraint)
|
||||||
|
if let Some(cycle) = self.find_strong_cycle() {
|
||||||
|
errors.push(OwnershipError::StrongCycle { cycle });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for bidirectional strong edges
|
||||||
|
for (child, parent) in &self.strong_edges {
|
||||||
|
if let Some(grandparent) = self.strong_edges.get(parent) {
|
||||||
|
if grandparent == child {
|
||||||
|
errors.push(OwnershipError::BidirectionalStrong {
|
||||||
|
first: *child,
|
||||||
|
second: *parent,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if errors.is_empty() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(errors)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a value has multiple strong owners
|
||||||
|
fn check_multiple_owners(&self, target: ValueId) -> Result<(), Vec<OwnershipError>> {
|
||||||
|
let owners: Vec<ValueId> = self.strong_edges
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, &parent)| parent == target)
|
||||||
|
.map(|(&child, _)| child)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if owners.len() > 1 {
|
||||||
|
Err(vec![OwnershipError::MultipleStrongOwners { target, owners }])
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if adding an edge would create a cycle
|
||||||
|
fn would_create_cycle(&self, parent: ValueId, child: ValueId) -> bool {
|
||||||
|
// DFS to see if parent is reachable from child through strong edges
|
||||||
|
let mut visited = HashSet::new();
|
||||||
|
let mut stack = vec![child];
|
||||||
|
|
||||||
|
while let Some(current) = stack.pop() {
|
||||||
|
if current == parent {
|
||||||
|
return true; // Cycle detected
|
||||||
|
}
|
||||||
|
|
||||||
|
if visited.insert(current) {
|
||||||
|
// Add all strong children of current to stack
|
||||||
|
for (&potential_child, &potential_parent) in &self.strong_edges {
|
||||||
|
if potential_parent == current {
|
||||||
|
stack.push(potential_child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find any strong cycle in the ownership graph
|
||||||
|
fn find_strong_cycle(&self) -> Option<Vec<ValueId>> {
|
||||||
|
let mut visited = HashSet::new();
|
||||||
|
let mut rec_stack = HashSet::new();
|
||||||
|
let mut path = Vec::new();
|
||||||
|
|
||||||
|
// Get all nodes in the graph
|
||||||
|
let mut all_nodes = HashSet::new();
|
||||||
|
for (&child, &parent) in &self.strong_edges {
|
||||||
|
all_nodes.insert(child);
|
||||||
|
all_nodes.insert(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DFS from each unvisited node
|
||||||
|
for &node in &all_nodes {
|
||||||
|
if !visited.contains(&node) {
|
||||||
|
if let Some(cycle) = self.dfs_cycle(node, &mut visited, &mut rec_stack, &mut path) {
|
||||||
|
return Some(cycle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DFS cycle detection helper
|
||||||
|
fn dfs_cycle(
|
||||||
|
&self,
|
||||||
|
node: ValueId,
|
||||||
|
visited: &mut HashSet<ValueId>,
|
||||||
|
rec_stack: &mut HashSet<ValueId>,
|
||||||
|
path: &mut Vec<ValueId>,
|
||||||
|
) -> Option<Vec<ValueId>> {
|
||||||
|
visited.insert(node);
|
||||||
|
rec_stack.insert(node);
|
||||||
|
path.push(node);
|
||||||
|
|
||||||
|
// Visit all strong children
|
||||||
|
for (&child, &parent) in &self.strong_edges {
|
||||||
|
if parent == node {
|
||||||
|
if rec_stack.contains(&child) {
|
||||||
|
// Found cycle - return path from child to current
|
||||||
|
let cycle_start = path.iter().position(|&x| x == child).unwrap();
|
||||||
|
return Some(path[cycle_start..].to_vec());
|
||||||
|
}
|
||||||
|
|
||||||
|
if !visited.contains(&child) {
|
||||||
|
if let Some(cycle) = self.dfs_cycle(child, visited, rec_stack, path) {
|
||||||
|
return Some(cycle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rec_stack.remove(&node);
|
||||||
|
path.pop();
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get ownership statistics for debugging
|
||||||
|
pub fn ownership_stats(&self) -> OwnershipStats {
|
||||||
|
OwnershipStats {
|
||||||
|
strong_edges: self.strong_edges.len(),
|
||||||
|
weak_edges: self.weak_edges.len(),
|
||||||
|
released_count: self.released.len(),
|
||||||
|
live_weak_refs: self.live_weak_refs.len(),
|
||||||
|
dead_targets: self.dead_targets.len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ownership statistics for debugging and analysis
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct OwnershipStats {
|
||||||
|
pub strong_edges: usize,
|
||||||
|
pub weak_edges: usize,
|
||||||
|
pub released_count: usize,
|
||||||
|
pub live_weak_refs: usize,
|
||||||
|
pub dead_targets: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for OwnershipVerifier {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::mir::{ValueIdGenerator, ConstValue};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ownership_forest_basic() {
|
||||||
|
let mut verifier = OwnershipVerifier::new();
|
||||||
|
let mut value_gen = ValueIdGenerator::new();
|
||||||
|
|
||||||
|
let parent = value_gen.next();
|
||||||
|
let child = value_gen.next();
|
||||||
|
|
||||||
|
// Create ownership relationship
|
||||||
|
let adopt = MirInstructionV2::Adopt { parent, child };
|
||||||
|
assert!(verifier.process_instruction(&adopt).is_ok());
|
||||||
|
|
||||||
|
// Verify forest properties
|
||||||
|
assert!(verifier.verify_ownership_forest().is_ok());
|
||||||
|
|
||||||
|
let stats = verifier.ownership_stats();
|
||||||
|
assert_eq!(stats.strong_edges, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_multiple_owners_error() {
|
||||||
|
let mut verifier = OwnershipVerifier::new();
|
||||||
|
let mut value_gen = ValueIdGenerator::new();
|
||||||
|
|
||||||
|
let parent1 = value_gen.next();
|
||||||
|
let parent2 = value_gen.next();
|
||||||
|
let child = value_gen.next();
|
||||||
|
|
||||||
|
// Create multiple ownership (invalid)
|
||||||
|
verifier.strong_edges.insert(child, parent1);
|
||||||
|
verifier.strong_edges.insert(child, parent2); // This overwrites, but we'll manually create conflict
|
||||||
|
|
||||||
|
// Manually create the conflicting state for testing
|
||||||
|
verifier.strong_edges.clear();
|
||||||
|
verifier.strong_edges.insert(parent1, child); // parent1 -> child
|
||||||
|
verifier.strong_edges.insert(parent2, child); // parent2 -> child (multiple owners of child)
|
||||||
|
|
||||||
|
let result = verifier.verify_ownership_forest();
|
||||||
|
assert!(result.is_err());
|
||||||
|
|
||||||
|
if let Err(errors) = result {
|
||||||
|
assert!(errors.iter().any(|e| matches!(e, OwnershipError::MultipleStrongOwners { .. })));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_strong_cycle_detection() {
|
||||||
|
let mut verifier = OwnershipVerifier::new();
|
||||||
|
let mut value_gen = ValueIdGenerator::new();
|
||||||
|
|
||||||
|
let a = value_gen.next();
|
||||||
|
let b = value_gen.next();
|
||||||
|
let c = value_gen.next();
|
||||||
|
|
||||||
|
// Create cycle: a -> b -> c -> a
|
||||||
|
verifier.strong_edges.insert(b, a);
|
||||||
|
verifier.strong_edges.insert(c, b);
|
||||||
|
verifier.strong_edges.insert(a, c);
|
||||||
|
|
||||||
|
let result = verifier.verify_ownership_forest();
|
||||||
|
assert!(result.is_err());
|
||||||
|
|
||||||
|
if let Err(errors) = result {
|
||||||
|
assert!(errors.iter().any(|e| matches!(e, OwnershipError::StrongCycle { .. })));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_weak_reference_safety() {
|
||||||
|
let mut verifier = OwnershipVerifier::new();
|
||||||
|
let mut value_gen = ValueIdGenerator::new();
|
||||||
|
|
||||||
|
let target = value_gen.next();
|
||||||
|
let weak_ref = value_gen.next();
|
||||||
|
|
||||||
|
// Create weak reference
|
||||||
|
let weak_new = MirInstructionV2::WeakNew {
|
||||||
|
dst: weak_ref,
|
||||||
|
box_val: target,
|
||||||
|
};
|
||||||
|
assert!(verifier.process_instruction(&weak_new).is_ok());
|
||||||
|
|
||||||
|
// Release the target
|
||||||
|
let release = MirInstructionV2::Release {
|
||||||
|
reference: target,
|
||||||
|
};
|
||||||
|
assert!(verifier.process_instruction(&release).is_ok());
|
||||||
|
|
||||||
|
// WeakLoad should handle expired reference gracefully
|
||||||
|
let weak_load = MirInstructionV2::WeakLoad {
|
||||||
|
dst: value_gen.next(),
|
||||||
|
weak_ref,
|
||||||
|
};
|
||||||
|
assert!(verifier.process_instruction(&weak_load).is_ok());
|
||||||
|
|
||||||
|
let stats = verifier.ownership_stats();
|
||||||
|
assert_eq!(stats.weak_edges, 1);
|
||||||
|
assert_eq!(stats.dead_targets, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_unsafe_ref_set() {
|
||||||
|
let mut verifier = OwnershipVerifier::new();
|
||||||
|
let mut value_gen = ValueIdGenerator::new();
|
||||||
|
|
||||||
|
let reference = value_gen.next();
|
||||||
|
let old_target = value_gen.next();
|
||||||
|
let new_target = value_gen.next();
|
||||||
|
|
||||||
|
// Set initial strong ownership
|
||||||
|
verifier.strong_edges.insert(reference, old_target);
|
||||||
|
|
||||||
|
// Try to change without Release (should error)
|
||||||
|
let ref_set = MirInstructionV2::RefSet { reference, new_target };
|
||||||
|
let result = verifier.process_instruction(&ref_set);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
if let Err(errors) = result {
|
||||||
|
assert!(errors.iter().any(|e| matches!(e, OwnershipError::UnsafeRefSet { .. })));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_safe_ref_set_with_release() {
|
||||||
|
let mut verifier = OwnershipVerifier::new();
|
||||||
|
let mut value_gen = ValueIdGenerator::new();
|
||||||
|
|
||||||
|
let reference = value_gen.next();
|
||||||
|
let old_target = value_gen.next();
|
||||||
|
let new_target = value_gen.next();
|
||||||
|
|
||||||
|
// Set initial strong ownership
|
||||||
|
verifier.strong_edges.insert(reference, old_target);
|
||||||
|
|
||||||
|
// Release old target first
|
||||||
|
verifier.released.insert(old_target);
|
||||||
|
|
||||||
|
// Now RefSet should be safe
|
||||||
|
let ref_set = MirInstructionV2::RefSet { reference, new_target };
|
||||||
|
assert!(verifier.process_instruction(&ref_set).is_ok());
|
||||||
|
}
|
||||||
|
}
|
||||||
478
tests/mir_phase8_5_hierarchical_25_instructions.rs
Normal file
478
tests/mir_phase8_5_hierarchical_25_instructions.rs
Normal file
@ -0,0 +1,478 @@
|
|||||||
|
/*!
|
||||||
|
* Phase 8.5 MIR 25-Instruction Hierarchical Implementation Tests
|
||||||
|
*
|
||||||
|
* Comprehensive test suite for the ChatGPT5 + AI Council designed MIR system
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::mir::{
|
||||||
|
MirInstructionV2, ConstValue, BinaryOp, CompareOp, AtomicOrdering,
|
||||||
|
EffectMask, Effect, ValueIdGenerator, BasicBlockIdGenerator,
|
||||||
|
OwnershipVerifier, OwnershipError,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Test that we have exactly 25 instructions in the specification
|
||||||
|
#[test]
|
||||||
|
fn test_mir_instruction_count() {
|
||||||
|
// This is verified at compile time by the instruction enum
|
||||||
|
// Each tier should have the correct count:
|
||||||
|
// Tier-0: 8 instructions
|
||||||
|
// Tier-1: 12 instructions
|
||||||
|
// Tier-2: 5 instructions
|
||||||
|
// Total: 25 instructions
|
||||||
|
|
||||||
|
let mut value_gen = ValueIdGenerator::new();
|
||||||
|
let mut bb_gen = BasicBlockIdGenerator::new();
|
||||||
|
|
||||||
|
// Tier-0: Universal Core (8 instructions)
|
||||||
|
let tier0_instructions = vec![
|
||||||
|
MirInstructionV2::Const { dst: value_gen.next(), value: ConstValue::Integer(42) },
|
||||||
|
MirInstructionV2::BinOp { dst: value_gen.next(), op: BinaryOp::Add, lhs: value_gen.next(), rhs: value_gen.next() },
|
||||||
|
MirInstructionV2::Compare { dst: value_gen.next(), op: CompareOp::Eq, lhs: value_gen.next(), rhs: value_gen.next() },
|
||||||
|
MirInstructionV2::Branch { condition: value_gen.next(), then_bb: bb_gen.next(), else_bb: bb_gen.next() },
|
||||||
|
MirInstructionV2::Jump { target: bb_gen.next() },
|
||||||
|
MirInstructionV2::Phi { dst: value_gen.next(), inputs: vec![(bb_gen.next(), value_gen.next())] },
|
||||||
|
MirInstructionV2::Call { dst: Some(value_gen.next()), func: value_gen.next(), args: vec![], effects: EffectMask::PURE },
|
||||||
|
MirInstructionV2::Return { value: Some(value_gen.next()) },
|
||||||
|
];
|
||||||
|
|
||||||
|
for inst in &tier0_instructions {
|
||||||
|
assert_eq!(inst.tier(), 0, "Tier-0 instruction should have tier 0");
|
||||||
|
}
|
||||||
|
assert_eq!(tier0_instructions.len(), 8, "Tier-0 should have exactly 8 instructions");
|
||||||
|
|
||||||
|
// Tier-1: Nyash Semantics (12 instructions)
|
||||||
|
let tier1_instructions = vec![
|
||||||
|
MirInstructionV2::NewBox { dst: value_gen.next(), box_type: "TestBox".to_string(), args: vec![] },
|
||||||
|
MirInstructionV2::BoxFieldLoad { dst: value_gen.next(), box_val: value_gen.next(), field: "value".to_string() },
|
||||||
|
MirInstructionV2::BoxFieldStore { box_val: value_gen.next(), field: "value".to_string(), value: value_gen.next() },
|
||||||
|
MirInstructionV2::BoxCall { dst: Some(value_gen.next()), box_val: value_gen.next(), method: "test".to_string(), args: vec![], effects: EffectMask::PURE },
|
||||||
|
MirInstructionV2::Safepoint,
|
||||||
|
MirInstructionV2::RefGet { dst: value_gen.next(), reference: value_gen.next() },
|
||||||
|
MirInstructionV2::RefSet { reference: value_gen.next(), new_target: value_gen.next() },
|
||||||
|
MirInstructionV2::WeakNew { dst: value_gen.next(), box_val: value_gen.next() },
|
||||||
|
MirInstructionV2::WeakLoad { dst: value_gen.next(), weak_ref: value_gen.next() },
|
||||||
|
MirInstructionV2::WeakCheck { dst: value_gen.next(), weak_ref: value_gen.next() },
|
||||||
|
MirInstructionV2::Send { bus: value_gen.next(), message: value_gen.next() },
|
||||||
|
MirInstructionV2::Recv { dst: value_gen.next(), bus: value_gen.next() },
|
||||||
|
];
|
||||||
|
|
||||||
|
for inst in &tier1_instructions {
|
||||||
|
assert_eq!(inst.tier(), 1, "Tier-1 instruction should have tier 1");
|
||||||
|
}
|
||||||
|
assert_eq!(tier1_instructions.len(), 12, "Tier-1 should have exactly 12 instructions");
|
||||||
|
|
||||||
|
// Tier-2: Implementation Assistance (5 instructions)
|
||||||
|
let tier2_instructions = vec![
|
||||||
|
MirInstructionV2::TailCall { func: value_gen.next(), args: vec![], effects: EffectMask::PURE },
|
||||||
|
MirInstructionV2::Adopt { parent: value_gen.next(), child: value_gen.next() },
|
||||||
|
MirInstructionV2::Release { reference: value_gen.next() },
|
||||||
|
MirInstructionV2::MemCopy { dest: value_gen.next(), src: value_gen.next(), size: value_gen.next() },
|
||||||
|
MirInstructionV2::AtomicFence { ordering: AtomicOrdering::SeqCst },
|
||||||
|
];
|
||||||
|
|
||||||
|
for inst in &tier2_instructions {
|
||||||
|
assert_eq!(inst.tier(), 2, "Tier-2 instruction should have tier 2");
|
||||||
|
}
|
||||||
|
assert_eq!(tier2_instructions.len(), 5, "Tier-2 should have exactly 5 instructions");
|
||||||
|
|
||||||
|
// Total verification
|
||||||
|
let total_instructions = tier0_instructions.len() + tier1_instructions.len() + tier2_instructions.len();
|
||||||
|
assert_eq!(total_instructions, 25, "Total instruction count must be exactly 25");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test the 4-category effect system
|
||||||
|
#[test]
|
||||||
|
fn test_effect_categories() {
|
||||||
|
let mut value_gen = ValueIdGenerator::new();
|
||||||
|
let mut bb_gen = BasicBlockIdGenerator::new();
|
||||||
|
|
||||||
|
// Test Pure effects
|
||||||
|
let pure_instructions = vec![
|
||||||
|
MirInstructionV2::Const { dst: value_gen.next(), value: ConstValue::Integer(42) },
|
||||||
|
MirInstructionV2::BinOp { dst: value_gen.next(), op: BinaryOp::Add, lhs: value_gen.next(), rhs: value_gen.next() },
|
||||||
|
MirInstructionV2::Compare { dst: value_gen.next(), op: CompareOp::Eq, lhs: value_gen.next(), rhs: value_gen.next() },
|
||||||
|
MirInstructionV2::Phi { dst: value_gen.next(), inputs: vec![(bb_gen.next(), value_gen.next())] },
|
||||||
|
MirInstructionV2::BoxFieldLoad { dst: value_gen.next(), box_val: value_gen.next(), field: "value".to_string() },
|
||||||
|
MirInstructionV2::RefGet { dst: value_gen.next(), reference: value_gen.next() },
|
||||||
|
MirInstructionV2::WeakNew { dst: value_gen.next(), box_val: value_gen.next() },
|
||||||
|
MirInstructionV2::WeakLoad { dst: value_gen.next(), weak_ref: value_gen.next() },
|
||||||
|
MirInstructionV2::WeakCheck { dst: value_gen.next(), weak_ref: value_gen.next() },
|
||||||
|
];
|
||||||
|
|
||||||
|
for inst in pure_instructions {
|
||||||
|
let effects = inst.effects();
|
||||||
|
assert!(effects.is_pure() || effects.primary_category() == Effect::Pure,
|
||||||
|
"Instruction should be pure: {:?}", inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Mut effects
|
||||||
|
let mut_instructions = vec![
|
||||||
|
MirInstructionV2::BoxFieldStore { box_val: value_gen.next(), field: "value".to_string(), value: value_gen.next() },
|
||||||
|
MirInstructionV2::RefSet { reference: value_gen.next(), new_target: value_gen.next() },
|
||||||
|
MirInstructionV2::Adopt { parent: value_gen.next(), child: value_gen.next() },
|
||||||
|
MirInstructionV2::Release { reference: value_gen.next() },
|
||||||
|
MirInstructionV2::MemCopy { dest: value_gen.next(), src: value_gen.next(), size: value_gen.next() },
|
||||||
|
];
|
||||||
|
|
||||||
|
for inst in mut_instructions {
|
||||||
|
let effects = inst.effects();
|
||||||
|
assert!(effects.is_mut() || effects.primary_category() == Effect::Mut,
|
||||||
|
"Instruction should be mut: {:?}", inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Io effects
|
||||||
|
let io_instructions = vec![
|
||||||
|
MirInstructionV2::Safepoint,
|
||||||
|
MirInstructionV2::Send { bus: value_gen.next(), message: value_gen.next() },
|
||||||
|
MirInstructionV2::Recv { dst: value_gen.next(), bus: value_gen.next() },
|
||||||
|
MirInstructionV2::AtomicFence { ordering: AtomicOrdering::SeqCst },
|
||||||
|
];
|
||||||
|
|
||||||
|
for inst in io_instructions {
|
||||||
|
let effects = inst.effects();
|
||||||
|
assert!(effects.is_io() || effects.primary_category() == Effect::Io,
|
||||||
|
"Instruction should be io: {:?}", inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Control effects
|
||||||
|
let control_instructions = vec![
|
||||||
|
MirInstructionV2::Branch { condition: value_gen.next(), then_bb: bb_gen.next(), else_bb: bb_gen.next() },
|
||||||
|
MirInstructionV2::Jump { target: bb_gen.next() },
|
||||||
|
MirInstructionV2::Return { value: Some(value_gen.next()) },
|
||||||
|
MirInstructionV2::TailCall { func: value_gen.next(), args: vec![], effects: EffectMask::PURE },
|
||||||
|
];
|
||||||
|
|
||||||
|
for inst in control_instructions {
|
||||||
|
let effects = inst.effects();
|
||||||
|
assert!(effects.is_control() || effects.primary_category() == Effect::Control,
|
||||||
|
"Instruction should be control: {:?}", inst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test optimization safety based on effect categories
|
||||||
|
#[test]
|
||||||
|
fn test_optimization_safety() {
|
||||||
|
let mut value_gen = ValueIdGenerator::new();
|
||||||
|
|
||||||
|
// Pure operations should be reorderable and eligible for CSE/LICM
|
||||||
|
let const_inst = MirInstructionV2::Const { dst: value_gen.next(), value: ConstValue::Integer(42) };
|
||||||
|
let binop_inst = MirInstructionV2::BinOp {
|
||||||
|
dst: value_gen.next(),
|
||||||
|
op: BinaryOp::Add,
|
||||||
|
lhs: value_gen.next(),
|
||||||
|
rhs: value_gen.next()
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(const_inst.effects().is_pure(), "Const should be pure and reorderable");
|
||||||
|
assert!(binop_inst.effects().is_pure(), "BinOp should be pure and reorderable");
|
||||||
|
|
||||||
|
// Mut operations should preserve same Box/Field dependencies
|
||||||
|
let store_inst = MirInstructionV2::BoxFieldStore {
|
||||||
|
box_val: value_gen.next(),
|
||||||
|
field: "value".to_string(),
|
||||||
|
value: value_gen.next()
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(store_inst.effects().is_mut(), "BoxFieldStore should be mut");
|
||||||
|
assert!(!store_inst.effects().is_pure(), "Mut operations cannot be reordered freely");
|
||||||
|
|
||||||
|
// Io operations should not be reordered
|
||||||
|
let send_inst = MirInstructionV2::Send {
|
||||||
|
bus: value_gen.next(),
|
||||||
|
message: value_gen.next()
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(send_inst.effects().is_io(), "Send should be io");
|
||||||
|
assert!(!send_inst.effects().is_read_only(), "Io operations have external effects");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test ownership forest verification
|
||||||
|
#[test]
|
||||||
|
fn test_ownership_forest_verification() {
|
||||||
|
let mut verifier = OwnershipVerifier::new();
|
||||||
|
let mut value_gen = ValueIdGenerator::new();
|
||||||
|
|
||||||
|
// Test basic ownership establishment
|
||||||
|
let parent = value_gen.next();
|
||||||
|
let child = value_gen.next();
|
||||||
|
|
||||||
|
let adopt_inst = MirInstructionV2::Adopt { parent, child };
|
||||||
|
assert!(verifier.process_instruction(&adopt_inst).is_ok(), "Basic adoption should succeed");
|
||||||
|
|
||||||
|
let stats = verifier.ownership_stats();
|
||||||
|
assert_eq!(stats.strong_edges, 1, "Should have one strong edge");
|
||||||
|
|
||||||
|
// Test forest property verification
|
||||||
|
assert!(verifier.verify_ownership_forest().is_ok(), "Basic forest should be valid");
|
||||||
|
|
||||||
|
// Test weak reference creation
|
||||||
|
let weak_ref = value_gen.next();
|
||||||
|
let weak_new_inst = MirInstructionV2::WeakNew { dst: weak_ref, box_val: child };
|
||||||
|
assert!(verifier.process_instruction(&weak_new_inst).is_ok(), "Weak reference creation should succeed");
|
||||||
|
|
||||||
|
let stats_after_weak = verifier.ownership_stats();
|
||||||
|
assert_eq!(stats_after_weak.weak_edges, 1, "Should have one weak edge");
|
||||||
|
assert_eq!(stats_after_weak.live_weak_refs, 1, "Should have one live weak reference");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test ownership forest violations
|
||||||
|
#[test]
|
||||||
|
fn test_ownership_violations() {
|
||||||
|
let mut verifier = OwnershipVerifier::new();
|
||||||
|
let mut value_gen = ValueIdGenerator::new();
|
||||||
|
|
||||||
|
// Test unsafe RefSet (changing strong reference without Release)
|
||||||
|
let reference = value_gen.next();
|
||||||
|
let old_target = value_gen.next();
|
||||||
|
let new_target = value_gen.next();
|
||||||
|
|
||||||
|
// Manually set up initial state
|
||||||
|
verifier.strong_edges.insert(reference, old_target);
|
||||||
|
|
||||||
|
// Try to change reference without releasing old target
|
||||||
|
let unsafe_ref_set = MirInstructionV2::RefSet { reference, new_target };
|
||||||
|
let result = verifier.process_instruction(&unsafe_ref_set);
|
||||||
|
|
||||||
|
assert!(result.is_err(), "Unsafe RefSet should be rejected");
|
||||||
|
if let Err(errors) = result {
|
||||||
|
assert!(errors.iter().any(|e| matches!(e, OwnershipError::UnsafeRefSet { .. })),
|
||||||
|
"Should detect unsafe RefSet");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test weak reference liveness tracking
|
||||||
|
#[test]
|
||||||
|
fn test_weak_reference_liveness() {
|
||||||
|
let mut verifier = OwnershipVerifier::new();
|
||||||
|
let mut value_gen = ValueIdGenerator::new();
|
||||||
|
|
||||||
|
let target = value_gen.next();
|
||||||
|
let weak_ref = value_gen.next();
|
||||||
|
|
||||||
|
// Create weak reference to target
|
||||||
|
let weak_new = MirInstructionV2::WeakNew { dst: weak_ref, box_val: target };
|
||||||
|
assert!(verifier.process_instruction(&weak_new).is_ok());
|
||||||
|
|
||||||
|
// Release the target
|
||||||
|
let release = MirInstructionV2::Release { reference: target };
|
||||||
|
assert!(verifier.process_instruction(&release).is_ok());
|
||||||
|
|
||||||
|
// Check that target is now considered dead
|
||||||
|
let stats = verifier.ownership_stats();
|
||||||
|
assert_eq!(stats.dead_targets, 1, "Target should be marked as dead");
|
||||||
|
|
||||||
|
// WeakLoad should handle expired reference deterministically
|
||||||
|
let weak_load = MirInstructionV2::WeakLoad { dst: value_gen.next(), weak_ref };
|
||||||
|
assert!(verifier.process_instruction(&weak_load).is_ok(),
|
||||||
|
"WeakLoad should handle expired reference gracefully");
|
||||||
|
|
||||||
|
// WeakCheck should also handle expired reference deterministically
|
||||||
|
let weak_check = MirInstructionV2::WeakCheck { dst: value_gen.next(), weak_ref };
|
||||||
|
assert!(verifier.process_instruction(&weak_check).is_ok(),
|
||||||
|
"WeakCheck should handle expired reference gracefully");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test Bus communication instructions
|
||||||
|
#[test]
|
||||||
|
fn test_bus_operations() {
|
||||||
|
let mut value_gen = ValueIdGenerator::new();
|
||||||
|
|
||||||
|
let bus = value_gen.next();
|
||||||
|
let message = value_gen.next();
|
||||||
|
|
||||||
|
// Test Send instruction
|
||||||
|
let send_inst = MirInstructionV2::Send { bus, message };
|
||||||
|
assert_eq!(send_inst.tier(), 1, "Send should be Tier-1");
|
||||||
|
assert!(send_inst.effects().is_io(), "Send should have io effects");
|
||||||
|
|
||||||
|
let used_values = send_inst.used_values();
|
||||||
|
assert_eq!(used_values.len(), 2, "Send should use bus and message");
|
||||||
|
assert!(used_values.contains(&bus) && used_values.contains(&message));
|
||||||
|
|
||||||
|
// Test Recv instruction
|
||||||
|
let recv_inst = MirInstructionV2::Recv { dst: value_gen.next(), bus };
|
||||||
|
assert_eq!(recv_inst.tier(), 1, "Recv should be Tier-1");
|
||||||
|
assert!(recv_inst.effects().is_io(), "Recv should have io effects");
|
||||||
|
|
||||||
|
let recv_used = recv_inst.used_values();
|
||||||
|
assert_eq!(recv_used.len(), 1, "Recv should use only bus");
|
||||||
|
assert!(recv_used.contains(&bus));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test implementation assistance instructions (Tier-2)
|
||||||
|
#[test]
|
||||||
|
fn test_implementation_assistance() {
|
||||||
|
let mut value_gen = ValueIdGenerator::new();
|
||||||
|
|
||||||
|
// Test TailCall
|
||||||
|
let tail_call = MirInstructionV2::TailCall {
|
||||||
|
func: value_gen.next(),
|
||||||
|
args: vec![value_gen.next()],
|
||||||
|
effects: EffectMask::PURE
|
||||||
|
};
|
||||||
|
assert_eq!(tail_call.tier(), 2, "TailCall should be Tier-2");
|
||||||
|
assert!(tail_call.effects().is_control(), "TailCall should be control flow");
|
||||||
|
|
||||||
|
// Test MemCopy
|
||||||
|
let mem_copy = MirInstructionV2::MemCopy {
|
||||||
|
dest: value_gen.next(),
|
||||||
|
src: value_gen.next(),
|
||||||
|
size: value_gen.next()
|
||||||
|
};
|
||||||
|
assert_eq!(mem_copy.tier(), 2, "MemCopy should be Tier-2");
|
||||||
|
assert!(mem_copy.effects().is_mut(), "MemCopy should be mut");
|
||||||
|
|
||||||
|
// Test AtomicFence
|
||||||
|
let atomic_fence = MirInstructionV2::AtomicFence { ordering: AtomicOrdering::AcqRel };
|
||||||
|
assert_eq!(atomic_fence.tier(), 2, "AtomicFence should be Tier-2");
|
||||||
|
assert!(atomic_fence.effects().is_io(), "AtomicFence should be io");
|
||||||
|
assert!(atomic_fence.effects().contains(Effect::Barrier), "AtomicFence should have barrier effect");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test instruction descriptions and display
|
||||||
|
#[test]
|
||||||
|
fn test_instruction_descriptions() {
|
||||||
|
let mut value_gen = ValueIdGenerator::new();
|
||||||
|
|
||||||
|
let const_inst = MirInstructionV2::Const { dst: value_gen.next(), value: ConstValue::Integer(42) };
|
||||||
|
assert_eq!(const_inst.description(), "Load constant value");
|
||||||
|
|
||||||
|
let send_inst = MirInstructionV2::Send { bus: value_gen.next(), message: value_gen.next() };
|
||||||
|
assert_eq!(send_inst.description(), "Send Bus message");
|
||||||
|
|
||||||
|
let adopt_inst = MirInstructionV2::Adopt { parent: value_gen.next(), child: value_gen.next() };
|
||||||
|
assert_eq!(adopt_inst.description(), "Transfer ownership");
|
||||||
|
|
||||||
|
// Test Display trait
|
||||||
|
assert_eq!(format!("{}", const_inst), "Load constant value");
|
||||||
|
assert_eq!(format!("{}", send_inst), "Send Bus message");
|
||||||
|
assert_eq!(format!("{}", adopt_inst), "Transfer ownership");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test value ID tracking for dependencies
|
||||||
|
#[test]
|
||||||
|
fn test_value_id_tracking() {
|
||||||
|
let mut value_gen = ValueIdGenerator::new();
|
||||||
|
|
||||||
|
let dst = value_gen.next();
|
||||||
|
let lhs = value_gen.next();
|
||||||
|
let rhs = value_gen.next();
|
||||||
|
|
||||||
|
let binop = MirInstructionV2::BinOp { dst, op: BinaryOp::Add, lhs, rhs };
|
||||||
|
|
||||||
|
// Test destination value
|
||||||
|
assert_eq!(binop.dst_value(), Some(dst), "BinOp should produce destination value");
|
||||||
|
|
||||||
|
// Test used values
|
||||||
|
let used = binop.used_values();
|
||||||
|
assert_eq!(used.len(), 2, "BinOp should use two values");
|
||||||
|
assert!(used.contains(&lhs) && used.contains(&rhs), "Should use lhs and rhs");
|
||||||
|
|
||||||
|
// Test instruction with no destination
|
||||||
|
let store = MirInstructionV2::BoxFieldStore {
|
||||||
|
box_val: value_gen.next(),
|
||||||
|
field: "value".to_string(),
|
||||||
|
value: value_gen.next()
|
||||||
|
};
|
||||||
|
assert_eq!(store.dst_value(), None, "BoxFieldStore should not produce destination value");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test the complete 25-instruction specification compliance
|
||||||
|
#[test]
|
||||||
|
fn test_complete_specification_compliance() {
|
||||||
|
// This test verifies that our implementation matches the exact specification
|
||||||
|
|
||||||
|
// Verify we can create all 25 instruction types without compilation errors
|
||||||
|
let mut value_gen = ValueIdGenerator::new();
|
||||||
|
let mut bb_gen = BasicBlockIdGenerator::new();
|
||||||
|
|
||||||
|
let all_instructions = vec![
|
||||||
|
// Tier-0: Universal Core (8)
|
||||||
|
MirInstructionV2::Const { dst: value_gen.next(), value: ConstValue::Integer(42) },
|
||||||
|
MirInstructionV2::BinOp { dst: value_gen.next(), op: BinaryOp::Add, lhs: value_gen.next(), rhs: value_gen.next() },
|
||||||
|
MirInstructionV2::Compare { dst: value_gen.next(), op: CompareOp::Eq, lhs: value_gen.next(), rhs: value_gen.next() },
|
||||||
|
MirInstructionV2::Branch { condition: value_gen.next(), then_bb: bb_gen.next(), else_bb: bb_gen.next() },
|
||||||
|
MirInstructionV2::Jump { target: bb_gen.next() },
|
||||||
|
MirInstructionV2::Phi { dst: value_gen.next(), inputs: vec![] },
|
||||||
|
MirInstructionV2::Call { dst: Some(value_gen.next()), func: value_gen.next(), args: vec![], effects: EffectMask::PURE },
|
||||||
|
MirInstructionV2::Return { value: Some(value_gen.next()) },
|
||||||
|
|
||||||
|
// Tier-1: Nyash Semantics (12)
|
||||||
|
MirInstructionV2::NewBox { dst: value_gen.next(), box_type: "TestBox".to_string(), args: vec![] },
|
||||||
|
MirInstructionV2::BoxFieldLoad { dst: value_gen.next(), box_val: value_gen.next(), field: "field".to_string() },
|
||||||
|
MirInstructionV2::BoxFieldStore { box_val: value_gen.next(), field: "field".to_string(), value: value_gen.next() },
|
||||||
|
MirInstructionV2::BoxCall { dst: Some(value_gen.next()), box_val: value_gen.next(), method: "method".to_string(), args: vec![], effects: EffectMask::PURE },
|
||||||
|
MirInstructionV2::Safepoint,
|
||||||
|
MirInstructionV2::RefGet { dst: value_gen.next(), reference: value_gen.next() },
|
||||||
|
MirInstructionV2::RefSet { reference: value_gen.next(), new_target: value_gen.next() },
|
||||||
|
MirInstructionV2::WeakNew { dst: value_gen.next(), box_val: value_gen.next() },
|
||||||
|
MirInstructionV2::WeakLoad { dst: value_gen.next(), weak_ref: value_gen.next() },
|
||||||
|
MirInstructionV2::WeakCheck { dst: value_gen.next(), weak_ref: value_gen.next() },
|
||||||
|
MirInstructionV2::Send { bus: value_gen.next(), message: value_gen.next() },
|
||||||
|
MirInstructionV2::Recv { dst: value_gen.next(), bus: value_gen.next() },
|
||||||
|
|
||||||
|
// Tier-2: Implementation Assistance (5)
|
||||||
|
MirInstructionV2::TailCall { func: value_gen.next(), args: vec![], effects: EffectMask::PURE },
|
||||||
|
MirInstructionV2::Adopt { parent: value_gen.next(), child: value_gen.next() },
|
||||||
|
MirInstructionV2::Release { reference: value_gen.next() },
|
||||||
|
MirInstructionV2::MemCopy { dest: value_gen.next(), src: value_gen.next(), size: value_gen.next() },
|
||||||
|
MirInstructionV2::AtomicFence { ordering: AtomicOrdering::SeqCst },
|
||||||
|
];
|
||||||
|
|
||||||
|
assert_eq!(all_instructions.len(), 25, "Must have exactly 25 instructions");
|
||||||
|
|
||||||
|
// Verify tier distribution
|
||||||
|
let tier0_count = all_instructions.iter().filter(|i| i.tier() == 0).count();
|
||||||
|
let tier1_count = all_instructions.iter().filter(|i| i.tier() == 1).count();
|
||||||
|
let tier2_count = all_instructions.iter().filter(|i| i.tier() == 2).count();
|
||||||
|
|
||||||
|
assert_eq!(tier0_count, 8, "Tier-0 should have 8 instructions");
|
||||||
|
assert_eq!(tier1_count, 12, "Tier-1 should have 12 instructions");
|
||||||
|
assert_eq!(tier2_count, 5, "Tier-2 should have 5 instructions");
|
||||||
|
|
||||||
|
// Verify each instruction has proper effect classification
|
||||||
|
for instruction in &all_instructions {
|
||||||
|
let effects = instruction.effects();
|
||||||
|
let category = effects.primary_category();
|
||||||
|
|
||||||
|
// Ensure every instruction has a valid effect category
|
||||||
|
assert!(
|
||||||
|
matches!(category, Effect::Pure | Effect::Mut | Effect::Io | Effect::Control),
|
||||||
|
"Instruction must have valid effect category: {:?}", instruction
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performance test: Ensure effect calculations are fast
|
||||||
|
#[test]
|
||||||
|
fn test_effect_calculation_performance() {
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
let mut value_gen = ValueIdGenerator::new();
|
||||||
|
let mut bb_gen = BasicBlockIdGenerator::new();
|
||||||
|
|
||||||
|
// Create a large number of instructions
|
||||||
|
let mut instructions = Vec::new();
|
||||||
|
for _ in 0..10000 {
|
||||||
|
instructions.push(MirInstructionV2::BinOp {
|
||||||
|
dst: value_gen.next(),
|
||||||
|
op: BinaryOp::Add,
|
||||||
|
lhs: value_gen.next(),
|
||||||
|
rhs: value_gen.next()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measure effect calculation time
|
||||||
|
let start = Instant::now();
|
||||||
|
for instruction in &instructions {
|
||||||
|
let _ = instruction.effects();
|
||||||
|
let _ = instruction.tier();
|
||||||
|
let _ = instruction.dst_value();
|
||||||
|
let _ = instruction.used_values();
|
||||||
|
}
|
||||||
|
let elapsed = start.elapsed();
|
||||||
|
|
||||||
|
// Should be very fast (< 10ms for 10k instructions)
|
||||||
|
assert!(elapsed.as_millis() < 100,
|
||||||
|
"Effect calculations should be fast, took {:?}", elapsed);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user