// Per-class facts the e-graph caches alongside the classes themselves:
// integer width and the bounds derived from it.
//
// These ride along with merging rather than driving it -- a merge has to
// reconcile them, which is why `note_type_conflict` lives here and is
// called from the merge path in `egraph.mbt`.
///|
/// Add an e-node with type information
/// bits: the bit width of the value (8, 16, 32, 64, 128)
pub fn EGraph::add_typed(self : EGraph, node : ENode, bits : Int) -> EClassId {
let class_id = self.add(node)
self.set_type(class_id, bits)
class_id
}
///|
/// Poison a class's width information: drop the entry, remember the class,
/// and record the observation. Idempotent per class root.
fn EGraph::note_type_conflict(self : EGraph, root : Int) -> Unit {
self.type_map.remove(root)
if !self.type_conflicts.contains(root) {
self.type_conflicts.add(root)
self.type_conflict_hits = self.type_conflict_hits + 1
}
}
///|
/// Set the type (bit width) for an equivalence class.
/// A conflicting assignment poisons the class: it reports no width from
/// then on, so width-dependent rules skip it rather than rewriting with
/// whichever width happened to be recorded first.
pub fn EGraph::set_type(self : EGraph, id : EClassId, bits : Int) -> Unit {
let canonical = self.find(id).0
match self.type_map.get(canonical) {
None =>
if !self.type_conflicts.contains(canonical) {
self.type_map.set(canonical, bits)
}
Some(existing) => if existing != bits { self.note_type_conflict(canonical) }
}
}
///|
/// Number of width-conflict observations so far (diagnostic).
pub fn EGraph::type_conflict_count(self : EGraph) -> Int {
self.type_conflict_hits
}
///|
/// Number of constant-cache conflict observations so far (diagnostic).
/// A nonzero value means some rule merged classes carrying different
/// constants — an unsound union whose constant is deliberately not
/// harvested.
pub fn EGraph::const_conflict_count(self : EGraph) -> Int {
self.const_conflict_hits
}
///|
/// Get the type (bit width) for an equivalence class
/// Returns None if no type information is available
pub fn EGraph::get_bits(self : EGraph, id : EClassId) -> Int? {
let canonical = self.find(id).0
self.type_map.get(canonical)
}
///|
/// Get the maximum unsigned value for a given bit width
pub fn ty_umax(bits : Int) -> Int64 {
match bits {
8 => 0xFFL
16 => 0xFFFFL
32 => 0xFFFF_FFFFL
64 => -1L // 0xFFFF_FFFF_FFFF_FFFF
_ => -1L
}
}
///|
const I32_SMIN_I64 : Int64 = -2147483648L
///|
const I32_SMAX_I64 : Int64 = 2147483647L
///|
/// Get the minimum signed value for a given bit width
pub fn ty_smin(bits : Int) -> Int64 {
match bits {
8 => -128L
16 => -32768L
32 => I32_SMIN_I64
64 => -9223372036854775808L
_ => -9223372036854775808L
}
}
///|
/// Get the maximum signed value for a given bit width
pub fn ty_smax(bits : Int) -> Int64 {
match bits {
8 => 127L
16 => 32767L
32 => I32_SMAX_I64
64 => 9223372036854775807L
_ => 9223372036854775807L
}
}