Skip to content

Commit 142b1f2

Browse files
celinvaltedinski
authored andcommitted
Create an RMC crate and load it as part of rmc-rustc (rust-lang#597)
* Adding support to new rmc prelude definitions - Create an rmc crate (rust-lang#231). - Used rust compliant names for the functions (rust-lang#589). - Changed rmc-rustc to inject the rmc prelude as part of the compilation as well as other rmc configuration flags. - Added options to rmc-rustc to print binary path and flags so it can be used in other scripts. * Add a script to build rmc library * Update tests to use new injected prelude.
1 parent 3ec6882 commit 142b1f2

File tree

154 files changed

+455
-632
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

154 files changed

+455
-632
lines changed

.github/workflows/rmc.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,12 @@ jobs:
5151
export RUST_BACKTRACE=1
5252
./x.py build -i --stage 1 library/std
5353
54+
- name: Build RMC Library
55+
run: bash -x ./scripts/setup/build_rmc_lib.sh
56+
5457
- name: Execute RMC regression
5558
run: ./scripts/rmc-regression.sh
5659

57-
5860
- name: Install dashboard dependencies
5961
if: ${{ matrix.os == 'ubuntu-20.04' && github.event_name == 'push' && startsWith('refs/heads/main', github.ref) }}
6062
run: ./scripts/setup/install_dashboard_deps.sh

Cargo.lock

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3310,6 +3310,10 @@ dependencies = [
33103310
"rls-span",
33113311
]
33123312

3313+
[[package]]
3314+
name = "rmc"
3315+
version = "0.1.0"
3316+
33133317
[[package]]
33143318
name = "rust-demangler"
33153319
version = "0.0.1"

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ members = [
44
"compiler/rustc",
55
"library/std",
66
"library/test",
7+
"library/rmc",
78
"src/rustdoc-json-types",
89
"src/tools/cargotest",
910
"src/tools/clippy",
@@ -55,7 +56,9 @@ exclude = [
5556
# stdarch has its own Cargo workspace
5657
"library/stdarch",
5758
# dependency tests have their own workspace
58-
"src/test/rmc-dependency-test/dependency3"
59+
"src/test/rmc-dependency-test/dependency3",
60+
# cargo rmc tests should also have their own workspace
61+
"src/test/cargo-rmc"
5962
]
6063

6164
[profile.release.package.compiler_builtins]

compiler/rustc_codegen_rmc/src/overrides/hooks.rs

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,13 @@ use super::stubs::{HashMapStub, VecStub};
1212
use crate::utils::{instance_name_is, instance_name_starts_with};
1313
use crate::GotocCtx;
1414
use cbmc::goto_program::{BuiltinFn, Expr, Location, Stmt, Symbol, Type};
15-
use rustc_hir::definitions::DefPathDataName;
1615
use rustc_middle::mir::{BasicBlock, Place};
1716
use rustc_middle::ty::layout::LayoutOf;
1817
use rustc_middle::ty::print::with_no_trimmed_paths;
1918
use rustc_middle::ty::{self, Instance, InstanceDef, Ty, TyCtxt};
2019
use rustc_span::Span;
2120
use std::rc::Rc;
22-
use tracing::debug;
21+
use tracing::{debug, warn};
2322

2423
pub trait GotocTypeHook<'tcx> {
2524
fn hook_applies(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool;
@@ -66,19 +65,29 @@ fn output_of_instance_is_never<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>
6665
}
6766
}
6867

68+
fn matches_function(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, attr_name: &str) -> bool {
69+
let attr_sym = rustc_span::symbol::Symbol::intern(attr_name);
70+
if let Some(attr_id) = tcx.all_diagnostic_items(()).name_to_id.get(&attr_sym) {
71+
if instance.def.def_id() == *attr_id {
72+
debug!("matched: {:?} {:?}", attr_id, attr_sym);
73+
return true;
74+
}
75+
}
76+
false
77+
}
78+
6979
struct ExpectFail;
7080
impl<'tcx> GotocHook<'tcx> for ExpectFail {
7181
fn hook_applies(&self, tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> bool {
72-
let def_path = tcx.def_path(instance.def.def_id());
73-
if let Some(data) = def_path.data.last() {
74-
match data.data.name() {
75-
DefPathDataName::Named(name) => {
76-
return name.to_string().starts_with("__VERIFIER_expect_fail");
77-
}
78-
DefPathDataName::Anon { .. } => (),
79-
}
82+
// Deprecate old __VERIFIER notation that doesn't respect rust naming conventions.
83+
// Complete removal is tracked here: https://github.com/model-checking/rmc/issues/599
84+
if instance_name_starts_with(tcx, instance, "__VERIFIER_expect_fail") {
85+
warn!(
86+
"The function __VERIFIER_expect_fail is deprecated. Use rmc::expect_fail instead"
87+
);
88+
return true;
8089
}
81-
false
90+
matches_function(tcx, instance, "RmcExpectFail")
8291
}
8392

8493
fn handle(
@@ -109,16 +118,13 @@ impl<'tcx> GotocHook<'tcx> for ExpectFail {
109118
struct Assume;
110119
impl<'tcx> GotocHook<'tcx> for Assume {
111120
fn hook_applies(&self, tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> bool {
112-
let def_path = tcx.def_path(instance.def.def_id());
113-
if let Some(data) = def_path.data.last() {
114-
match data.data.name() {
115-
DefPathDataName::Named(name) => {
116-
return name.to_string().starts_with("__VERIFIER_assume");
117-
}
118-
DefPathDataName::Anon { .. } => (),
119-
}
121+
// Deprecate old __VERIFIER notation that doesn't respect rust naming conventions.
122+
// Complete removal is tracked here: https://github.com/model-checking/rmc/issues/599
123+
if instance_name_starts_with(tcx, instance, "__VERIFIER_assume") {
124+
warn!("The function __VERIFIER_assume is deprecated. Use rmc::assume instead");
125+
return true;
120126
}
121-
false
127+
matches_function(tcx, instance, "RmcAssume")
122128
}
123129

124130
fn handle(
@@ -149,7 +155,13 @@ struct Nondet;
149155

150156
impl<'tcx> GotocHook<'tcx> for Nondet {
151157
fn hook_applies(&self, tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> bool {
152-
instance_name_starts_with(tcx, instance, "__nondet")
158+
// Deprecate old __nondet since it doesn't match rust naming conventions.
159+
// Complete removal is tracked here: https://github.com/model-checking/rmc/issues/599
160+
if instance_name_starts_with(tcx, instance, "__nondet") {
161+
warn!("The function __nondet is deprecated. Use rmc::nondet instead");
162+
return true;
163+
}
164+
matches_function(tcx, instance, "RmcNonDet")
153165
}
154166

155167
fn handle(

compiler/rustc_codegen_rmc/src/overrides/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33

44
//! This module provides a mechanism which RMC can use to override standard codegen.
5-
//! For example, we the RMC provides pseudo-functions, such as __VERIFIER_assume().
5+
//! For example, we the RMC provides pseudo-functions, such as rmc::assume().
66
//! These functions should not be codegenned as MIR.
77
//! Instead, we use a "hook" to generate the correct CBMC intrinsic.
88

library/rmc/Cargo.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
[package]
5+
name = "rmc"
6+
version = "0.1.0"
7+
edition = "2018"
8+
license = "MIT OR Apache-2.0"
9+
10+
[dependencies]

library/rmc/src/lib.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
#![feature(rustc_attrs)] // Used for rustc_diagnostic_item.
4+
5+
/// Creates an assumption that will be valid after this statement run. Note that the assumption
6+
/// will only be applied for paths that follow the assumption. If the assumption doesn't hold, the
7+
/// program will exit successfully.
8+
///
9+
/// # Example:
10+
///
11+
/// The code snippet below should never panic.
12+
///
13+
/// ```rust
14+
/// let i : i32 = rmc::nondet();
15+
/// rmc::assume(i > 10);
16+
/// if i < 0 {
17+
/// panic!("This will never panic");
18+
/// }
19+
/// ```
20+
///
21+
/// The following code may panic though:
22+
///
23+
/// ```rust
24+
/// let i : i32 = rmc::nondet();
25+
/// assert!(i < 0, "This may panic and verification should fail.");
26+
/// rmc::assume(i > 10);
27+
/// ```
28+
#[inline(never)]
29+
#[rustc_diagnostic_item = "RmcAssume"]
30+
pub fn assume(_cond: bool) {}
31+
32+
/// This creates an unconstrained value of type `T`. You can assign the return value of this
33+
/// function to a variable that you want to make symbolic.
34+
///
35+
/// # Example:
36+
///
37+
/// In the snippet below, we are verifying the behavior of the function `fn_under_verification`
38+
/// under all possible i32 input values.
39+
///
40+
/// ```rust
41+
/// let inputA = rmc::nondet::<i32>();
42+
/// fn_under_verification(inputA);
43+
/// ```
44+
#[inline(never)]
45+
#[rustc_diagnostic_item = "RmcNonDet"]
46+
pub fn nondet<T>() -> T {
47+
unimplemented!("RMC nondet")
48+
}
49+
50+
/// Function used in tests for cases where the condition is not always true.
51+
#[inline(never)]
52+
#[rustc_diagnostic_item = "RmcExpectFail"]
53+
pub fn expect_fail(_cond: bool, _message: &str) {}

scripts/codegen-firecracker.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ echo
2424
# At the moment, we only test codegen for the virtio module
2525
cd $RMC_DIR/firecracker/src/devices/src/virtio/
2626
# Disable warnings until https://github.com/model-checking/rmc/issues/573 is fixed
27-
RUSTC_LOG=error RUST_BACKTRACE=1 RUSTFLAGS="-Z trim-diagnostic-paths=no -Z codegen-backend=gotoc --cfg=rmc" RUSTC=rmc-rustc cargo build --target x86_64-unknown-linux-gnu
27+
export RUSTC_LOG=error
28+
export RUST_BACKTRACE=1
29+
export RUSTFLAGS=$(${SCRIPT_DIR}/rmc-rustc --rmc-flags)
30+
export RUSTC=$(${SCRIPT_DIR}/rmc-rustc --rmc-path)
31+
cargo build --target x86_64-unknown-linux-gnu
2832

2933
echo
3034
echo "Finished Firecracker codegen regression successfully..."

scripts/rmc-rustc

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,62 @@
22
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
33
# SPDX-License-Identifier: Apache-2.0 OR MIT
44

5+
# Usage:
6+
# rmc-rustc (--rmc-flags | --rmc-path)
7+
# - This will print the configurations used to run rmc version of rustc.
8+
# rmc-rustc RUSTC_OPTIONS
9+
# - This will run RUSTC with RMC flags + the given RUSTC_OPTIONS
510
set -eu
611

712
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
813
REPO_DIR="$(dirname $SCRIPT_DIR)"
914

10-
shopt -s nullglob
11-
RMC_CANDIDATES=("$REPO_DIR"/build/*/stage1/bin/rustc)
15+
get_rmc_path() {
16+
shopt -s nullglob
17+
RMC_CANDIDATES=("$REPO_DIR"/build/*/stage1/bin/rustc)
1218

13-
if [[ ${#RMC_CANDIDATES[@]} -ne 1 ]]; then
14-
echo "ERROR: Could not find RMC binary."
15-
echo "Looked for: $REPO_DIR/build/*/stage1/bin/rustc"
16-
echo "Was RMC successfully built first?"
17-
exit 1
18-
fi
19+
if [[ ${#RMC_CANDIDATES[@]} -ne 1 ]]; then
20+
echo "ERROR: Could not find RMC binary."
21+
echo "Looked for: $REPO_DIR/build/*/stage1/bin/rustc"
22+
echo "Was RMC successfully built first?"
23+
exit 1
24+
fi
25+
echo ${RMC_CANDIDATES[0]}
26+
}
27+
28+
get_rmc_lib_path() {
29+
if [ -z "${RMC_LIB_PATH:-""}" ]
30+
then
31+
RMC_LIB_CANDIDATES=("$REPO_DIR"/target/*/librmc.rlib)
32+
if [[ ${#RMC_LIB_CANDIDATES[@]} -ne 1 ]]; then
33+
echo "ERROR: Could not find RMC library."
34+
echo "Looked for: \"$REPO_DIR/target/*/librmc.rlib\""
35+
echo "Was RMC library successfully built first?"
36+
exit 1
37+
fi
38+
echo $(dirname ${RMC_LIB_CANDIDATES[0]})
39+
else
40+
echo ${RMC_LIB_PATH}
41+
fi
42+
}
43+
44+
RMC_PATH=$(get_rmc_path)
1945

20-
"${RMC_CANDIDATES[0]}" "$@"
46+
RMC_FLAGS="--crate-type=lib \
47+
-Z codegen-backend=gotoc \
48+
-Z trim-diagnostic-paths=no \
49+
-Z human_readable_cgu_names \
50+
--cfg=rmc \
51+
-L $(get_rmc_lib_path) \
52+
--extern rmc"
53+
54+
if [ "${1:-''}" == "--rmc-flags" ]
55+
then
56+
echo ${RMC_FLAGS}
57+
elif [ "${1:-''}" == "--rmc-path" ]
58+
then
59+
echo ${RMC_PATH}
60+
else
61+
# Execute rmc
62+
"${RMC_PATH}" ${RMC_FLAGS} "$@"
63+
fi

scripts/rmc.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,12 +149,9 @@ def run_cmd(
149149

150150
def rustc_flags(mangler, symbol_table_passes):
151151
flags = [
152-
"-Z", "codegen-backend=gotoc",
153-
"-Z", "trim-diagnostic-paths=no",
154152
"-Z", f"symbol-mangling-version={mangler}",
155153
"-Z", f"symbol_table_passes={' '.join(symbol_table_passes)}",
156-
"-Z", "human_readable_cgu_names",
157-
f"--cfg={RMC_CFG}"]
154+
]
158155
if "RUSTFLAGS" in os.environ:
159156
flags += os.environ["RUSTFLAGS"].split(" ")
160157
return flags
@@ -176,7 +173,7 @@ def compile_single_rust_file(
176173
atexit.register(delete_file, output_filename)
177174
atexit.register(delete_file, base + ".type_map.json")
178175

179-
build_cmd = [RMC_RUSTC_EXE, "--crate-type=lib"] + rustc_flags(mangler, symbol_table_passes)
176+
build_cmd = [RMC_RUSTC_EXE] + rustc_flags(mangler, symbol_table_passes)
180177

181178
if use_abs:
182179
build_cmd += ["-Z", "force-unstable-if-unmarked=yes",
@@ -199,10 +196,14 @@ def cargo_build(crate, target_dir, verbose=False, debug=False, mangler="v0", dry
199196
build_cmd = ["cargo", "build", "--lib", "--target-dir", str(target_dir)]
200197
build_env = {"RUSTFLAGS": " ".join(rustflags),
201198
"RUSTC": RMC_RUSTC_EXE,
202-
"PATH": os.environ["PATH"],
199+
"PATH": os.environ["PATH"]
203200
}
204201
if debug:
205202
add_rmc_rustc_debug_to_env(build_env)
203+
if verbose:
204+
build_cmd.append("-v")
205+
if dry_run:
206+
print("{}".format(build_env))
206207
return run_cmd(build_cmd, env=build_env, cwd=crate, label="build", verbose=verbose, debug=debug, dry_run=dry_run)
207208

208209
# Adds information about unwinding to the RMC output

scripts/setup/build_rmc_lib.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/bash
2+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
# SPDX-License-Identifier: Apache-2.0 OR MIT
4+
5+
set -o errexit
6+
set -o nounset
7+
8+
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
9+
SCRIPTS_DIR="$(dirname $SCRIPT_DIR)"
10+
REPO_DIR="$(dirname $SCRIPTS_DIR)"
11+
12+
export RUSTC=$(${SCRIPTS_DIR}/rmc-rustc --rmc-path)
13+
cargo build --manifest-path "${REPO_DIR}/library/rmc/Cargo.toml" $@
14+

scripts/std-lib-regression.sh

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,17 @@ fi
4040
STD_LIB_LOG="/tmp/StdLibTest/log.txt"
4141

4242
echo "Starting cargo build with RMC"
43-
RUSTC_LOG=error \
44-
RUSTFLAGS="-Z trim-diagnostic-paths=no -Z codegen-backend=gotoc --cfg=rmc" \
45-
RUSTC=rmc-rustc cargo +nightly build -Z build-std --target $TARGET 2>&1 \
43+
export RUSTC_LOG=error
44+
export RUSTFLAGS=$(${SCRIPT_DIR}/rmc-rustc --rmc-flags)
45+
export RUSTC=$(${SCRIPT_DIR}/rmc-rustc --rmc-path)
46+
cargo +nightly build -Z build-std --target $TARGET 2>&1 \
4647
| tee $STD_LIB_LOG
4748

4849
# For now, we expect a linker error, but no modules should fail with a compiler
49-
# panic.
50+
# panic.
5051
#
5152
# With https://github.com/model-checking/rmc/issues/109, this check can be
52-
# removed to just allow the success of the previous line to determine the
53+
# removed to just allow the success of the previous line to determine the
5354
# success of this script (with no $STD_LIB_LOG needed)
5455

5556
# TODO: this check is insufficient if the failure is before codegen

src/test/benchmarks/Vector/append.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ include!{"../benchmark-prelude.rs"}
99
fn operate_on_vec(times: usize) {
1010
let mut v: Vec<u32> = Vec::with_capacity(times);
1111
for i in 0..times {
12-
v.push(__nondet());
12+
v.push(rmc::nondet());
1313
}
1414
let mut v2: Vec<u32> = Vec::with_capacity(times);
1515
for i in 0..times {
16-
v2.push(__nondet());
16+
v2.push(rmc::nondet());
1717
}
1818
v.append(&mut v2);
1919
assert!(v.len() == times * 2);

0 commit comments

Comments
 (0)