Skip to main content

parallax_pcir/
lib.rs

1//! Parallax Continuation IR (PCIR).
2//!
3//! A portable **control-flow** subset used at Continuum safepoints.
4//! PCIR is not a full guest ISA — adapters lower only supported regions.
5//!
6//! Distinct from PIR: PIR models **values**; PCIR models **control**.
7
8#![deny(unsafe_code)]
9#![warn(missing_docs)]
10
11mod ops;
12mod program;
13mod version;
14
15pub use ops::{BinaryOperator, CompareOperator, Operand, PcirOp, UnaryOperator, ValueId};
16pub use program::{PcirBlock, PcirFunction, PcirModule, PcirProgram};
17pub use version::{check_pcir_schema, PCIR_SCHEMA_VERSION};
18
19use parallax_core::{ErrorCode, ParallaxError};
20
21/// Result alias.
22pub type Result<T> = parallax_core::Result<T>;
23
24/// Serialize a PCIR program to pretty JSON.
25pub fn to_json_bytes(program: &PcirProgram) -> Result<Vec<u8>> {
26    serde_json::to_vec_pretty(program).map_err(|e| {
27        ParallaxError::new(ErrorCode::SerializationFailure, e.to_string())
28            .with_source("parallax-pcir")
29            .with_operation("to_json_bytes")
30    })
31}
32
33/// Serialize a PCIR program to compact JSON.
34pub fn to_json_bytes_compact(program: &PcirProgram) -> Result<Vec<u8>> {
35    serde_json::to_vec(program).map_err(|e| {
36        ParallaxError::new(ErrorCode::SerializationFailure, e.to_string())
37            .with_source("parallax-pcir")
38            .with_operation("to_json_bytes_compact")
39    })
40}
41
42/// Deserialize and validate a PCIR program.
43pub fn from_json_bytes(bytes: &[u8]) -> Result<PcirProgram> {
44    let program: PcirProgram = serde_json::from_slice(bytes).map_err(|e| {
45        ParallaxError::new(ErrorCode::SerializationFailure, e.to_string())
46            .with_source("parallax-pcir")
47            .with_operation("from_json_bytes")
48    })?;
49    program.validate()?;
50    Ok(program)
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56    use crate::ops::{BinaryOperator, Operand, PcirOp, ValueId};
57
58    #[test]
59    fn round_trip_straight_line() {
60        let mut prog = PcirProgram::new();
61        let mut func = PcirFunction::new("main");
62        let mut block = PcirBlock::new("entry");
63        block.ops.push(PcirOp::Constant {
64            dest: ValueId(0),
65            value: serde_json::json!({"t":"int","v":{"decimal":"1"}}),
66        });
67        block.ops.push(PcirOp::Constant {
68            dest: ValueId(1),
69            value: serde_json::json!({"t":"int","v":{"decimal":"2"}}),
70        });
71        block.ops.push(PcirOp::BinaryOp {
72            dest: ValueId(2),
73            op: BinaryOperator::Add,
74            lhs: Operand::Value(ValueId(0)),
75            rhs: Operand::Value(ValueId(1)),
76        });
77        block.ops.push(PcirOp::Return {
78            value: Some(Operand::Value(ValueId(2))),
79        });
80        func.blocks.push(block);
81        prog.functions.push(func);
82        let bytes = to_json_bytes(&prog).unwrap();
83        let back = from_json_bytes(&bytes).unwrap();
84        assert_eq!(back.schema, PCIR_SCHEMA_VERSION);
85        assert_eq!(back.functions[0].blocks[0].ops.len(), 4);
86    }
87
88    #[test]
89    fn rejects_future_schema() {
90        let raw = br#"{"schema":99,"functions":[],"metadata":{}}"#;
91        let err = from_json_bytes(raw).unwrap_err();
92        assert_eq!(err.code, ErrorCode::InvalidSnapshot);
93        assert!(err.message.contains("unsupported PCIR schema"));
94    }
95}