1use crate::ops::PcirOp;
4use crate::version::{check_pcir_schema, PCIR_SCHEMA_VERSION};
5use indexmap::IndexMap;
6use parallax_core::{ErrorCode, ParallaxError, Result};
7use serde::{Deserialize, Serialize};
8
9#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
11pub struct PcirBlock {
12 pub name: String,
14 pub ops: Vec<PcirOp>,
16}
17
18impl PcirBlock {
19 pub fn new(name: impl Into<String>) -> Self {
21 Self {
22 name: name.into(),
23 ops: Vec::new(),
24 }
25 }
26}
27
28#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
30pub struct PcirFunction {
31 pub name: String,
33 #[serde(default)]
35 pub params: Vec<String>,
36 pub blocks: Vec<PcirBlock>,
38 #[serde(default)]
40 pub metadata: IndexMap<String, serde_json::Value>,
41}
42
43impl PcirFunction {
44 pub fn new(name: impl Into<String>) -> Self {
46 Self {
47 name: name.into(),
48 params: Vec::new(),
49 blocks: Vec::new(),
50 metadata: IndexMap::new(),
51 }
52 }
53}
54
55#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
57pub struct PcirModule {
58 pub name: String,
60 pub functions: Vec<String>,
62}
63
64#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
66pub struct PcirProgram {
67 pub schema: u32,
69 pub functions: Vec<PcirFunction>,
71 #[serde(default)]
73 pub modules: Vec<PcirModule>,
74 #[serde(default, skip_serializing_if = "Option::is_none")]
76 pub entry: Option<String>,
77 #[serde(default)]
79 pub metadata: IndexMap<String, serde_json::Value>,
80}
81
82impl PcirProgram {
83 pub fn new() -> Self {
85 Self {
86 schema: PCIR_SCHEMA_VERSION,
87 functions: Vec::new(),
88 modules: Vec::new(),
89 entry: None,
90 metadata: IndexMap::new(),
91 }
92 }
93
94 pub fn validate(&self) -> Result<()> {
96 check_pcir_schema(self.schema)?;
97 for func in &self.functions {
98 if func.name.is_empty() {
99 return Err(ParallaxError::new(
100 ErrorCode::InvalidSnapshot,
101 "PCIR function with empty name",
102 )
103 .with_source("parallax-pcir")
104 .with_operation("validate"));
105 }
106 let mut names = std::collections::HashSet::new();
107 for block in &func.blocks {
108 if !names.insert(block.name.clone()) {
109 return Err(ParallaxError::new(
110 ErrorCode::InvalidSnapshot,
111 format!(
112 "duplicate PCIR block name '{}' in function '{}'",
113 block.name, func.name
114 ),
115 )
116 .with_source("parallax-pcir")
117 .with_operation("validate"));
118 }
119 }
120 }
121 Ok(())
122 }
123
124 pub fn checkpoint_stub(label: &str) -> Self {
126 let mut prog = Self::new();
127 let mut func = PcirFunction::new("__parallax_checkpoint_region");
128 let mut block = PcirBlock::new("entry");
129 block.ops.push(PcirOp::Intrinsic {
130 dest: None,
131 name: "parallax.checkpoint".into(),
132 args: vec![crate::ops::Operand::Imm(serde_json::json!({
133 "t": "string",
134 "v": label
135 }))],
136 });
137 func.blocks.push(block);
138 prog.functions.push(func);
139 prog.entry = Some("__parallax_checkpoint_region".into());
140 prog.metadata
141 .insert("kind".into(), serde_json::json!("checkpoint_stub"));
142 prog.metadata
143 .insert("label".into(), serde_json::json!(label));
144 prog
145 }
146}
147
148impl Default for PcirProgram {
149 fn default() -> Self {
150 Self::new()
151 }
152}