diff --git a/crates/console/prometeu-bytecode/src/opcode_spec.rs b/crates/console/prometeu-bytecode/src/opcode_spec.rs index 1706b0d1..d2907136 100644 --- a/crates/console/prometeu-bytecode/src/opcode_spec.rs +++ b/crates/console/prometeu-bytecode/src/opcode_spec.rs @@ -11,6 +11,8 @@ pub struct OpcodeSpec { pub is_branch: bool, // has a control-flow target pub is_terminator: bool, // ends basic block: JMP/RET/TRAP/HALT pub may_trap: bool, // runtime trap possible + /// Marks this opcode as a VM safepoint. Used by GC/scheduler policies. + pub is_safepoint: bool, } pub trait OpCodeSpecExt { @@ -28,6 +30,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::Halt => OpcodeSpec { name: "HALT", @@ -37,6 +40,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: true, may_trap: false, + is_safepoint: false, }, OpCode::Jmp => OpcodeSpec { name: "JMP", @@ -46,6 +50,7 @@ impl OpCodeSpecExt for OpCode { is_branch: true, is_terminator: true, may_trap: false, + is_safepoint: false, }, OpCode::JmpIfFalse => OpcodeSpec { name: "JMP_IF_FALSE", @@ -55,6 +60,7 @@ impl OpCodeSpecExt for OpCode { is_branch: true, is_terminator: false, may_trap: true, + is_safepoint: false, }, OpCode::JmpIfTrue => OpcodeSpec { name: "JMP_IF_TRUE", @@ -64,6 +70,7 @@ impl OpCodeSpecExt for OpCode { is_branch: true, is_terminator: false, may_trap: true, + is_safepoint: false, }, OpCode::Trap => OpcodeSpec { name: "TRAP", @@ -73,6 +80,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: true, may_trap: true, + is_safepoint: false, }, OpCode::PushConst => OpcodeSpec { name: "PUSH_CONST", @@ -82,6 +90,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::Pop => OpcodeSpec { name: "POP", @@ -91,6 +100,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::PopN => OpcodeSpec { name: "POP_N", @@ -100,6 +110,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::Dup => OpcodeSpec { name: "DUP", @@ -109,6 +120,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::Swap => OpcodeSpec { name: "SWAP", @@ -118,6 +130,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::PushI64 => OpcodeSpec { name: "PUSH_I64", @@ -127,6 +140,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::PushF64 => OpcodeSpec { name: "PUSH_F64", @@ -136,6 +150,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::PushBool => OpcodeSpec { name: "PUSH_BOOL", @@ -145,6 +160,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::PushI32 => OpcodeSpec { name: "PUSH_I32", @@ -154,6 +170,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::PushBounded => OpcodeSpec { name: "PUSH_BOUNDED", @@ -163,6 +180,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: true, + is_safepoint: false, }, OpCode::Add => OpcodeSpec { name: "ADD", @@ -172,6 +190,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: true, + is_safepoint: false, }, OpCode::Sub => OpcodeSpec { name: "SUB", @@ -181,6 +200,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: true, + is_safepoint: false, }, OpCode::Mul => OpcodeSpec { name: "MUL", @@ -190,6 +210,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: true, + is_safepoint: false, }, OpCode::Div => OpcodeSpec { name: "DIV", @@ -199,6 +220,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: true, + is_safepoint: false, }, OpCode::Mod => OpcodeSpec { name: "MOD", @@ -208,6 +230,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: true, + is_safepoint: false, }, OpCode::BoundToInt => OpcodeSpec { name: "BOUND_TO_INT", @@ -217,6 +240,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::IntToBoundChecked => OpcodeSpec { name: "INT_TO_BOUND_CHECKED", @@ -226,6 +250,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: true, + is_safepoint: false, }, OpCode::Eq => OpcodeSpec { name: "EQ", @@ -235,6 +260,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::Neq => OpcodeSpec { name: "NEQ", @@ -244,6 +270,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::Lt => OpcodeSpec { name: "LT", @@ -253,6 +280,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::Gt => OpcodeSpec { name: "GT", @@ -262,6 +290,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::And => OpcodeSpec { name: "AND", @@ -271,6 +300,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::Or => OpcodeSpec { name: "OR", @@ -280,6 +310,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::Not => OpcodeSpec { name: "NOT", @@ -289,6 +320,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::BitAnd => OpcodeSpec { name: "BIT_AND", @@ -298,6 +330,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::BitOr => OpcodeSpec { name: "BIT_OR", @@ -307,6 +340,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::BitXor => OpcodeSpec { name: "BIT_XOR", @@ -316,6 +350,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::Shl => OpcodeSpec { name: "SHL", @@ -325,6 +360,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::Shr => OpcodeSpec { name: "SHR", @@ -334,6 +370,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::Lte => OpcodeSpec { name: "LTE", @@ -343,6 +380,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::Gte => OpcodeSpec { name: "GTE", @@ -352,6 +390,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::Neg => OpcodeSpec { name: "NEG", @@ -361,6 +400,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::GetGlobal => OpcodeSpec { name: "GET_GLOBAL", @@ -370,6 +410,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::SetGlobal => OpcodeSpec { name: "SET_GLOBAL", @@ -379,6 +420,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::GetLocal => OpcodeSpec { name: "GET_LOCAL", @@ -388,6 +430,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::SetLocal => OpcodeSpec { name: "SET_LOCAL", @@ -397,6 +440,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::Call => OpcodeSpec { name: "CALL", @@ -406,6 +450,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: true, + is_safepoint: false, }, OpCode::Ret => OpcodeSpec { name: "RET", @@ -415,6 +460,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: true, may_trap: false, + is_safepoint: false, }, OpCode::PushScope => OpcodeSpec { name: "PUSH_SCOPE", @@ -424,6 +470,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::PopScope => OpcodeSpec { name: "POP_SCOPE", @@ -433,6 +480,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: false, }, OpCode::Syscall => OpcodeSpec { name: "SYSCALL", @@ -442,6 +490,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: true, + is_safepoint: false, }, OpCode::FrameSync => OpcodeSpec { name: "FRAME_SYNC", @@ -451,6 +500,7 @@ impl OpCodeSpecExt for OpCode { is_branch: false, is_terminator: false, may_trap: false, + is_safepoint: true, }, } } diff --git a/docs/bytecode/ISA_CORE.md b/docs/bytecode/ISA_CORE.md index c763f797..3e635443 100644 --- a/docs/bytecode/ISA_CORE.md +++ b/docs/bytecode/ISA_CORE.md @@ -73,3 +73,22 @@ For exact immediates and stack effects, see `CoreOpCode::spec()` which is the si - `CoreOpCode` — the opcode enum of the core profile. - `CoreOpcodeSpec` and `CoreOpCodeSpecExt` — spec with `imm_bytes`, stack effects, and flags. - Consumers (encoder/decoder/disasm/verifier) should import from this module to avoid depending on internal layout. + +#### FRAME_SYNC — Semantics and Placement (Bytecode Level) + +- Semantics: + - `FRAME_SYNC` is a zero-operand instruction and does not modify the operand stack. + - It marks a VM safepoint for GC and the cooperative scheduler. In `CoreOpcodeSpec` this is exposed as `spec.is_safepoint == true`. + - On execution, the VM may suspend the current fiber/coroutine until the next frame boundary (e.g., vsync) and/or perform GC. After resuming, execution continues at the next instruction. + +- Placement rules (representable and checkable): + - `FRAME_SYNC` may appear anywhere inside a function body where normal instructions can appear. It is NOT a block terminator (`spec.is_terminator == false`). + - Instruction boundaries are canonical: encoders/emitters must only place `FRAME_SYNC` at valid instruction PCs. The verifier already enforces “jump-to-boundary” and end-exclusive `[start, end)` function ranges using the canonical layout routine. + - Entrypoints that represent a render/update loop SHOULD ensure at least one reachable `FRAME_SYNC` along every long-running path to provide deterministic safepoints for GC/scheduling. This policy is semantic and may be enforced by higher-level tooling; at the bytecode level it is representable via `spec.is_safepoint` and can be counted by static analyzers. + +- Disassembly: + - Disassemblers must print the mnemonic `FRAME_SYNC` verbatim for this opcode. + - Tools MAY optionally annotate it as a safepoint in comments, e.g., `FRAME_SYNC ; safepoint`. + +- Verification notes: + - The bytecode verifier treats `FRAME_SYNC` as a normal instruction with no stack effect and no control-flow targets. It is permitted before `RET`, between basic blocks, and as the last instruction of a function. Jumps targeting the function end (`pc == end`) remain valid under the end-exclusive rule. diff --git a/files/TODOs.md b/files/TODOs.md index 9da9607c..b7ad64a6 100644 --- a/files/TODOs.md +++ b/files/TODOs.md @@ -1,40 +1,3 @@ -# PR-1.6 — Canonical Function Boundaries & `FRAME_SYNC` Placement (Bytecode Layout) - -### Briefing - -GC and coroutine scheduling rely on deterministic safepoints. The bytecode layout must define canonical function boundaries and `FRAME_SYNC` semantics at the bytecode layer. - -### Target - -* Enforce canonical function ranges and end labels. -* Ensure `FRAME_SYNC` placement rules are representable and checkable. - -### Work items - -* Review current layout utilities and make them canonical for: - - * Computing function `code_len`. - * Determining valid jump targets (instruction boundaries). - * Determining function end boundary. -* Document `FRAME_SYNC` at the bytecode level: - - * What it signals. - * Where it is required (as per updated specs). -* Update disasm to clearly display `FRAME_SYNC`. - -### Acceptance checklist - -* [ ] Function boundaries are computed via a single canonical routine. -* [ ] `FRAME_SYNC` is clearly represented and documented. -* [ ] Existing verifier/layout tests (if any) are updated. -* [ ] `cargo test` passes. - -### Tests - -* Add or update unit tests for layout boundary correctness (e.g., end-exclusive semantics). - ---- - # PR-1.7 — Bytecode Roundtrip Tests (Encode/Decode/Disasm Sanity) ### Briefing