charon_lib/transform/remove_arithmetic_overflow_checks.rs
1//! # Micro-pass: remove the overflow checks for arithmetic operations we couldn't remove in
2//! [`remove_dynamic_checks`]. See comments there for more details.
3use crate::transform::TransformCtx;
4use crate::ullbc_ast::*;
5
6use super::ctx::UllbcPass;
7
8pub struct Transform;
9
10impl Transform {
11 /// After `remove_dynamic_checks`, the only remaining dynamic checks are overflow checks. We
12 /// couldn't remove them in ullbc because the generated code spans two basic blocks. They are
13 /// inserted only in constants since we otherwise compile in release mode. These assertions
14 /// look like:
15 /// ```text
16 /// r := x checked.+ y;
17 /// assert(move r.1 == false);
18 /// z := move r.0;
19 /// ```
20 /// We replace that with:
21 /// ```text
22 /// z := x + y;
23 /// ```
24 fn update_statements(seq: &mut [Statement]) {
25 if let [Statement {
26 content:
27 RawStatement::Assign(
28 binop,
29 Rvalue::BinaryOp(
30 op @ (BinOp::CheckedAdd | BinOp::CheckedSub | BinOp::CheckedMul),
31 _,
32 _,
33 ),
34 ),
35 ..
36 }, Statement {
37 content:
38 RawStatement::Assert(Assert {
39 cond: Operand::Move(assert_cond),
40 expected: false,
41 ..
42 }),
43 ..
44 }, Statement {
45 content: RawStatement::Assign(final_value, Rvalue::Use(Operand::Move(assigned))),
46 ..
47 }, ..] = seq
48 {
49 // assigned should be: binop.0
50 // assert_cond should be: binop.1
51 if let Some((sub0, ProjectionElem::Field(FieldProjKind::Tuple(..), fid0))) =
52 assigned.as_projection()
53 && fid0.index() == 0
54 && let Some((sub1, ProjectionElem::Field(FieldProjKind::Tuple(..), fid1))) =
55 assert_cond.as_projection()
56 && fid1.index() == 1
57 && binop.is_local()
58 && *sub0 == *binop
59 && *sub1 == *binop
60 {
61 // Switch to the unchecked operation.
62 *op = match op {
63 BinOp::CheckedAdd => BinOp::Add,
64 BinOp::CheckedSub => BinOp::Sub,
65 BinOp::CheckedMul => BinOp::Mul,
66 _ => unreachable!(),
67 };
68 // Assign to the correct value in the first statement.
69 std::mem::swap(binop, final_value);
70 // Remove the other two statements.
71 seq[1].content = RawStatement::Nop;
72 seq[2].content = RawStatement::Nop;
73 }
74 }
75 }
76}
77
78impl UllbcPass for Transform {
79 fn transform_body(&self, _ctx: &mut TransformCtx, b: &mut ExprBody) {
80 b.transform_sequences(|_, seq| {
81 Transform::update_statements(seq);
82 Vec::new()
83 })
84 }
85}