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

Implement delayed widening #1483

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
19 changes: 19 additions & 0 deletions src/config/options.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1126,6 +1126,25 @@
"description": "Delay widenings using widening tokens.",
"type": "boolean",
"default": false
},
"delay": {
"title": "ana.widen.delay",
"type": "object",
"properties": {
"local": {
"title": "ana.widen.delay.local",
"description": "Delay local widenings by count.",
"type": "integer",
"default": 0
},
"global": {
"title": "ana.widen.delay.global",
"description": "Delay global widenings by count.",
"type": "integer",
"default": 0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
Expand Down
2 changes: 2 additions & 0 deletions src/framework/control.ml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ let spec_module: (module Spec) Lazy.t = lazy (
(module MCP.MCP2 : Spec)
|> lift (get_int "ana.context.gas_value" >= 0) (module ContextGasLifter)
|> lift true (module WidenContextLifterSide) (* option checked in functor *)
|> lift (get_int "ana.widen.delay.local" > 0) (module WideningDelay.DLifter)
(* hashcons before witness to reduce duplicates, because witness re-uses contexts in domain and requires tag for PathSensitive3 *)
|> lift (get_bool "ana.opt.hashcons" || arg_enabled) (module HashconsContextLifter)
|> lift arg_enabled (module HashconsLifter)
Expand All @@ -41,6 +42,7 @@ let spec_module: (module Spec) Lazy.t = lazy (
|> lift (get_bool "ana.widen.tokens") (module WideningTokens.Lifter)
|> lift true (module LongjmpLifter)
|> lift termination_enabled (module RecursionTermLifter) (* Always activate the recursion termination analysis, when the loop termination analysis is activated*)
|> lift (get_int "ana.widen.delay.global" > 0) (module WideningDelay.GLifter)
)
in
GobConfig.building_spec := false;
Expand Down
12 changes: 10 additions & 2 deletions src/goblint_lib.ml
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,16 @@ module AbortUnless = AbortUnless
module PtranalAnalysis = PtranalAnalysis


(** {1 Analysis lifters}

Transformations of analyses into extended analyses. *)

module WideningDelay = WideningDelay
module WideningTokens = WideningTokens

module WitnessConstraints = WitnessConstraints


(** {1 Domains}

Domains used by analysis specifications and constraint systems are {{!Lattice.S} lattices}.
Expand Down Expand Up @@ -326,7 +336,6 @@ module WitnessUtil = WitnessUtil
Automaton-based GraphML witnesses used in SV-COMP. *)

module MyARG = MyARG
module WitnessConstraints = WitnessConstraints
module ArgTools = ArgTools
module Witness = Witness
module Graphml = Graphml
Expand All @@ -337,7 +346,6 @@ module Graphml = Graphml

module YamlWitness = YamlWitness
module YamlWitnessType = YamlWitnessType
module WideningTokens = WideningTokens

(** {3 Violation}

Expand Down
181 changes: 181 additions & 0 deletions src/lifters/wideningDelay.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
(** Standard widening delay with counters.

Abstract elements are paired with an integer counter, indicating how many times it has been widened.
Lifted abstract elements are only widened if the counter exceeds a predefined limit. *)

open Batteries
open Lattice
open Analyses

module LocalChainParams: Printable.ChainParams =
struct
let n () = GobConfig.get_int "ana.widen.delay.local"
let names = string_of_int
end

module GlobalChainParams: Printable.ChainParams =
struct
let n () = GobConfig.get_int "ana.widen.delay.global"
let names = string_of_int
end

module Dom (Base: S) (ChainParams: Printable.ChainParams) =
struct
module Chain = Printable.Chain (ChainParams)
include Printable.Prod (Base) (Chain)

let bot () = (Base.bot (), 0)
let is_bot (b, _) = Base.is_bot b
let top () = (Base.top (), ChainParams.n ())
let is_top (b, _) = Base.is_top b

let leq (b1, _) (b2, _) = Base.leq b1 b2

(** All operations keep maximal counter. *)
let join (b1, i1) (b2, i2) = (Base.join b1 b2, max i1 i2)
let meet (b1, i1) (b2, i2) = (Base.meet b1 b2, max i1 i2)
let widen (b1, i1) (b2, i2) =
let i' = max i1 i2 in
if i' < ChainParams.n () then
(Base.join b1 b2, i' + 1)
else
(Base.widen b1 b2, i') (* Don't increase beyond n, otherwise TD3 will not reach fixpoint w.r.t. equal. *)
let narrow (b1, i1) (b2, i2) = (Base.narrow b1 b2, max i1 i2)

let pretty_diff () ((b1, _), (b2, _)) =
Base.pretty_diff () (b1, b2) (* Counters cannot violate leq. *)
end


module DLifter (S: Spec): Spec =
struct
module D =
struct
include Dom (S.D) (LocalChainParams)

let printXml f (b, i) =
BatPrintf.fprintf f "%a<analysis name=\"widen-delay\">%a</analysis>" S.D.printXml b Chain.printXml i
end
module G = S.G
module C = S.C
module V = S.V
module P =
struct
include S.P
let of_elt (x, _) = of_elt x
end

let name () = S.name () ^ " with widening delay"

type marshal = S.marshal
let init = S.init
let finalize = S.finalize

let startstate v = (S.startstate v, 0)
let exitstate v = (S.exitstate v, 0)
let morphstate v (d, l) = (S.morphstate v d, l)

let conv (ctx: (D.t, G.t, C.t, V.t) ctx): (S.D.t, S.G.t, S.C.t, S.V.t) ctx =
{ ctx with local = fst ctx.local
; split = (fun d es -> ctx.split (d, 0) es)
}

let context ctx fd (d, _) = S.context (conv ctx) fd d
let startcontext () = S.startcontext ()

let lift_fun ctx f g h =
f @@ h (g (conv ctx))

let lift d = (d, 0)

let sync ctx reason = lift_fun ctx lift S.sync ((|>) reason)
let query ctx (type a) (q: a Queries.t): a Queries.result = S.query (conv ctx) q
let assign ctx lv e = lift_fun ctx lift S.assign ((|>) e % (|>) lv)
let vdecl ctx v = lift_fun ctx lift S.vdecl ((|>) v)
let branch ctx e tv = lift_fun ctx lift S.branch ((|>) tv % (|>) e)
let body ctx f = lift_fun ctx lift S.body ((|>) f)
let return ctx r f = lift_fun ctx lift S.return ((|>) f % (|>) r)
let asm ctx = lift_fun ctx lift S.asm identity
let skip ctx = lift_fun ctx lift S.skip identity
let special ctx r f args = lift_fun ctx lift S.special ((|>) args % (|>) f % (|>) r)

let enter ctx r f args =
let liftmap = List.map (Tuple2.mapn lift) in
lift_fun ctx liftmap S.enter ((|>) args % (|>) f % (|>) r)
let combine_env ctx r fe f args fc es f_ask = lift_fun ctx lift S.combine_env (fun p -> p r fe f args fc (fst es) f_ask)
let combine_assign ctx r fe f args fc es f_ask = lift_fun ctx lift S.combine_assign (fun p -> p r fe f args fc (fst es) f_ask)

let threadenter ctx ~multiple lval f args = lift_fun ctx (List.map lift) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval)
let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx lift (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval)

let paths_as_set ctx =
let liftmap = List.map (fun x -> (x, snd ctx.local)) in
lift_fun ctx liftmap S.paths_as_set Fun.id

let event ctx e octx =
lift_fun ctx lift S.event ((|>) (conv octx) % (|>) e)
end

module GLifter (S: Spec): Spec =
struct
module D = S.D
module G =
struct
include Dom (S.G) (GlobalChainParams)

let printXml f (b, i) =
BatPrintf.fprintf f "%a<analysis name=\"widen-delay\">%a</analysis>" S.G.printXml b Chain.printXml i
end
module C = S.C
module V = S.V
module P = S.P

let name () = S.name () ^ " with widening delay"

type marshal = S.marshal
let init = S.init
let finalize = S.finalize

let startstate v = S.startstate v
let exitstate v = S.exitstate v
let morphstate v d = S.morphstate v d

let conv (ctx: (D.t, G.t, C.t, V.t) ctx): (S.D.t, S.G.t, S.C.t, S.V.t) ctx =
{ ctx with global = (fun v -> fst (ctx.global v))
; sideg = (fun v g -> ctx.sideg v (g, 0))
}

let context ctx fd d = S.context (conv ctx) fd d
let startcontext () = S.startcontext ()

let lift_fun ctx f g h =
f @@ h (g (conv ctx))

let lift d = d

let sync ctx reason = lift_fun ctx lift S.sync ((|>) reason)
let query ctx (type a) (q: a Queries.t): a Queries.result = S.query (conv ctx) q
let assign ctx lv e = lift_fun ctx lift S.assign ((|>) e % (|>) lv)
let vdecl ctx v = lift_fun ctx lift S.vdecl ((|>) v)
let branch ctx e tv = lift_fun ctx lift S.branch ((|>) tv % (|>) e)
let body ctx f = lift_fun ctx lift S.body ((|>) f)
let return ctx r f = lift_fun ctx lift S.return ((|>) f % (|>) r)
let asm ctx = lift_fun ctx lift S.asm identity
let skip ctx = lift_fun ctx lift S.skip identity
let special ctx r f args = lift_fun ctx lift S.special ((|>) args % (|>) f % (|>) r)

let enter ctx r f args =
let liftmap = List.map (Tuple2.mapn lift) in
lift_fun ctx liftmap S.enter ((|>) args % (|>) f % (|>) r)
let combine_env ctx r fe f args fc es f_ask = lift_fun ctx lift S.combine_env (fun p -> p r fe f args fc es f_ask)
let combine_assign ctx r fe f args fc es f_ask = lift_fun ctx lift S.combine_assign (fun p -> p r fe f args fc es f_ask)

let threadenter ctx ~multiple lval f args = lift_fun ctx (List.map lift) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval)
let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx lift (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval)

let paths_as_set ctx =
lift_fun ctx Fun.id S.paths_as_set Fun.id

let event ctx e octx =
lift_fun ctx lift S.event ((|>) (conv octx) % (|>) e)
end
File renamed without changes.
14 changes: 14 additions & 0 deletions tests/regression/82-widen/01-mine-tutorial-ex4.8-delay.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// PARAM: --disable ana.int.def_exc --enable ana.int.interval --enable ana.sv-comp.functions --set ana.widen.delay.local 1
#include <goblint.h>
extern _Bool __VERIFIER_nondet_bool();
int main() {
int v = 0;
while (__VERIFIER_nondet_bool() == 0) {
__goblint_check(0 <= v);
__goblint_check(v <= 1);
if (v == 0)
v = 1;
// ...
}
return 0;
}
20 changes: 20 additions & 0 deletions tests/regression/82-widen/02-mihaila-widen-fig7-delay.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// PARAM: --disable ana.int.def_exc --enable ana.int.interval --enable ana.sv-comp.functions --set ana.widen.delay.local 3
// From "Widening as Abstract Domain", Fig. 7: https://doi.org/10.1007/978-3-642-38088-4_12
// They claim delay 2 (https://bytebucket.org/mihaila/bindead/wiki/resources/widening-talk.pdf), we need 3 for some reason. Why?
#include <goblint.h>
extern _Bool __VERIFIER_nondet_bool();

int main() {
int x = 0;
int y = 0;
while (x < 100) {
__goblint_check(0 <= y);
__goblint_check(y <= 1);
if (__VERIFIER_nondet_bool())
y = 1;
x = x + 4;
}
__goblint_check(0 <= y);
__goblint_check(y <= 1);
return 0;
}
35 changes: 35 additions & 0 deletions tests/regression/82-widen/03-mine14-delay.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// PARAM: --set ana.path_sens[+] threadflag --set ana.base.privatization mutex-meet-tid --enable ana.int.interval --set ana.widen.delay.local 100 --set ana.widen.delay.global 100
// Fig 5a from Miné 2014
#include <pthread.h>
#include <stdio.h>
#include <goblint.h>

int x;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void *t_fun(void *arg) {
int top;
while(top) {
pthread_mutex_lock(&mutex);
if(x<100) {
x++;
}
pthread_mutex_unlock(&mutex);
}
return NULL;
}


int main(void) {
int top, top2;


pthread_t id;
pthread_t id2;
pthread_create(&id, NULL, t_fun, NULL);
pthread_create(&id2, NULL, t_fun, NULL);
pthread_mutex_lock(&mutex);
__goblint_check(x <= 100);
pthread_mutex_unlock(&mutex);
return 0;
}
Loading