use std::collections::{HashMap, HashSet};
use crate::facts::{FunctionId, GuardKind, SourceLocation};
pub(crate) type ValueId = u32;
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct ModuleIr {
pub(crate) functions: Vec<FunctionIr>,
pub(crate) diagnostics: Vec<String>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct FunctionIr {
pub(crate) id: FunctionId,
pub(crate) params: Vec<ValueId>,
pub(crate) blocks: Vec<BlockIr>,
pub(crate) values: Vec<ValueNode>,
pub(crate) dbg_values: Vec<DbgValue>,
pub(crate) scopes: Vec<Option<ScopeId>>,
pub(crate) scope_parents: HashMap<ScopeId, ScopeId>,
pub(crate) subprogram: Option<ScopeId>,
pub(crate) subprograms: HashSet<ScopeId>,
}
pub(crate) type ScopeId = u32;
impl FunctionIr {
pub(crate) fn may_own(&self, scope: Option<ScopeId>) -> bool {
let Some(mut at) = scope else {
return true;
};
for _ in 0..=self.scope_parents.len() {
if Some(at) == self.subprogram {
return true;
}
match self.scope_parents.get(&at) {
Some(&parent) => at = parent,
None => return !self.subprograms.contains(&at),
}
}
true
}
pub(crate) fn within(&self, inner: Option<ScopeId>, outer: Option<ScopeId>) -> bool {
let (Some(mut at), Some(outer)) = (inner, outer) else {
return false;
};
for _ in 0..=self.scope_parents.len() {
if at == outer {
return true;
}
match self.scope_parents.get(&at) {
Some(&parent) => at = parent,
None => return false,
}
}
false
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct BlockIr {
pub(crate) instrs: Vec<ValueId>,
pub(crate) successors: Vec<usize>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct ValueNode {
pub(crate) block: usize,
pub(crate) opcode: OpcodeIr,
pub(crate) operands: Vec<Operand>,
pub(crate) incoming: Vec<usize>,
pub(crate) call: Option<CallIr>,
pub(crate) guard: Option<GuardOpRaw>,
pub(crate) location: Option<SourceLocation>,
pub(crate) volatile: bool,
pub(crate) ty: Option<TypeTag>,
pub(crate) element: Option<String>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct TypeTag {
pub(crate) kind: ScalarKind,
pub(crate) bits: u32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum ScalarKind {
Int,
Float,
Ptr,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum OpcodeIr {
Param,
Call,
CallBr,
ICmp,
Select,
BinOp {
op: BinaryOp,
},
Cast {
op: CastOp,
},
Phi,
Gep,
Load,
Store,
Ret,
Br,
Other,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum BinaryOp {
Add,
FAdd,
Sub,
FSub,
Mul,
FMul,
UDiv,
SDiv,
FDiv,
URem,
SRem,
FRem,
Shl,
LShr,
AShr,
And,
Or,
Xor,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum CastOp {
Trunc,
ZExt,
SExt,
FPToUI,
FPToSI,
UIToFP,
SIToFP,
FPTrunc,
FPExt,
PtrToInt,
PtrToAddr,
IntToPtr,
BitCast,
AddrSpaceCast,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum Operand {
Value(ValueId),
Const(ConstVal),
Func(FunctionId),
Other,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct ConstVal {
pub(crate) value: String,
pub(crate) bits: Option<u32>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct CallIr {
pub(crate) callee: Option<FunctionId>,
pub(crate) inline_asm: bool,
pub(crate) returns_void: bool,
pub(crate) args: Vec<Operand>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct GuardOpRaw {
pub(crate) kind: GuardKind,
pub(crate) predicate: Option<String>,
pub(crate) bound: Option<String>,
pub(crate) bits: Option<u32>,
}
pub(crate) struct IntPredicate {
pub(crate) spelling: &'static str,
pub(crate) swapped: &'static str,
pub(crate) inverse: &'static str,
pub(crate) clamps: Option<[&'static str; 2]>,
}
pub(crate) const INT_PREDICATES: [IntPredicate; 10] = [
predicate("eq", "eq", "ne", None),
predicate("ne", "ne", "eq", None),
predicate("ugt", "ult", "ule", Some(["umax", "umin"])),
predicate("uge", "ule", "ult", Some(["umax", "umin"])),
predicate("ult", "ugt", "uge", Some(["umin", "umax"])),
predicate("ule", "uge", "ugt", Some(["umin", "umax"])),
predicate("sgt", "slt", "sle", Some(["smax", "smin"])),
predicate("sge", "sle", "slt", Some(["smax", "smin"])),
predicate("slt", "sgt", "sge", Some(["smin", "smax"])),
predicate("sle", "sge", "sgt", Some(["smin", "smax"])),
];
const fn predicate(
spelling: &'static str,
swapped: &'static str,
inverse: &'static str,
clamps: Option<[&'static str; 2]>,
) -> IntPredicate {
IntPredicate {
spelling,
swapped,
inverse,
clamps,
}
}
pub(crate) fn int_predicate(spelling: &str) -> Option<&'static IntPredicate> {
INT_PREDICATES.iter().find(|row| row.spelling == spelling)
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct DbgValue {
pub(crate) name: String,
pub(crate) variable: u32,
pub(crate) declared_file: String,
pub(crate) declared_line: u32,
pub(crate) scope: Option<ScopeId>,
pub(crate) value: Operand,
pub(crate) at: ValueId,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_predicate_inverts_back_to_itself() {
for row in &INT_PREDICATES {
let inverse = int_predicate(row.inverse)
.unwrap_or_else(|| panic!("{} has no inverse row", row.spelling));
assert_ne!(inverse.spelling, row.spelling);
assert_eq!(inverse.inverse, row.spelling);
}
}
}