diff --git a/src/plugins/e-acsl/src/code_generator/injector.ml b/src/plugins/e-acsl/src/code_generator/injector.ml index f095fa81d2cf4144cf04e60d98175a71b82b5bda..d9d96e9c8d7b05790452beef63497ffde4f3a958 100644 --- a/src/plugins/e-acsl/src/code_generator/injector.ml +++ b/src/plugins/e-acsl/src/code_generator/injector.ml @@ -224,8 +224,8 @@ let add_new_block_in_stmt env kf stmt = end else env in - (* handle loop invariants *) - let new_stmt, env = Loops.preserve_invariant env kf stmt in + (* handle loop annotations *) + let new_stmt, env = Loops.handle_annotations env kf stmt in new_stmt, env else stmt, env diff --git a/src/plugins/e-acsl/src/code_generator/loops.ml b/src/plugins/e-acsl/src/code_generator/loops.ml index ab0c7fa3d38c81d2a0e42f34332bdcb9897d0ed0..656f790c6b03964797ddc44cf27dd361cb7cb684 100644 --- a/src/plugins/e-acsl/src/code_generator/loops.ml +++ b/src/plugins/e-acsl/src/code_generator/loops.ml @@ -40,17 +40,129 @@ let term_to_exp_ref = Extlib.mk_fun "term_to_exp_ref" (**************************************************************************) -(************************* Loop invariants ********************************) +(************************* Loop annotations *******************************) (**************************************************************************) -let preserve_invariant env kf stmt = match stmt.skind with +let handle_annotations env kf stmt = + match stmt.skind with | Loop(_, ({ bstmts = stmts } as blk), loc, cont, break) -> - let rec handle_invariants (stmts, env as acc) = function - | [] -> - (* empty loop body: no need to verify the invariant twice *) - acc + (* Loop variant, save the current value of the variant *) + let stmts, env, variant = + match Env.top_loop_variant env with + | Some (t, measure_opt) -> + let env = Env.set_annotation_kind env Smart_stmt.Variant in + let env = Env.push env in + Typing.type_term ~use_gmp_opt:true t; + let ty = Typing.get_typ t in + let e, env = !term_to_exp_ref kf env t in + let vi_old, e_old, env = + Env.new_var + ~loc + ~scope:Varname.Function + ~name:"old_variant" + env + kf + (Some t) + ty + (fun _ _ -> []) + in + let stmt = + Smart_stmt.assigns ~loc ~result:(Cil.var vi_old) e + in + let blk, env = + Env.pop_and_get env stmt ~global_clear:false Env.Middle + in + Smart_stmt.block_stmt blk :: stmts, env, Some (t, e_old, measure_opt) + | None -> stmts, env, None + in + (* Auxiliary function to generate variant and invariant checks *) + let rec aux (stmts, env) = function + | [] -> begin + (* No statements remaining in the loop: variant check *) + let env = Env.set_annotation_kind env Smart_stmt.Variant in + match variant with + | Some (t, e_old, Some measure) -> + let env = Env.push env in + let t_old = { t with term_node = t.term_node } in + let tapp = + { term_node = Tapp(measure, [], [t_old; t]); + term_loc = loc; + term_name = []; + term_type = Linteger;} + in + Typing.type_term ~use_gmp_opt:true tapp; + let e, env = !term_to_exp_ref kf env t in + let e_tapp, env = + Logic_functions.tapp_to_exp kf env ~eargs:[e_old; e] tapp + in + let msg = + Format.asprintf + "%s(old %a, %a)" + measure.l_var_info.lv_name + Printer.pp_term t_old + Printer.pp_term t + in + let stmt = + Smart_stmt.runtime_check_with_msg + ~loc + msg + ~pred_kind:Assert + Smart_stmt.Variant + kf + e_tapp + in + let blk, env = + Env.pop_and_get env stmt ~global_clear:false Env.Middle + in + let stmts = Smart_stmt.block_stmt blk :: stmts in + stmts, env + | Some (t, e_old, None) -> + let env = Env.push env in + Typing.type_term ~use_gmp_opt:true t; + let e, env = !term_to_exp_ref kf env t in + let msg1 = + Format.asprintf + "(old %a) %a 0" + Printer.pp_term t + Printer.pp_relation Rge + in + let msg2 = + Format.asprintf + "(old %a) > %a" + Printer.pp_term t + Printer.pp_term t + in + let stmt = + Smart_stmt.block_from_stmts [ + Smart_stmt.runtime_check_with_msg + ~loc + msg1 + ~pred_kind:Assert + Smart_stmt.Variant + kf + (Cil.mkBinOp ~loc Ge e_old (Cil.zero ~loc)); + Smart_stmt.runtime_check_with_msg + ~loc + msg2 + ~pred_kind:Assert + Smart_stmt.Variant + kf + (Cil.mkBinOp ~loc Gt e_old e) + ] + in + let blk, env = + Env.pop_and_get env stmt ~global_clear:false Env.Middle + in + let stmts = Smart_stmt.block_stmt blk :: stmts in + stmts, env + | None -> + stmts, env + end | [ last ] -> - let invariants, env = Env.pop_loop env in + (* Last statement of the loop: invariant check *) + (* Optimisation to only verify invariant on a non-empty body loop. *) + let invariants = Env.top_loop_invariants env in + let env = Env.set_annotation_kind env Smart_stmt.Invariant in let env = Env.push env in let env = let translate_named_predicate = !translate_predicate_ref in @@ -59,15 +171,15 @@ let preserve_invariant env kf stmt = match stmt.skind with let blk, env = Env.pop_and_get env last ~global_clear:false Env.Before in - Smart_stmt.block last blk :: stmts, env - | s :: tl -> - handle_invariants (s :: stmts, env) tl + aux (Smart_stmt.block last blk :: stmts, env) [] + | hd :: tl -> aux (hd :: stmts, env) tl in - let env = Env.set_annotation_kind env Smart_stmt.Invariant in - let stmts, env = handle_invariants ([], env) stmts in + let stmts, env = aux ([], env) stmts in + (* The loop annotations have been handled, the loop environment can be + popped *) + let env = Env.pop_loop env in let new_blk = { blk with bstmts = List.rev stmts } in - { stmt with skind = Loop([], new_blk, loc, cont, break) }, - env + { stmt with skind = Loop([], new_blk, loc, cont, break) }, env | _ -> stmt, env diff --git a/src/plugins/e-acsl/src/code_generator/loops.mli b/src/plugins/e-acsl/src/code_generator/loops.mli index c797919dabca98e48323974115c2c0dc560f9d56..20b0a1262aceec6a10d19f14324afbfbb318eaff 100644 --- a/src/plugins/e-acsl/src/code_generator/loops.mli +++ b/src/plugins/e-acsl/src/code_generator/loops.mli @@ -25,13 +25,14 @@ open Cil_types (**************************************************************************) -(************************* Loop invariants ********************************) +(************************* Loop annotations *******************************) (**************************************************************************) -val preserve_invariant: +val handle_annotations: Env.t -> Kernel_function.t -> stmt -> stmt * Env.t -(** Modify the given stmt loop to insert the code which preserves its loop - invariants. Also return the modified environment. *) +(** Modify the given stmt loop to insert the code which verifies the loop + annotations, ie. preserves its loop invariants and checks the loop variant. + Also return the modified environment. *) (**************************************************************************) (**************************** Nested loops ********************************) diff --git a/src/plugins/e-acsl/src/code_generator/translate_annots.ml b/src/plugins/e-acsl/src/code_generator/translate_annots.ml index 27a8185b7cabd368f0d4d4adea8795df51d523ae..e4f26ec3919ed0ecbc5de7c3e0c9186077de6bb1 100644 --- a/src/plugins/e-acsl/src/code_generator/translate_annots.ml +++ b/src/plugins/e-acsl/src/code_generator/translate_annots.ml @@ -111,9 +111,9 @@ let pre_code_annotation kf stmt env annot = else env else env - | AVariant _ -> + | AVariant (t, measure) -> if must_translate (Property.ip_of_code_annot_single kf stmt annot) - then Env.not_yet env "variant" + then Env.set_loop_variant env ?measure t else env | AAssigns _ -> (* TODO: it is not a precondition --> should not be handled here,