-
Notifications
You must be signed in to change notification settings - Fork 88
Expand file tree
/
Copy pathloopTermination.ml
More file actions
91 lines (76 loc) · 3.55 KB
/
loopTermination.ml
File metadata and controls
91 lines (76 loc) · 3.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
(** Termination analysis for loops and [goto] statements ([termination]).
@see <https://theses.hal.science/tel-00288805> Halbwachs, N. Détermination automatique de relations linéaires vérifiées par les variables d’un programme. PhD thesis. Section 8.3. *)
open Analyses
open GoblintCil
open TerminationPreprocessing
(** Contains all loop counter variables (varinfo) and maps them to their corresponding loop statement. *)
let loop_counters : stmt VarToStmt.t ref = ref VarToStmt.empty
(** Checks whether a variable can be bounded. *)
let ask_bound man varinfo =
let open IntDomain.IntDomTuple in
let exp = Lval (Var varinfo, NoOffset) in
match man.ask (EvalInt exp) with
| `Top -> `Top
| `Lifted v when is_top_of (ikind v) v -> `Top
| `Lifted v -> `Lifted v
| `Bot -> failwith "Loop counter variable is Bot."
(** The termination analysis considering loops and gotos *)
module Spec : Analyses.MCPSpec =
struct
include Analyses.IdentitySpec
let name () = "termination"
module D = Lattice.Unit
include Analyses.ValueContexts(D)
module V = struct
include UnitV
let is_write_only _ = true
end
(** We want to record termination information of loops and use the loop statements for that. *)
module G = MapDomain.PatriciaMapBot (CilType.Stmt) (BoolDomain.MustBool)
let startstate _ = ()
let exitstate = startstate
(** Recognizes a call of [__goblint_bounded] to check the EvalInt of the
* respective loop counter variable at that position. *)
let special man (lval : lval option) (f : varinfo) (arglist : exp list) =
if !AnalysisState.postsolving then (
match f.vname, arglist with
| "__goblint_bounded", [Lval (Var loop_counter, NoOffset)] ->
begin match VarToStmt.find_opt loop_counter !loop_counters with
| Some loop_statement ->
let bound = ask_bound man loop_counter in
let is_bounded = bound <> `Top in
man.sideg () (G.add loop_statement is_bounded (man.global ()));
let loc = M.Location.CilLocation (Cilfacade.get_stmtLoc loop_statement) in
begin match bound with
| `Top ->
M.warn ~category:Termination ~loc "The program might not terminate! (Loop analysis)"
| `Lifted bound ->
(* TODO: aggregate these per loop (if unrolled) and warn using WarnGlobal? *)
if GobConfig.get_bool "dbg.termination-bounds" then
M.success ~category:Termination ~loc "Loop terminates: bounded by %a iteration(s)" IntDomain.IntDomTuple.pretty bound;
end
| None ->
failwith "Encountered a call to __goblint_bounded with an unknown loop counter variable."
end
| "__goblint_bounded", _ ->
failwith "__goblint_bounded call unexpected arguments"
| _ -> ()
)
let query man (type a) (q: a Queries.t): a Queries.result =
match q with
| Queries.MustTermLoop loop_statement ->
let multithreaded = man.ask Queries.IsEverMultiThreaded in
(not multithreaded)
&& (BatOption.default false (G.find_opt loop_statement (man.global ())))
| Queries.MustTermAllLoops ->
let multithreaded = man.ask Queries.IsEverMultiThreaded in
if multithreaded then (
M.warn ~category:Termination "The program might not terminate! (Multithreaded)";
false)
else
G.for_all (fun _ term_info -> term_info) (man.global ())
| _ -> Queries.Result.top q
end
let () =
Cilfacade.register_preprocess (Spec.name ()) (new loopCounterVisitor loop_counters);
MCP.register_analysis (module Spec : MCPSpec)