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

Time reasoning #1275

Closed
wants to merge 26 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
cf62085
Add resource predicates
Pialex99 Nov 9, 2022
19027ad
Add tests for time resources
Pialex99 Nov 22, 2022
72ecf6a
Fix unfolding issue
Pialex99 Nov 22, 2022
76fdd0e
Add resource predicates generation
Pialex99 Nov 23, 2022
b674e34
Feedback
Pialex99 Nov 25, 2022
5f54d60
Add flag to enable time reasoning
Pialex99 Nov 29, 2022
09ef945
Add more test for time reasoning
Pialex99 Nov 29, 2022
72ab443
Add new tick builtin method
Pialex99 Dec 13, 2022
d341f99
Add calls to tick in loops
Pialex99 Dec 14, 2022
9366b68
Update tests
Pialex99 Dec 16, 2022
4f9b4da
Add more tests
Pialex99 Dec 21, 2022
dbbd4f0
Remove calls to tick to an exhale and an inhale for error reporting
Pialex99 Dec 23, 2022
4881b52
More tests and add an expression simplifier for methods
Pialex99 Dec 23, 2022
659b98f
Formating
Pialex99 Dec 23, 2022
e958a4a
Add position to time credits/receipts expr
Pialex99 Jan 3, 2023
c36040b
Add temporary error reporting
Pialex99 Jan 4, 2023
5ba2c1f
Add more complex tests
Pialex99 Jan 4, 2023
b029e94
Fix position for error reporting
Pialex99 Jan 4, 2023
b9f2128
Fix rebase
Pialex99 Jan 6, 2023
4267cde
Fix clippy error
Pialex99 Jan 6, 2023
b85cab5
Pull request reviews
Pialex99 Jan 11, 2023
3693f84
Add scope id to resource predicate for loop body
Pialex99 Jan 17, 2023
3356338
Add functions to modify resource predicate scope
Pialex99 Jan 17, 2023
4b46a5d
Add loop cost
Pialex99 Jan 18, 2023
0bb8266
More quadratic loops in a seperate test
Pialex99 Jan 19, 2023
31c8871
Clippy
Pialex99 Jan 25, 2023
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
9 changes: 9 additions & 0 deletions docs/dev-guide/src/config/flags.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
| [`SMT_SOLVER_WRAPPER_PATH`](#smt_solver_wrapper_path) | `Option<String>` | `None` | A |
| [`SMT_UNIQUE_TRIGGERS_BOUND`](#smt_unique_triggers_bound) | `Option<u64>` | `None` | A |
| [`SMT_UNIQUE_TRIGGERS_BOUND_TOTAL`](#smt_unique_triggers_bound_total) | `Option<u64>` | `None` | A |
| [`TIME_REASONING`](#time_reasoning) | `bool` | `false` | A |
| [`UNSAFE_CORE_PROOF`](#unsafe_core_proof) | `bool` | `false` | A |
| [`USE_MORE_COMPLETE_EXHALE`](#use_more_complete_exhale) | `bool` | `true` | A |
| [`USE_SMT_WRAPPER`](#use_smt_wrapper) | `bool` | `false` | A |
Expand Down Expand Up @@ -310,6 +311,7 @@ Comma-separated list of optimizations to enable, or `"all"` to enable all. Possi
- `"purify_vars"`
- `"fix_quantifiers"`
- `"fix_unfoldings"`
- `"simplify_exprs"`
- `"remove_unused_vars"`
- `"remove_trivial_assertions"`
- `"clean_cfg"`
Expand Down Expand Up @@ -429,6 +431,13 @@ If not `None`, checks that the total number of unique triggers reported by the S

> **Note:** Requires `USE_SMT_WRAPPER` to be `true`.

## `TIME_REASONING`

When enabled, the `time_credits` and `time_receipts` predicates are usable and functions' and loops' bodies will concume time credits and produce time receipts.

> **Note:** This is an experimental feature.
> **Note:** Requires `SIMPLIFY_ENCODING` to be `true` and the `simplify_exprs` optimization to be enabled.

## `UNSAFE_CORE_PROOF`

When enabled, the new core proof is used, suitable for unsafe code
Expand Down
1 change: 1 addition & 0 deletions prusti-common/src/vir/optimizations/bitvectors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ pub fn replace_all_ints(program: &mut vir_poly::Program) {
}
}
vir_poly::Predicate::Bodyless(..) => {}
vir_poly::Predicate::ResourceAccess(_) => {}
}
}
let types = [
Expand Down
7 changes: 7 additions & 0 deletions prusti-common/src/vir/optimizations/folding/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,13 @@ impl ast::FallibleExprFolder for ExprOptimizer {
Err(())
}

fn fallible_fold_resource_access_predicate(
&mut self,
_resource_access_predicate: vir::polymorphic::ResourceAccessPredicate,
) -> Result<vir::polymorphic::Expr, Self::Error> {
Err(())
}

fn fallible_fold_field_access_predicate(
&mut self,
_field_access_predicate: ast::FieldAccessPredicate,
Expand Down
113 changes: 49 additions & 64 deletions prusti-common/src/vir/optimizations/functions/simplifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ impl Simplifier for ast::Function {
/// <https://github.com/viperproject/silicon/issues/387>
fn simplify(mut self) -> Self {
trace!("[enter] simplify = {}", self);
let new_body = self.body.map(|b| b.simplify());
self.body = new_body;
self.body = self.body.map(|b| b.simplify());
self.posts = self.posts.into_iter().map(|p| p.simplify()).collect();
self.pres = self.pres.into_iter().map(|p| p.simplify()).collect();
trace!("[exit] simplify = {}", self);
self
}
Expand All @@ -30,8 +31,11 @@ impl Simplifier for ast::Function {
impl Simplifier for ast::Expr {
#[must_use]
fn simplify(self) -> Self {
trace!("[enter] simplify = {:?}", self);
let mut folder = ExprSimplifier {};
folder.fold(self)
let res = folder.fold(self);
trace!("[exit] simplify = {:?}", res);
res
}
}

Expand All @@ -46,134 +50,115 @@ impl ExprSimplifier {
argument:
box ast::Expr::Const(ast::ConstExpr {
value: ast::Const::Bool(b),
..
position: inner_pos,
}),
position: pos,
}) => ast::Expr::Const(ast::ConstExpr {
value: ast::Const::Bool(!b),
position: pos,
}),
})
.set_default_pos(inner_pos),
Pialex99 marked this conversation as resolved.
Show resolved Hide resolved
ast::Expr::UnaryOp(ast::UnaryOp {
op_kind: ast::UnaryOpKind::Not,
argument:
box ast::Expr::BinOp(ast::BinOp {
op_kind: ast::BinaryOpKind::EqCmp,
box left,
box right,
..
position: inner_pos,
}),
position: pos,
}) => ast::Expr::BinOp(ast::BinOp {
}) if !matches!(left.get_type(), ast::Type::Float(_)) => ast::Expr::BinOp(ast::BinOp {
op_kind: ast::BinaryOpKind::NeCmp,
left: box left,
right: box right,
position: pos,
}),
ast::Expr::BinOp(ast::BinOp {
op_kind: ast::BinaryOpKind::And,
left:
box ast::Expr::Const(ast::ConstExpr {
value: ast::Const::Bool(b1),
..
}),
right:
box ast::Expr::Const(ast::ConstExpr {
value: ast::Const::Bool(b2),
..
}),
position: pos,
}) => ast::Expr::Const(ast::ConstExpr {
value: ast::Const::Bool(b1 && b2),
position: pos,
}),
})
.set_default_pos(inner_pos),
ast::Expr::BinOp(ast::BinOp {
op_kind: ast::BinaryOpKind::And,
left:
box ast::Expr::Const(ast::ConstExpr {
value: ast::Const::Bool(b),
..
position: inner_pos,
}),
right: box conjunct,
..
position: pos,
})
| ast::Expr::BinOp(ast::BinOp {
op_kind: ast::BinaryOpKind::And,
left: box conjunct,
right:
box ast::Expr::Const(ast::ConstExpr {
value: ast::Const::Bool(b),
..
position: inner_pos,
}),
..
}) => {
if b {
conjunct
} else {
false.into()
}
position: pos,
}) => if b {
conjunct
Comment on lines +97 to +98
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vakaras If we inline a dummy trigger function body (with the body true) and also simplify, this would change semantics of quantifiers. Do you know in what order the inlining is performed?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this can be a problem. For this and other reasons, it is better to use domain functions as triggers (with an axiom that says its value is always true).

} else {
Into::<ast::Expr>::into(false).set_pos(inner_pos)
}
.set_default_pos(pos),
ast::Expr::BinOp(ast::BinOp {
op_kind: ast::BinaryOpKind::Or,
left:
box ast::Expr::Const(ast::ConstExpr {
value: ast::Const::Bool(b),
..
position: inner_pos,
}),
right: box disjunct,
..
position: pos,
})
| ast::Expr::BinOp(ast::BinOp {
op_kind: ast::BinaryOpKind::Or,
left: box disjunct,
right:
box ast::Expr::Const(ast::ConstExpr {
value: ast::Const::Bool(b),
..
position: inner_pos,
}),
..
}) => {
if b {
true.into()
} else {
disjunct
}
position: pos,
}) => if b {
Into::<ast::Expr>::into(true).set_pos(inner_pos)
} else {
disjunct
}
.set_default_pos(pos),
ast::Expr::BinOp(ast::BinOp {
op_kind: ast::BinaryOpKind::Implies,
left: guard,
right:
box ast::Expr::Const(ast::ConstExpr {
value: ast::Const::Bool(b),
..
position: inner_pos,
}),
position: pos,
}) => {
if b {
true.into()
} else {
ast::Expr::UnaryOp(ast::UnaryOp {
op_kind: ast::UnaryOpKind::Not,
argument: guard,
position: pos,
})
}
}) => if b {
Into::<ast::Expr>::into(true).set_pos(pos)
} else {
ast::Expr::UnaryOp(ast::UnaryOp {
op_kind: ast::UnaryOpKind::Not,
argument: guard,
position: pos,
})
}
.set_default_pos(inner_pos),
ast::Expr::BinOp(ast::BinOp {
op_kind: ast::BinaryOpKind::Implies,
left:
box ast::Expr::Const(ast::ConstExpr {
value: ast::Const::Bool(b),
..
position: inner_pos,
}),
right: box body,
..
}) => {
if b {
body
} else {
true.into()
}
position: pos,
}) => if b {
body
} else {
Into::<ast::Expr>::into(true).set_pos(inner_pos)
}
.set_default_pos(pos),
ast::Expr::BinOp(ast::BinOp {
op_kind: ast::BinaryOpKind::And,
left: box op1,
Expand Down
4 changes: 3 additions & 1 deletion prusti-common/src/vir/optimizations/methods/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ mod purifier;
mod quantifier_fixer;
mod unfolding_fixer;
mod var_remover;
mod simplifier;

use super::log_method;
use crate::{config::Optimizations, vir::polymorphic_vir::cfg::CfgMethod};

use self::{
assert_remover::remove_trivial_assertions, cfg_cleaner::clean_cfg,
empty_if_remover::remove_empty_if, purifier::purify_vars, quantifier_fixer::fix_quantifiers,
unfolding_fixer::fix_unfoldings, var_remover::remove_unused_vars,
simplifier::simplify_exprs, unfolding_fixer::fix_unfoldings, var_remover::remove_unused_vars,
};

#[allow(clippy::let_and_return)]
Expand Down Expand Up @@ -57,6 +58,7 @@ pub fn optimize_method_encoding(
};
let cfg = apply!(fix_unfoldings, cfg);
let cfg = apply!(fix_quantifiers, cfg);
let cfg = apply!(simplify_exprs, cfg);
let cfg = apply!(remove_empty_if, cfg);
let cfg = apply!(remove_unused_vars, cfg);
let cfg = apply!(remove_trivial_assertions, cfg);
Expand Down
7 changes: 4 additions & 3 deletions prusti-common/src/vir/optimizations/methods/purifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,9 @@ impl ast::StmtWalker for VarCollector {
if is_purifiable_method(method_name) {
self.is_pure_context = true;
}
assert!(arguments.is_empty());
for arg in arguments {
self.walk_expr(arg);
}
for target in targets {
self.walk_local_var(target);
}
Expand Down Expand Up @@ -464,8 +466,7 @@ impl ast::StmtFolder for VarPurifier {
mut targets,
}: ast::MethodCall,
) -> ast::Stmt {
assert!(targets.len() == 1);
if self.pure_vars.contains(&targets[0]) {
if targets.len() == 1 && self.pure_vars.contains(&targets[0]) {
let target = &targets[0];
let replacement = self
.replacements
Expand Down
45 changes: 45 additions & 0 deletions prusti-common/src/vir/optimizations/methods/simplifier.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// © 2022, ETH Zurich
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use log::debug;
use std::mem;
use vir::polymorphic::*;

use crate::vir::optimizations::functions::Simplifier;

/// This optimization simplifies all expressions in a method.
/// It is required when resource access predicates appear on the RHS of
/// implications as implications are transformed into ors which need to be
/// transformed back into implications otherwise, we would have an impure
/// expression in ors which is disallowed in Viper.

pub fn simplify_exprs(mut cfg: CfgMethod) -> CfgMethod {
debug!("Simplifying exprs in {}", cfg.name());
let mut sentinel_stmt = Stmt::comment("moved out stmt");
for block in &mut cfg.basic_blocks {
for stmt in &mut block.stmts {
mem::swap(&mut sentinel_stmt, stmt);
sentinel_stmt = sentinel_stmt.simplify();
mem::swap(&mut sentinel_stmt, stmt);
}
}
cfg
}

struct StmtSimplifier;

impl Simplifier for Stmt {
fn simplify(self) -> Self {
let mut folder = StmtSimplifier;
folder.fold(self)
}
}

impl StmtFolder for StmtSimplifier {
fn fold_expr(&mut self, expr: Expr) -> Expr {
expr.simplify()
}
}
1 change: 1 addition & 0 deletions prusti-common/src/vir/optimizations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ pub fn optimize_program(p: Program, source_file_name: &str) -> Program {
program.viper_predicates = predicates::delete_unused_predicates(
&program.methods,
&program.functions,
&program.builtin_methods,
program.viper_predicates,
);
}
Expand Down
Loading