use crate::arch::bpf::{DecodedInsn, InsnKind};
use crate::ast::{Param, Signature, Type};
use crate::ir::Function;
#[must_use]
pub fn infer_bpf_signature(f: &Function<DecodedInsn>) -> Option<Signature> {
let mut read_before_write = [false; 11];
let mut written = [false; 11];
for block in &f.blocks {
for insn in &block.insns {
for r in reads_of(insn) {
if !written[r as usize] {
read_before_write[r as usize] = true;
}
}
for w in writes_of(insn) {
written[w as usize] = true;
}
}
}
let mut arity = 0;
for r in 1..=5u8 {
if read_before_write[r as usize] {
arity = r;
} else {
break;
}
}
let returns_u64 = written[0];
if arity == 0 && !returns_u64 {
return None;
}
let params = (0..arity)
.map(|i| Param {
name: format!("arg_{i}"),
ty: Type::U64,
location: Some(format!("r{}", i + 1)),
})
.collect();
let return_type = if returns_u64 {
Type::U64
} else {
Type::Unknown
};
Some(Signature {
params,
return_type,
})
}
#[allow(clippy::match_same_arms)]
fn reads_of(insn: &DecodedInsn) -> Vec<u8> {
let class = insn.opcode & 0x07;
let op_nibble = insn.opcode >> 4;
let is_reg_src = (insn.opcode & 0x08) != 0;
let mut out = Vec::new();
match class {
0x00 => {}
0x01 => out.push(insn.src),
0x02 => out.push(insn.dst),
0x03 => {
out.push(insn.dst);
out.push(insn.src);
}
0x04 | 0x07 => {
if !matches!(op_nibble, 0xb | 0xd) {
out.push(insn.dst);
}
if is_reg_src {
out.push(insn.src);
}
}
0x05 | 0x06 => match insn.kind {
InsnKind::Call => {
out.extend_from_slice(&[1, 2, 3, 4, 5]);
}
InsnKind::CallReg => out.push(insn.dst),
InsnKind::Exit => out.push(0),
InsnKind::Jmp => {}
_ => {
out.push(insn.dst);
if is_reg_src {
out.push(insn.src);
}
}
},
_ => {}
}
out.retain(|&r| r <= 10);
out
}
#[allow(clippy::match_same_arms)]
fn writes_of(insn: &DecodedInsn) -> Vec<u8> {
let class = insn.opcode & 0x07;
match class {
0x00 | 0x01 => vec![insn.dst],
0x02 | 0x03 => Vec::new(),
0x04 | 0x07 => vec![insn.dst],
0x05 | 0x06 => match insn.kind {
InsnKind::Call | InsnKind::CallReg => vec![0],
_ => Vec::new(),
},
_ => Vec::new(),
}
.into_iter()
.filter(|&r| r <= 10)
.collect()
}