Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
5637aa7
GPS-related modifications
ruijiefang Jul 5, 2024
9e83097
update API of srk
ruijiefang Jul 10, 2024
c1de851
Various updates
ruijiefang Aug 28, 2024
c4d2177
Various updates
ruijiefang Aug 28, 2024
e26228a
:wq
ruijiefang Sep 9, 2024
262d615
Update
ruijiefang Sep 23, 2024
e35c6c9
Fixed bug: NNF conversion when positive formula is expected
zkincaid Oct 25, 2024
5dadb86
GPS: display graphs only when enabled on command line
zkincaid Oct 25, 2024
7ea75bf
GPS: Suppress trace info by default
zkincaid Oct 25, 2024
2850c10
GPS: only query oracle for non-deterministic transitions
zkincaid Oct 26, 2024
13f5bda
Merge remote-tracking branch 'upstream/modern' into modern
zkincaid Oct 26, 2024
bf5d25c
Fixed build
zkincaid Oct 26, 2024
f91a6a3
Bug fix: eliminate floor, mod, and div before abstraction
zkincaid Oct 26, 2024
9325974
GPS: merge all error locations
zkincaid Oct 26, 2024
fd183b1
GPS: fix force_cover
zkincaid Nov 2, 2024
fc1f132
GPS: bug fix in gas instrumentation
zkincaid Nov 2, 2024
e92366f
GPS: restore loop acceleration
zkincaid Nov 2, 2024
a228968
Bugfix in WeightedGraph/path_weight for vertices not reachable from src
zkincaid Nov 2, 2024
3bd4157
Fix test build
zkincaid Nov 2, 2024
9a097ab
GPS: dualized Tarjan's algorithm
zkincaid Nov 2, 2024
25ffd6b
Fixed build
zkincaid Nov 2, 2024
d0a88c7
Bugfix for TransitionSytem.remove_temporaries transformation
zkincaid Nov 5, 2024
452304b
GPS: interpolation bug
zkincaid Nov 5, 2024
5e08662
GPS: new flag that substitutes T/F for summaries
ruijiefang Nov 5, 2024
a18a90c
gps: fixup
ruijiefang Nov 5, 2024
6e1e8e6
translate bitwise ops as logical ops + __VERIFIER_nondet_uchar, __VER…
ruijiefang Nov 5, 2024
9a521f0
incremental maintenence of leaf nodes to avoid stack blowup
ruijiefang Nov 6, 2024
dca0454
gps: minor fixes
ruijiefang Nov 7, 2024
5faab7d
[gps] perform instrumentation before invariant generation
ruijiefang Nov 18, 2024
adf58dc
Debugging changes
ruijiefang Mar 18, 2025
3b44dab
Merge branch 'gps' into HEAD
ruijiefang Mar 19, 2025
11c322c
[GPS] performance fixes and summary-guided testing
ruijiefang Mar 21, 2025
dbc4bab
oops
ruijiefang Mar 21, 2025
0010c11
treat free variables as universally quantified in interpolate
ruijiefang Mar 23, 2025
c30b1e1
Remove dead code
zkincaid Apr 18, 2025
bfd475e
Refactoring
zkincaid Apr 23, 2025
c0df794
Spacing
zkincaid Apr 23, 2025
c20d16a
Removed unncessary ref cells
zkincaid Apr 24, 2025
ee48b2a
Removed explicit leaf-tracking from ART
zkincaid Apr 24, 2025
56eb088
GPS: Use a dynamic array instead of maps to represent totally-defined…
zkincaid Apr 25, 2025
b2106f3
Refactored GPS
zkincaid Apr 28, 2025
b571571
IMPACT implementation
zkincaid Apr 28, 2025
9afc69f
Removed redundant data from GPS intraprocedural context
zkincaid Apr 28, 2025
ea07f9f
Merge remote-tracking branch 'ruijie/gps-interpolation-bugfix' into gps
zkincaid Apr 28, 2025
1a678b1
add inlining [1/n]
ruijiefang Jun 13, 2025
bd5ca70
-w
ruijiefang Jun 14, 2025
b07f52f
remove printing
ruijiefang Jun 14, 2025
d627330
more changes to inliner
ruijiefang Jun 27, 2025
148cfad
inliner bugfix
ruijiefang Jun 28, 2025
6df9d74
restore CIL translation
ruijiefang Jun 28, 2025
eef8cc1
refactorings to GPS
ruijiefang Jul 3, 2025
e1d04ca
refactor intraprocedural algorithm, take out interprocedural code for…
ruijiefang Jul 3, 2025
a81eb59
remove dead code
ruijiefang Jul 21, 2025
0edf91f
update .gitignore
ruijiefang Jul 28, 2025
f992c51
start work on interpolation module
ruijiefang Jul 28, 2025
b2184d7
finish up refactored interpolation interface
ruijiefang Jul 28, 2025
ef4fd8b
move things around
ruijiefang Jul 28, 2025
b05273e
first cut of future-live variables analysis
ruijiefang Jul 29, 2025
d57a2bf
future- and past-variables analysis
ruijiefang Jul 29, 2025
f20272f
past-live analysis code
ruijiefang Jul 29, 2025
efd2769
fix generate_test
ruijiefang Aug 7, 2025
064eadc
more fixes
ruijiefang Aug 7, 2025
ac22c76
add new stats
ruijiefang Aug 8, 2025
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
*.install
*.annot
*.cmo
*.cma
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
all: build

build:
dune build duet
dune build duet

clean:
dune clean

test:
dune runtest -f
dune runtest --profile release -f

install:
dune build @install
Expand Down
8 changes: 7 additions & 1 deletion duet/cmdLine.ml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ let verbose_arg =
Arg.String (fun v -> Log.set_verbosity_level v `info),
" Raise verbosity for a particular module")

let trace_arg =
("-trace",
Arg.String (fun v -> Log.set_verbosity_level v `trace),
" Set verbosity for a particular module to trace")

let verbose_list_arg =
("-verbose-list",
Arg.Unit (fun () ->
Expand All @@ -36,7 +41,7 @@ let verbose_list_arg =
) Log.loggers;
exit 0;
),
" List modules which can be used with -verbose")
" List modules which can be used with -verbose/-trace")

let stats_arg =
("-stats", Arg.Set show_stats, " Display statistics")
Expand Down Expand Up @@ -113,6 +118,7 @@ let debug_args = ref
let config_args = ref
[
verbose_arg;
trace_arg;
verbose_list_arg;
verbosity_arg;
stats_arg;
Expand Down
237 changes: 188 additions & 49 deletions duet/cra.ml
Original file line number Diff line number Diff line change
Expand Up @@ -90,45 +90,79 @@ let map_value f = function
| VPos v -> VPos (f v)
| VWidth v -> VWidth (f v)

module V = struct
module I = struct
type t = value [@@deriving ord]
let pp formatter = function
| VVal v -> Var.pp formatter v
| VWidth v -> Format.fprintf formatter "%a@@width" Var.pp v
| VPos v -> Format.fprintf formatter "%a@@pos" Var.pp v
let show = SrkUtil.mk_show pp
let equal x y = compare x y = 0
let hash = function
| VVal v -> Hashtbl.hash (Var.hash v, 0)
| VPos v -> Hashtbl.hash (Var.hash v, 1)
| VWidth v -> Hashtbl.hash (Var.hash v, 2)
end
include I

let sym_to_var = Hashtbl.create 991
let var_to_sym = ValueHT.create 991

let typ v = tr_typ (Var.get_type (var_of_value v))

let symbol_of var =
if ValueHT.mem var_to_sym var then
ValueHT.find var_to_sym var
else begin
let sym = Ctx.mk_symbol ~name:(show var) (typ var) in
ValueHT.add var_to_sym var sym;
Hashtbl.add sym_to_var sym var;
sym
module V = struct
module I = struct
type t = value [@@deriving ord]
let pp formatter = function
| VVal v -> Var.pp formatter v
| VWidth v -> Format.fprintf formatter "%a@@width" Var.pp v
| VPos v -> Format.fprintf formatter "%a@@pos" Var.pp v
let show = SrkUtil.mk_show pp
let equal x y = compare x y = 0
let hash = function
| VVal v -> Hashtbl.hash (Var.hash v, 0)
| VPos v -> Hashtbl.hash (Var.hash v, 1)
| VWidth v -> Hashtbl.hash (Var.hash v, 2)
end

let of_symbol sym =
if Hashtbl.mem sym_to_var sym then
Some (Hashtbl.find sym_to_var sym)
else
None

let is_global = Var.is_global % var_of_value
end
include I



let sym_to_var = Hashtbl.create 991
let var_to_sym = ValueHT.create 991
let prophecy_vars = ValueHT.create 991 (* var -> var mapping *)
let var_of_prophecy_vars = ValueHT.create 991 (* reverse mapping of prophecy_vars *)

let typ v = tr_typ (Var.get_type (var_of_value v))

let symbol_of var =
if ValueHT.mem var_to_sym var then
ValueHT.find var_to_sym var
else begin
let sym = Ctx.mk_symbol ~name:(show var) (typ var) in
ValueHT.add var_to_sym var sym;
Hashtbl.add sym_to_var sym var;
sym
end

let of_symbol sym =
if Hashtbl.mem sym_to_var sym then
Some (Hashtbl.find sym_to_var sym)
else
None

let is_global = Var.is_global % var_of_value

let make_var sym is_global =
let v =
begin match is_global with
| true ->
Varinfo.mk_global (Syntax.show_symbol srk sym) (Concrete (Int 8))
| false ->
Varinfo.mk_local (Syntax.show_symbol srk sym) (Concrete (Int 8))
end |> Var.mk
in
Hashtbl.add sym_to_var sym (VVal v);
ValueHT.add var_to_sym (VVal v) sym;
VVal v

let prophesize var =

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Do we need prophesy vars?

let sym = symbol_of var in
let sym_name = (Syntax.show_symbol srk sym) ^ "_prophecy" in
let sym' = Syntax.mk_symbol srk ~name:sym_name (Syntax.typ_symbol srk sym) in
let var' = make_var sym' false in
ValueHT.add prophecy_vars var var';
ValueHT.add var_of_prophecy_vars var' var;
var'

let var_of_prophecy_var var' = ValueHT.find_opt var_of_prophecy_vars var'
let prophecy_var_of_var var = ValueHT.find_opt prophecy_vars var
let is_prophecy_var v =
match var_of_prophecy_var v with
| Some _ -> true
| None -> false
end


module K = struct
module Tr = Transition.Make(Ctx)(V)
Expand Down Expand Up @@ -212,6 +246,8 @@ module K = struct
Log.time "cra:star" star x

let project = exists V.is_global

let project_custom v = exists v
end

type ptr_term =
Expand Down Expand Up @@ -751,21 +787,114 @@ let decorate_transition_system predicates ts entry =
let invariant =
abstract_domain.formula_of (abstract_domain.exists (member live) (inv v))
in
logf "loop header location: %d" v;
logf "Found invariant at %d:@;%a" v (Syntax.Formula.pp srk) invariant;
logf "New ID: %d" fresh_id;
WG.split_vertex ts v (Weight (K.assume invariant)) fresh_id)
ts

let make_transition_system rg =
module VSet = BatSet.Make(V)
module ISet = BatSet.Make(Int)

let create_gas_variable () =
let mk_int k = Ctx.mk_real (QQ.of_int k) in
let gas_var = Var.mk (Varinfo.mk_global "__duet_gas" (Concrete (Int 8))) in
let gas_var_sym = Syntax.mk_symbol srk ~name:"__duet_gas" `TyInt in
let gas_var_term = Syntax.mk_const srk gas_var_sym in
Hashtbl.add V.sym_to_var gas_var_sym (VVal gas_var);
ValueHT.add V.var_to_sym (VVal gas_var) gas_var_sym;
let gasexpr = (Syntax.mk_lt srk (mk_int 0) gas_var_term) in
let gasweight =
let assume_positive = K.assume gasexpr in
let decr_by_one = Syntax.mk_sub srk gas_var_term (mk_int 1) |> K.assign (VVal gas_var) in
K.mul assume_positive decr_by_one in
let initial_gas = K.havoc [ VVal gas_var ] in
(gasweight, gasexpr, initial_gas)

let new_vtx () = (Def.mk (Assume Bexpr.ktrue)).did

let instrument_with_gas (ts: TSG.t) gasexpr : TSG.t =
let modify_pre ts u =
Printf.printf " --- %d is call edge\n" u;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Generally use Log.logf in place of Printf.printf so that debugging info can be turned on / off selectively

let g = ref ts in
(* step 1: add new in-edge to (u, v) *)
let x = new_vtx () in
g := WG.add_vertex !g x;
(* step 2: add weighted edge x-(gasexpr)->u *)
g := WG.add_edge !g x (Weight gasexpr) u;
(* step 3: redirect every p->u to be y->x->u *)
WG.iter_pred_e (fun (p, weight, _) ->
Printf.printf "changing %d->%d to %d->%d->%d\n" p u p x u;
g := WG.add_edge !g p weight x;
g := WG.remove_edge !g p u
) ts u;
!g in
let modify_post ts u =
Printf.printf " --- %d is loop header\n" u;
let g = ref ts in
let x = new_vtx () in
(* step 1: add new out-edge from u -> x *)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

WG.split_vertex g u (new_vtx ()) (Weight gasexpr) does steps 1 + 2.

g := WG.add_vertex !g x;
g := WG.add_edge !g u (Weight gasexpr) x;
(* step 2: for each u -> v, make it u -> x -> v *)
WG.iter_succ_e (fun (_, weight, v) ->
Printf.printf "changing %d->%d to %d->%d->%d\n" u v u x v;
g := WG.add_edge !g x weight v;
g := WG.remove_edge !g u v
) ts u;
!g in
(* for each call-edge, u->v, add new predecessor edge x->u->v where x->u is an instrumented edge. *)
let loop_headers =
let module L = Loop.Make(TSG) in
(List.map (fun loop -> L.header loop) @@ L.all_loops (L.loop_nest ts))
|> List.map (fun x -> (x, true)) in
let call_edge_headers, _ =
WG.fold_edges (fun (u, w, _) (headers, callees) ->
match w with
| Call (s, _) -> ISet.add u headers, ISet.add s callees
| Weight _ -> (headers, callees)) ts (ISet.empty, ISet.empty) in

List.fold_left (fun ts (header, is_call_edge) ->
if is_call_edge then
modify_post ts header
else
modify_pre ts header) ts
(loop_headers
@ (List.map (fun x -> (x, false)) @@ ISet.to_list call_edge_headers))

let instrument_main tg entry init_gas_expr =
let post_entry = new_vtx () in
(* step 1: add a vertex called post_entry *)
let tg = WG.add_vertex tg post_entry in

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Do we need to initialize gas?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

If we don't initialize it, it retains its (non-deterministic) initial value.

(* step 2: remove each edge (entry->v) and make it (post_entry->v) *)
let gg = ref tg in
WG.iter_succ_e (fun (u, w, v) ->
assert (u = entry);
gg := WG.remove_edge !gg entry v;
gg := WG.add_edge !gg post_entry w v
) tg entry;
(* step 3: add edge from entry->post_entry labeled by init_gas_expr *)
WG.add_edge !gg entry (Weight init_gas_expr) post_entry

let make_transition_system ?(simplify=true) ?(instr_gas=false) (main_entry: int) rg =
let gasweight, gasexpr, init_gas_weight = create_gas_variable () in
let call_edge block =
Call ((RG.block_entry rg block).did, (RG.block_exit rg block).did)
in
let assertions = ref SrkUtil.Int.Map.empty in
let add_assert v e =
assertions := SrkUtil.Int.Map.add v e (!assertions)
let assert_vars = ref VSet.empty in
let add_assert v (cond, loc, msg) =
assertions := SrkUtil.Int.Map.add v (cond, loc, msg) (!assertions);
let open Syntax in
Symbol.Set.iter (fun (s : Syntax.symbol) ->
match V.of_symbol s with
| Some v -> assert_vars := VSet.add v (!assert_vars)
| None -> ())
(Syntax.symbols cond)
in
let ts =
BatEnum.fold (fun ts (block, graph) ->
let tg =
BatEnum.fold (fun ts (block, graph) -> (* CFG of current function *)
let tg = (* TS of current CFG *)
RG.G.fold_vertex (fun def tg ->
let tg = WG.add_vertex tg def.did in
let label =
Expand Down Expand Up @@ -820,8 +949,14 @@ let make_transition_system rg =
let point_of_interest v =
v = entry || v = exit || SrkUtil.Int.Map.mem v (!assertions)
in
let tg = TS.simplify point_of_interest tg in
let tg = TS.remove_temporaries tg in
let elim_var v =
V.is_global v || VSet.mem v (!assert_vars)
in
let tg = if (instr_gas && entry = main_entry) then instrument_main tg entry init_gas_weight else tg in
let tg = if instr_gas then instrument_with_gas tg gasweight else tg in
let predicates = if instr_gas then gasexpr :: predicates else predicates in

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Shouldn't need to add gasexpr to predicates -- the abstract domain is the reduced product of affine relations + signs + 2^predicates -- so sign analysis will already infer gas >= 0.

let tg = if simplify then TS.simplify point_of_interest tg else tg in
let tg = TS.remove_temporaries elim_var tg in
let tg =
if !forward_inv_gen then
Log.phase "Forward invariant generation"
Expand All @@ -841,7 +976,10 @@ let make_transition_system rg =
TS.empty
(RG.bodies rg)
in
(ts, !assertions)
(* perform some inlining *)
let inlined_ts, new_assertions =
TS.inline ts main_entry (fun _ -> ()) (!assertions) in
(inlined_ts, new_assertions)

let mk_query ts entry =
TS.mk_query ts entry
Expand All @@ -856,7 +994,7 @@ let analyze file =
| [main] -> begin
let rg = Interproc.make_recgraph file in
let entry = (RG.block_entry rg main).did in
let (ts, assertions) = make_transition_system rg in
let (ts, assertions) = make_transition_system entry rg in
(*TSDisplay.display ts;*)
let query = mk_query ts entry in
assertions |> SrkUtil.Int.Map.iter (fun v (phi, loc, msg) ->
Expand Down Expand Up @@ -1093,7 +1231,7 @@ let prove_termination_main file =
| [main] -> begin
let rg = Interproc.make_recgraph file in
let entry = (RG.block_entry rg main).did in
let (ts, _) = make_transition_system rg in
let (ts, _) = make_transition_system entry rg in
if !CmdLine.display_graphs then
TSDisplay.display ts;
let query = mk_query ts entry in
Expand Down Expand Up @@ -1152,7 +1290,8 @@ let resource_bound_analysis file =
match file.entry_points with
| [main] -> begin
let rg = Interproc.make_recgraph file in
let (ts, _) = make_transition_system rg in
let entry = (RG.block_entry rg main).did in
let (ts, _) = make_transition_system entry rg in
let entry = (RG.block_entry rg main).did in
let query = mk_query ts entry in
let cost =
Expand Down
7 changes: 7 additions & 0 deletions duet/duet.ml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,20 @@ open! Cra
open! Proofspace
open! Dependence
open! Categorize
open! Gps

let usage_msg = "Duet program analyzer\nUsage: duet [OPTIONS] file.[c|bp]"

let anon_fun s = ignore (CmdLine.parse s)

let _ =
Sys.set_signal Sys.sigtstp (Sys.Signal_handle (fun _ -> Log.print_stats ()));
Printexc.record_backtrace true;;
Sys.set_signal Sys.sigint (Sys.Signal_handle (fun _ ->
Printf.eprintf "SIGINT received. Backtrace:\n%!";
Printexc.print_backtrace stderr;
exit 1
));;
let spec_list = CmdLine.spec_list () in
Arg.parse (Arg.align spec_list) anon_fun usage_msg;
match !CfgIr.gfile with
Expand Down
Loading