Skip to content

Commit 91bbf20

Browse files
authored
Merge pull request #1580 from goblint/issue-1577
Add termination analysis success messages for loop bounds
2 parents fde89dd + 298a394 commit 91bbf20

File tree

2 files changed

+32
-20
lines changed

2 files changed

+32
-20
lines changed

src/analyses/loopTermination.ml

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ open TerminationPreprocessing
88
let loop_counters : stmt VarToStmt.t ref = ref VarToStmt.empty
99

1010
(** Checks whether a variable can be bounded. *)
11-
let check_bounded man varinfo =
11+
let ask_bound man varinfo =
1212
let open IntDomain.IntDomTuple in
1313
let exp = Lval (Var varinfo, NoOffset) in
1414
match man.ask (EvalInt exp) with
15-
| `Top -> false
16-
| `Lifted v -> not (is_top_of (ikind v) v)
15+
| `Top -> `Top
16+
| `Lifted v when is_top_of (ikind v) v -> `Top
17+
| `Lifted v -> `Lifted v
1718
| `Bot -> failwith "Loop counter variable is Bot."
1819

1920
(** We want to record termination information of loops and use the loop
@@ -41,28 +42,33 @@ struct
4142
let startstate _ = ()
4243
let exitstate = startstate
4344

44-
let find_loop ~loop_counter =
45-
VarToStmt.find loop_counter !loop_counters
46-
4745
(** Recognizes a call of [__goblint_bounded] to check the EvalInt of the
4846
* respective loop counter variable at that position. *)
4947
let special man (lval : lval option) (f : varinfo) (arglist : exp list) =
50-
if !AnalysisState.postsolving then
48+
if !AnalysisState.postsolving then (
5149
match f.vname, arglist with
52-
"__goblint_bounded", [Lval (Var loop_counter, NoOffset)] ->
53-
(try
54-
let loop_statement = find_loop ~loop_counter in
55-
let is_bounded = check_bounded man loop_counter in
56-
man.sideg () (G.add (`Lifted loop_statement) is_bounded (man.global ()));
57-
(* In case the loop is not bounded, a warning is created. *)
58-
if not (is_bounded) then (
59-
M.warn ~loc:(M.Location.CilLocation (Cilfacade.get_stmtLoc loop_statement)) ~category:Termination "The program might not terminate! (Loop analysis)"
60-
);
61-
()
62-
with Not_found ->
63-
failwith "Encountered a call to __goblint_bounded with an unknown loop counter variable.")
50+
| "__goblint_bounded", [Lval (Var loop_counter, NoOffset)] ->
51+
begin match VarToStmt.find_opt loop_counter !loop_counters with
52+
| Some loop_statement ->
53+
let bound = ask_bound man loop_counter in
54+
let is_bounded = bound <> `Top in
55+
man.sideg () (G.add (`Lifted loop_statement) is_bounded (man.global ()));
56+
let loc = M.Location.CilLocation (Cilfacade.get_stmtLoc loop_statement) in
57+
begin match bound with
58+
| `Top ->
59+
M.warn ~category:Termination ~loc "The program might not terminate! (Loop analysis)"
60+
| `Lifted bound ->
61+
(* TODO: aggregate these per loop (if unrolled) and warn using WarnGlobal? *)
62+
if GobConfig.get_bool "dbg.termination-bounds" then
63+
M.success ~category:Termination ~loc "Loop terminates: bounded by %a iteration(s)" IntDomain.IntDomTuple.pretty bound;
64+
end
65+
| None ->
66+
failwith "Encountered a call to __goblint_bounded with an unknown loop counter variable."
67+
end
68+
| "__goblint_bounded", _ ->
69+
failwith "__goblint_bounded call unexpected arguments"
6470
| _ -> ()
65-
else ()
71+
)
6672

6773
let query man (type a) (q: a Queries.t): a Queries.result =
6874
match q with

src/config/options.schema.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2197,6 +2197,12 @@
21972197
"description": "Output abstract values, etc. with full internal details, without readability-oriented simplifications.",
21982198
"type": "boolean",
21992199
"default": false
2200+
},
2201+
"termination-bounds": {
2202+
"title": "dbg.termination-bounds",
2203+
"description": "Output loop iteration bounds for terminating loops when termination analysis is activated.",
2204+
"type": "boolean",
2205+
"default": false
22002206
}
22012207
},
22022208
"additionalProperties": false

0 commit comments

Comments
 (0)