use crate::value::{
LocalValueId,
function::FunctionId,
insn::{
Apply, Assert, BadInsn, Binary, Branch, BranchInd, CBranch, Call, CallInd, Carry, Extract,
FloatToFloat, FloatToInt, Gep, IntToFloat, IntrinsicApp, IsFloatNaN, Load, LzCount, Map,
PCodeOp, PopCount, Range, Return, ReturnValue, SBorrow, SCarry, Scan, Sext, Store, Switch,
TailCall, Tuple, Unary, Zext,
},
};
use smallvec::SmallVec;
pub type Args = SmallVec<[LocalValueId; 2]>;
pub trait MnemonicKind {
fn opcode(&self) -> &'static str;
fn args(&self) -> Args;
fn is_terminator(&self) -> bool {
false
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum Mnemonic {
Load(Load),
Store(Store),
Branch(Branch),
CBranch(CBranch),
BranchInd(BranchInd),
Switch(Switch),
Call(Call),
TailCall(TailCall),
Apply(Apply),
CallInd(CallInd),
Return(Return),
ReturnValue(ReturnValue),
BadInsn(BadInsn),
Unop(Unary),
Binop(Binary),
Range(Range),
IntToFloat(IntToFloat),
FloatToFloat(FloatToFloat),
FloatToInt(FloatToInt),
Zext(Zext),
Sext(Sext),
IsFloatNaN(IsFloatNaN),
PopCount(PopCount),
LzCount(LzCount),
Carry(Carry),
SCarry(SCarry),
SBorrow(SBorrow),
Assert(Assert),
PCodeOp(PCodeOp),
Intrinsic(IntrinsicApp),
Tuple(Tuple),
Extract(Extract),
Gep(Gep),
Map(Map),
Scan(Scan),
}
impl Mnemonic {
pub fn minted_callee_slot(&self) -> Option<u32> {
match self {
Self::Call(call) => call.target.minted(),
Self::TailCall(call) => call.target.minted(),
Self::Apply(apply) => apply.target.minted(),
Self::Map(map) => map.body.minted(),
Self::Scan(scan) => scan.body.minted(),
_ => None,
}
}
pub fn resolve_minted_callee(&mut self, slot: u32, real: FunctionId) -> bool {
let callee = match self {
Self::Call(call) => Some(&mut call.target),
Self::TailCall(call) => Some(&mut call.target),
Self::Apply(apply) => Some(&mut apply.target),
Self::Map(map) => Some(&mut map.body),
Self::Scan(scan) => Some(&mut scan.body),
_ => None,
};
let Some(callee) = callee else {
return false;
};
if *callee != super::Callee::Minted(slot) {
return false;
}
*callee = super::Callee::Real(real);
true
}
fn as_kind(&self) -> &dyn MnemonicKind {
match self {
Mnemonic::Load(m) => m,
Mnemonic::Store(m) => m,
Mnemonic::Branch(m) => m,
Mnemonic::CBranch(m) => m,
Mnemonic::BranchInd(m) => m,
Mnemonic::Switch(m) => m,
Mnemonic::Call(m) => m,
Mnemonic::TailCall(m) => m,
Mnemonic::Apply(m) => m,
Mnemonic::CallInd(m) => m,
Mnemonic::Return(m) => m,
Mnemonic::ReturnValue(m) => m,
Mnemonic::BadInsn(m) => m,
Mnemonic::Range(m) => m,
Mnemonic::Unop(m) => m,
Mnemonic::Binop(m) => m,
Mnemonic::IsFloatNaN(m) => m,
Mnemonic::IntToFloat(m) => m,
Mnemonic::FloatToFloat(m) => m,
Mnemonic::FloatToInt(m) => m,
Mnemonic::Zext(m) => m,
Mnemonic::Sext(m) => m,
Mnemonic::PopCount(m) => m,
Mnemonic::LzCount(m) => m,
Mnemonic::Carry(m) => m,
Mnemonic::SCarry(m) => m,
Mnemonic::SBorrow(m) => m,
Mnemonic::Assert(m) => m,
Mnemonic::PCodeOp(m) => m,
Mnemonic::Intrinsic(m) => m,
Mnemonic::Tuple(m) => m,
Mnemonic::Extract(m) => m,
Mnemonic::Gep(m) => m,
Mnemonic::Map(m) => m,
Mnemonic::Scan(m) => m,
}
}
pub fn opcode(&self) -> &'static str {
self.as_kind().opcode()
}
pub fn is_terminator(&self) -> bool {
self.as_kind().is_terminator()
}
pub fn has_side_effects(&self) -> bool {
matches!(
self,
Mnemonic::Store(_)
| Mnemonic::Call(_)
| Mnemonic::CallInd(_)
| Mnemonic::PCodeOp(_)
| Mnemonic::Assert(_)
) || self.is_terminator()
}
pub fn call_target(&self) -> Option<FunctionId> {
match self {
Mnemonic::Call(call) => call.target.real(),
Mnemonic::TailCall(tc) => tc.target.real(),
Mnemonic::Apply(apply) => apply.target.real(),
Mnemonic::Map(map) => map.body.real(),
Mnemonic::Scan(scan) => scan.body.real(),
_ => None,
}
}
pub fn target_blocks(&self) -> smallvec::SmallVec<[crate::value::LocalBlockId; 2]> {
match self {
Mnemonic::Branch(b) => smallvec::smallvec![b.target],
Mnemonic::CBranch(c) => smallvec::smallvec![c.success_block, c.failure_block],
Mnemonic::Switch(s) => s
.cases
.iter()
.map(|case| case.target)
.chain(s.default)
.collect(),
_ => smallvec::SmallVec::new(),
}
}
pub fn args(&self) -> Args {
self.as_kind().args()
}
pub fn replace_value(&mut self, old: LocalValueId, new: LocalValueId) {
match self {
Mnemonic::Load(m) => {
if m.ptr == old {
m.ptr = new;
}
}
Mnemonic::Store(m) => {
if m.ptr == old {
m.ptr = new;
}
if m.src == old {
m.src = new;
}
}
Mnemonic::CBranch(m) => {
if m.condition == old {
m.condition = new;
}
m.success_args.iter_mut().for_each(|a| {
if *a == old {
*a = new;
}
});
m.failure_args.iter_mut().for_each(|a| {
if *a == old {
*a = new;
}
});
}
Mnemonic::BranchInd(m) => {
if m.ptr == old {
m.ptr = new;
}
}
Mnemonic::Switch(m) => {
if m.scrutinee == old {
m.scrutinee = new;
}
for case in m.cases.iter_mut() {
case.args.iter_mut().for_each(|a| {
if *a == old {
*a = new;
}
});
}
m.default_args.iter_mut().for_each(|a| {
if *a == old {
*a = new;
}
});
}
Mnemonic::Call(m) => {
m.args.iter_mut().for_each(|a| {
if *a == old {
*a = new;
}
});
}
Mnemonic::TailCall(m) => {
m.args.iter_mut().for_each(|a| {
if *a == old {
*a = new;
}
});
}
Mnemonic::Apply(m) => {
m.args.iter_mut().for_each(|a| {
if *a == old {
*a = new;
}
});
}
Mnemonic::CallInd(m) => {
if m.ptr == old {
m.ptr = new;
}
m.args.iter_mut().for_each(|a| {
if *a == old {
*a = new;
}
});
}
Mnemonic::Return(m) => {
if m.ptr == old {
m.ptr = new;
}
if let Some(v) = m.value.as_mut()
&& *v == old
{
*v = new;
}
}
Mnemonic::ReturnValue(m) => {
if m.value == old {
m.value = new;
}
}
Mnemonic::BadInsn(_) => {}
Mnemonic::Unop(m) => {
if m.src == old {
m.src = new;
}
}
Mnemonic::Binop(m) => {
if m.lhs == old {
m.lhs = new;
}
if m.rhs == old {
m.rhs = new;
}
}
Mnemonic::Range(m) => {
if m.src == old {
m.src = new;
}
}
Mnemonic::Zext(m) => {
if m.src == old {
m.src = new;
}
}
Mnemonic::Sext(m) => {
if m.src == old {
m.src = new;
}
}
Mnemonic::IntToFloat(m) => {
if m.src == old {
m.src = new;
}
}
Mnemonic::FloatToFloat(m) => {
if m.src == old {
m.src = new;
}
}
Mnemonic::FloatToInt(m) => {
if m.src == old {
m.src = new;
}
}
Mnemonic::IsFloatNaN(m) => {
if m.src == old {
m.src = new;
}
}
Mnemonic::PopCount(m) => {
if m.src == old {
m.src = new;
}
}
Mnemonic::LzCount(m) => {
if m.src == old {
m.src = new;
}
}
Mnemonic::Carry(m) => {
if m.lhs == old {
m.lhs = new;
}
if m.rhs == old {
m.rhs = new;
}
}
Mnemonic::SCarry(m) => {
if m.lhs == old {
m.lhs = new;
}
if m.rhs == old {
m.rhs = new;
}
}
Mnemonic::SBorrow(m) => {
if m.lhs == old {
m.lhs = new;
}
if m.rhs == old {
m.rhs = new;
}
}
Mnemonic::PCodeOp(m) => {
m.args.iter_mut().for_each(|a| {
if *a == old {
*a = new;
}
});
if let Some(v) = m.dst.as_mut()
&& *v == old
{
*v = new;
}
}
Mnemonic::Branch(m) => {
m.args.iter_mut().for_each(|a| {
if *a == old {
*a = new;
}
});
}
Mnemonic::Intrinsic(m) => {
m.args.iter_mut().for_each(|a| {
if *a == old {
*a = new;
}
});
}
Mnemonic::Tuple(m) => {
m.fields.iter_mut().for_each(|a| {
if *a == old {
*a = new;
}
});
}
Mnemonic::Assert(m) => {
if m.condition == old {
m.condition = new;
}
}
Mnemonic::Extract(m) => {
if m.agg == old {
m.agg = new;
}
}
Mnemonic::Gep(m) => {
if m.base == old {
m.base = new;
}
}
Mnemonic::Map(m) => {
if m.src == old {
m.src = new;
}
m.captures.iter_mut().for_each(|a| {
if *a == old {
*a = new;
}
});
}
Mnemonic::Scan(m) => {
if m.init == old {
m.init = new;
}
if m.src == old {
m.src = new;
}
m.captures.iter_mut().for_each(|a| {
if *a == old {
*a = new;
}
});
}
}
}
}