Skip to content

Commit

Permalink
Add size_of annotation to help CBMC's allocator
Browse files Browse the repository at this point in the history
CBMC's heap allocator will produce objects that aren't just a byte array
when a prior call to `size_of` was involved in computing the number of
bytes to be allocated via malloc/calloc/realloc. This, in turn, permits
type-safe member accesses to heap-allocated objects. This should speed
up copying as well as all other operations on the resulting object
(which will then not have to go through byte extract/byte update
operations).

It also seems that there no longer is a "size_of" intrinsic.

Fixes: #1286
Fixes: #1781
  • Loading branch information
tautschnig committed Apr 21, 2023
1 parent c7b38c2 commit d3bc70b
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 55 deletions.
25 changes: 23 additions & 2 deletions cprover_bindings/src/goto_program/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,16 @@ use std::fmt::Debug;
///////////////////////////////////////////////////////////////////////////////////////////////

/// An `Expr` represents an expression type: i.e. a computation that returns a value.
/// Every expression has a type, a value, and a location (which may be `None`).
/// Every expression has a type, a value, and a location (which may be `None`). An expression may
/// also include a type annotation (`size_of_annotation`).
///
/// The `size_of_annotation` is eventually picked up by CBMC's symbolic execution when simulating
/// heap allocations: for a requested allocation of N bytes, CBMC can either create a byte array of
/// size N, or, when a type T is annotated and N is a multiple of the size of T, an array of
/// N/size_of(T) elements. The latter will facilitate updates using member operations (when T is an
/// aggregate type), and pointer-typed members can be tracked more precisely. Note that this is
/// merely a hint: failing to provide such an annotation may hamper performance, but will never
/// affect correctness.
///
/// The fields of `Expr` are kept private, and there are no getters that return mutable references.
/// This means that the only way to create and update `Expr`s is using the constructors and setters.
Expand All @@ -41,6 +50,7 @@ pub struct Expr {
value: Box<ExprValue>,
typ: Type,
location: Location,
size_of_annotation: Option<Type>,
}

/// The different kinds of values an expression can have.
Expand Down Expand Up @@ -293,6 +303,10 @@ impl Expr {
&self.value
}

pub fn size_of_annotation(&self) -> Option<&Type> {
self.size_of_annotation.as_ref()
}

/// If the expression is an Int constant type, return its value
pub fn int_constant_value(&self) -> Option<BigInt> {
match &*self.value {
Expand Down Expand Up @@ -404,12 +418,19 @@ impl Expr {
}
}

impl Expr {
pub fn with_size_of_annotation(mut self, ty: Type) -> Self {
self.size_of_annotation = Some(ty);
self
}
}

/// Private constructor. Making this a macro allows multiple reference to self in the same call.
macro_rules! expr {
( $value:expr, $typ:expr) => {{
let typ = $typ;
let value = Box::new($value);
Expr { value, typ, location: Location::none() }
Expr { value, typ, location: Location::none(), size_of_annotation: None }
}};
}

Expand Down
4 changes: 4 additions & 0 deletions cprover_bindings/src/irep/to_irep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ impl ToIrep for Expr {
} else {
self.value().to_irep(mm).with_location(self.location(), mm).with_type(self.typ(), mm)
}
.with_named_sub_option(
IrepId::CCSizeofType,
self.size_of_annotation().map(|ty| ty.to_irep(mm)),
)
}
}

Expand Down
9 changes: 5 additions & 4 deletions kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ impl<'tcx> GotocCtx<'tcx> {
}};
}

// Intrinsics which encode a value known during compilation (e.g., `size_of`)
// Intrinsics which encode a value known during compilation
macro_rules! codegen_intrinsic_const {
() => {{
let value = self
Expand Down Expand Up @@ -611,7 +611,7 @@ impl<'tcx> GotocCtx<'tcx> {
loc,
),
"simd_xor" => codegen_intrinsic_binop!(bitxor),
"size_of" => codegen_intrinsic_const!(),
"size_of" => unreachable!(),
"size_of_val" => codegen_size_align!(size),
"sqrtf32" => unstable_codegen!(codegen_simple_intrinsic!(Sqrtf)),
"sqrtf64" => unstable_codegen!(codegen_simple_intrinsic!(Sqrt)),
Expand Down Expand Up @@ -1267,11 +1267,12 @@ impl<'tcx> GotocCtx<'tcx> {
/// This function computes the size and alignment of a dynamically-sized type.
/// The implementations follows closely the SSA implementation found in
/// `rustc_codegen_ssa::glue::size_and_align_of_dst`.
fn size_and_align_of_dst(&self, t: Ty<'tcx>, arg: Expr) -> SizeAlign {
fn size_and_align_of_dst(&mut self, t: Ty<'tcx>, arg: Expr) -> SizeAlign {
let layout = self.layout_of(t);
let usizet = Type::size_t();
if !layout.is_unsized() {
let size = Expr::int_constant(layout.size.bytes_usize(), Type::size_t());
let size = Expr::int_constant(layout.size.bytes_usize(), Type::size_t())
.with_size_of_annotation(self.codegen_ty(t));
let align = Expr::int_constant(layout.align.abi.bytes(), usizet);
return SizeAlign { size, align };
}
Expand Down
3 changes: 2 additions & 1 deletion kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,8 @@ impl<'tcx> GotocCtx<'tcx> {
let t = self.monomorphize(*t);
let layout = self.layout_of(t);
match k {
NullOp::SizeOf => Expr::int_constant(layout.size.bytes_usize(), Type::size_t()),
NullOp::SizeOf => Expr::int_constant(layout.size.bytes_usize(), Type::size_t())
.with_size_of_annotation(self.codegen_ty(t)),
NullOp::AlignOf => Expr::int_constant(layout.align.abi.bytes(), Type::size_t()),
}
}
Expand Down
2 changes: 0 additions & 2 deletions tests/kani/Drop/drop_after_moving_across_channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

//! This test checks whether dropping objects passed through
//! std::sync::mpsc::channel is handled.
//! This test only passes on MacOS today, so we duplicate the test for now.
#![cfg(target_os = "macos")]

use std::sync::mpsc::*;

Expand Down
46 changes: 0 additions & 46 deletions tests/kani/Drop/fixme_drop_after_moving_across_channel.rs

This file was deleted.

0 comments on commit d3bc70b

Please sign in to comment.