Skip to content

Commit 718b876

Browse files
adpaco-awsdanielsn
authored andcommitted
Handle common CBMC flags via RMC (rust-lang#639)
* Handle common CBMC flags via RMC * Address some PR comments * Format * Improve help messages for CBMC common flags * Remove `--default-values` and add `--auto-unwind`. * Add feature to `cargo-rmc` * Move default values to `rmc_flags`, add them to help message Co-authored-by: Daniel Schwartz-Narbonne <[email protected]>
1 parent 57bb5ca commit 718b876

File tree

10 files changed

+111
-7
lines changed

10 files changed

+111
-7
lines changed

scripts/cargo-rmc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,10 @@ def parse_args():
219219

220220
# Add some CBMC flags by default unless `--no-default-checks` is being used
221221
if args.default_checks:
222-
rmc.add_selected_default_cbmc_flags(args)
222+
rmc.add_selected_default_cbmc_checks(args)
223+
224+
# Conditionally add common CBMC flags
225+
rmc.process_common_cbmc_flags(args)
223226

224227
# Remove "rmc" from arg if invoked `cargo rmc ...`
225228
if len(sys.argv) >= 2 and sys.argv[1] == "rmc":

scripts/rmc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,10 @@ def parse_args():
153153

154154
# Add some CBMC flags by default unless `--no-default-checks` is being used
155155
if args.default_checks:
156-
rmc.add_selected_default_cbmc_flags(args)
156+
rmc.add_selected_default_cbmc_checks(args)
157+
158+
# Conditionally add common CBMC flags
159+
rmc.process_common_cbmc_flags(args)
157160

158161
parser = create_parser()
159162
args = parser.parse_args()

scripts/rmc.py

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import re
1010
import pathlib
1111

12+
import rmc_flags
1213

1314
RMC_CFG = "rmc"
1415
RMC_RUSTC_EXE = "rmc-rustc"
@@ -30,6 +31,7 @@
3031
"--unsigned-overflow-check"]
3132
UNWINDING_CHECKS = ["--unwinding-assertions"]
3233

34+
3335
# A Scanner is intended to match a pattern with an output
3436
# and edit the output based on an edit function
3537
class Scanner:
@@ -78,15 +80,62 @@ def add_set_cbmc_flags(args, flags):
7880
else:
7981
args.cbmc_args.append(arg)
8082

81-
# Add sets of selected default CBMC flags
82-
def add_selected_default_cbmc_flags(args):
83+
# Add sets of selected default CBMC checks
84+
def add_selected_default_cbmc_checks(args):
8385
if args.memory_safety_checks:
8486
add_set_cbmc_flags(args, MEMORY_SAFETY_CHECKS)
8587
if args.overflow_checks:
8688
add_set_cbmc_flags(args, OVERFLOW_CHECKS)
8789
if args.unwinding_checks:
8890
add_set_cbmc_flags(args, UNWINDING_CHECKS)
8991

92+
# Add a common CBMC flag to `cbmc_args`
93+
def add_common_cbmc_flag(args, flag_info):
94+
(cbmc_arg, rmc_arg, _) = flag_info
95+
rmc_value = getattr(args, rmc_arg)
96+
if rmc_value is not None:
97+
args.cbmc_args.extend([cbmc_arg, rmc_value])
98+
99+
# Set a common CBMC flag by examining both RMC & CBMC flags
100+
def set_common_cbmc_flag(args, flag_info):
101+
(cbmc_arg, rmc_arg, default_value) = flag_info
102+
if getattr(args, rmc_arg) is not None:
103+
if cbmc_arg in args.cbmc_args:
104+
# Case #1: The flag is specified twice - Result: Raise an exception
105+
raise Exception(f"Conflicting flags: `{cbmc_arg}` was specified twice.")
106+
# Case #2: Flag specified via `args.rmc_arg` only - Result: Use `args.rmc_arg`
107+
return
108+
if cbmc_arg in args.cbmc_args:
109+
# Case #3: Flag specified via `cbmc_arg` only - Result: Use `cbmc_arg`
110+
# Note: `args.rmc_arg` is `None` so nothing will be added later
111+
return
112+
# Case #4: The flag has not been specified - Result: Assign default value
113+
setattr(args, rmc_arg, default_value)
114+
115+
def process_object_bits_flag(args):
116+
flag_info = ("--object-bits", "object_bits", rmc_flags.DEFAULT_OBJECT_BITS_VALUE)
117+
set_common_cbmc_flag(args, flag_info)
118+
add_common_cbmc_flag(args, flag_info)
119+
120+
def process_unwind_flag(args):
121+
# We raise an exception if `--auto-unwind` is being used in
122+
# addition to other `--unwind` flags in RMC or CBMC
123+
if args.auto_unwind:
124+
if args.unwind is not None or "--unwind" in args.cbmc_args:
125+
raise Exception("Conflicting flags: `--auto-unwind` is not"
126+
" compatible with other `--unwind` flags.")
127+
return
128+
flag_info = ("--unwind", "unwind", rmc_flags.DEFAULT_UNWIND_VALUE)
129+
set_common_cbmc_flag(args, flag_info)
130+
add_common_cbmc_flag(args, flag_info)
131+
132+
# Process common CBMC flags
133+
def process_common_cbmc_flags(args):
134+
# For each CBMC flag we set the RMC flag if needed, then
135+
# we add the associated CBMC flag if RMC flag has been set
136+
process_object_bits_flag(args)
137+
process_unwind_flag(args)
138+
90139
# Updates environment to use gotoc backend debugging
91140
def add_rmc_rustc_debug_to_env(env):
92141
env["RUSTC_LOG"] = env.get("RUSTC_LOG", "rustc_codegen_rmc")

scripts/rmc_flags.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@
55
import argparse
66
import pathlib as pl
77

8+
# The default object bits value in CBMC is 8, which is not enough to handle most
9+
# medium-sized Rust programs. Increasing it to 16 should have no impact in
10+
# 64-bit architectures.
11+
DEFAULT_OBJECT_BITS_VALUE = "16"
12+
# CBMC performs automatic loop unwinding if no unwinding value is specified.
13+
# Even though this procedure is not guaranteed to terminate, passing a default
14+
# value for unwinding would prevent users from running automatic loop unwinding.
15+
DEFAULT_UNWIND_VALUE = None
16+
817
# Taken from https://github.com/python/cpython/blob/3.9/Lib/argparse.py#L858
918
# Cannot use `BooleanOptionalAction` with Python 3.8
1019
class BooleanOptionalAction(argparse.Action):
@@ -139,6 +148,22 @@ def add_check_flags(make_group, add_flag, config):
139148
add_flag(group, "--unwinding-checks", default=True, action=BooleanOptionalAction,
140149
help="Turn on default unwinding checks")
141150

151+
# Add flags for common CBMC flags
152+
def add_common_flags(make_group, add_flag, config):
153+
# Note: The code for handling common CBMC flags is more complex than usual,
154+
# since the flag may have been set via `--cbmc-args`. Here, we print the
155+
# default values here but we set them later using `process_common_cbmc_flags`
156+
default_unwind_value = DEFAULT_UNWIND_VALUE if DEFAULT_UNWIND_VALUE else "None"
157+
group = make_group("Common flags", "Common CBMC flags handled by RMC.")
158+
add_flag(group, "--object-bits", type=str,
159+
help="Specify the number of bits used for representing object IDs in CBMC"
160+
" (default: " + DEFAULT_OBJECT_BITS_VALUE + ")")
161+
add_flag(group, "--unwind", type=str,
162+
help="Specify the value used for loop unwinding in CBMC"
163+
" (default: " + default_unwind_value + ")")
164+
add_flag(group, "--auto-unwind", default=False, action=BooleanOptionalAction,
165+
help="Turn on automatic loop unwinding")
166+
142167
# Add flags needed only for visualizer.
143168
def add_visualizer_flags(make_group, add_flag, config):
144169
group = make_group(
@@ -207,6 +232,7 @@ def add_flag(group, flag, *args, **kwargs):
207232
add_linking_flags,
208233
add_artifact_flags,
209234
add_check_flags,
235+
add_common_flags,
210236
add_visualizer_flags,
211237
add_other_flags,
212238
add_developer_flags
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Conflicting flags: `--auto-unwind` is not compatible with other `--unwind` flags.
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+
// rmc-flags: --dry-run --auto-unwind
5+
// cbmc-flags: --unwind 2
6+
7+
// `--dry-run` causes RMC to print out commands instead of running them
8+
// In `expected` you will find substrings of these commands because the
9+
// concrete paths depend on your working directory.
10+
pub fn main() {}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Exception: Conflicting flags: `--object-bits` was specified twice.
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+
// rmc-flags: --dry-run --object-bits 10
5+
// cbmc-flags: --object-bits 8
6+
7+
// `--dry-run` causes RMC to print out commands instead of running them
8+
// In `expected` you will find substrings of these commands because the
9+
// concrete paths depend on your working directory.
10+
pub fn main() {}

src/test/expected/dry-run/expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
rmc-rustc -Z symbol-mangling-version=v0 -Z symbol_table_passes=
22
symtab2gb
33
goto-cc --function main
4-
cbmc --bounds-check --pointer-check --pointer-primitive-check --conversion-check --div-by-zero-check --float-overflow-check --nan-check --pointer-overflow-check --signed-overflow-check --undefined-shift-check --unsigned-overflow-check --unwinding-assertions --function main
4+
cbmc --bounds-check --pointer-check --pointer-primitive-check --conversion-check --div-by-zero-check --float-overflow-check --nan-check --pointer-overflow-check --signed-overflow-check --undefined-shift-check --unsigned-overflow-check --unwinding-assertions --object-bits 16 --function main

src/tools/compiletest/src/runtest.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2435,8 +2435,9 @@ impl<'test> TestCx<'test> {
24352435
/// Print an error if the verification output does not contain the expected
24362436
/// lines.
24372437
fn verify_output(&self, proc_res: &ProcRes, expected: &str) {
2438-
if let Some(line) = TestCx::contains_lines(&proc_res.stdout, expected.split('\n').collect())
2439-
{
2438+
// Include the output from stderr here for cases where there are exceptions
2439+
let output = proc_res.stdout.to_string() + &proc_res.stderr;
2440+
if let Some(line) = TestCx::contains_lines(&output, expected.split('\n').collect()) {
24402441
self.fatal_proc_rec(
24412442
&format!("test failed: expected output to contain the line: {}", line),
24422443
&proc_res,

0 commit comments

Comments
 (0)