+use crate::{
+ error::{Error, Result},
+ index::{BlockIdx, InstIdx, InstRange, SSAValIdx},
+ interned::Interned,
+ loc::{Loc, Ty},
+ loc_set::LocSet,
+};
+use core::fmt;
+use serde::{Deserialize, Serialize};
+use std::ops::Index;
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
+pub struct SSAVal {
+ pub ty: Ty,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Serialize, Deserialize)]
+#[repr(u8)]
+pub enum InstStage {
+ Early = 0,
+ Late = 1,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
+#[serde(try_from = "SerializedProgPoint", into = "SerializedProgPoint")]
+pub struct ProgPoint(usize);
+
+impl ProgPoint {
+ pub const fn new(inst: InstIdx, stage: InstStage) -> Self {
+ const_unwrap_res!(Self::try_new(inst, stage))
+ }
+ pub const fn try_new(inst: InstIdx, stage: InstStage) -> Result<Self> {
+ let Some(inst) = inst.get().checked_shl(1) else {
+ return Err(Error::InstIdxTooBig);
+ };
+ Ok(Self(inst | stage as usize))
+ }
+ pub const fn inst(self) -> InstIdx {
+ InstIdx::new(self.0 >> 1)
+ }
+ pub const fn stage(self) -> InstStage {
+ if self.0 & 1 != 0 {
+ InstStage::Late
+ } else {
+ InstStage::Early
+ }
+ }
+ pub const fn next(self) -> Self {
+ Self(self.0 + 1)
+ }
+ pub const fn prev(self) -> Self {
+ Self(self.0 - 1)
+ }
+}
+
+impl fmt::Debug for ProgPoint {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("ProgPoint")
+ .field("inst", &self.inst())
+ .field("stage", &self.stage())
+ .finish()
+ }
+}
+
+#[derive(Serialize, Deserialize)]
+struct SerializedProgPoint {
+ inst: InstIdx,
+ stage: InstStage,
+}
+
+impl From<ProgPoint> for SerializedProgPoint {
+ fn from(value: ProgPoint) -> Self {
+ Self {
+ inst: value.inst(),
+ stage: value.stage(),
+ }
+ }
+}
+
+impl TryFrom<SerializedProgPoint> for ProgPoint {
+ type Error = Error;
+
+ fn try_from(value: SerializedProgPoint) -> Result<Self, Self::Error> {
+ ProgPoint::try_new(value.inst, value.stage)
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Serialize, Deserialize)]
+#[repr(u8)]
+pub enum OperandKind {
+ Use = 0,
+ Def = 1,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Serialize, Deserialize)]
+pub enum Constraint {
+ /// any register or stack location
+ Any,
+ /// r1-r32
+ BaseGpr,
+ /// r2,r4,r6,r8,...r126
+ SVExtra2VGpr,
+ /// r1-63
+ SVExtra2SGpr,
+ /// r1-127
+ SVExtra3Gpr,
+ /// any stack location
+ Stack,
+ FixedLoc(Loc),
+ Reuse(usize),
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
+pub struct Operand {
+ pub ssa_val: SSAValIdx,
+ pub constraint: Constraint,
+ pub kind: OperandKind,
+ pub stage: InstStage,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
+pub struct BranchSucc {
+ pub block: BlockIdx,
+ pub params: Vec<SSAValIdx>,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
+pub enum InstKind {
+ Normal,
+ /// copy concatenates all `srcs` together and de-concatenates the result into all `dests`.
+ Copy {
+ srcs: Vec<Operand>,
+ dests: Vec<Operand>,
+ },
+ Return,
+ Branch {
+ succs: Vec<BranchSucc>,
+ },
+}
+
+impl InstKind {
+ pub fn is_normal(&self) -> bool {
+ matches!(self, Self::Normal)
+ }
+ pub fn is_block_term(&self) -> bool {
+ matches!(self, Self::Return | Self::Branch { .. })
+ }
+ pub fn succs(&self) -> Option<&[BranchSucc]> {
+ match self {
+ InstKind::Normal | InstKind::Copy { .. } => None,
+ InstKind::Return => Some(&[]),
+ InstKind::Branch { succs } => Some(succs),
+ }
+ }
+}
+
+impl Default for InstKind {
+ fn default() -> Self {
+ InstKind::Normal
+ }
+}
+
+#[derive(Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
+pub struct Inst {
+ #[serde(default, skip_serializing_if = "InstKind::is_normal")]
+ pub kind: InstKind,
+ pub operands: Vec<Operand>,
+ pub clobbers: Interned<LocSet>,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
+pub struct Block {
+ pub params: Vec<SSAValIdx>,
+ pub insts: InstRange,
+ pub preds: Vec<BlockIdx>,
+}
+
+validated_fields! {
+ #[fields_ty = FnFields]
+ #[derive(Clone, PartialEq, Eq, Debug, Hash)]
+ pub struct Function {
+ pub ssa_vals: Vec<SSAVal>,
+ pub insts: Vec<Inst>,
+ pub blocks: Vec<Block>,
+ }
+}
+
+impl Function {
+ pub fn new(fields: FnFields) -> Result<Self> {
+ let FnFields {
+ ssa_vals,
+ insts: insts_vec,
+ blocks,
+ } = &fields;
+ let entry_block = blocks
+ .get(BlockIdx::ENTRY_BLOCK.get())
+ .ok_or(Error::MissingEntryBlock)?;
+ if !entry_block.params.is_empty() {
+ return Err(Error::EntryBlockCantHaveParams);
+ }
+ if !entry_block.preds.is_empty() {
+ return Err(Error::EntryBlockCantHavePreds);
+ }
+ let mut expected_start = InstIdx::new(0);
+ for (block_idx, block) in fields.blocks.iter().enumerate() {
+ let block_idx = BlockIdx::new(block_idx);
+ let Block {
+ params,
+ insts: inst_range,
+ preds,
+ } = block;
+ if inst_range.start != expected_start {
+ return Err(Error::BlockHasInvalidStart {
+ start: inst_range.start,
+ expected_start,
+ });
+ }
+ let Some((term_idx, non_term_inst_range)) = inst_range.split_last() else {
+ return Err(Error::BlockIsEmpty { block: block_idx });
+ };
+ expected_start = inst_range.end;
+ let Some(Inst { kind: term_kind, .. }) = insts_vec.get(term_idx.get()) else {
+ return Err(Error::BlockEndOutOfRange { end: inst_range.end });
+ };
+ if !term_kind.is_block_term() {
+ return Err(Error::BlocksLastInstMustBeTerm { term_idx });
+ }
+ for inst_idx in non_term_inst_range {
+ if insts_vec[inst_idx].kind.is_block_term() {
+ return Err(Error::TermInstOnlyAllowedAtBlockEnd { inst_idx });
+ }
+ }
+ }
+ todo!()
+ }
+ pub fn entry_block(&self) -> &Block {
+ &self.blocks[0]
+ }
+ pub fn block_succs(&self, block: BlockIdx) -> &[BranchSucc] {
+ self.insts[self.blocks[block].insts.last().unwrap()]
+ .kind
+ .succs()
+ .unwrap()
+ }
+}
+
+impl Index<SSAValIdx> for Vec<SSAVal> {
+ type Output = SSAVal;
+
+ fn index(&self, index: SSAValIdx) -> &Self::Output {
+ &self[index.get()]
+ }
+}
+
+impl Index<InstIdx> for Vec<Inst> {
+ type Output = Inst;
+
+ fn index(&self, index: InstIdx) -> &Self::Output {
+ &self[index.get()]
+ }
+}
+
+impl Index<BlockIdx> for Vec<Block> {
+ type Output = Block;
+
+ fn index(&self, index: BlockIdx) -> &Self::Output {
+ &self[index.get()]
+ }
+}