use crate::{
ir::{Block, ExtUnit, Inst, Unit, UnitBuilder, Value},
ty::{array_ty, int_ty, pointer_ty, signal_ty, struct_ty, void_ty, Type},
value::{IntValue, TimeValue},
};
use bitflags::bitflags;
use std::borrow::Cow;
pub struct InstBuilder<'a, 'b> {
builder: &'b mut UnitBuilder<'a>,
name: Option<String>,
}
impl<'a, 'b> InstBuilder<'a, 'b> {
pub fn new(builder: &'b mut UnitBuilder<'a>) -> Self {
Self {
builder,
name: None,
}
}
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
}
impl<'a, 'b> InstBuilder<'a, 'b> {
pub fn const_zero(&mut self, ty: &Type) -> Value {
use crate::ty::TypeKind::*;
match ty.as_ref() {
TimeType => self.const_time(TimeValue::zero()),
IntType(w) => self.const_int(IntValue::zero(*w)),
ArrayType(l, ty) => {
let name = self.name.take();
let elem = self.const_zero(ty);
self.name = name;
self.array_uniform(*l, elem)
}
StructType(tys) => {
let name = self.name.take();
let elems = tys.iter().map(|ty| self.const_zero(ty)).collect();
self.name = name;
self.strukt(elems)
}
_ => panic!("no zero value for {}", ty),
}
}
pub fn const_int(&mut self, value: impl Into<IntValue>) -> Value {
let value = value.into();
let ty = value.ty();
let data = InstData::ConstInt {
opcode: Opcode::ConstInt,
imm: value,
};
let inst = self.build(data, ty);
self.inst_result(inst)
}
pub fn const_time(&mut self, value: impl Into<TimeValue>) -> Value {
let value = value.into();
let ty = value.ty();
let data = InstData::ConstTime {
opcode: Opcode::ConstTime,
imm: value,
};
let inst = self.build(data, ty);
self.inst_result(inst)
}
pub fn alias(&mut self, x: Value) -> Value {
let ty = self.value_type(x);
let inst = self.build_unary(Opcode::Alias, ty, x);
self.inst_result(inst)
}
pub fn array_uniform(&mut self, imm: usize, x: Value) -> Value {
let ty = array_ty(imm, self.value_type(x));
let inst = self.build(
InstData::Array {
opcode: Opcode::ArrayUniform,
imms: [imm],
args: [x],
},
ty,
);
self.inst_result(inst)
}
pub fn array(&mut self, args: Vec<Value>) -> Value {
assert!(!args.is_empty());
let ty = array_ty(args.len(), self.value_type(args[0]));
let inst = self.build(
InstData::Aggregate {
opcode: Opcode::Array,
args: args,
},
ty,
);
self.inst_result(inst)
}
pub fn strukt(&mut self, args: Vec<Value>) -> Value {
let ty = struct_ty(
args.iter()
.cloned()
.map(|arg| self.value_type(arg))
.collect(),
);
let inst = self.build(
InstData::Aggregate {
opcode: Opcode::Struct,
args: args,
},
ty,
);
self.inst_result(inst)
}
pub fn not(&mut self, x: Value) -> Value {
let ty = self.value_type(x);
let inst = self.build_unary(Opcode::Not, ty, x);
self.inst_result(inst)
}
pub fn neg(&mut self, x: Value) -> Value {
let ty = self.value_type(x);
let inst = self.build_unary(Opcode::Neg, ty, x);
self.inst_result(inst)
}
pub fn add(&mut self, x: Value, y: Value) -> Value {
let ty = self.value_type(x);
let inst = self.build_binary(Opcode::Add, ty, x, y);
self.inst_result(inst)
}
pub fn sub(&mut self, x: Value, y: Value) -> Value {
let ty = self.value_type(x);
let inst = self.build_binary(Opcode::Sub, ty, x, y);
self.inst_result(inst)
}
pub fn and(&mut self, x: Value, y: Value) -> Value {
let ty = self.value_type(x);
let inst = self.build_binary(Opcode::And, ty, x, y);
self.inst_result(inst)
}
pub fn or(&mut self, x: Value, y: Value) -> Value {
let ty = self.value_type(x);
let inst = self.build_binary(Opcode::Or, ty, x, y);
self.inst_result(inst)
}
pub fn xor(&mut self, x: Value, y: Value) -> Value {
let ty = self.value_type(x);
let inst = self.build_binary(Opcode::Xor, ty, x, y);
self.inst_result(inst)
}
pub fn smul(&mut self, x: Value, y: Value) -> Value {
let ty = self.value_type(x);
let inst = self.build_binary(Opcode::Smul, ty, x, y);
self.inst_result(inst)
}
pub fn sdiv(&mut self, x: Value, y: Value) -> Value {
let ty = self.value_type(x);
let inst = self.build_binary(Opcode::Sdiv, ty, x, y);
self.inst_result(inst)
}
pub fn smod(&mut self, x: Value, y: Value) -> Value {
let ty = self.value_type(x);
let inst = self.build_binary(Opcode::Smod, ty, x, y);
self.inst_result(inst)
}
pub fn srem(&mut self, x: Value, y: Value) -> Value {
let ty = self.value_type(x);
let inst = self.build_binary(Opcode::Srem, ty, x, y);
self.inst_result(inst)
}
pub fn umul(&mut self, x: Value, y: Value) -> Value {
let ty = self.value_type(x);
let inst = self.build_binary(Opcode::Umul, ty, x, y);
self.inst_result(inst)
}
pub fn udiv(&mut self, x: Value, y: Value) -> Value {
let ty = self.value_type(x);
let inst = self.build_binary(Opcode::Udiv, ty, x, y);
self.inst_result(inst)
}
pub fn umod(&mut self, x: Value, y: Value) -> Value {
let ty = self.value_type(x);
let inst = self.build_binary(Opcode::Umod, ty, x, y);
self.inst_result(inst)
}
pub fn urem(&mut self, x: Value, y: Value) -> Value {
let ty = self.value_type(x);
let inst = self.build_binary(Opcode::Urem, ty, x, y);
self.inst_result(inst)
}
pub fn eq(&mut self, x: Value, y: Value) -> Value {
let inst = self.build_binary(Opcode::Eq, int_ty(1), x, y);
self.inst_result(inst)
}
pub fn neq(&mut self, x: Value, y: Value) -> Value {
let inst = self.build_binary(Opcode::Neq, int_ty(1), x, y);
self.inst_result(inst)
}
pub fn slt(&mut self, x: Value, y: Value) -> Value {
let inst = self.build_binary(Opcode::Slt, int_ty(1), x, y);
self.inst_result(inst)
}
pub fn sgt(&mut self, x: Value, y: Value) -> Value {
let inst = self.build_binary(Opcode::Sgt, int_ty(1), x, y);
self.inst_result(inst)
}
pub fn sle(&mut self, x: Value, y: Value) -> Value {
let inst = self.build_binary(Opcode::Sle, int_ty(1), x, y);
self.inst_result(inst)
}
pub fn sge(&mut self, x: Value, y: Value) -> Value {
let inst = self.build_binary(Opcode::Sge, int_ty(1), x, y);
self.inst_result(inst)
}
pub fn ult(&mut self, x: Value, y: Value) -> Value {
let inst = self.build_binary(Opcode::Ult, int_ty(1), x, y);
self.inst_result(inst)
}
pub fn ugt(&mut self, x: Value, y: Value) -> Value {
let inst = self.build_binary(Opcode::Ugt, int_ty(1), x, y);
self.inst_result(inst)
}
pub fn ule(&mut self, x: Value, y: Value) -> Value {
let inst = self.build_binary(Opcode::Ule, int_ty(1), x, y);
self.inst_result(inst)
}
pub fn uge(&mut self, x: Value, y: Value) -> Value {
let inst = self.build_binary(Opcode::Uge, int_ty(1), x, y);
self.inst_result(inst)
}
pub fn shl(&mut self, x: Value, y: Value, z: Value) -> Value {
let ty = self.value_type(x);
let inst = self.build_ternary(Opcode::Shl, ty, x, y, z);
self.inst_result(inst)
}
pub fn shr(&mut self, x: Value, y: Value, z: Value) -> Value {
let ty = self.value_type(x);
let inst = self.build_ternary(Opcode::Shr, ty, x, y, z);
self.inst_result(inst)
}
pub fn mux(&mut self, x: Value, y: Value) -> Value {
let ty = self.value_type(x);
assert!(ty.is_array(), "argument to `mux` must be of array type");
let ty = ty.unwrap_array().1.clone();
let inst = self.build_binary(Opcode::Mux, ty, x, y);
self.inst_result(inst)
}
pub fn reg(&mut self, x: Value, data: Vec<RegTrigger>) -> Inst {
let mut args = vec![x];
let mut modes = vec![];
args.extend(data.iter().map(|x| x.data));
args.extend(data.iter().map(|x| x.trigger));
args.extend(data.iter().map(|x| x.gate.unwrap_or(Value::invalid())));
modes.extend(data.iter().map(|x| x.mode));
assert_eq!(args.len(), modes.len() * 3 + 1);
self.build(
InstData::Reg {
opcode: Opcode::Reg,
args,
modes,
},
void_ty(),
)
}
pub fn ins_field(&mut self, x: Value, y: Value, imm: usize) -> Value {
let ty = self.value_type(x);
let inst = self.build(
InstData::InsExt {
opcode: Opcode::InsField,
args: [x, y],
imms: [imm, 0],
},
ty,
);
self.inst_result(inst)
}
pub fn ins_slice(&mut self, x: Value, y: Value, imm0: usize, imm1: usize) -> Value {
let ty = self.value_type(x);
let inst = self.build(
InstData::InsExt {
opcode: Opcode::InsSlice,
args: [x, y],
imms: [imm0, imm1],
},
ty,
);
self.inst_result(inst)
}
pub fn ext_field(&mut self, x: Value, imm: usize) -> Value {
let ty = with_unpacked_sigptr(self.value_type(x), |ty| {
if ty.is_struct() {
let fields = ty.unwrap_struct();
assert!(imm < fields.len(), "field index in `extf` out of range");
fields[imm].clone()
} else if ty.is_array() {
ty.unwrap_array().1.clone()
} else {
panic!("argument to `extf` must be of struct or array type");
}
});
let inst = self.build(
InstData::InsExt {
opcode: Opcode::ExtField,
args: [x, Value::invalid()],
imms: [imm, 0],
},
ty,
);
self.inst_result(inst)
}
pub fn ext_slice(&mut self, x: Value, imm0: usize, imm1: usize) -> Value {
let ty = with_unpacked_sigptr(self.value_type(x), |ty| {
if ty.is_array() {
array_ty(imm1, ty.unwrap_array().1.clone())
} else if ty.is_int() {
int_ty(imm1)
} else {
panic!("argument to `exts` must be of array or integer type");
}
});
let inst = self.build(
InstData::InsExt {
opcode: Opcode::ExtSlice,
args: [x, Value::invalid()],
imms: [imm0, imm1],
},
ty,
);
self.inst_result(inst)
}
pub fn con(&mut self, x: Value, y: Value) -> Inst {
self.build_binary(Opcode::Con, void_ty(), x, y)
}
pub fn del(&mut self, target: Value, source: Value, delay: Value) -> Inst {
self.build_ternary(Opcode::Del, void_ty(), target, source, delay)
}
pub fn call(&mut self, unit: ExtUnit, args: Vec<Value>) -> Inst {
let ty = self.builder.extern_sig(unit).return_type();
let data = InstData::Call {
opcode: Opcode::Call,
unit,
ins: args.len() as u16,
args,
};
self.build(data, ty)
}
pub fn inst(&mut self, unit: ExtUnit, mut inputs: Vec<Value>, outputs: Vec<Value>) -> Inst {
let ins = inputs.len() as u16;
inputs.extend(outputs);
let data = InstData::Call {
opcode: Opcode::Inst,
unit,
ins,
args: inputs,
};
self.build(data, void_ty())
}
pub fn sig(&mut self, x: Value) -> Value {
let ty = self.value_type(x);
let ty = if ty.is_signal() { ty } else { signal_ty(ty) };
let inst = self.build_unary(Opcode::Sig, ty, x);
self.inst_result(inst)
}
pub fn prb(&mut self, x: Value) -> Value {
let ty = self.value_type(x);
assert!(ty.is_signal(), "argument to `prb` must be of signal type");
let ty = ty.unwrap_signal().clone();
let inst = self.build_unary(Opcode::Prb, ty, x);
self.inst_result(inst)
}
pub fn drv(&mut self, signal: Value, value: Value, delay: Value) -> Inst {
self.build_ternary(Opcode::Drv, void_ty(), signal, value, delay)
}
pub fn drv_cond(&mut self, signal: Value, value: Value, delay: Value, cond: Value) -> Inst {
self.build_quaternary(Opcode::DrvCond, void_ty(), signal, value, delay, cond)
}
pub fn var(&mut self, x: Value) -> Value {
let ty = pointer_ty(self.value_type(x));
let inst = self.build_unary(Opcode::Var, ty, x);
self.inst_result(inst)
}
pub fn ld(&mut self, x: Value) -> Value {
let ty = self.value_type(x);
assert!(ty.is_pointer(), "argument to `ld` must be of pointer type");
let ty = ty.unwrap_pointer().clone();
let inst = self.build_unary(Opcode::Ld, ty, x);
self.inst_result(inst)
}
pub fn st(&mut self, x: Value, y: Value) -> Inst {
self.build_binary(Opcode::St, void_ty(), x, y)
}
pub fn halt(&mut self) -> Inst {
self.build_nullary(Opcode::Halt)
}
pub fn ret(&mut self) -> Inst {
self.build_nullary(Opcode::Ret)
}
pub fn ret_value(&mut self, x: Value) -> Inst {
self.build_unary(Opcode::RetValue, void_ty(), x)
}
pub fn phi(&mut self, args: Vec<Value>, bbs: Vec<Block>) -> Value {
assert!(args.len() > 0);
assert_eq!(args.len(), bbs.len());
let ty = self.value_type(args[0]);
let data = InstData::Phi {
opcode: Opcode::Phi,
args,
bbs,
};
let inst = self.build(data, ty);
self.inst_result(inst)
}
pub fn br(&mut self, bb: Block) -> Inst {
let data = InstData::Jump {
opcode: Opcode::Br,
bbs: [bb],
};
self.build(data, void_ty())
}
pub fn br_cond(&mut self, x: Value, bb0: Block, bb1: Block) -> Inst {
let data = InstData::Branch {
opcode: Opcode::BrCond,
args: [x],
bbs: [bb0, bb1],
};
self.build(data, void_ty())
}
pub fn wait(&mut self, bb: Block, args: Vec<Value>) -> Inst {
let data = InstData::Wait {
opcode: Opcode::Wait,
bbs: [bb],
args: args,
};
self.build(data, void_ty())
}
pub fn wait_time(&mut self, bb: Block, time: Value, mut args: Vec<Value>) -> Inst {
args.insert(0, time);
let data = InstData::Wait {
opcode: Opcode::WaitTime,
bbs: [bb],
args: args,
};
self.build(data, void_ty())
}
}
impl<'a, 'b> InstBuilder<'a, 'b> {
fn build_nullary(&mut self, opcode: Opcode) -> Inst {
let data = InstData::Nullary { opcode };
self.build(data, void_ty())
}
fn build_unary(&mut self, opcode: Opcode, ty: Type, x: Value) -> Inst {
let data = InstData::Unary { opcode, args: [x] };
self.build(data, ty)
}
fn build_binary(&mut self, opcode: Opcode, ty: Type, x: Value, y: Value) -> Inst {
let data = InstData::Binary {
opcode,
args: [x, y],
};
self.build(data, ty)
}
fn build_ternary(&mut self, opcode: Opcode, ty: Type, x: Value, y: Value, z: Value) -> Inst {
let data = InstData::Ternary {
opcode,
args: [x, y, z],
};
self.build(data, ty)
}
fn build_quaternary(
&mut self,
opcode: Opcode,
ty: Type,
x: Value,
y: Value,
z: Value,
w: Value,
) -> Inst {
let data = InstData::Quaternary {
opcode,
args: [x, y, z, w],
};
self.build(data, ty)
}
}
impl<'a, 'b> InstBuilder<'a, 'b> {
pub(crate) fn build(&mut self, data: InstData, ty: Type) -> Inst {
let has_result = !ty.is_void();
let inst = self.builder.build_inst(data, ty);
if let Some(name) = self.name.take() {
if has_result {
let value = self.inst_result(inst);
self.builder.set_name(value, name);
}
}
inst
}
fn value_type(&self, value: Value) -> Type {
self.builder.value_type(value)
}
fn inst_result(&self, inst: Inst) -> Value {
self.builder.inst_result(inst)
}
pub fn suffix<'c>(mut self, value: Value, suffix: impl Into<Cow<'c, str>>) -> Self {
let suffix = suffix.into();
self.name = if let Some(name) = self.builder.get_name(value) {
Some(format!("{}.{}", name, suffix))
} else {
Some(suffix.into_owned())
};
self
}
}
#[allow(missing_docs)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum InstData {
ConstInt { opcode: Opcode, imm: IntValue },
ConstTime { opcode: Opcode, imm: TimeValue },
Array {
opcode: Opcode,
imms: [usize; 1],
args: [Value; 1],
},
Aggregate { opcode: Opcode, args: Vec<Value> },
Nullary { opcode: Opcode },
Unary { opcode: Opcode, args: [Value; 1] },
Binary { opcode: Opcode, args: [Value; 2] },
Ternary { opcode: Opcode, args: [Value; 3] },
Quaternary { opcode: Opcode, args: [Value; 4] },
Jump { opcode: Opcode, bbs: [Block; 1] },
Phi {
opcode: Opcode,
args: Vec<Value>,
bbs: Vec<Block>,
},
Branch {
opcode: Opcode,
args: [Value; 1],
bbs: [Block; 2],
},
Wait {
opcode: Opcode,
bbs: [Block; 1],
args: Vec<Value>,
},
Call {
opcode: Opcode,
unit: ExtUnit,
ins: u16,
args: Vec<Value>,
},
InsExt {
opcode: Opcode,
args: [Value; 2],
imms: [usize; 2],
},
Reg {
opcode: Opcode,
args: Vec<Value>,
modes: Vec<RegMode>,
},
}
impl InstData {
pub fn opcode(&self) -> Opcode {
match *self {
InstData::ConstInt { opcode, .. } => opcode,
InstData::ConstTime { opcode, .. } => opcode,
InstData::Array { opcode, .. } => opcode,
InstData::Aggregate { opcode, .. } => opcode,
InstData::Nullary { opcode, .. } => opcode,
InstData::Unary { opcode, .. } => opcode,
InstData::Binary { opcode, .. } => opcode,
InstData::Ternary { opcode, .. } => opcode,
InstData::Quaternary { opcode, .. } => opcode,
InstData::Phi { opcode, .. } => opcode,
InstData::Jump { opcode, .. } => opcode,
InstData::Branch { opcode, .. } => opcode,
InstData::Wait { opcode, .. } => opcode,
InstData::Call { opcode, .. } => opcode,
InstData::InsExt { opcode, .. } => opcode,
InstData::Reg { opcode, .. } => opcode,
}
}
pub fn args(&self) -> &[Value] {
match self {
InstData::ConstInt { .. } => &[],
InstData::ConstTime { .. } => &[],
InstData::Array { args, .. } => args,
InstData::Aggregate { args, .. } => args,
InstData::Nullary { .. } => &[],
InstData::Unary { args, .. } => args,
InstData::Binary { args, .. } => args,
InstData::Ternary { args, .. } => args,
InstData::Quaternary { args, .. } => args,
InstData::Phi { args, .. } => args,
InstData::Jump { .. } => &[],
InstData::Branch { args, .. } => args,
InstData::Wait { args, .. } => args,
InstData::Call { args, .. } => args,
InstData::InsExt {
opcode: Opcode::ExtField,
args,
..
}
| InstData::InsExt {
opcode: Opcode::ExtSlice,
args,
..
} => &args[0..1],
InstData::InsExt { args, .. } => args,
InstData::Reg { args, .. } => args,
}
}
#[deprecated = "do not use directly"]
pub(crate) fn args_mut(&mut self) -> &mut [Value] {
match self {
InstData::ConstInt { .. } => &mut [],
InstData::ConstTime { .. } => &mut [],
InstData::Array { args, .. } => args,
InstData::Aggregate { args, .. } => args,
InstData::Nullary { .. } => &mut [],
InstData::Unary { args, .. } => args,
InstData::Binary { args, .. } => args,
InstData::Ternary { args, .. } => args,
InstData::Quaternary { args, .. } => args,
InstData::Phi { args, .. } => args,
InstData::Jump { .. } => &mut [],
InstData::Branch { args, .. } => args,
InstData::Wait { args, .. } => args,
InstData::Call { args, .. } => args,
InstData::InsExt {
opcode: Opcode::ExtField,
args,
..
}
| InstData::InsExt {
opcode: Opcode::ExtSlice,
args,
..
} => &mut args[0..1],
InstData::InsExt { args, .. } => args,
InstData::Reg { args, .. } => args,
}
}
pub fn imms(&self) -> &[usize] {
match self {
InstData::ConstInt { .. } => &[],
InstData::ConstTime { .. } => &[],
InstData::Array { imms, .. } => imms,
InstData::Aggregate { .. } => &[],
InstData::Nullary { .. } => &[],
InstData::Unary { .. } => &[],
InstData::Binary { .. } => &[],
InstData::Ternary { .. } => &[],
InstData::Quaternary { .. } => &[],
InstData::Phi { .. } => &[],
InstData::Jump { .. } => &[],
InstData::Branch { .. } => &[],
InstData::Wait { .. } => &[],
InstData::Call { .. } => &[],
InstData::InsExt {
opcode: Opcode::InsField,
imms,
..
}
| InstData::InsExt {
opcode: Opcode::ExtField,
imms,
..
} => &imms[0..1],
InstData::InsExt { imms, .. } => imms,
InstData::Reg { .. } => &[],
}
}
pub fn input_args(&self) -> &[Value] {
match *self {
InstData::Call { ref args, ins, .. } => &args[0..ins as usize],
_ => &[],
}
}
pub fn output_args(&self) -> &[Value] {
match *self {
InstData::Call { ref args, ins, .. } => &args[ins as usize..],
_ => &[],
}
}
pub fn data_args(&self) -> impl Iterator<Item = Value> + '_ {
match self {
InstData::Reg { args, modes, .. } => &args[1..1 + modes.len()],
_ => &[],
}
.iter()
.cloned()
}
pub fn trigger_args(&self) -> impl Iterator<Item = Value> + '_ {
match self {
InstData::Reg { args, modes, .. } => &args[1 + modes.len()..1 + 2 * modes.len()],
_ => &[],
}
.iter()
.cloned()
}
pub fn gating_args(&self) -> impl Iterator<Item = Option<Value>> + '_ {
match self {
InstData::Reg { args, modes, .. } => &args[1 + 2 * modes.len()..],
_ => &[],
}
.iter()
.map(|&v| if v == Value::invalid() { None } else { Some(v) })
}
pub fn mode_args(&self) -> impl Iterator<Item = RegMode> + '_ {
match self {
InstData::Reg { modes, .. } => modes.as_slice(),
_ => &[],
}
.iter()
.cloned()
}
pub fn triggers(&self) -> impl Iterator<Item = RegTrigger> + '_ {
self.data_args()
.zip(self.mode_args())
.zip(self.trigger_args())
.zip(self.gating_args())
.map(|(((data, mode), trigger), gate)| RegTrigger {
data,
mode,
trigger,
gate,
})
}
pub fn blocks(&self) -> &[Block] {
match self {
InstData::ConstInt { .. } => &[],
InstData::ConstTime { .. } => &[],
InstData::Array { .. } => &[],
InstData::Aggregate { .. } => &[],
InstData::Nullary { .. } => &[],
InstData::Unary { .. } => &[],
InstData::Binary { .. } => &[],
InstData::Ternary { .. } => &[],
InstData::Quaternary { .. } => &[],
InstData::Phi { bbs, .. } => bbs,
InstData::Jump { bbs, .. } => bbs,
InstData::Branch { bbs, .. } => bbs,
InstData::Wait { bbs, .. } => bbs,
InstData::Call { .. } => &[],
InstData::InsExt { .. } => &[],
InstData::Reg { .. } => &[],
}
}
#[deprecated = "do not use directly"]
pub(crate) fn blocks_mut(&mut self) -> &mut [Block] {
match self {
InstData::ConstInt { .. } => &mut [],
InstData::ConstTime { .. } => &mut [],
InstData::Array { .. } => &mut [],
InstData::Aggregate { .. } => &mut [],
InstData::Nullary { .. } => &mut [],
InstData::Unary { .. } => &mut [],
InstData::Binary { .. } => &mut [],
InstData::Ternary { .. } => &mut [],
InstData::Quaternary { .. } => &mut [],
InstData::Phi { bbs, .. } => bbs,
InstData::Jump { bbs, .. } => bbs,
InstData::Branch { bbs, .. } => bbs,
InstData::Wait { bbs, .. } => bbs,
InstData::Call { .. } => &mut [],
InstData::InsExt { .. } => &mut [],
InstData::Reg { .. } => &mut [],
}
}
#[deprecated = "use DataFlowGraph::replace_value_within_inst instead"]
pub(crate) fn replace_value(&mut self, from: Value, to: Value) -> usize {
let mut count = 0;
#[allow(deprecated)]
for arg in self.args_mut() {
if *arg == from {
*arg = to;
count += 1;
}
}
count
}
#[deprecated = "use DataFlowGraph::replace_block_within_inst instead"]
pub(crate) fn replace_block(&mut self, from: Block, to: Block) -> usize {
let mut count = 0;
#[allow(deprecated)]
for bb in self.blocks_mut() {
if *bb == from {
*bb = to;
count += 1;
}
}
count
}
#[deprecated = "use DataFlowGraph::remove_block_from_inst instead"]
pub(crate) fn remove_block(&mut self, block: Block) -> usize {
match self {
InstData::Phi { bbs, args, .. } => {
let mut count = 0;
for i in 0..bbs.len() {
if i >= bbs.len() {
break;
}
if bbs[i] == block {
bbs.swap_remove(i);
args.swap_remove(i);
count += 1;
}
}
count
}
#[allow(deprecated)]
_ => self.replace_block(block, Block::invalid()),
}
}
pub fn get_const_int(&self) -> Option<&IntValue> {
match self {
InstData::ConstInt { imm, .. } => Some(imm),
_ => None,
}
}
pub fn get_const_time(&self) -> Option<&TimeValue> {
match self {
InstData::ConstTime { imm, .. } => Some(imm),
_ => None,
}
}
pub fn get_ext_unit(&self) -> Option<ExtUnit> {
match self {
InstData::Call { unit, .. } => Some(*unit),
_ => None,
}
}
}
impl Default for InstData {
fn default() -> InstData {
InstData::Nullary {
opcode: Opcode::Ret,
}
}
}
bitflags! {
#[derive(Default, Serialize, Deserialize)]
pub struct UnitFlags: u8 {
const FUNCTION = 0b001;
const PROCESS = 0b010;
const ENTITY = 0b100;
const ALL = 0b111;
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum RegMode {
Low,
High,
Rise,
Fall,
Both,
}
impl std::fmt::Display for RegMode {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
RegMode::Low => write!(f, "low"),
RegMode::High => write!(f, "high"),
RegMode::Rise => write!(f, "rise"),
RegMode::Fall => write!(f, "fall"),
RegMode::Both => write!(f, "both"),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct RegTrigger {
pub data: Value,
pub mode: RegMode,
pub trigger: Value,
pub gate: Option<Value>,
}
#[allow(missing_docs)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Opcode {
ConstInt,
ConstTime,
Alias,
ArrayUniform,
Array,
Struct,
Not,
Neg,
Add,
Sub,
And,
Or,
Xor,
Smul,
Sdiv,
Smod,
Srem,
Umul,
Udiv,
Umod,
Urem,
Eq,
Neq,
Slt,
Sgt,
Sle,
Sge,
Ult,
Ugt,
Ule,
Uge,
Shl,
Shr,
Mux,
Reg,
InsField,
InsSlice,
ExtField,
ExtSlice,
Con,
Del,
Call,
Inst,
Sig,
Prb,
Drv,
DrvCond,
Var,
Ld,
St,
Halt,
Ret,
RetValue,
Phi,
Br,
BrCond,
Wait,
WaitTime,
}
impl std::fmt::Display for Opcode {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"{}",
match *self {
Opcode::ConstInt => "const",
Opcode::ConstTime => "const",
Opcode::Alias => "alias",
Opcode::ArrayUniform => "array",
Opcode::Array => "array",
Opcode::Struct => "struct",
Opcode::Not => "not",
Opcode::Neg => "neg",
Opcode::Add => "add",
Opcode::Sub => "sub",
Opcode::And => "and",
Opcode::Or => "or",
Opcode::Xor => "xor",
Opcode::Smul => "smul",
Opcode::Sdiv => "sdiv",
Opcode::Smod => "smod",
Opcode::Srem => "srem",
Opcode::Umul => "umul",
Opcode::Udiv => "udiv",
Opcode::Umod => "umod",
Opcode::Urem => "urem",
Opcode::Eq => "eq",
Opcode::Neq => "neq",
Opcode::Slt => "slt",
Opcode::Sgt => "sgt",
Opcode::Sle => "sle",
Opcode::Sge => "sge",
Opcode::Ult => "ult",
Opcode::Ugt => "ugt",
Opcode::Ule => "ule",
Opcode::Uge => "uge",
Opcode::Shl => "shl",
Opcode::Shr => "shr",
Opcode::Mux => "mux",
Opcode::Reg => "reg",
Opcode::InsField => "insf",
Opcode::InsSlice => "inss",
Opcode::ExtField => "extf",
Opcode::ExtSlice => "exts",
Opcode::Con => "con",
Opcode::Del => "del",
Opcode::Call => "call",
Opcode::Inst => "inst",
Opcode::Sig => "sig",
Opcode::Drv => "drv",
Opcode::DrvCond => "drv",
Opcode::Prb => "prb",
Opcode::Var => "var",
Opcode::Ld => "ld",
Opcode::St => "st",
Opcode::Halt => "halt",
Opcode::Ret => "ret",
Opcode::RetValue => "ret",
Opcode::Phi => "phi",
Opcode::Br => "br",
Opcode::BrCond => "br",
Opcode::Wait => "wait",
Opcode::WaitTime => "wait",
}
)
}
}
impl Opcode {
pub fn valid_in(self) -> UnitFlags {
match self {
Opcode::Halt => UnitFlags::PROCESS | UnitFlags::ENTITY,
Opcode::Wait => UnitFlags::PROCESS,
Opcode::WaitTime => UnitFlags::PROCESS,
Opcode::Ret => UnitFlags::FUNCTION,
Opcode::RetValue => UnitFlags::FUNCTION,
Opcode::Phi => UnitFlags::FUNCTION | UnitFlags::PROCESS,
Opcode::Br => UnitFlags::FUNCTION | UnitFlags::PROCESS,
Opcode::BrCond => UnitFlags::FUNCTION | UnitFlags::PROCESS,
Opcode::Con => UnitFlags::ENTITY,
Opcode::Del => UnitFlags::ENTITY,
Opcode::Reg => UnitFlags::ENTITY,
Opcode::Inst => UnitFlags::ENTITY,
_ => UnitFlags::ALL,
}
}
pub fn valid_in_function(self) -> bool {
self.valid_in().contains(UnitFlags::FUNCTION)
}
pub fn valid_in_process(self) -> bool {
self.valid_in().contains(UnitFlags::PROCESS)
}
pub fn valid_in_entity(self) -> bool {
self.valid_in().contains(UnitFlags::ENTITY)
}
pub fn is_const(self) -> bool {
match self {
Opcode::ConstInt => true,
Opcode::ConstTime => true,
_ => false,
}
}
pub fn is_phi(self) -> bool {
match self {
Opcode::Phi => true,
_ => false,
}
}
pub fn is_terminator(self) -> bool {
match self {
Opcode::Halt
| Opcode::Ret
| Opcode::RetValue
| Opcode::Br
| Opcode::BrCond
| Opcode::Wait
| Opcode::WaitTime => true,
_ => false,
}
}
pub fn is_return(self) -> bool {
match self {
Opcode::Ret | Opcode::RetValue => true,
_ => false,
}
}
pub fn is_temporal(self) -> bool {
match self {
Opcode::Halt | Opcode::Wait | Opcode::WaitTime => true,
_ => false,
}
}
}
impl Inst {
pub fn dump<'a>(self, unit: &Unit<'a>) -> InstDumper<'a> {
InstDumper(self, *unit)
}
}
pub struct InstDumper<'a>(Inst, Unit<'a>);
impl std::fmt::Display for InstDumper<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let inst = self.0;
let unit = self.1;
let data = &unit[inst];
if unit.has_result(inst) {
let result = unit.inst_result(inst);
write!(
f,
"{} = {} {}",
result.dump(&unit),
data.opcode(),
unit.value_type(result)
)?;
} else {
write!(f, "{}", data.opcode())?;
}
if let InstData::Call { unit: ext_unit, .. } = *data {
write!(f, " {}", unit[ext_unit].name)?;
write!(f, " (")?;
let mut comma = false;
for arg in data.input_args() {
if comma {
write!(f, ", ")?;
}
write!(f, "{}", arg.dump(&unit))?;
comma = true;
}
write!(f, ")")?;
if data.opcode() == Opcode::Inst {
write!(f, " -> (")?;
let mut comma = false;
for arg in data.output_args() {
if comma {
write!(f, ", ")?;
}
write!(f, "{}", arg.dump(&unit))?;
comma = true;
}
write!(f, ")")?;
}
} else if let InstData::Reg { .. } = *data {
write!(f, " {}", data.args()[0])?;
for arg in data.data_args() {
write!(f, ", {}", arg.dump(&unit))?;
}
for arg in data.mode_args() {
write!(f, ", {}", arg)?;
}
for arg in data.trigger_args() {
write!(f, ", {}", arg.dump(&unit))?;
}
} else if let InstData::Phi { .. } = *data {
let mut comma = false;
write!(f, " ")?;
for (arg, block) in data.args().iter().zip(data.blocks().iter()) {
if comma {
write!(f, ", ")?;
}
write!(f, "[{}, {}]", arg.dump(&unit), block.dump(&unit))?;
comma = true;
}
} else {
let mut comma = false;
for arg in data.args() {
if comma {
write!(f, ",")?;
}
write!(f, " {}", arg.dump(&unit))?;
comma = true;
}
for block in data.blocks() {
if comma {
write!(f, ",")?;
}
write!(f, " {}", block.dump(&unit))?;
comma = true;
}
for imm in data.imms() {
if comma {
write!(f, ",")?;
}
write!(f, " {}", imm)?;
comma = true;
}
match data {
InstData::ConstInt { imm, .. } => write!(f, " {}", imm.value)?,
InstData::ConstTime { imm, .. } => write!(f, " {}", imm)?,
InstData::Array { imms, .. } => write!(f, ", {}", imms[0])?,
_ => (),
}
}
Ok(())
}
}
fn with_unpacked_sigptr(ty: Type, f: impl FnOnce(Type) -> Type) -> Type {
if ty.is_pointer() {
pointer_ty(f(ty.unwrap_pointer().clone()))
} else if ty.is_signal() {
signal_ty(f(ty.unwrap_signal().clone()))
} else {
f(ty)
}
}