use crate::op::*;
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Program {
pub constants: Vec<Const>,
pub functions: Vec<Function>,
pub function_names: IndexMap<String, u32>,
pub module_aliases: IndexMap<String, String>,
pub entry: Option<u32>,
#[serde(default)]
pub record_shapes: Vec<Vec<u32>>,
}
impl Program {
pub fn lookup(&self, name: &str) -> Option<u32> {
self.function_names.get(name).copied()
}
pub fn declared_effects(&self) -> Vec<DeclaredEffect> {
let mut out: Vec<DeclaredEffect> = Vec::new();
for f in &self.functions {
for e in &f.effects {
if !out.iter().any(|x| x == e) {
out.push(e.clone());
}
}
}
out
}
}
pub type BodyHash = [u8; 16];
pub const ZERO_BODY_HASH: BodyHash = [0u8; 16];
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Function {
pub name: String,
pub arity: u16,
pub locals_count: u16,
pub code: Vec<Op>,
#[serde(default)]
pub effects: Vec<DeclaredEffect>,
#[serde(default = "zero_body_hash")]
pub body_hash: BodyHash,
#[serde(default)]
pub refinements: Vec<Option<Refinement>>,
#[serde(default)]
pub field_ic_sites: u16,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Refinement {
pub binding: String,
pub predicate: lex_ast::CExpr,
}
fn zero_body_hash() -> BodyHash { ZERO_BODY_HASH }
pub fn compute_body_hash(
arity: u16,
locals_count: u16,
code: &[Op],
record_shapes: &[Vec<u32>],
) -> BodyHash {
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
hasher.update(arity.to_le_bytes());
hasher.update(locals_count.to_le_bytes());
hasher.update((code.len() as u64).to_le_bytes());
for op in code {
let bytes = match op {
Op::MakeRecord { shape_idx, .. }
| Op::AllocStackRecord { shape_idx, .. }
| Op::AllocArenaRecord { shape_idx, .. } => {
let shape = &record_shapes[*shape_idx as usize];
#[derive(Serialize)]
struct LegacyMakeRecord<'a> {
field_name_indices: &'a [u32],
}
#[derive(Serialize)]
enum LegacyOp<'a> {
MakeRecord(LegacyMakeRecord<'a>),
}
serde_json::to_vec(&LegacyOp::MakeRecord(LegacyMakeRecord {
field_name_indices: shape,
}))
.expect("Op serialization must succeed")
}
Op::LoadLocalAddIntConst { local_idx, .. }
| Op::LoadLocalAddIntConstStoreLocal { src: local_idx, .. }
| Op::LoadLocalAddLocal { lhs_idx: local_idx, .. }
| Op::LoadLocalSubLocal { lhs_idx: local_idx, .. }
| Op::LoadLocalMulLocal { lhs_idx: local_idx, .. }
| Op::LoadLocalEqIntConstJumpIfNot { local_idx, .. }
| Op::LoadLocalStoreEqIntConstJumpIfNot { src: local_idx, .. }
| Op::LoadLocalGetFieldAdd { local_idx, .. }
| Op::LoadLocalGetFieldSub { local_idx, .. }
| Op::LoadLocalGetFieldMul { local_idx, .. }
| Op::LoadLocalGetField { local_idx, .. } => {
serde_json::to_vec(&Op::LoadLocal(*local_idx))
.expect("Op serialization must succeed")
}
Op::GetField { name_idx, .. } => {
#[derive(Serialize)]
enum LegacyOp { GetField(u32) }
serde_json::to_vec(&LegacyOp::GetField(*name_idx))
.expect("Op serialization must succeed")
}
Op::AllocStackTuple { arity }
| Op::AllocArenaTuple { arity } =>
serde_json::to_vec(&Op::MakeTuple(*arity)).expect("Op serialization must succeed"),
Op::IntAdd | Op::FloatAdd => serde_json::to_vec(&Op::NumAdd).unwrap(),
Op::IntSub | Op::FloatSub => serde_json::to_vec(&Op::NumSub).unwrap(),
Op::IntMul | Op::FloatMul => serde_json::to_vec(&Op::NumMul).unwrap(),
Op::IntDiv | Op::FloatDiv => serde_json::to_vec(&Op::NumDiv).unwrap(),
Op::IntMod => serde_json::to_vec(&Op::NumMod).unwrap(),
Op::IntNeg | Op::FloatNeg => serde_json::to_vec(&Op::NumNeg).unwrap(),
Op::IntEq | Op::FloatEq => serde_json::to_vec(&Op::NumEq).unwrap(),
Op::IntLt | Op::FloatLt => serde_json::to_vec(&Op::NumLt).unwrap(),
Op::IntLe | Op::FloatLe => serde_json::to_vec(&Op::NumLe).unwrap(),
_ => serde_json::to_vec(op).expect("Op serialization must succeed"),
};
hasher.update((bytes.len() as u64).to_le_bytes());
hasher.update(&bytes);
}
let full = hasher.finalize();
let mut out = [0u8; 16];
out.copy_from_slice(&full[..16]);
out
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DeclaredEffect {
pub kind: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub arg: Option<EffectArg>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum EffectArg {
Str(String),
Int(i64),
Ident(String),
}