machine_check_common/iir/
stmt.rs

1use std::fmt::Debug;
2
3use mck::{
4    abstr::{AbstractValue, BitvectorDomain},
5    misc::BitvectorBound,
6    ParamValuation,
7};
8use serde::{Deserialize, Serialize};
9
10use crate::{
11    iir::{
12        context::{IContext, IFnContext},
13        expr::IExpr,
14        func::IBlock,
15        ty::{IElementaryType, IGeneralType, IType},
16        variable::IVarId,
17        IAbstr, IRefin,
18    },
19    ir_common::IrReference,
20};
21
22#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
23pub enum IStmt {
24    Assign(IAssignStmt),
25    If(IIfStmt),
26}
27
28impl IStmt {
29    pub fn forward_interpret(&self, context: &IFnContext, abstr: &mut IAbstr) {
30        match self {
31            IStmt::Assign(stmt_assign) => stmt_assign.forward_interpret(context, abstr),
32            IStmt::If(stmt_if) => stmt_if.forward_interpret(context, abstr),
33        }
34    }
35
36    pub fn backward_interpret(&self, context: &IFnContext, abstr: &IAbstr, refin: &mut IRefin) {
37        match self {
38            IStmt::Assign(stmt_assign) => stmt_assign.backward_interpret(context, abstr, refin),
39            IStmt::If(stmt_if) => stmt_if.backward_interpret(context, abstr, refin),
40        }
41    }
42}
43
44#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
45pub struct IAssignStmt {
46    pub left: IVarId,
47    pub right: IExpr,
48}
49
50impl IAssignStmt {
51    fn forward_interpret(&self, context: &IFnContext, abstr: &mut IAbstr) {
52        let left_var_id = self.left;
53        if let Some(left_value) = self.right.forward_interpret(context, abstr) {
54            let left_type = &context
55                .func
56                .variables
57                .get(&left_var_id)
58                .expect("Function variable should have type")
59                .ty;
60
61            ensure_abstract_general_type(context.context, &left_value, left_type);
62
63            abstr.insert_value(left_var_id, left_value);
64        }
65    }
66
67    pub fn backward_interpret(&self, context: &IFnContext, abstr: &IAbstr, refin: &mut IRefin) {
68        // when interpreting backwards, we take the later (left) refinement value
69        // and the earlier (right) abstract values and process them
70        // to arrive at the earlier (right) refinement values
71
72        // in the statement, we just take the later refinement value and move it into the expression
73
74        let left_ident = self.left;
75        if let Some(later_refinement_value) = refin.value_opt(left_ident) {
76            self.right
77                .backward_interpret(context, abstr, refin, later_refinement_value.clone());
78        }
79    }
80}
81
82#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
83pub struct IIfStmt {
84    pub condition: IVarId,
85    pub then_block: IBlock,
86    pub else_block: IBlock,
87}
88
89impl IIfStmt {
90    fn forward_interpret(&self, context: &IFnContext, abstr: &mut IAbstr) {
91        let (can_take_then, can_take_else) = self.can_take_then_else(abstr);
92        if can_take_then {
93            self.then_block.forward_interpret(context, abstr);
94        }
95        if can_take_else {
96            self.else_block.forward_interpret(context, abstr);
97        }
98    }
99
100    pub fn backward_interpret(&self, context: &IFnContext, abstr: &IAbstr, refin: &mut IRefin) {
101        let (can_take_then, can_take_else) = self.can_take_then_else(abstr);
102        if can_take_then {
103            self.then_block.backward_interpret(context, abstr, refin);
104        }
105        if can_take_else {
106            self.else_block.backward_interpret(context, abstr, refin);
107        }
108    }
109
110    fn can_take_then_else(&self, abstr: &IAbstr) -> (bool, bool) {
111        let condition_value = abstr.value(self.condition);
112
113        let AbstractValue::Boolean(condition_value) = condition_value else {
114            panic!("Condition value should be bool");
115        };
116
117        let condition_value = condition_value.value();
118
119        let can_take_then = matches!(
120            condition_value,
121            ParamValuation::True | ParamValuation::Unknown | ParamValuation::Dependent
122        );
123        let can_take_else = matches!(
124            condition_value,
125            ParamValuation::False | ParamValuation::Unknown | ParamValuation::Dependent
126        );
127
128        (can_take_then, can_take_else)
129    }
130}
131
132impl Debug for IStmt {
133    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134        match self {
135            IStmt::Assign(assign_stmt) => assign_stmt.fmt(f),
136            IStmt::If(if_stmt) => if_stmt.fmt(f),
137        }
138    }
139}
140
141impl Debug for IAssignStmt {
142    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143        write!(f, "{:?} = {:?}", self.left, self.right)
144    }
145}
146
147impl Debug for IIfStmt {
148    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149        write!(f, "if {:?} ", self.condition)?;
150
151        let mut franz = f.debug_set();
152        for stmt in &self.then_block.stmts {
153            franz.entry(stmt);
154        }
155        franz.finish()?;
156
157        write!(f, " else ")?;
158
159        let mut franz = f.debug_set();
160        for stmt in &self.else_block.stmts {
161            franz.entry(stmt);
162        }
163        franz.finish()?;
164
165        Ok(())
166    }
167}
168
169fn ensure_abstract_general_type(context: &IContext, value: &AbstractValue, ty: &IGeneralType) {
170    match ty {
171        IGeneralType::Normal(ty) => ensure_abstract_type(context, value, ty),
172        IGeneralType::PanicResult(ty) => {
173            let AbstractValue::Struct(fields) = value else {
174                panic!("Expected panic result type of value (represented by fields)");
175            };
176            assert_eq!(fields.len(), 2);
177            ensure_abstract_type(context, &fields[0], ty);
178            ensure_abstract_type(
179                context,
180                &fields[1],
181                &IType {
182                    reference: IrReference::None,
183                    inner: IElementaryType::Bitvector(32),
184                },
185            );
186        }
187        IGeneralType::PhiArg(ty) => ensure_abstract_type(context, value, ty),
188    }
189}
190
191fn ensure_abstract_type(context: &IContext, value: &AbstractValue, ty: &IType) {
192    match &ty.inner {
193        IElementaryType::Bitvector(width) => {
194            let AbstractValue::Bitvector(bitvector) = value else {
195                panic!("Expected bitvector type of value");
196            };
197
198            assert_eq!(*width, bitvector.bound().width());
199        }
200        IElementaryType::Array(array_type) => {
201            let AbstractValue::Array(array) = value else {
202                panic!("Expected array type of value");
203            };
204
205            assert_eq!(array_type.index_width, array.index_bound().width());
206            assert_eq!(array_type.element_width, array.element_bound().width());
207        }
208        IElementaryType::Boolean => {
209            let AbstractValue::Boolean(_) = value else {
210                panic!("Expected boolean type of value");
211            };
212        }
213        IElementaryType::Struct(struct_id) => {
214            let AbstractValue::Struct(fields) = value else {
215                panic!("Expected struct type of value");
216            };
217
218            let struct_data = context.struct_with_id(*struct_id);
219            assert_eq!(fields.len(), struct_data.fields.len());
220
221            for (value, ty) in fields.iter().zip(struct_data.fields.values()) {
222                ensure_abstract_type(
223                    context,
224                    value,
225                    &IType {
226                        reference: IrReference::None,
227                        inner: ty.clone(),
228                    },
229                );
230            }
231        }
232    }
233}