change ir to ir_vm and make it feature frozen
This commit is contained in:
parent
4174d01a43
commit
c34166435f
@ -4,13 +4,13 @@
|
||||
//! converting the Intermediate Representation (IR) into the binary Prometeu ByteCode (PBC) format.
|
||||
//!
|
||||
//! It performs two main tasks:
|
||||
//! 1. **Instruction Lowering**: Translates `ir::Instruction` into `prometeu_bytecode::asm::Asm` ops.
|
||||
//! 1. **Instruction Lowering**: Translates `ir_vm::Instruction` into `prometeu_bytecode::asm::Asm` ops.
|
||||
//! 2. **Symbol Mapping**: Associates bytecode offsets (Program Counter) with source code locations.
|
||||
|
||||
use crate::common::files::FileManager;
|
||||
use crate::common::symbols::Symbol;
|
||||
use crate::ir;
|
||||
use crate::ir::instr::InstrKind;
|
||||
use crate::ir_vm;
|
||||
use crate::ir_vm::instr::InstrKind;
|
||||
use crate::ir_core::ConstantValue;
|
||||
use anyhow::{anyhow, Result};
|
||||
use prometeu_bytecode::asm::{assemble, update_pc_by_operand, Asm, Operand};
|
||||
@ -26,7 +26,7 @@ pub struct EmitResult {
|
||||
}
|
||||
|
||||
/// Entry point for emitting a bytecode module from the IR.
|
||||
pub fn emit_module(module: &ir::Module, file_manager: &FileManager) -> Result<EmitResult> {
|
||||
pub fn emit_module(module: &ir_vm::Module, file_manager: &FileManager) -> Result<EmitResult> {
|
||||
let mut emitter = BytecodeEmitter::new(file_manager);
|
||||
emitter.emit(module)
|
||||
}
|
||||
@ -69,7 +69,7 @@ impl<'a> BytecodeEmitter<'a> {
|
||||
}
|
||||
|
||||
/// Transforms an IR module into a binary PBC file.
|
||||
fn emit(&mut self, module: &ir::Module) -> Result<EmitResult> {
|
||||
fn emit(&mut self, module: &ir_vm::Module) -> Result<EmitResult> {
|
||||
let mut asm_instrs = Vec::new();
|
||||
let mut ir_instr_map = Vec::new(); // Maps Asm index to IR instruction (for symbols)
|
||||
|
||||
@ -232,9 +232,9 @@ impl<'a> BytecodeEmitter<'a> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::ir::module::{Module, Function};
|
||||
use crate::ir::instr::{Instruction, InstrKind};
|
||||
use crate::ir::types::Type;
|
||||
use crate::ir_vm::module::{Module, Function};
|
||||
use crate::ir_vm::instr::{Instruction, InstrKind};
|
||||
use crate::ir_vm::types::Type;
|
||||
use crate::ir_core::ids::FunctionId;
|
||||
use crate::ir_core::const_pool::ConstantValue;
|
||||
use crate::common::files::FileManager;
|
||||
|
||||
@ -8,7 +8,7 @@ use crate::common::config::ProjectConfig;
|
||||
use crate::common::files::FileManager;
|
||||
use crate::common::symbols::Symbol;
|
||||
use crate::frontends::Frontend;
|
||||
use crate::ir;
|
||||
use crate::ir_vm;
|
||||
use anyhow::Result;
|
||||
use std::path::Path;
|
||||
|
||||
@ -87,7 +87,7 @@ pub fn compile(project_dir: &Path) -> Result<CompilationUnit> {
|
||||
// 3. IR Validation
|
||||
// Ensures the generated IR is sound and doesn't violate any VM constraints
|
||||
// before we spend time generating bytecode.
|
||||
ir::validate::validate_module(&ir_module)
|
||||
ir_vm::validate::validate_module(&ir_module)
|
||||
.map_err(|bundle| anyhow::anyhow!("IR Validation failed: {:?}", bundle))?;
|
||||
|
||||
// 4. Emit Bytecode
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use crate::common::diagnostics::DiagnosticBundle;
|
||||
use crate::ir;
|
||||
use crate::ir_vm;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::common::files::FileManager;
|
||||
@ -13,5 +13,5 @@ pub trait Frontend {
|
||||
&self,
|
||||
entry: &Path,
|
||||
file_manager: &mut FileManager,
|
||||
) -> Result<ir::Module, DiagnosticBundle>;
|
||||
) -> Result<ir_vm::Module, DiagnosticBundle>;
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ pub use lowering::Lowerer;
|
||||
use crate::common::diagnostics::DiagnosticBundle;
|
||||
use crate::common::files::FileManager;
|
||||
use crate::frontends::Frontend;
|
||||
use crate::ir;
|
||||
use crate::ir_vm;
|
||||
use crate::lowering::core_to_vm;
|
||||
use std::path::Path;
|
||||
|
||||
@ -36,7 +36,7 @@ impl Frontend for PbsFrontend {
|
||||
&self,
|
||||
entry: &Path,
|
||||
file_manager: &mut FileManager,
|
||||
) -> Result<ir::Module, DiagnosticBundle> {
|
||||
) -> Result<ir_vm::Module, DiagnosticBundle> {
|
||||
let source = std::fs::read_to_string(entry).map_err(|e| {
|
||||
DiagnosticBundle::error(format!("Failed to read file: {}", e), None)
|
||||
})?;
|
||||
|
||||
@ -4,8 +4,8 @@
|
||||
//! The IR is a higher-level representation of the program than bytecode, but lower
|
||||
//! than the source code AST. It is organized into Modules, Functions, and Globals.
|
||||
|
||||
use crate::ir::instr::Instruction;
|
||||
use crate::ir::types::Type;
|
||||
use crate::ir_vm::instr::Instruction;
|
||||
use crate::ir_vm::types::Type;
|
||||
use crate::ir_core::const_pool::ConstPool;
|
||||
use crate::ir_core::ids::FunctionId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -1,5 +1,5 @@
|
||||
use crate::common::diagnostics::DiagnosticBundle;
|
||||
use crate::ir::module::Module;
|
||||
use crate::ir_vm::module::Module;
|
||||
|
||||
pub fn validate_module(_module: &Module) -> Result<(), DiagnosticBundle> {
|
||||
// TODO: Implement common IR validations:
|
||||
@ -38,7 +38,7 @@
|
||||
//! See the [`compiler`] module for the main entry point to trigger a compilation programmatically.
|
||||
|
||||
pub mod common;
|
||||
pub mod ir;
|
||||
pub mod ir_vm;
|
||||
pub mod ir_core;
|
||||
pub mod lowering;
|
||||
pub mod backend;
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
use crate::ir;
|
||||
use crate::ir_vm;
|
||||
use crate::ir_core;
|
||||
use anyhow::Result;
|
||||
|
||||
/// Lowers a Core IR program into a VM IR module.
|
||||
pub fn lower_program(program: &ir_core::Program) -> Result<ir::Module> {
|
||||
pub fn lower_program(program: &ir_core::Program) -> Result<ir_vm::Module> {
|
||||
// For now, we assume a single module program or lower the first one.
|
||||
// In the future, we might want to lower all modules and link them.
|
||||
if let Some(core_module) = program.modules.first() {
|
||||
@ -14,8 +14,8 @@ pub fn lower_program(program: &ir_core::Program) -> Result<ir::Module> {
|
||||
}
|
||||
|
||||
/// Lowers a single Core IR module into a VM IR module.
|
||||
pub fn lower_module(core_module: &ir_core::Module, const_pool: &ir_core::ConstPool) -> Result<ir::Module> {
|
||||
let mut vm_module = ir::Module::new(core_module.name.clone());
|
||||
pub fn lower_module(core_module: &ir_core::Module, const_pool: &ir_core::ConstPool) -> Result<ir_vm::Module> {
|
||||
let mut vm_module = ir_vm::Module::new(core_module.name.clone());
|
||||
vm_module.const_pool = const_pool.clone();
|
||||
|
||||
for core_func in &core_module.functions {
|
||||
@ -26,11 +26,11 @@ pub fn lower_module(core_module: &ir_core::Module, const_pool: &ir_core::ConstPo
|
||||
}
|
||||
|
||||
/// Lowers a Core IR function into a VM IR function.
|
||||
pub fn lower_function(core_func: &ir_core::Function) -> Result<ir::Function> {
|
||||
let mut vm_func = ir::Function {
|
||||
pub fn lower_function(core_func: &ir_core::Function) -> Result<ir_vm::Function> {
|
||||
let mut vm_func = ir_vm::Function {
|
||||
id: core_func.id,
|
||||
name: core_func.name.clone(),
|
||||
params: core_func.params.iter().map(|p| ir::Param {
|
||||
params: core_func.params.iter().map(|p| ir_vm::Param {
|
||||
name: p.name.clone(),
|
||||
r#type: lower_type(&p.ty),
|
||||
}).collect(),
|
||||
@ -40,62 +40,62 @@ pub fn lower_function(core_func: &ir_core::Function) -> Result<ir::Function> {
|
||||
|
||||
for block in &core_func.blocks {
|
||||
// Core blocks map to labels in the flat VM IR instruction list.
|
||||
vm_func.body.push(ir::Instruction::new(
|
||||
ir::InstrKind::Label(ir::Label(format!("block_{}", block.id))),
|
||||
vm_func.body.push(ir_vm::Instruction::new(
|
||||
ir_vm::InstrKind::Label(ir_vm::Label(format!("block_{}", block.id))),
|
||||
None,
|
||||
));
|
||||
|
||||
for instr in &block.instrs {
|
||||
let kind = match instr {
|
||||
ir_core::Instr::PushConst(id) => ir::InstrKind::PushConst(*id),
|
||||
ir_core::Instr::Call(func_id, arg_count) => ir::InstrKind::Call {
|
||||
ir_core::Instr::PushConst(id) => ir_vm::InstrKind::PushConst(*id),
|
||||
ir_core::Instr::Call(func_id, arg_count) => ir_vm::InstrKind::Call {
|
||||
func_id: *func_id,
|
||||
arg_count: *arg_count
|
||||
},
|
||||
ir_core::Instr::Syscall(id) => ir::InstrKind::Syscall(*id),
|
||||
ir_core::Instr::GetLocal(slot) => ir::InstrKind::GetLocal(*slot),
|
||||
ir_core::Instr::SetLocal(slot) => ir::InstrKind::SetLocal(*slot),
|
||||
ir_core::Instr::Pop => ir::InstrKind::Pop,
|
||||
ir_core::Instr::Dup => ir::InstrKind::Dup,
|
||||
ir_core::Instr::Add => ir::InstrKind::Add,
|
||||
ir_core::Instr::Sub => ir::InstrKind::Sub,
|
||||
ir_core::Instr::Mul => ir::InstrKind::Mul,
|
||||
ir_core::Instr::Div => ir::InstrKind::Div,
|
||||
ir_core::Instr::Neg => ir::InstrKind::Neg,
|
||||
ir_core::Instr::Eq => ir::InstrKind::Eq,
|
||||
ir_core::Instr::Neq => ir::InstrKind::Neq,
|
||||
ir_core::Instr::Lt => ir::InstrKind::Lt,
|
||||
ir_core::Instr::Lte => ir::InstrKind::Lte,
|
||||
ir_core::Instr::Gt => ir::InstrKind::Gt,
|
||||
ir_core::Instr::Gte => ir::InstrKind::Gte,
|
||||
ir_core::Instr::And => ir::InstrKind::And,
|
||||
ir_core::Instr::Or => ir::InstrKind::Or,
|
||||
ir_core::Instr::Not => ir::InstrKind::Not,
|
||||
ir_core::Instr::Alloc => ir::InstrKind::Alloc,
|
||||
ir_core::Instr::ReadGate => ir::InstrKind::LoadRef(0),
|
||||
ir_core::Instr::WriteGate => ir::InstrKind::StoreRef(0),
|
||||
ir_core::Instr::Free => ir::InstrKind::Nop,
|
||||
ir_core::Instr::Syscall(id) => ir_vm::InstrKind::Syscall(*id),
|
||||
ir_core::Instr::GetLocal(slot) => ir_vm::InstrKind::GetLocal(*slot),
|
||||
ir_core::Instr::SetLocal(slot) => ir_vm::InstrKind::SetLocal(*slot),
|
||||
ir_core::Instr::Pop => ir_vm::InstrKind::Pop,
|
||||
ir_core::Instr::Dup => ir_vm::InstrKind::Dup,
|
||||
ir_core::Instr::Add => ir_vm::InstrKind::Add,
|
||||
ir_core::Instr::Sub => ir_vm::InstrKind::Sub,
|
||||
ir_core::Instr::Mul => ir_vm::InstrKind::Mul,
|
||||
ir_core::Instr::Div => ir_vm::InstrKind::Div,
|
||||
ir_core::Instr::Neg => ir_vm::InstrKind::Neg,
|
||||
ir_core::Instr::Eq => ir_vm::InstrKind::Eq,
|
||||
ir_core::Instr::Neq => ir_vm::InstrKind::Neq,
|
||||
ir_core::Instr::Lt => ir_vm::InstrKind::Lt,
|
||||
ir_core::Instr::Lte => ir_vm::InstrKind::Lte,
|
||||
ir_core::Instr::Gt => ir_vm::InstrKind::Gt,
|
||||
ir_core::Instr::Gte => ir_vm::InstrKind::Gte,
|
||||
ir_core::Instr::And => ir_vm::InstrKind::And,
|
||||
ir_core::Instr::Or => ir_vm::InstrKind::Or,
|
||||
ir_core::Instr::Not => ir_vm::InstrKind::Not,
|
||||
ir_core::Instr::Alloc => ir_vm::InstrKind::Alloc,
|
||||
ir_core::Instr::ReadGate => ir_vm::InstrKind::LoadRef(0),
|
||||
ir_core::Instr::WriteGate => ir_vm::InstrKind::StoreRef(0),
|
||||
ir_core::Instr::Free => ir_vm::InstrKind::Nop,
|
||||
};
|
||||
vm_func.body.push(ir::Instruction::new(kind, None));
|
||||
vm_func.body.push(ir_vm::Instruction::new(kind, None));
|
||||
}
|
||||
|
||||
match &block.terminator {
|
||||
ir_core::Terminator::Return => {
|
||||
vm_func.body.push(ir::Instruction::new(ir::InstrKind::Ret, None));
|
||||
vm_func.body.push(ir_vm::Instruction::new(ir_vm::InstrKind::Ret, None));
|
||||
}
|
||||
ir_core::Terminator::Jump(target) => {
|
||||
vm_func.body.push(ir::Instruction::new(
|
||||
ir::InstrKind::Jmp(ir::Label(format!("block_{}", target))),
|
||||
vm_func.body.push(ir_vm::Instruction::new(
|
||||
ir_vm::InstrKind::Jmp(ir_vm::Label(format!("block_{}", target))),
|
||||
None,
|
||||
));
|
||||
}
|
||||
ir_core::Terminator::JumpIfFalse { target, else_target } => {
|
||||
vm_func.body.push(ir::Instruction::new(
|
||||
ir::InstrKind::JmpIfFalse(ir::Label(format!("block_{}", target))),
|
||||
vm_func.body.push(ir_vm::Instruction::new(
|
||||
ir_vm::InstrKind::JmpIfFalse(ir_vm::Label(format!("block_{}", target))),
|
||||
None,
|
||||
));
|
||||
vm_func.body.push(ir::Instruction::new(
|
||||
ir::InstrKind::Jmp(ir::Label(format!("block_{}", else_target))),
|
||||
vm_func.body.push(ir_vm::Instruction::new(
|
||||
ir_vm::InstrKind::Jmp(ir_vm::Label(format!("block_{}", else_target))),
|
||||
None,
|
||||
));
|
||||
}
|
||||
@ -105,20 +105,20 @@ pub fn lower_function(core_func: &ir_core::Function) -> Result<ir::Function> {
|
||||
Ok(vm_func)
|
||||
}
|
||||
|
||||
fn lower_type(ty: &ir_core::Type) -> ir::Type {
|
||||
fn lower_type(ty: &ir_core::Type) -> ir_vm::Type {
|
||||
match ty {
|
||||
ir_core::Type::Void => ir::Type::Void,
|
||||
ir_core::Type::Int => ir::Type::Int,
|
||||
ir_core::Type::Float => ir::Type::Float,
|
||||
ir_core::Type::Bool => ir::Type::Bool,
|
||||
ir_core::Type::String => ir::Type::String,
|
||||
ir_core::Type::Optional(inner) => ir::Type::Array(Box::new(lower_type(inner))), // Approximation
|
||||
ir_core::Type::Void => ir_vm::Type::Void,
|
||||
ir_core::Type::Int => ir_vm::Type::Int,
|
||||
ir_core::Type::Float => ir_vm::Type::Float,
|
||||
ir_core::Type::Bool => ir_vm::Type::Bool,
|
||||
ir_core::Type::String => ir_vm::Type::String,
|
||||
ir_core::Type::Optional(inner) => ir_vm::Type::Array(Box::new(lower_type(inner))), // Approximation
|
||||
ir_core::Type::Result(ok, _) => lower_type(ok), // Approximation
|
||||
ir_core::Type::Struct(_) => ir::Type::Object,
|
||||
ir_core::Type::Service(_) => ir::Type::Object,
|
||||
ir_core::Type::Contract(_) => ir::Type::Object,
|
||||
ir_core::Type::ErrorType(_) => ir::Type::Object,
|
||||
ir_core::Type::Function { .. } => ir::Type::Function,
|
||||
ir_core::Type::Struct(_) => ir_vm::Type::Object,
|
||||
ir_core::Type::Service(_) => ir_vm::Type::Object,
|
||||
ir_core::Type::Contract(_) => ir_vm::Type::Object,
|
||||
ir_core::Type::ErrorType(_) => ir_vm::Type::Object,
|
||||
ir_core::Type::Function { .. } => ir_vm::Type::Function,
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,7 +127,7 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::ir_core;
|
||||
use crate::ir_core::*;
|
||||
use crate::ir::*;
|
||||
use crate::ir_vm::*;
|
||||
|
||||
#[test]
|
||||
fn test_full_lowering() {
|
||||
|
||||
@ -1,357 +1,223 @@
|
||||
# PBS Compiler — Junie PR Plan
|
||||
# PBS ⇄ VM Alignment — Junie PRs (HIP Semantics Hardening)
|
||||
|
||||
> **Purpose:** this document defines a sequence of small, focused Pull Requests to be implemented by *Junie*, one at a time.
|
||||
> **Purpose:** fix semantic mismatches between the PBS frontend (Core IR) and the VM **before** any VM heap/gate implementation.
|
||||
>
|
||||
> **Audience:** compiler implementer (AI or human).
|
||||
> These PRs are **surgical**, **mandatory**, and **non-creative**.
|
||||
> Junie must follow them **exactly**.
|
||||
|
||||
> **Context:**
|
||||
>
|
||||
> **Scope:** PBS-first compiler architecture. TS and Lua frontends are assumed **removed**.
|
||||
>
|
||||
> **Hard rules:**
|
||||
>
|
||||
> * Each PR must compile and pass tests.
|
||||
> * Each PR must include tests.
|
||||
> * No speculative features.
|
||||
> * Follow the `Prometeu Base Script (PBS) - Implementation Spec`.
|
||||
> * VM IR is frozen: new opcodes are forbidden unless explicitly planned in a PR titled “VM Instruction Set Change” with a full rationale + golden bytecode tests.
|
||||
> * PBS frontend is implemented and produces Core IR.
|
||||
> * Bytecode stability is a hard requirement.
|
||||
> * VM currently has stack + const pool; heap exists but is unused.
|
||||
> * HIP semantics (gates/storage) are currently **incorrectly lowered**.
|
||||
> * `ir_vm` is feature-frozen at the moment. we are going to validate only `ir_core`
|
||||
> * Lowering is the only place `ir_core` and `ir_vm` touch each other.
|
||||
> - VM IR is never imported from Core IR.
|
||||
> - Core IR never imports VM IR.
|
||||
|
||||
---
|
||||
|
||||
## Global Architectural Direction (Non-negotiable)
|
||||
## Global Rules (Read Before Any PR)
|
||||
|
||||
* PBS is the **primary language**.
|
||||
* Frontend is implemented **before** runtime integration.
|
||||
* Architecture uses **two IR layers**:
|
||||
* **Core IR** (PBS-semantic, typed, resolved)
|
||||
* **VM IR** (stack-based, backend-friendly)
|
||||
* VM IR remains simple and stable.
|
||||
* Lowering is explicit and testable.
|
||||
1. **No new features.** Only semantic correction.
|
||||
2. **No new VM opcodes yet.** VM changes come later.
|
||||
3. **No fallback values** (e.g. `FunctionId(0)`). Fail with diagnostics.
|
||||
4. **Every PR must include tests** (golden or unit).
|
||||
5. **Core IR is the source of semantic truth.**
|
||||
|
||||
---
|
||||
|
||||
# PR-01 — ProjectConfig and Frontend Selection
|
||||
# PR-20 — Core IR: Make HIP Semantics Explicit (No Handle Loss)
|
||||
|
||||
### Goal
|
||||
|
||||
Introduce a project-level configuration that selects the frontend and entry file explicitly.
|
||||
Fix the Core IR so HIP operations never lose the destination gate.
|
||||
|
||||
### Motivation
|
||||
### Problem (Current Bug)
|
||||
|
||||
The compiler must not hardcode entry points or languages. PBS will be the first frontend, others may return later.
|
||||
Current lowering evaluates a gate, reads storage, stores the result in a local, and later attempts to write back **without having the gate anymore**.
|
||||
|
||||
### Scope
|
||||
This violates PBS semantics: storage access must always be mediated by the **original gate**.
|
||||
|
||||
* Add `ProjectConfig` (serde-deserializable) loaded from `prometeu.json`
|
||||
* Fields (v0):
|
||||
### Required Changes
|
||||
|
||||
* `script_fe: "pbs"`
|
||||
* `entry: "main.pbs"`
|
||||
* Refactor compiler entry point to:
|
||||
#### 1. Extend Core IR instructions
|
||||
|
||||
* load config
|
||||
* select frontend by `script_fe`
|
||||
* resolve entry path relative to project root
|
||||
Add explicit HIP instructions:
|
||||
|
||||
### Files Likely Touched
|
||||
```rust
|
||||
enum CoreInstr {
|
||||
// existing …
|
||||
|
||||
* `compiler/mod.rs`
|
||||
* `compiler/driver.rs`
|
||||
* `common/config.rs` (new)
|
||||
Alloc { ty: TypeId, slots: u32 },
|
||||
|
||||
### Tests (mandatory)
|
||||
BeginPeek { gate: ValueId },
|
||||
BeginBorrow { gate: ValueId },
|
||||
BeginMutate { gate: ValueId },
|
||||
|
||||
* unit test: load valid `prometeu.json`
|
||||
* unit test: invalid frontend → diagnostic
|
||||
* integration test: project root + entry resolution
|
||||
EndPeek,
|
||||
EndBorrow,
|
||||
EndMutate,
|
||||
}
|
||||
```
|
||||
|
||||
### Notes to Junie
|
||||
Rules:
|
||||
|
||||
Do **not** add PBS parsing yet. This PR is infrastructure only.
|
||||
* `Begin*` instructions **do not consume** the gate.
|
||||
* Gate identity must remain available until the matching `End*`.
|
||||
|
||||
#### 2. Remove any lowering that copies HIP storage into locals
|
||||
|
||||
* No `ReadGate → SetLocal` pattern.
|
||||
* Storage views are **not locals**.
|
||||
|
||||
### Tests (Mandatory)
|
||||
|
||||
* Golden Core IR test showing `BeginMutate(gate)` … `EndMutate` wrapping body
|
||||
* Test asserting gate is still live at `EndMutate`
|
||||
|
||||
---
|
||||
|
||||
# PR-02 — Core IR Skeleton (PBS-first)
|
||||
# PR-21 — Distinguish `peek`, `borrow`, and `mutate` in Core IR
|
||||
|
||||
### Goal
|
||||
|
||||
Introduce a **Core IR** layer independent from the VM IR.
|
||||
Restore the semantic distinction mandated by PBS.
|
||||
|
||||
### Motivation
|
||||
### Required Semantics
|
||||
|
||||
PBS semantics must be represented before lowering to VM instructions.
|
||||
| Operation | Effect |
|
||||
| --------- | ------------------------------- |
|
||||
| `peek` | Copy storage → stack value |
|
||||
| `borrow` | Temporary read-only view |
|
||||
| `mutate` | Temporary mutable view + commit |
|
||||
|
||||
### Scope
|
||||
### Required Changes
|
||||
|
||||
* Add new module: `ir_core`
|
||||
* Define minimal structures:
|
||||
* Lower PBS `peek` → `BeginPeek` / `EndPeek`
|
||||
* Lower PBS `borrow` → `BeginBorrow` / `EndBorrow`
|
||||
* Lower PBS `mutate` → `BeginMutate` / `EndMutate`
|
||||
|
||||
* `Program`
|
||||
* `Module`
|
||||
* `Function`
|
||||
* `Block`
|
||||
* `Instr`
|
||||
* `Terminator`
|
||||
* IDs only (no string-based calls):
|
||||
|
||||
* `FunctionId`
|
||||
* `ConstId`
|
||||
* `TypeId`
|
||||
|
||||
### Constraints
|
||||
|
||||
* Core IR must NOT reference VM opcodes
|
||||
* No lowering yet
|
||||
These **must not** share the same lowering path.
|
||||
|
||||
### Tests
|
||||
|
||||
* construct Core IR manually in tests
|
||||
* snapshot test (JSON) for deterministic shape
|
||||
* PBS snippet with all three operations
|
||||
* Assert distinct Core IR instruction sequences
|
||||
|
||||
---
|
||||
|
||||
# PR-03 — Constant Pool and IDs
|
||||
# PR-22 — Make Allocation Shape Explicit in Core IR
|
||||
|
||||
### Goal
|
||||
|
||||
Introduce a stable constant pool shared by Core IR and VM IR.
|
||||
Stop implicit / guessed heap layouts.
|
||||
|
||||
### Scope
|
||||
### Required Changes
|
||||
|
||||
* Add `ConstPool`:
|
||||
* Replace any shape-less `Alloc` with:
|
||||
|
||||
* strings
|
||||
* numbers
|
||||
* Replace inline literals in VM IR with `ConstId`
|
||||
* Update existing VM IR to accept `PushConst(ConstId)`
|
||||
```rust
|
||||
Alloc { ty: TypeId, slots: u32 }
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
* `TypeId` comes from frontend type checking
|
||||
* `slots` is derived deterministically (struct fields / array size)
|
||||
|
||||
### Tests
|
||||
|
||||
* const pool deduplication
|
||||
* deterministic ConstId assignment
|
||||
* IR snapshot stability
|
||||
* Allocating storage struct emits correct `slots`
|
||||
* Allocating array emits correct `slots`
|
||||
|
||||
---
|
||||
|
||||
# PR-04 — VM IR Cleanup (Stabilization)
|
||||
# PR-23 — Eliminate Invalid Call Fallbacks
|
||||
|
||||
### Goal
|
||||
|
||||
Stabilize VM IR as a **lowering target**, not a language IR.
|
||||
Prevent invalid bytecode generation.
|
||||
|
||||
### Scope
|
||||
### Required Changes
|
||||
|
||||
* Replace string-based calls with `FunctionId`
|
||||
* Ensure locals are accessed via slots
|
||||
* Remove or internalize `PushScope` / `PopScope`
|
||||
* Remove **all** fallbacks to `FunctionId(0)` or equivalent
|
||||
* On unresolved symbols during lowering:
|
||||
|
||||
* Emit canonical diagnostic (`E_RESOLVE_UNDEFINED` or `E_LOWER_UNSUPPORTED`)
|
||||
* Abort lowering
|
||||
|
||||
### Tests
|
||||
|
||||
* golden VM IR tests
|
||||
* lowering smoke test (Core IR → VM IR)
|
||||
* PBS program calling missing function → compile error
|
||||
* No Core IR or VM IR emitted
|
||||
|
||||
---
|
||||
|
||||
# PR-05 — Core IR → VM IR Lowering Pass
|
||||
# PR-24 — Validate Contract Calls in Frontend (Arity + Types)
|
||||
|
||||
### Goal
|
||||
|
||||
Implement the lowering pass from Core IR to VM IR.
|
||||
Move contract validation to compile time.
|
||||
|
||||
### Scope
|
||||
### Required Changes
|
||||
|
||||
* New module: `lowering/core_to_vm.rs`
|
||||
* Lowering rules:
|
||||
* During PBS type checking:
|
||||
|
||||
* Core blocks → labels
|
||||
* Core calls → VM calls
|
||||
* Host calls preserved
|
||||
* No PBS frontend yet
|
||||
* Validate argument count against contract signature
|
||||
* Validate argument types
|
||||
|
||||
* Lower only validated calls to `HostCall`
|
||||
|
||||
### Tests
|
||||
|
||||
* lowering correctness
|
||||
* instruction ordering
|
||||
* label resolution
|
||||
* Wrong arity → `E_TYPE_MISMATCH`
|
||||
* Correct call lowers to Core IR `HostCall`
|
||||
|
||||
---
|
||||
|
||||
# PR-06 — PBS Frontend: Lexer
|
||||
# PR-25 — Core IR Invariants Test Suite
|
||||
|
||||
### Goal
|
||||
|
||||
Implement PBS lexer according to the spec.
|
||||
Lock in correct semantics before touching the VM.
|
||||
|
||||
### Scope
|
||||
### Required Invariants
|
||||
|
||||
* Token kinds
|
||||
* Keyword table
|
||||
* Span tracking
|
||||
* Every `Begin*` has a matching `End*`
|
||||
* Gate passed to `Begin*` is available at `End*`
|
||||
* No storage writes without `BeginMutate`
|
||||
* No silent fallbacks
|
||||
|
||||
### Tests
|
||||
|
||||
* tokenization tests
|
||||
* keyword vs identifier tests
|
||||
* bounded literals
|
||||
* Property-style tests or golden IR assertions
|
||||
|
||||
---
|
||||
|
||||
# PR-07 — PBS Frontend: Parser (Raw AST)
|
||||
## STOP POINT
|
||||
|
||||
### Goal
|
||||
After PR-25:
|
||||
|
||||
Parse PBS source into a raw AST.
|
||||
* Core IR correctly represents PBS HIP semantics
|
||||
* Lowering is deterministic and safe
|
||||
* VM is still unchanged
|
||||
|
||||
### Scope
|
||||
**Only after this point may VM PRs begin.**
|
||||
|
||||
* Imports
|
||||
* Top-level declarations
|
||||
* Blocks
|
||||
* Expressions (calls, literals, control flow)
|
||||
|
||||
### Tests
|
||||
|
||||
* valid programs
|
||||
* syntax error recovery
|
||||
Any VM work before this is a hard rejection.
|
||||
|
||||
---
|
||||
|
||||
# PR-08 — PBS Frontend: Symbol Collection and Resolver
|
||||
## Instruction to Junie
|
||||
|
||||
### Goal
|
||||
If any rule in this document is unclear:
|
||||
|
||||
Resolve names, modules, and visibility.
|
||||
* Stop
|
||||
* Add a failing test
|
||||
* Document the ambiguity
|
||||
|
||||
### Scope
|
||||
Do not invent behavior.
|
||||
|
||||
* Type namespace vs value namespace
|
||||
* Visibility rules
|
||||
* Import resolution
|
||||
|
||||
### Tests
|
||||
|
||||
* duplicate symbols
|
||||
* invalid imports
|
||||
* visibility errors
|
||||
|
||||
---
|
||||
|
||||
# PR-09 — PBS Frontend: Type Checking
|
||||
|
||||
### Goal
|
||||
|
||||
Validate PBS semantics.
|
||||
|
||||
### Scope
|
||||
|
||||
* Primitive types
|
||||
* Structs
|
||||
* `optional<T>` and `result<T, E>`
|
||||
* Mutability rules
|
||||
* Return path validation
|
||||
|
||||
### Tests
|
||||
|
||||
* type mismatch
|
||||
* mutability violations
|
||||
* implicit `none` behavior
|
||||
|
||||
---
|
||||
|
||||
# PR-10 — PBS Frontend: Semantic Lowering to Core IR
|
||||
|
||||
### Goal
|
||||
|
||||
Lower typed PBS AST into Core IR.
|
||||
|
||||
### Scope
|
||||
|
||||
* ID-based calls
|
||||
* ConstPool usage
|
||||
* Control flow lowering
|
||||
* SAFE vs HIP effects represented explicitly
|
||||
|
||||
### Tests
|
||||
|
||||
* PBS → Core IR snapshots
|
||||
* semantic correctness
|
||||
|
||||
---
|
||||
|
||||
# PR-11 — Host-bound Contracts and Syscall Mapping
|
||||
|
||||
### Goal
|
||||
|
||||
Connect PBS host-bound contracts to runtime syscalls (without executing them).
|
||||
|
||||
### Scope
|
||||
|
||||
* Contract registry
|
||||
* Mapping: contract.method → syscall id
|
||||
* Core IR host call nodes
|
||||
|
||||
### Tests
|
||||
|
||||
* invalid contract calls
|
||||
* correct syscall mapping
|
||||
|
||||
---
|
||||
|
||||
# PR-12 — Diagnostics Canonicalization
|
||||
|
||||
### Goal
|
||||
|
||||
Standardize diagnostics output.
|
||||
|
||||
### Scope
|
||||
|
||||
* Error codes (`E_*`, `W_*`)
|
||||
* Stable messages
|
||||
* Span accuracy
|
||||
|
||||
### Tests
|
||||
|
||||
* golden diagnostics
|
||||
|
||||
---
|
||||
|
||||
# PR-13 — Backend Integration (VM IR → Bytecode)
|
||||
|
||||
### Goal
|
||||
|
||||
Reconnect the pipeline to the Prometeu runtime backend.
|
||||
|
||||
### Scope
|
||||
|
||||
* VM IR → bytecode emission
|
||||
* No PBS semantics here
|
||||
|
||||
### Tests
|
||||
|
||||
* bytecode emission smoke test
|
||||
|
||||
---
|
||||
|
||||
# PR-14 — End-to-End PBS Compile Test
|
||||
|
||||
### Goal
|
||||
|
||||
Prove the full pipeline works.
|
||||
|
||||
### Scope
|
||||
|
||||
* Sample PBS project
|
||||
* Compile → bytecode
|
||||
* Diagnostics only (no execution)
|
||||
|
||||
### Tests
|
||||
|
||||
* golden bytecode snapshot
|
||||
|
||||
---
|
||||
|
||||
## Final Note to Junie
|
||||
|
||||
Do **not** skip PRs.
|
||||
Do **not** merge multiple PRs together.
|
||||
If the spec is unclear, create a failing test and document the ambiguity.
|
||||
|
||||
This plan is the authoritative roadmap for PBS frontend implementation.
|
||||
This document is binding.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user