use crate::{
ir::{Block, DataFlowGraph, ExtUnit, Inst, Unit, UnitBuilder, Value},
ty::{array_ty, int_ty, pointer_ty, signal_ty, struct_ty, time_ty, void_ty, Type},
ConstTime,
};
use bitflags::bitflags;
use num::BigInt;
pub struct InstBuilder<B> {
builder: B,
}
impl<B> InstBuilder<B> {
pub fn new(builder: B) -> Self {
Self { builder }
}
}
impl<B: UnitBuilder> InstBuilder<&mut B> {
pub fn const_int(&mut self, width: usize, value: impl Into<BigInt>) -> Value {
let data = InstData::ConstInt {
opcode: Opcode::ConstInt,
imm: value.into(),
};
let inst = self.build(data, int_ty(width));
self.inst_result(inst)
}
pub fn const_time(&mut self, value: impl Into<ConstTime>) -> Value {
let data = InstData::ConstTime {
opcode: Opcode::ConstTime,
imm: value.into(),
};
let inst = self.build(data, time_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<(Value, RegMode, Value)>) -> Value {
let ty = self.value_type(x);
let mut args = vec![x];
let mut modes = vec![];
for (data, mode, trigger) in data {
args.push(data);
args.push(trigger);
modes.push(mode);
}
assert_eq!(args.len(), modes.len() * 2 + 1);
let inst = self.build(
InstData::Reg {
opcode: Opcode::Reg,
args,
modes,
},
ty,
);
self.inst_result(inst)
}
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 = self.value_type(x);
let (ty, is_ptr, is_sig) = if ty.is_pointer() {
(ty.unwrap_pointer(), true, false)
} else if ty.is_signal() {
(ty.unwrap_signal(), false, true)
} else {
(&ty, false, false)
};
let 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 ty = if is_ptr {
pointer_ty(ty)
} else if is_sig {
signal_ty(ty)
} else {
ty
};
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 = self.value_type(x);
let (ty, is_ptr, is_sig) = if ty.is_pointer() {
(ty.unwrap_pointer(), true, false)
} else if ty.is_signal() {
(ty.unwrap_signal(), false, true)
} else {
(&ty, false, false)
};
let 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 ty = if is_ptr {
pointer_ty(ty)
} else if is_sig {
signal_ty(ty)
} else {
ty
};
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, x: Value, y: Value) -> Inst {
let ty = self.value_type(x);
self.build_binary(Opcode::Del, ty, x, y)
}
pub fn call(&mut self, unit: ExtUnit, args: Vec<Value>) -> Inst {
let ty = self.builder.unit().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 = signal_ty(self.value_type(x));
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, x: Value, y: Value, z: Value) -> Inst {
self.build_ternary(Opcode::Drv, void_ty(), x, y, z)
}
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 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<B: UnitBuilder> InstBuilder<&mut 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)
}
}
impl<B: UnitBuilder> InstBuilder<&mut B> {
fn build(&mut self, data: InstData, ty: Type) -> Inst {
self.builder.build_inst(data, ty)
}
fn value_type(&self, value: Value) -> Type {
self.builder.unit().value_type(value)
}
fn inst_result(&self, inst: Inst) -> Value {
self.builder.unit().inst_result(inst)
}
}
#[allow(missing_docs)]
#[derive(Debug, Clone)]
pub enum InstData {
ConstInt { opcode: Opcode, imm: BigInt },
ConstTime { opcode: Opcode, imm: ConstTime },
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] },
Jump { opcode: Opcode, bbs: [Block; 1] },
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::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::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,
}
}
pub 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::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::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) -> &[Value] {
match self {
InstData::Reg { args, modes, .. } => &args[1..1 + modes.len()],
_ => &[],
}
}
pub fn trigger_args(&self) -> &[Value] {
match self {
InstData::Reg { args, modes, .. } => &args[1 + modes.len()..],
_ => &[],
}
}
pub fn mode_args(&self) -> &[RegMode] {
match self {
InstData::Reg { modes, .. } => modes,
_ => &[],
}
}
pub fn blocks(&self) -> &[Block] {
match self {
InstData::ConstInt { .. } => &[],
InstData::ConstTime { .. } => &[],
InstData::Array { .. } => &[],
InstData::Aggregate { .. } => &[],
InstData::Nullary { .. } => &[],
InstData::Unary { .. } => &[],
InstData::Binary { .. } => &[],
InstData::Ternary { .. } => &[],
InstData::Jump { bbs, .. } => bbs,
InstData::Branch { bbs, .. } => bbs,
InstData::Wait { bbs, .. } => bbs,
InstData::Call { .. } => &[],
InstData::InsExt { .. } => &[],
InstData::Reg { .. } => &[],
}
}
pub 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::Jump { bbs, .. } => bbs,
InstData::Branch { bbs, .. } => bbs,
InstData::Wait { bbs, .. } => bbs,
InstData::Call { .. } => &mut [],
InstData::InsExt { .. } => &mut [],
InstData::Reg { .. } => &mut [],
}
}
pub fn replace_value(&mut self, from: Value, to: Value) -> usize {
let mut count = 0;
for arg in self.args_mut() {
if *arg == from {
*arg = to;
count += 1;
}
}
count
}
pub fn replace_block(&mut self, from: Block, to: Block) -> usize {
let mut count = 0;
for bb in self.blocks_mut() {
if *bb == from {
*bb = to;
count += 1;
}
}
count
}
pub fn get_const_int(&self) -> Option<&BigInt> {
match self {
InstData::ConstInt { imm, .. } => Some(imm),
_ => None,
}
}
pub fn get_const_time(&self) -> Option<&ConstTime> {
match self {
InstData::ConstTime { imm, .. } => Some(imm),
_ => None,
}
}
pub fn get_ext_unit(&self) -> Option<ExtUnit> {
match self {
InstData::Call { unit, .. } => Some(*unit),
_ => None,
}
}
}
bitflags! {
#[derive(Default)]
pub struct UnitFlags: u8 {
const FUNCTION = 0b001;
const PROCESS = 0b010;
const ENTITY = 0b100;
const ALL = 0b111;
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
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(Debug, Copy, Clone, PartialEq, Eq)]
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,
Drv,
Prb,
Var,
Ld,
St,
Halt,
Ret,
RetValue,
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::Prb => "prb",
Opcode::Var => "var",
Opcode::Ld => "ld",
Opcode::St => "st",
Opcode::Halt => "halt",
Opcode::Ret => "ret",
Opcode::RetValue => "ret",
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,
Opcode::Ret | Opcode::RetValue => UnitFlags::FUNCTION,
Opcode::Br | Opcode::BrCond => UnitFlags::FUNCTION | UnitFlags::PROCESS,
Opcode::Con => UnitFlags::ENTITY,
Opcode::Del => UnitFlags::ENTITY,
Opcode::Reg => 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_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,
}
}
}
impl Inst {
pub fn dump(self, dfg: &DataFlowGraph) -> InstDumper {
InstDumper(self, dfg)
}
}
pub struct InstDumper<'a>(Inst, &'a DataFlowGraph);
impl std::fmt::Display for InstDumper<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let inst = self.0;
let dfg = self.1;
let data = &dfg[inst];
if dfg.has_result(inst) {
let result = dfg.inst_result(inst);
write!(
f,
"%{} = {} {}",
result,
data.opcode(),
dfg.value_type(result)
)?;
} else {
write!(f, "{}", data.opcode())?;
}
if let InstData::Call { unit, .. } = *data {
write!(f, " {}", dfg[unit].name)?;
write!(f, " (")?;
let mut comma = false;
for arg in data.input_args() {
if comma {
write!(f, ", ")?;
}
write!(f, "%{}", arg)?;
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)?;
comma = true;
}
write!(f, ")")?;
}
} else if let InstData::Reg { .. } = *data {
write!(f, " {}", data.args()[0])?;
for arg in data.data_args() {
write!(f, ", %{}", arg)?;
}
for arg in data.mode_args() {
write!(f, ", {}", arg)?;
}
for arg in data.trigger_args() {
write!(f, ", %{}", arg)?;
}
} else {
let mut comma = false;
for arg in data.args() {
if comma {
write!(f, ",")?;
}
write!(f, " %{}", arg)?;
comma = true;
}
for block in data.blocks() {
if comma {
write!(f, ",")?;
}
write!(f, " %{}", block)?;
comma = true;
}
for imm in data.imms() {
if comma {
write!(f, ",")?;
}
write!(f, " {}", imm)?;
comma = true;
}
match data {
InstData::ConstInt { imm, .. } => write!(f, " {}", imm)?,
InstData::ConstTime { imm, .. } => write!(f, " {}", imm)?,
InstData::Array { imms, .. } => write!(f, ", {}", imms[0])?,
_ => (),
}
}
Ok(())
}
}