Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
c1002a8
Added gen-c transformer.
vecchiot-aws Jul 9, 2021
e585575
Add --gen-c-readable flag
vecchiot-aws Jul 12, 2021
5fe1784
Added extra gen-c support.
vecchiot-aws Jul 12, 2021
033cc25
temporary commit/
vecchiot-aws Jul 12, 2021
795e946
Add non_det functions to symbol table.
vecchiot-aws Jul 19, 2021
c5e04b0
Fix merge issues and compilation warnings.
vecchiot-aws Jul 19, 2021
e209c68
Force normalize_identifier to be injective.
vecchiot-aws Jul 19, 2021
b54cd12
Prevent padding from producing unused nondet function calls.
vecchiot-aws Jul 20, 2021
1f59a96
Remove extra files.
vecchiot-aws Jul 20, 2021
7b89303
Fix missing extern functions (translate to nondet).
vecchiot-aws Jul 20, 2021
c3d9bc4
Fixed implies operator not existing in C.
vecchiot-aws Jul 20, 2021
4467f73
Moved removal of macros to after gotoc to avoid missing function error.
vecchiot-aws Jul 20, 2021
af681e2
Added __CPROVER_atomic_begin/end to gen_c library.
vecchiot-aws Jul 21, 2021
104a33c
Add default to illegal identifiers.
vecchiot-aws Jul 21, 2021
237e2f0
Add powi and powif to gen_c library.
vecchiot-aws Jul 21, 2021
a017493
Add POINTER_OBJECT macro.
vecchiot-aws Jul 21, 2021
8ad60c1
Fix illegal default identifier.
vecchiot-aws Jul 21, 2021
64ac31d
Fixed byte_extract_little_endian macro.
vecchiot-aws Jul 21, 2021
011d288
Add c-lib to gen-c-runnable and dry_run to gen-c postprocess.
vecchiot-aws Jul 22, 2021
1996c3e
Fix issue with name mapping memory, and don't remove '-' from 'tag-...'
vecchiot-aws Jul 22, 2021
39a6f97
Fix u128 constants not being compilable.
vecchiot-aws Jul 26, 2021
2c4fa0b
Add --gen-c-runnable back in after rebase.
vecchiot-aws Jul 26, 2021
05a1f31
Fixed issue with tag- appearing in identifiers that weren't structs.
vecchiot-aws Jul 26, 2021
bded814
Fix SIMD vector access.
vecchiot-aws Jul 27, 2021
231d27c
Fix generation of nondet functions with empty return type.
vecchiot-aws Jul 27, 2021
cc779b9
Add gen-c-runnable flag to cargo rmc, and rearrange scripts a bit.
vecchiot-aws Jul 27, 2021
95af961
Add purpose flags.
vecchiot-aws Jul 27, 2021
bd2e650
Fix issue with extern functions having no return value.
vecchiot-aws Aug 2, 2021
c60b228
Fix issue with extern functions returning void.
vecchiot-aws Aug 3, 2021
53801b0
Fix insert position for include.
vecchiot-aws Aug 3, 2021
a1e3083
Add sputc to list of removals.
vecchiot-aws Aug 3, 2021
47ddf49
Initialize missing extern statics to nondet.
vecchiot-aws Aug 4, 2021
0ef69a3
Change transformers to take mutable reference to self.
vecchiot-aws Aug 4, 2021
6faa384
Remove statics.
vecchiot-aws Aug 4, 2021
b8523b2
Actually remember used names.
vecchiot-aws Aug 4, 2021
9fc264b
Create owned getters for maps.
vecchiot-aws Aug 4, 2021
e5f0c56
Restructure gen-c transformer into three parts.
vecchiot-aws Aug 4, 2021
220f265
Fixup documentation.
vecchiot-aws Aug 4, 2021
9da4405
Fix bug with multiple missing parameter identifiers of same type.
vecchiot-aws Aug 4, 2021
5de610c
Handle return type being empty.
vecchiot-aws Aug 4, 2021
65275d0
Simplify logic of generating default values in non_det functions.
vecchiot-aws Aug 5, 2021
a5e9c8d
Revert accidental replacement.
vecchiot-aws Aug 5, 2021
1d714ee
Simplify logic of generating default bodies for extern functions.
vecchiot-aws Aug 5, 2021
8e45389
Correctly handle when no main function present.
vecchiot-aws Aug 5, 2021
9566d1b
Minor comments and formatting.
vecchiot-aws Aug 5, 2021
6022a1b
Add assert to avoid nondets returning empty.
vecchiot-aws Aug 5, 2021
fde17e4
Fixed bug with generated statics not being global.
vecchiot-aws Aug 11, 2021
8e827c9
Fix issue with parameter names matching local variable names.
vecchiot-aws Aug 19, 2021
396a4ed
Update type_to_string to be injective.
vecchiot-aws Aug 19, 2021
b5f54b0
Add docstring.
vecchiot-aws Aug 19, 2021
5ab65a2
Fix short-circuiting issue with implies.
vecchiot-aws Aug 19, 2021
ccfd664
Clean up transform_symbol.
vecchiot-aws Aug 19, 2021
4e792ec
Fix transforming bigints to use bit shifting.
vecchiot-aws Aug 19, 2021
6059b04
Add test for bigints.
vecchiot-aws Aug 19, 2021
1f77a84
Cleanup nondet transformer.
vecchiot-aws Aug 19, 2021
ad69d90
Cleanup name transformer.
vecchiot-aws Aug 19, 2021
a013a4d
Cleanup gen_c_lib
vecchiot-aws Aug 19, 2021
715763c
Use binop constructor.
vecchiot-aws Aug 19, 2021
e4ace26
Move transforming 'main' to 'main_' into transform_symbol.
vecchiot-aws Aug 19, 2021
3ab1884
Move type_to_string into typ.rs
vecchiot-aws Aug 19, 2021
1b0b53d
Add is_c_integer method to Type.
vecchiot-aws Aug 19, 2021
d76983b
Clean up bignum_to_expr
vecchiot-aws Aug 19, 2021
4c7df7d
Clean up some expr_transformer things.
vecchiot-aws Aug 19, 2021
eda36d6
Add pseudocode for SIMD transformation.
vecchiot-aws Aug 19, 2021
4a8a111
Clarify remembering mapped names is for correctness, not just optimiz…
vecchiot-aws Aug 19, 2021
b5c90bb
Add clarification for used_names and mapped_names.
vecchiot-aws Aug 19, 2021
a27b6bc
Add issue for handling all reserved C names.
vecchiot-aws Aug 19, 2021
6f0bd09
Add exapmle for transform_expr_nondet.
vecchiot-aws Aug 19, 2021
3631f83
Add example for why we need to ignore padding in nondet transformer.
vecchiot-aws Aug 19, 2021
7dddb2c
Add note that order matters to passes.rs
vecchiot-aws Aug 19, 2021
68a4a99
Cleanup some stuff in gen_c_lib.c
vecchiot-aws Aug 19, 2021
acf6338
Switch to not use stcmp.
vecchiot-aws Aug 19, 2021
8e45a7b
Change goto-instrument to directly pass in output file.
vecchiot-aws Aug 19, 2021
e618e35
Added examples to Type.to_identifier for code and variadic_code.
vecchiot-aws Aug 19, 2021
b2c8767
Add documentation to README.
vecchiot-aws Aug 20, 2021
6480d3b
Replace match with if let.
vecchiot-aws Aug 20, 2021
16c975b
Change .not().not() to cast to bool.
vecchiot-aws Aug 20, 2021
084eb28
-Create tracking issue for SIMD vector indexing.
vecchiot-aws Aug 20, 2021
6641afe
Switch overflow to use builtin overflow check functions.
vecchiot-aws Aug 20, 2021
5392324
Fix compiler warnings.
vecchiot-aws Aug 20, 2021
769215d
Switch imports to be absolute.
vecchiot-aws Aug 20, 2021
5a8ce3f
Merge branch 'main-154-2021-08-17' into gen-c-transformer
danielsn Aug 20, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,13 @@ For example, consider the `CopyIntrinsics` regression test:
rmc --gen-c main.rs <other-args>
```
This generates a file `main.c` which contains a "C" like formatting of the CBMC IR.
1. The `--gen-c` flag does not produce runnable C code due to differences in the Rust and C languages.
To produce a runnable C program, try passing the `--gen-c-runnable` flag to RMC
```
rmc --gen-c-runnable main.rs <other-args>
```
This generates a file `main_runnable.c`.
Note that this makes some compromises to produce runnable C code, so you should not expect exact semantic equivalence.
1. You can also view the raw CBMC internal representation using the `--keep-temps` option.

### Experimental Cargo integration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -878,7 +878,7 @@ impl Expr {
}
}
/// self op right;
fn binop(self, op: BinaryOperand, rhs: Expr) -> Expr {
pub fn binop(self, op: BinaryOperand, rhs: Expr) -> Expr {
assert!(
Expr::typecheck_binop_args(op, &self, &rhs),
"BinaryOperation Expression does not typecheck {:?} {:?} {:?}",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR MIT

use std::ops::{BitAnd, Shl, Shr};

use super::super::Transformer;
use crate::gotoc::cbmc::goto_program::{
BinaryOperand, CIntType, Expr, Location, Parameter, Stmt, Symbol, SymbolTable, SymbolValues,
Type,
};
use num::bigint::BigInt;
use rustc_data_structures::fx::FxHashMap;

/// Create an expr from an int constant using only values <= u64::MAX;
/// this is needed because gcc allows 128 bit int variables, but not 128 bit constants
fn bignum_to_expr(num: &BigInt, typ: &Type) -> Expr {
// CInteger types should already be valid in C
if typ.is_c_integer() {
return Expr::int_constant(num.clone(), typ.clone());
}

// Only need to handle type wider than 64 bits
if let Some(width) = typ.width() {
if width <= 64 {
return Expr::int_constant(num.clone(), typ.clone());
}
}

// Only types that should be left are i128 and u128
assert_eq!(typ.width(), Some(128), "Unexpected int width: {:?}", typ.width());

// Work with unsigned ints, and cast to original type at end
let unsigned_typ = Type::unsigned_int(128);

// To transform a 128 bit num, we break it into two 64 bit nums

// left_mask = 11..1100..00 (64 1's followed by 64 0's)
// left_mask = 00..0011..11 (64 0's followed by 64 1's)
let left_mask = BigInt::from(u64::MAX).shl(64);
let right_mask = BigInt::from(u64::MAX);

// Construct the two 64 bit ints such that
// num = (left_half << 64) | right_half
// = (left_half * 2^64) + right_half
let left_half = {
// Split into two parts to help type inference
let temp: BigInt = num.bitand(left_mask);
temp.shr(64)
};
let right_half = num.bitand(right_mask);

// Create CBMC constants for the left and right halfs
let left_constant = Expr::int_constant(left_half, unsigned_typ.clone());
let right_constant = Expr::int_constant(right_half, unsigned_typ);

// Construct CBMC expression: (typ) ((left << 64) | right)
left_constant
.shl(Expr::int_constant(64, Type::c_int()))
.bitor(right_constant)
.cast_to(typ.clone())
}

/// Struct for handling the expression replacement transformations for --gen-c-runnable.
pub struct ExprTransformer {
new_symbol_table: SymbolTable,
/// The `empty_statics` field is used to track extern static variables;
/// when such a symbol is encountered, we add it to this map;
/// in postprocessing, we initialize each of these variables
/// with a default value to emphasize that these are externally defined.
empty_statics: FxHashMap<String, Expr>,
}

impl ExprTransformer {
/// Replace expressions which lead to invalid C with alternatives.
pub fn transform(original_symbol_table: &SymbolTable) -> SymbolTable {
let new_symbol_table = SymbolTable::new(original_symbol_table.machine_model().clone());
ExprTransformer { new_symbol_table, empty_statics: FxHashMap::default() }
.transform_symbol_table(original_symbol_table)
}

/// Extract `empty_statics` map for final processing.
fn empty_statics_owned(&mut self) -> FxHashMap<String, Expr> {
std::mem::replace(&mut self.empty_statics, FxHashMap::default())
}

/// Add identifier to a transformed parameter if it's missing;
/// necessary when function wasn't originally a definition, e.g. extern functions,
/// so that we can give them a function body.
fn add_parameter_identifier(&mut self, parameter: &Parameter) -> Parameter {
if parameter.identifier().is_some() {
parameter.clone()
} else {
let name = format!("__{}", parameter.typ().to_identifier());
let parameter_sym = self.mut_symbol_table().ensure(&name, |_symtab, name| {
Symbol::variable(
name.to_string(),
name.to_string(),
parameter.typ().clone(),
Location::none(),
)
});
parameter_sym.to_function_parameter()
}
}
}

impl Transformer for ExprTransformer {
/// Get reference to symbol table.
fn symbol_table(&self) -> &SymbolTable {
&self.new_symbol_table
}

/// Get mutable reference to symbol table.
fn mut_symbol_table(&mut self) -> &mut SymbolTable {
&mut self.new_symbol_table
}

/// Get owned symbol table.
fn extract_symbol_table(self) -> SymbolTable {
self.new_symbol_table
}

/// Translate Implies into Or/Not.
fn transform_expr_bin_op(
&mut self,
_typ: &Type,
op: &BinaryOperand,
lhs: &Expr,
rhs: &Expr,
) -> Expr {
let lhs = self.transform_expr(lhs);
let rhs = self.transform_expr(rhs);

match op {
BinaryOperand::Implies => lhs.not().bitor(rhs).cast_to(Type::bool()),
_ => lhs.binop(*op, rhs),
}
}

/// Prevent error for too large constants with u128.
fn transform_expr_int_constant(&mut self, typ: &Type, value: &BigInt) -> Expr {
let transformed_typ = self.transform_type(typ);
bignum_to_expr(value, &transformed_typ)
}

/// When indexing into a SIMD vector, cast to a pointer first to make legal indexing in C.
/// `typ __attribute__((vector_size (size * sizeof(typ)))) var;`
/// `((typ*) &var)[index]`
/// Tracking issue: https://github.com/model-checking/rmc/issues/444
fn transform_expr_index(&mut self, _typ: &Type, array: &Expr, index: &Expr) -> Expr {
let transformed_array = self.transform_expr(array);
let transformed_index = self.transform_expr(index);
if transformed_array.typ().is_vector() {
let base_type = transformed_array.typ().base_type().unwrap().clone();
transformed_array.address_of().cast_to(base_type.to_pointer()).index(transformed_index)
} else {
transformed_array.index(transformed_index)
}
}

/// Replace `extern` functions and values with `nondet` so linker doesn't break.
fn transform_symbol(&mut self, symbol: &Symbol) -> Symbol {
let mut new_symbol = symbol.clone();

if symbol.is_extern {
if symbol.typ.is_code() || symbol.typ.is_variadic_code() {
// Replace `extern` function with one which returns `nondet`
assert!(symbol.value.is_none(), "Extern function should have no body.");

let transformed_typ = self.transform_type(&symbol.typ);

// Fill missing parameter names with dummy name
let parameters = transformed_typ
.parameters()
.unwrap()
.iter()
.map(|parameter| self.add_parameter_identifier(parameter))
.collect();
let ret_typ = transformed_typ.return_type().unwrap().clone();
let new_typ = if transformed_typ.is_code() {
Type::code(parameters, ret_typ.clone())
} else {
Type::variadic_code(parameters, ret_typ.clone())
};

// Create body, which returns nondet
let ret_e = if ret_typ.is_empty() { None } else { Some(Expr::nondet(ret_typ)) };
let body = Stmt::ret(ret_e, Location::none());
let new_value = SymbolValues::Stmt(body);

new_symbol.is_extern = false;
new_symbol.typ = new_typ;
new_symbol.value = new_value;
} else {
// Replace `extern static`s and initialize in `main`
assert!(
symbol.is_static_lifetime,
"Extern objects that aren't functions should be static variables."
);
let new_typ = self.transform_type(&symbol.typ);
self.empty_statics.insert(symbol.name.clone(), Expr::nondet(new_typ.clone()));

// Symbol is no longer extern
new_symbol.is_extern = false;

// Set location to none so that it is a global static
new_symbol.location = Location::none();

new_symbol.typ = new_typ;
new_symbol.value = SymbolValues::None;
}
} else if &symbol.name == "main" {
// Replace `main` with `main_` since it has the wrong return type
new_symbol.name = "main_".to_string();
new_symbol.base_name = Some("main_".to_string());
new_symbol.pretty_name = Some("main_".to_string());

let new_typ = self.transform_type(&symbol.typ);
let new_value = self.transform_value(&symbol.value);

new_symbol.typ = new_typ;
new_symbol.value = new_value;
} else {
// Handle all other symbols normally
let new_typ = self.transform_type(&symbol.typ);
let new_value = self.transform_value(&symbol.value);
new_symbol.typ = new_typ;
new_symbol.value = new_value;
}

new_symbol
}

/// Move `main` to `main_`, and create a wrapper `main` to initialize statics and return `int`.
fn postprocess(&mut self) {
// The body of the new `main` function
let mut main_body = Vec::new();

// Initialize statics
for (name, value) in self.empty_statics_owned() {
let sym_expr = Expr::symbol_expression(name, value.typ().clone());
main_body.push(Stmt::assign(sym_expr, value, Location::none()));
}

// `main_();`, if it is present
if let Some(main_) = self.symbol_table().lookup("main_") {
main_body
.push(Stmt::code_expression(main_.to_expr().call(Vec::new()), Location::none()));
}

// `return 0;`
main_body.push(Stmt::ret(
Some(Expr::int_constant(0, Type::CInteger(CIntType::Int))),
Location::none(),
));

// Create `main` symbol
let new_main = Symbol::function(
"main",
Type::code(Vec::new(), Type::CInteger(CIntType::Int)),
Some(Stmt::block(main_body, Location::none())),
Some("main".to_string()),
Location::none(),
);

self.mut_symbol_table().insert(new_main);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR MIT

mod expr_transformer;
mod name_transformer;
mod nondet_transformer;

pub use expr_transformer::ExprTransformer;
pub use name_transformer::NameTransformer;
pub use nondet_transformer::NondetTransformer;
Loading