Skip to content

Commit

Permalink
Reimplement remove_dynamic_checks on ullbc
Browse files Browse the repository at this point in the history
  • Loading branch information
Nadrieril committed Apr 17, 2024
1 parent 10a1ca9 commit 6c6b114
Show file tree
Hide file tree
Showing 16 changed files with 269 additions and 347 deletions.
17 changes: 8 additions & 9 deletions charon/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use crate::get_mir::MirLevel;
use crate::reorder_decls;
use crate::transform::{
index_to_function_calls, insert_assign_return_unit, ops_to_function_calls, reconstruct_asserts,
remove_drop_never, remove_dynamic_checks, remove_nops, remove_read_discriminant,
remove_unused_locals, simplify_constants, update_closure_signatures,
remove_drop_never, remove_dynamic_checks, remove_nops, remove_overflow_tuples,
remove_read_discriminant, remove_unused_locals, simplify_constants, update_closure_signatures,
};
use crate::translate_crate_to_ullbc;
use crate::translate_ctx;
Expand Down Expand Up @@ -230,6 +230,10 @@ pub fn translate(
// we simply apply some micro-passes to make the code cleaner, before
// serializing the result.

// # Micro-pass: Remove overflow/div-by-zero/bounds checks since they are already part of the
// arithmetic/array operation in the semantics of (U)LLBC.
remove_dynamic_checks::transform(&mut ctx);

// # Micro-pass: desugar the constants to other values/operands as much
// as possible.
simplify_constants::transform(&mut ctx);
Expand Down Expand Up @@ -263,13 +267,8 @@ pub fn translate(
// which ignores this first variable. This micro-pass updates this.
update_closure_signatures::transform(&ctx, &mut llbc_funs);

// # Micro-pass: remove the dynamic checks for array/slice bounds
// and division by zero.
// **WARNING**: this pass uses the fact that the dynamic checks
// introduced by Rustc use a special "assert" construct. Because of
// this, it must happen *before* the [reconstruct_asserts] pass.
// See the comments in [crate::remove_dynamic_checks].
remove_dynamic_checks::transform(&mut ctx, &mut llbc_funs, &mut llbc_globals);
// # Micro-pass: clean up the tuples left over from `remove_dynamic_checks`.
remove_overflow_tuples::transform(&mut ctx, &mut llbc_funs, &mut llbc_globals);

// # Micro-pass: reconstruct the asserts
reconstruct_asserts::transform(&mut ctx, &mut llbc_funs, &mut llbc_globals);
Expand Down
1 change: 1 addition & 0 deletions charon/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#![recursion_limit = "256"]
#![feature(trait_alias)]
#![feature(let_chains)]
#![feature(if_let_guard)]
#![feature(iterator_try_collect)]

extern crate rustc_driver;
Expand Down
1 change: 1 addition & 0 deletions charon/src/transform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub mod reconstruct_asserts;
pub mod remove_drop_never;
pub mod remove_dynamic_checks;
pub mod remove_nops;
pub mod remove_overflow_tuples;
pub mod remove_read_discriminant;
pub mod remove_unused_locals;
pub mod simplify_constants;
Expand Down
446 changes: 128 additions & 318 deletions charon/src/transform/remove_dynamic_checks.rs

Large diffs are not rendered by default.

117 changes: 117 additions & 0 deletions charon/src/transform/remove_overflow_tuples.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//! # Micro-pass: After the `remove_dynamic_checks` pass, the addition, subtraction and
//! multiplication operations can look like (depending on release mode ornot):
//! tuple.0 := copy x + copy y
//! assert(..)
//! result := move (tuple.0)
//! Moreover we know that this tuple isn't used anywhere else. We simplify this to:
//! result := copy x + copy y
//! This is the only remaining use of `assert` in ullbc.
use std::mem;

use crate::formatter::{Formatter, IntoFormatter};
use crate::llbc_ast::*;
use crate::translate_ctx::{error_assert_then, error_or_panic, TransCtx};
use crate::types::*;
use take_mut::take;

struct RemoveOverflowTuples<'tcx, 'ctx, 'a> {
/// We use the context for error reporting
ctx: &'a mut TransCtx<'tcx, 'ctx>,
}

impl<'tcx, 'ctx, 'a> MutTypeVisitor for RemoveOverflowTuples<'tcx, 'ctx, 'a> {}
impl<'tcx, 'ctx, 'a> MutExprVisitor for RemoveOverflowTuples<'tcx, 'ctx, 'a> {}

impl<'tcx, 'ctx, 'a> RemoveOverflowTuples<'tcx, 'ctx, 'a> {
fn simplify(&mut self, s: &mut Statement) {
let RawStatement::Sequence(s0, s1) = &mut s.content else { return };
let RawStatement::Sequence(s1, s2) = &mut s1.content else { return };
let s2_inner = if let RawStatement::Sequence(s2, _) = &mut s2.content {
s2
} else {
s2
};

let RawStatement::Assert(..) = &s1.content else { return };

// The `assert` must be preceded by:
// tuple.0 := copy x + copy y
// and followed by:
// result := move (tuple.0)
if let RawStatement::Assign(tuple_field, Rvalue::BinaryOp(BinOp::Add | BinOp::Sub | BinOp::Mul, _, _),) = &mut s0.content
&& let RawStatement::Assign(result, Rvalue::Use(Operand::Move(assign_from))) = &mut s2_inner.content
&& tuple_field == assign_from
&& let [ProjectionElem::Field(FieldProjKind::Tuple(2), p_id)] = tuple_field.projection.as_slice()
&& p_id.index() == 0
{
// Set `s0` to `result := copy x + copy y`.
mem::swap(tuple_field, result);
// Set `s1_inner` to `result := copy x + copy y`.
mem::swap(s0, s2_inner);
// Discard `s0` and `s1`.
take(s, |s| {
let (_s0, s1) = s.content.to_sequence();
let (_s1, s2) = s1.content.to_sequence();
*s2
});
} else {
// The only possible use of `assert` is this.
let span = s0.meta.span;
let fmt_ctx = self.ctx.into_fmt();
let msg = format!("Overflowing binary operation generated unexpected code:\n{}", s.fmt_with_ctx("", &fmt_ctx));
error_or_panic!(
self.ctx,
span,
msg,
return
);
}
}
}

impl<'tcx, 'ctx, 'a> MutAstVisitor for RemoveOverflowTuples<'tcx, 'ctx, 'a> {
fn spawn(&mut self, visitor: &mut dyn FnMut(&mut Self)) {
visitor(self)
}

fn merge(&mut self) {}

fn visit_statement(&mut self, s: &mut Statement) {
// These should have been removed in the `remove_dynamic_checks` pass.
error_assert_then!(
self.ctx,
s.meta.span,
!s.content.is_assert(),
return,
"Found an assert which was not simplified"
);
if s.content.is_assign() {
let (_, rv) = s.content.as_assign();
error_assert_then!(
self.ctx,
s.meta.span,
!rv.is_len(),
return,
"Found an occurrence of Len which was not simplified"
);
}

// Simplify this statement.
self.simplify(s);
// Continue simplifying.
self.default_visit_raw_statement(&mut s.content);
}
}

pub fn transform(ctx: &mut TransCtx, funs: &mut FunDecls, globals: &mut GlobalDecls) {
ctx.iter_bodies(funs, globals, |ctx, name, b| {
let fmt_ctx = ctx.into_fmt();
trace!(
"# About to remove the overflow tuples: {}:\n{}",
name.fmt_with_ctx(&fmt_ctx),
fmt_ctx.format_object(&*b)
);
let mut visitor = RemoveOverflowTuples { ctx };
visitor.visit_statement(&mut b.body);
})
}
2 changes: 1 addition & 1 deletion charon/tests/ui/closures.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[ INFO charon_lib::driver:332] [translate]: # Final LLBC before serialization:
[ INFO charon_lib::driver:331] [translate]: # Final LLBC before serialization:

fn test_crate::incr_u32(@1: u32) -> u32
{
Expand Down
2 changes: 1 addition & 1 deletion charon/tests/ui/issue-114-opaque-bodies.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[ INFO charon_lib::driver:332] [translate]: # Final LLBC before serialization:
[ INFO charon_lib::driver:331] [translate]: # Final LLBC before serialization:

fn issue_114_opaque_bodies_aux::inline_always() -> u32
{
Expand Down
2 changes: 1 addition & 1 deletion charon/tests/ui/issue-118-generic-copy.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[ INFO charon_lib::driver:332] [translate]: # Final LLBC before serialization:
[ INFO charon_lib::driver:331] [translate]: # Final LLBC before serialization:

struct test_crate::Foo = {}

Expand Down
2 changes: 1 addition & 1 deletion charon/tests/ui/issue-4-traits.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[ INFO charon_lib::driver:332] [translate]: # Final LLBC before serialization:
[ INFO charon_lib::driver:331] [translate]: # Final LLBC before serialization:

enum core::result::Result<T, E> =
| Ok(T)
Expand Down
2 changes: 1 addition & 1 deletion charon/tests/ui/issue-45-misc.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[ INFO charon_lib::driver:332] [translate]: # Final LLBC before serialization:
[ INFO charon_lib::driver:331] [translate]: # Final LLBC before serialization:

fn test_crate::map::closure<'_0>(@1: &'_0 mut (()), @2: (i32)) -> i32
{
Expand Down
2 changes: 1 addition & 1 deletion charon/tests/ui/issue-72-hash-missing-impl.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[ INFO charon_lib::driver:332] [translate]: # Final LLBC before serialization:
[ INFO charon_lib::driver:331] [translate]: # Final LLBC before serialization:

trait test_crate::Hasher<Self>

Expand Down
2 changes: 1 addition & 1 deletion charon/tests/ui/issue-73-extern.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[ INFO charon_lib::driver:332] [translate]: # Final LLBC before serialization:
[ INFO charon_lib::driver:331] [translate]: # Final LLBC before serialization:

unsafe fn test_crate::foo(@1: i32)

Expand Down
2 changes: 1 addition & 1 deletion charon/tests/ui/issue-92-nonpositive-variant-indices.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[ INFO charon_lib::driver:332] [translate]: # Final LLBC before serialization:
[ INFO charon_lib::driver:331] [translate]: # Final LLBC before serialization:

enum test_crate::Ordering =
| Less()
Expand Down
2 changes: 1 addition & 1 deletion charon/tests/ui/issue-97-missing-parent-item-clause.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[ INFO charon_lib::driver:332] [translate]: # Final LLBC before serialization:
[ INFO charon_lib::driver:331] [translate]: # Final LLBC before serialization:

trait test_crate::Ord<Self>

Expand Down
14 changes: 4 additions & 10 deletions charon/tests/ui/remove-dynamic-checks.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[ INFO charon_lib::driver:332] [translate]: # Final LLBC before serialization:
[ INFO charon_lib::driver:331] [translate]: # Final LLBC before serialization:

fn test_crate::neg_test(@1: i32) -> i32
{
Expand Down Expand Up @@ -393,16 +393,10 @@ global test_crate::div_signed_with_constant::FOO {
fn test_crate::div_signed_with_constant() -> i32
{
let @0: i32; // return
let @1: bool; // anonymous local
let @2: bool; // anonymous local
let @3: i32; // anonymous local
let @4: i32; // anonymous local
let @1: i32; // anonymous local

@1 := const (2 : i32) == const (-1 : i32)
@3 := test_crate::div_signed_with_constant::FOO
@2 := move (@3) == const (-2147483648 : i32)
@4 := test_crate::div_signed_with_constant::FOO
@0 := move (@4) / const (2 : i32)
@1 := test_crate::div_signed_with_constant::FOO
@0 := move (@1) / const (2 : i32)
return
}

Expand Down
2 changes: 1 addition & 1 deletion charon/tests/ui/unsupported/option-is_some.out
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
error: A discriminant read must be followed by a `SwitchInt`
--> /rustc/d59363ad0b6391b7fc5bbb02c9ccf9300eef3753/library/core/src/option.rs:598:5

[ INFO charon_lib::driver:332] [translate]: # Final LLBC before serialization:
[ INFO charon_lib::driver:331] [translate]: # Final LLBC before serialization:

enum core::option::Option<T> =
| None()
Expand Down

0 comments on commit 6c6b114

Please sign in to comment.