Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Credit Specification Parsing & Encoding #559

Draft
wants to merge 77 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
093f07e
First version of preparser addition for credit polynomials
Jun 10, 2021
29996e3
bug fix nested parse_terminated
Jun 11, 2021
daa4d37
added credit polynomial to untyped & json
Jun 14, 2021
0c27106
structural to typed conversion for credit polynomial
Jun 15, 2021
7838a3e
removed TODO
Jun 15, 2021
ef2ee2c
added first encoding of credit specifications
ELowis Jun 19, 2021
de2c26e
removed artifacts from older changes
ELowis Jun 21, 2021
e147bb9
resolved linter issues
ELowis Jun 21, 2021
162a430
fixed more linter issues
ELowis Jun 22, 2021
8146ccb
exhale precondition for call, removed TODOs & credits function in pru…
ELowis Jul 2, 2021
434c7fb
added unexpected errorctxt for type spec exhale
ELowis Jul 3, 2021
baa73ea
Merge remote-tracking branch 'upstream/master' into credit_spec_encoding
ELowis Jul 6, 2021
fd3881e
finish merging with new parser
ELowis Jul 7, 2021
531873e
fix minor merge issues
ELowis Jul 7, 2021
2f0b5c3
added assertion folder
ELowis Jul 13, 2021
1787bdc
first prototype with exhale of abstract terms
ELowis Jul 13, 2021
073bc92
use user-provided ids for coefficient functions
ELowis Jul 13, 2021
1e01bd5
added support to use abstract coefficients in annotations
ELowis Jul 14, 2021
7040daa
bug fix flag for precondition
ELowis Jul 14, 2021
03daa2f
Merge remote-tracking branch 'upstream/master' into abstract_precondi…
ELowis Jul 14, 2021
40f7d53
Merge remote-tracking branch 'upstream/master' into cost_inference
ELowis Jul 18, 2021
317b53b
adapted to merge changes
ELowis Jul 18, 2021
e8c9d2d
fix construction of abstract terms (ALL smaller exponents)
ELowis Jul 28, 2021
ea5dcf0
fix credit power variable transfer into typed assertion; use expressi…
ELowis Jul 28, 2021
7e3690e
First cost encoder version with type map
ELowis Aug 9, 2021
39db16e
cost encoder with condition -> type mapping to fix empty branches & u…
ELowis Aug 11, 2021
2c2df6f
fix predicate def to exclude negative perm amount
ELowis Aug 11, 2021
19c1193
Extended VIR Exprs for asymp bound check (predicate instance & perm e…
ELowis Aug 11, 2021
bbd2819
supertype for mir interpreter with substitution
ELowis Aug 11, 2021
d067753
lib & mod
ELowis Aug 11, 2021
2be41ac
removed 1 TODO
ELowis Aug 11, 2021
b6089fc
added cost encoder to precondition encoding
ELowis Aug 11, 2021
86e5c88
cleanup procedure encoder
ELowis Aug 11, 2021
5399624
Merge remote-tracking branch 'upstream/master' into cost_inference
ELowis Aug 11, 2021
8ec3689
adapt to merge changes
ELowis Aug 12, 2021
cc4ad2a
comment unused in cost_encoder
ELowis Aug 12, 2021
85c9efa
Merge remote-tracking branch 'upstream/master' into cost_inference
ELowis Aug 12, 2021
288e515
fix some linter issues
ELowis Aug 12, 2021
b14348b
fix more linter issues
ELowis Aug 13, 2021
d8b2811
Allow encoding error in substitution & use single substitution fn
ELowis Aug 13, 2021
65f954b
added more substitutions to cost inference
ELowis Aug 17, 2021
331e777
added credit access predicate to more visitors in expr
ELowis Aug 17, 2021
ccbbeb3
remove post condition from coeff functions
ELowis Aug 17, 2021
c187967
collect required permissions inside new expressions, removed some TOD…
ELowis Aug 17, 2021
d86bc29
added commas for fmt checker
ELowis Aug 17, 2021
4d56f72
Merge remote-tracking branch 'upstream/master' into cost_folding
ELowis Aug 17, 2021
a0502ca
remove SnapApps
ELowis Aug 18, 2021
c1dfbb3
store credit conversions for each substitution
ELowis Aug 19, 2021
96bc2f1
change cost encoder interface, store results for use in procedure enc…
ELowis Aug 19, 2021
032cb10
ErrorCtxt for asymp bound check & fix checked exponents
ELowis Aug 27, 2021
be86187
(temporarily) extend CfgMethod with viper pre- & postconditions & for…
ELowis Aug 27, 2021
81fcf53
(temporarily) allow CfgMethod to have no body
ELowis Aug 27, 2021
edad115
allow method calls to have arguments & no target
ELowis Aug 27, 2021
6bac4c7
move sorting of credit predicate args to method side
ELowis Aug 27, 2021
2734034
simplify credit predicate hierarchy & add _ in name
ELowis Aug 27, 2021
2c5d2af
fix: add predicate instance to used predicate collector
ELowis Aug 27, 2021
38055b2
add stmts to fold & unfold credits
ELowis Aug 27, 2021
011c70f
store partial conversion & const_val & fix replacement
ELowis Aug 27, 2021
945d09e
fix func preconditions for call
ELowis Aug 27, 2021
f7171ff
fix overwrite of conversions if multiple substititutions at 1 location
ELowis Aug 27, 2021
bfad5e8
encode credit conversions
ELowis Aug 27, 2021
6ebe60b
remove unnecessary comments
ELowis Aug 29, 2021
993268e
fix unconditional bound combinations
ELowis Aug 29, 2021
64b9cdc
add config flags for conditional inference & verify conversions
ELowis Aug 29, 2021
5025736
added parser support for asymptotic spec
ELowis Aug 29, 2021
e15c777
fix coeff id & allow O(1)
ELowis Aug 29, 2021
52601ed
bug fix: also union conversions from otherwise branch
ELowis Aug 29, 2021
0c64436
bug fix: added conditional conversions
ELowis Aug 29, 2021
218f7f7
bug fix: also detect constants inside SnapApps
ELowis Aug 30, 2021
91b50de
added proper support for constant subtractions with neg predicates
ELowis Aug 31, 2021
2525632
cleanup & comments
ELowis Aug 31, 2021
df1fbc7
only run conditional inference if there is a conditional annotation
ELowis Sep 1, 2021
6c0daa6
added termination check with existential (not really working)
ELowis Sep 1, 2021
060128e
more tracing in cost encoder
ELowis Oct 7, 2021
ce3459f
conditional control only via flag
ELowis Oct 7, 2021
0f0e3f3
added sub place-place conversion & inlining conversions (ugly)
ELowis Oct 7, 2021
3836f06
(bug fix): substitute pure function calls in conditions
ELowis Oct 7, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions prusti-common/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ lazy_static! {
settings.set_default("intern_names", true).unwrap();
settings.set_default("enable_purification_optimization", false).unwrap();
settings.set_default("enable_manual_axiomatization", false).unwrap();
settings.set_default("verify_credit_conversions", false).unwrap();
settings.set_default("conditional_cost_inference", true).unwrap();
settings.set_default("verify_finite_credits", true).unwrap();

settings.set_default("print_desugared_specs", false).unwrap();
settings.set_default("print_typeckd_specs", false).unwrap();
Expand Down Expand Up @@ -463,3 +466,19 @@ pub fn full_compilation() -> bool {
pub fn intern_names() -> bool {
read_setting("intern_names")
}

/// Verify the conversions of credits by assignments inside Viper.
pub fn verify_credit_conversions() -> bool {
read_setting("verify_credit_conversions")
}

/// Enable the verification of different asymptotic bounds depending on a condition.
/// Otherwise, the code will be verified against the highest (combined) asymptotic bound.
pub fn conditional_cost_inference() -> bool {
read_setting("conditional_cost_inference")
}

/// Verify that only a finite amount of credits is consumed.
pub fn verify_finite_credits() -> bool {
read_setting("verify_finite_credits")
}
66 changes: 34 additions & 32 deletions prusti-common/src/vir/optimizations/methods/cfg_cleaner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,50 @@ use crate::vir::{self, cfg};

/// Merge consequitive basic blocks that are joined by only one edge.
pub fn clean_cfg(mut method: cfg::CfgMethod) -> cfg::CfgMethod {
let predecessors = method.predecessors();
let traversal_order = method.get_topological_sort();
assert_eq!(traversal_order[0].block_index, 0, "The start block should be first.");
let mut new_basic_blocks = Vec::new();
let mut basic_blocks: HashMap<_, _> = method.basic_blocks.into_iter().enumerate().collect();
let mut new_indices = HashMap::new();
if !method.basic_blocks.is_empty() {
let predecessors = method.predecessors();
let traversal_order = method.get_topological_sort();
assert_eq!(traversal_order[0].block_index, 0, "The start block should be first.");
let mut new_basic_blocks = Vec::new();
let mut basic_blocks: HashMap<_, _> = method.basic_blocks.into_iter().enumerate().collect();
let mut new_indices = HashMap::new();

for cfg::CfgBlockIndex{ block_index, .. } in traversal_order {
if let Some(mut basic_block) = basic_blocks.remove(&block_index) {
let new_index = new_basic_blocks.len();
assert!(new_indices.insert(block_index, new_index).is_none());
while let vir::cfg::Successor::Goto(target) = &basic_block.successor {
// If the current basic block has only one successor.
if predecessors[&target.block_index].len() == 1 {
// If the successor block has only one predecessor.
for cfg::CfgBlockIndex { block_index, .. } in traversal_order {
if let Some(mut basic_block) = basic_blocks.remove(&block_index) {
let new_index = new_basic_blocks.len();
assert!(new_indices.insert(block_index, new_index).is_none());
while let vir::cfg::Successor::Goto(target) = &basic_block.successor {
// If the current basic block has only one successor.
if predecessors[&target.block_index].len() == 1 {
// If the successor block has only one predecessor.

assert!(new_indices.insert(target.block_index, new_index).is_none());
let target_block = basic_blocks.remove(&target.block_index).unwrap();
assert!(new_indices.insert(target.block_index, new_index).is_none());
let target_block = basic_blocks.remove(&target.block_index).unwrap();

basic_block.stmts.extend(target_block.stmts);
basic_block.successor = target_block.successor;
} else {
break;
basic_block.stmts.extend(target_block.stmts);
basic_block.successor = target_block.successor;
} else {
break;
}
}
new_basic_blocks.push(basic_block);
}
new_basic_blocks.push(basic_block);
}
}
for basic_block in &mut new_basic_blocks {
match &mut basic_block.successor {
vir::cfg::Successor::Undefined | vir::cfg::Successor::Return => {},
vir::cfg::Successor::Goto(target) => {
target.block_index = new_indices[&target.index()];
}
vir::cfg::Successor::GotoSwitch(conditional_targets, default_target) => {
default_target.block_index = new_indices[&default_target.index()];
for (_, target) in conditional_targets {
for basic_block in &mut new_basic_blocks {
match &mut basic_block.successor {
vir::cfg::Successor::Undefined | vir::cfg::Successor::Return => {},
vir::cfg::Successor::Goto(target) => {
target.block_index = new_indices[&target.index()];
}
vir::cfg::Successor::GotoSwitch(conditional_targets, default_target) => {
default_target.block_index = new_indices[&default_target.index()];
for (_, target) in conditional_targets {
target.block_index = new_indices[&target.index()];
}
}
}
}
method.basic_blocks = new_basic_blocks;
}
method.basic_blocks = new_basic_blocks;
method
}
42 changes: 24 additions & 18 deletions prusti-common/src/vir/optimizations/methods/purifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ pub fn purify_vars(mut method: cfg::CfgMethod) -> cfg::CfgMethod {
}
};
method.local_vars = method.local_vars.into_iter().map(fix_var).collect();
// method.viper_formal_args = method.viper_formal_args.into_iter().map(fix_var).collect(); //TODO: ?
method
}

Expand Down Expand Up @@ -204,7 +205,10 @@ impl ast::StmtWalker for VarCollector {
if is_purifiable_method(method_name) {
self.is_pure_context = true;
}
assert!(args.is_empty());
//assert!(args.is_empty());
for arg in args {
self.walk_expr(arg); //TODO: ?
}
for target in targets {
self.walk_local_var(target);
}
Expand Down Expand Up @@ -418,23 +422,25 @@ impl ast::StmtFolder for VarPurifier {
args: Vec<ast::Expr>,
mut targets: Vec<ast::LocalVar>,
) -> ast::Stmt {
assert!(targets.len() == 1);
if self.pure_vars.contains(&targets[0]) {
let target = &targets[0];
let replacement = self
.replacements
.get(target)
.expect(&format!("key: {}", target))
.clone();
name = match replacement.typ {
ast::Type::Int => "builtin$havoc_int",
ast::Type::Bool => "builtin$havoc_bool",
ast::Type::TypedRef(_) => "builtin$havoc_ref",
ast::Type::Domain(_)
| ast::Type::Snapshot(_)
| ast::Type::Seq(_) => unreachable!(),
}.to_string();
targets = vec![replacement];
assert!(targets.len() <= 1);
if targets.len() == 1 {
if self.pure_vars.contains(&targets[0]) {
let target = &targets[0];
let replacement = self
.replacements
.get(target)
.expect(&format!("key: {}", target))
.clone();
name = match replacement.typ {
ast::Type::Int => "builtin$havoc_int",
ast::Type::Bool => "builtin$havoc_bool",
ast::Type::TypedRef(_) => "builtin$havoc_ref",
ast::Type::Domain(_)
| ast::Type::Snapshot(_)
| ast::Type::Seq(_) => unreachable!(),
}.to_string();
targets = vec![replacement];
}
}
ast::Stmt::MethodCall(
name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,17 @@ fn get_used_predicates_in_predicate_map(
map.insert(p.name.clone(), res);
}
Predicate::Bodyless(_, _) => { /* ignore */ }
Predicate::CreditUnit(CreditUnitPredicate {
name,
body: Some(e),
..
}) => {
ExprWalker::walk(&mut collector, e);
let mut res = BTreeSet::new();
std::mem::swap(&mut res, &mut collector.used_predicates);
map.insert(name.clone(), res);
}
Predicate::CreditUnit(CreditUnitPredicate { body: None, .. }) => { /* ignore */ }
}
}
return map;
Expand Down Expand Up @@ -169,6 +180,13 @@ impl UsedPredicateCollector {
}

impl ExprWalker for UsedPredicateCollector {
fn walk_predicate_instance(&mut self, name: &str, args: &Vec<Expr>, _pos: &Position) {
self.used_predicates.insert(name.to_string());
for arg in args {
ExprWalker::walk(self, arg)
}
}

fn walk_predicate_access_predicate(
&mut self,
name: &str,
Expand All @@ -180,6 +198,19 @@ impl ExprWalker for UsedPredicateCollector {
ExprWalker::walk(self, arg);
}

fn walk_credit_access_predicate(
&mut self,
name: &str,
args: &Vec<Expr>,
_frac_perm_amount: &FracPermAmount,
_pos: &Position
) {
self.used_predicates.insert(name.to_string());
for arg in args {
ExprWalker::walk(self, arg);
}
}

fn walk_unfolding(
&mut self,
name: &str,
Expand Down
2 changes: 1 addition & 1 deletion prusti-common/src/vir/optimizations/purification/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ fn purify_method(method: &mut CfgMethod, predicates: &[Predicate]) {
}
}

/// This is a ExprWalkerand StmtWalker used to collect information about which
/// This is a ExprWalker and StmtWalker used to collect information about which
/// local variables can be purified.
///
/// The current implementation is only for ints/bools. So to check if a
Expand Down