Skip to content

Commit b5c116d

Browse files
authored
Add size_of annotation to help CBMC's allocator (rust-lang#2395)
1 parent 939a29f commit b5c116d

File tree

6 files changed

+46
-61
lines changed

6 files changed

+46
-61
lines changed

Diff for: cprover_bindings/src/goto_program/expr.rs

+24-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,17 @@ use std::fmt::Debug;
1919
///////////////////////////////////////////////////////////////////////////////////////////////
2020

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

4657
/// The different kinds of values an expression can have.
@@ -293,6 +304,10 @@ impl Expr {
293304
&self.value
294305
}
295306

307+
pub fn size_of_annotation(&self) -> Option<&Type> {
308+
self.size_of_annotation.as_ref()
309+
}
310+
296311
/// If the expression is an Int constant type, return its value
297312
pub fn int_constant_value(&self) -> Option<BigInt> {
298313
match &*self.value {
@@ -404,12 +419,19 @@ impl Expr {
404419
}
405420
}
406421

422+
impl Expr {
423+
pub fn with_size_of_annotation(mut self, ty: Type) -> Self {
424+
self.size_of_annotation = Some(ty);
425+
self
426+
}
427+
}
428+
407429
/// Private constructor. Making this a macro allows multiple reference to self in the same call.
408430
macro_rules! expr {
409431
( $value:expr, $typ:expr) => {{
410432
let typ = $typ;
411433
let value = Box::new($value);
412-
Expr { value, typ, location: Location::none() }
434+
Expr { value, typ, location: Location::none(), size_of_annotation: None }
413435
}};
414436
}
415437

Diff for: cprover_bindings/src/irep/to_irep.rs

+4
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,10 @@ impl ToIrep for Expr {
162162
} else {
163163
self.value().to_irep(mm).with_location(self.location(), mm).with_type(self.typ(), mm)
164164
}
165+
.with_named_sub_option(
166+
IrepId::CCSizeofType,
167+
self.size_of_annotation().map(|ty| ty.to_irep(mm)),
168+
)
165169
}
166170
}
167171

Diff for: kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs

+13-8
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ impl<'tcx> GotocCtx<'tcx> {
288288
}};
289289
}
290290

291-
// Intrinsics which encode a value known during compilation (e.g., `size_of`)
291+
// Intrinsics which encode a value known during compilation
292292
macro_rules! codegen_intrinsic_const {
293293
() => {{
294294
let value = self
@@ -611,7 +611,7 @@ impl<'tcx> GotocCtx<'tcx> {
611611
loc,
612612
),
613613
"simd_xor" => codegen_intrinsic_binop!(bitxor),
614-
"size_of" => codegen_intrinsic_const!(),
614+
"size_of" => unreachable!(),
615615
"size_of_val" => codegen_size_align!(size),
616616
"sqrtf32" => unstable_codegen!(codegen_simple_intrinsic!(Sqrtf)),
617617
"sqrtf64" => unstable_codegen!(codegen_simple_intrinsic!(Sqrt)),
@@ -1183,7 +1183,8 @@ impl<'tcx> GotocCtx<'tcx> {
11831183
let dst = fargs.remove(0).cast_to(Type::void_pointer());
11841184
let val = fargs.remove(0).cast_to(Type::void_pointer());
11851185
let layout = self.layout_of(ty);
1186-
let sz = Expr::int_constant(layout.size.bytes(), Type::size_t());
1186+
let sz = Expr::int_constant(layout.size.bytes(), Type::size_t())
1187+
.with_size_of_annotation(self.codegen_ty(ty));
11871188
let e = BuiltinFn::Memcmp
11881189
.call(vec![dst, val, sz], loc)
11891190
.eq(Type::c_int().zero())
@@ -1267,11 +1268,12 @@ impl<'tcx> GotocCtx<'tcx> {
12671268
/// This function computes the size and alignment of a dynamically-sized type.
12681269
/// The implementations follows closely the SSA implementation found in
12691270
/// `rustc_codegen_ssa::glue::size_and_align_of_dst`.
1270-
fn size_and_align_of_dst(&self, t: Ty<'tcx>, arg: Expr) -> SizeAlign {
1271+
fn size_and_align_of_dst(&mut self, t: Ty<'tcx>, arg: Expr) -> SizeAlign {
12711272
let layout = self.layout_of(t);
12721273
let usizet = Type::size_t();
12731274
if !layout.is_unsized() {
1274-
let size = Expr::int_constant(layout.size.bytes_usize(), Type::size_t());
1275+
let size = Expr::int_constant(layout.size.bytes_usize(), Type::size_t())
1276+
.with_size_of_annotation(self.codegen_ty(t));
12751277
let align = Expr::int_constant(layout.align.abi.bytes(), usizet);
12761278
return SizeAlign { size, align };
12771279
}
@@ -1294,6 +1296,7 @@ impl<'tcx> GotocCtx<'tcx> {
12941296
// The info in this case is the length of the str, so the size is that
12951297
// times the unit size.
12961298
let size = Expr::int_constant(unit.size.bytes_usize(), Type::size_t())
1299+
.with_size_of_annotation(self.codegen_ty(*unit_t))
12971300
.mul(arg.member("len", &self.symbol_table));
12981301
let align = Expr::int_constant(layout.align.abi.bytes(), usizet);
12991302
SizeAlign { size, align }
@@ -1316,7 +1319,8 @@ impl<'tcx> GotocCtx<'tcx> {
13161319
// FIXME: We assume they are aligned according to the machine-preferred alignment given by layout abi.
13171320
let n = layout.fields.count() - 1;
13181321
let sized_size =
1319-
Expr::int_constant(layout.fields.offset(n).bytes(), Type::size_t());
1322+
Expr::int_constant(layout.fields.offset(n).bytes(), Type::size_t())
1323+
.with_size_of_annotation(self.codegen_ty(t));
13201324
let sized_align = Expr::int_constant(layout.align.abi.bytes(), Type::size_t());
13211325

13221326
// Call this function recursively to compute the size and align for the last field.
@@ -1724,7 +1728,7 @@ impl<'tcx> GotocCtx<'tcx> {
17241728
/// * The result expression of the computation.
17251729
/// * An assertion statement to ensure the operation has not overflowed.
17261730
fn count_in_bytes(
1727-
&self,
1731+
&mut self,
17281732
count: Expr,
17291733
ty: Ty<'tcx>,
17301734
res_ty: Type,
@@ -1733,7 +1737,8 @@ impl<'tcx> GotocCtx<'tcx> {
17331737
) -> (Expr, Stmt) {
17341738
assert!(res_ty.is_integer());
17351739
let layout = self.layout_of(ty);
1736-
let size_of_elem = Expr::int_constant(layout.size.bytes(), res_ty);
1740+
let size_of_elem = Expr::int_constant(layout.size.bytes(), res_ty)
1741+
.with_size_of_annotation(self.codegen_ty(ty));
17371742
let size_of_count_elems = count.mul_overflow(size_of_elem);
17381743
let message =
17391744
format!("{intrinsic}: attempt to compute number in bytes which would overflow");

Diff for: kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,8 @@ impl<'tcx> GotocCtx<'tcx> {
509509
let t = self.monomorphize(*t);
510510
let layout = self.layout_of(t);
511511
match k {
512-
NullOp::SizeOf => Expr::int_constant(layout.size.bytes_usize(), Type::size_t()),
512+
NullOp::SizeOf => Expr::int_constant(layout.size.bytes_usize(), Type::size_t())
513+
.with_size_of_annotation(self.codegen_ty(t)),
513514
NullOp::AlignOf => Expr::int_constant(layout.align.abi.bytes(), Type::size_t()),
514515
}
515516
}
@@ -1053,11 +1054,12 @@ impl<'tcx> GotocCtx<'tcx> {
10531054
/// When we get the size and align of a ty::Ref, the TyCtxt::layout_of
10541055
/// returns the correct size to match rustc vtable values. Checked via
10551056
/// Kani-compile-time and CBMC assertions in check_vtable_size.
1056-
fn codegen_vtable_size_and_align(&self, operand_type: Ty<'tcx>) -> (Expr, Expr) {
1057+
fn codegen_vtable_size_and_align(&mut self, operand_type: Ty<'tcx>) -> (Expr, Expr) {
10571058
debug!("vtable_size_and_align {:?}", operand_type.kind());
10581059
let vtable_layout = self.layout_of(operand_type);
10591060
assert!(!vtable_layout.is_unsized(), "Can't create a vtable for an unsized type");
1060-
let vt_size = Expr::int_constant(vtable_layout.size.bytes(), Type::size_t());
1061+
let vt_size = Expr::int_constant(vtable_layout.size.bytes(), Type::size_t())
1062+
.with_size_of_annotation(self.codegen_ty(operand_type));
10611063
let vt_align = Expr::int_constant(vtable_layout.align.abi.bytes(), Type::size_t());
10621064

10631065
(vt_size, vt_align)

Diff for: tests/kani/Drop/drop_after_moving_across_channel.rs

-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33

44
//! This test checks whether dropping objects passed through
55
//! std::sync::mpsc::channel is handled.
6-
//! This test only passes on MacOS today, so we duplicate the test for now.
7-
#![cfg(target_os = "macos")]
86
97
use std::sync::mpsc::*;
108

Diff for: tests/kani/Drop/fixme_drop_after_moving_across_channel.rs

-46
This file was deleted.

0 commit comments

Comments
 (0)