Reviewed By: cristianoc Differential Revision: D3014280 fb-gh-sync-id: ce60320 shipit-source-id: ce60320master
parent
2991bd3fc3
commit
dec843f5f5
@ -0,0 +1,144 @@
|
||||
(*
|
||||
* Copyright (c) 2016 - present Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*)
|
||||
|
||||
module F = Format
|
||||
module L = Logging
|
||||
|
||||
type var =
|
||||
| ProgramVar of Sil.pvar
|
||||
| LogicalVar of Ident.t
|
||||
|
||||
let var_compare v1 v2 = match v1, v2 with
|
||||
| ProgramVar pv1, ProgramVar pv2 -> Sil.pvar_compare pv1 pv2
|
||||
| LogicalVar sv1, LogicalVar sv2 -> Ident.compare sv1 sv2
|
||||
| ProgramVar _, _ -> 1
|
||||
| LogicalVar _, _ -> -1
|
||||
|
||||
let var_equal v1 v2 =
|
||||
var_compare v1 v2 = 0
|
||||
|
||||
let pp_var fmt = function
|
||||
| ProgramVar pv -> (Sil.pp_pvar pe_text) fmt pv
|
||||
| LogicalVar id -> (Ident.pp pe_text) fmt id
|
||||
|
||||
module Domain = struct
|
||||
module VarMap = PrettyPrintable.MakePPMap(struct
|
||||
type t = var
|
||||
let compare = var_compare
|
||||
let pp_key = pp_var
|
||||
end)
|
||||
|
||||
type astate = var VarMap.t
|
||||
|
||||
let initial = VarMap.empty
|
||||
|
||||
let is_bottom _ = false
|
||||
|
||||
(* return true if the key-value bindings in [rhs] are a subset of the key-value bindings in
|
||||
[lhs] *)
|
||||
let (<=) ~lhs ~rhs =
|
||||
VarMap.for_all
|
||||
(fun k v ->
|
||||
try var_equal v (VarMap.find k lhs)
|
||||
with Not_found -> false)
|
||||
rhs
|
||||
|
||||
let join astate1 astate2 =
|
||||
let keep_if_same _ v1_opt v2_opt = match v1_opt, v2_opt with
|
||||
| Some v1, Some v2 ->
|
||||
if var_equal v1 v2 then Some v1 else None
|
||||
| _ -> None in
|
||||
VarMap.merge keep_if_same astate1 astate2
|
||||
|
||||
let widen ~prev ~next ~num_iters:_=
|
||||
join prev next
|
||||
|
||||
let pp fmt astate =
|
||||
let pp_value = pp_var in
|
||||
VarMap.pp ~pp_value fmt astate
|
||||
|
||||
let gen var1 var2 astate =
|
||||
(* don't add tautological copies *)
|
||||
if var_equal var1 var2
|
||||
then astate
|
||||
else VarMap.add var1 var2 astate
|
||||
|
||||
let kill_copies_with_var var astate =
|
||||
let do_kill lhs_var rhs_var astate_acc =
|
||||
if var_equal var lhs_var
|
||||
then astate_acc (* kill copies vith [var] on lhs *)
|
||||
else
|
||||
if var_equal var rhs_var
|
||||
then (* delete [var] = [rhs_var] copy, but add [lhs_var] = M(rhs_var) copy*)
|
||||
try VarMap.add lhs_var (VarMap.find rhs_var astate) astate_acc
|
||||
with Not_found -> astate_acc
|
||||
else (* copy is unaffected by killing [var]; keep it *)
|
||||
VarMap.add lhs_var rhs_var astate_acc in
|
||||
VarMap.fold do_kill astate VarMap.empty
|
||||
|
||||
(* kill the previous binding for [lhs_var], and add a new [lhs_var] -> [rhs_var] binding *)
|
||||
let kill_then_gen lhs_var rhs_var astate =
|
||||
let already_has_binding lhs_var rhs_var astate =
|
||||
try var_equal rhs_var (VarMap.find lhs_var astate)
|
||||
with Not_found -> false in
|
||||
if var_equal lhs_var rhs_var || already_has_binding lhs_var rhs_var astate
|
||||
then astate (* already have this binding; no need to kill or gen *)
|
||||
else
|
||||
kill_copies_with_var lhs_var astate
|
||||
|> gen lhs_var rhs_var
|
||||
end
|
||||
|
||||
module TransferFunctions = struct
|
||||
|
||||
type astate = Domain.astate
|
||||
|
||||
let exec_instr astate = function
|
||||
| Sil.Letderef (lhs_id, Sil.Var rhs_id, _, _) ->
|
||||
(* note: logical vars are SSA, don't need to worry about overwriting existing bindings *)
|
||||
Domain.gen (LogicalVar lhs_id) (LogicalVar rhs_id) astate
|
||||
| Sil.Letderef (lhs_id, Sil.Lvar rhs_pvar, _, _) when not (Sil.pvar_is_global rhs_pvar) ->
|
||||
Domain.gen (LogicalVar lhs_id) (ProgramVar rhs_pvar) astate
|
||||
| Sil.Set (Sil.Lvar lhs_pvar, _, Sil.Var rhs_id, _) when not (Sil.pvar_is_global lhs_pvar) ->
|
||||
Domain.kill_then_gen (ProgramVar lhs_pvar) (LogicalVar rhs_id) astate
|
||||
| Sil.Set (Sil.Lvar lhs_pvar, _, Sil.Lvar rhs_pvar, _)
|
||||
when not (Sil.pvar_is_global lhs_pvar || Sil.pvar_is_global rhs_pvar) ->
|
||||
Domain.kill_then_gen (ProgramVar lhs_pvar) (ProgramVar rhs_pvar) astate
|
||||
| Sil.Letderef (lhs_id, _, _, _) ->
|
||||
(* non-copy assignment (or assignment to global); can only kill *)
|
||||
Domain.kill_copies_with_var (LogicalVar lhs_id) astate
|
||||
| Sil.Set (Sil.Lvar lhs_pvar, _, _, _) ->
|
||||
(* non-copy assignment (or assignment to global); can only kill *)
|
||||
Domain.kill_copies_with_var (ProgramVar lhs_pvar) astate
|
||||
| Sil.Call (ret_ids, _, actuals, _, _) ->
|
||||
let kill_ret_ids astate_acc id =
|
||||
Domain.kill_copies_with_var (LogicalVar id) astate_acc in
|
||||
let kill_actuals_by_ref astate_acc = function
|
||||
| (Sil.Lvar pvar, Sil.Tptr _) -> Domain.kill_copies_with_var (ProgramVar pvar) astate_acc
|
||||
| _ -> astate_acc in
|
||||
let astate' = IList.fold_left kill_ret_ids astate ret_ids in
|
||||
if !Config.curr_language = Config.Java
|
||||
then astate' (* Java doesn't have pass-by-reference *)
|
||||
else IList.fold_left kill_actuals_by_ref astate' actuals
|
||||
| Sil.Set (Sil.Var _, _, _, _) ->
|
||||
(* this should never happen *)
|
||||
assert false
|
||||
| Sil.Set _ | Sil.Prune _ | Sil.Nullify _ | Sil.Abstract _ | Sil.Remove_temps _
|
||||
| Sil.Declare_locals _ | Sil.Goto_node _ | Sil.Stackop _ ->
|
||||
(* none of these can assign to program vars or logical vars *)
|
||||
astate
|
||||
end
|
||||
|
||||
module Analyzer =
|
||||
AbstractInterpreter.Make
|
||||
(ProcCfg.Forward)
|
||||
(Scheduler.ReversePostorder)
|
||||
(Domain)
|
||||
(TransferFunctions)
|
||||
|
||||
let checker { Callbacks.proc_desc; } = ignore(Analyzer.exec_pdesc proc_desc)
|
@ -0,0 +1,181 @@
|
||||
(*
|
||||
* Copyright (c) 2015 - present Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*)
|
||||
|
||||
module F = Format
|
||||
|
||||
module TestInterpreter = AnalyzerTester.Make
|
||||
(ProcCfg.Forward)
|
||||
(Scheduler.ReversePostorder)
|
||||
(CopyPropagation.Domain)
|
||||
(CopyPropagation.TransferFunctions)
|
||||
|
||||
let tests =
|
||||
let open OUnit2 in
|
||||
let open AnalyzerTester.StructuredSil in
|
||||
let assert_empty = invariant "{ }" in
|
||||
let test_list = [
|
||||
"var_tautology",
|
||||
[
|
||||
var_assign_var "a" "a";
|
||||
assert_empty
|
||||
];
|
||||
"id_tautology",
|
||||
[
|
||||
id_assign_id "a" "a";
|
||||
assert_empty
|
||||
];
|
||||
"id_assign_id_gen",
|
||||
[
|
||||
id_assign_id "b" "a";
|
||||
invariant "{ b$0 -> a$0 }"
|
||||
];
|
||||
"id_assign_var_gen",
|
||||
[
|
||||
id_assign_var "b" "a";
|
||||
invariant "{ b$0 -> &a }"
|
||||
];
|
||||
"var_assign_var_gen",
|
||||
[
|
||||
var_assign_var "b" "a";
|
||||
invariant "{ &b -> &a }"
|
||||
];
|
||||
"var_assign_id_gen",
|
||||
[
|
||||
var_assign_id "b" "a";
|
||||
invariant "{ &b -> a$0 }"
|
||||
];
|
||||
"multi_command_gen",
|
||||
[
|
||||
var_assign_var "b" "a";
|
||||
var_assign_var "c" "b";
|
||||
var_assign_var "d" "c";
|
||||
invariant "{ &b -> &a, &c -> &b, &d -> &c }"
|
||||
];
|
||||
"simple_kill",
|
||||
[
|
||||
var_assign_var "b" "a";
|
||||
invariant "{ &b -> &a }";
|
||||
var_assign_int "a" 1;
|
||||
assert_empty
|
||||
];
|
||||
"kill_then_gen",
|
||||
[
|
||||
var_assign_var "b" "a";
|
||||
invariant "{ &b -> &a }";
|
||||
var_assign_var "a" "b";
|
||||
invariant "{ &a -> &b }"
|
||||
];
|
||||
"harder_kill",
|
||||
[
|
||||
var_assign_var "b" "a";
|
||||
var_assign_var "c" "b";
|
||||
var_assign_var "d" "c";
|
||||
invariant "{ &b -> &a, &c -> &b, &d -> &c }";
|
||||
var_assign_int "b" 1;
|
||||
invariant "{ &c -> &a, &d -> &c }";
|
||||
var_assign_int "c" 1;
|
||||
invariant "{ &d -> &a }"
|
||||
];
|
||||
"same_copy",
|
||||
[
|
||||
var_assign_var "b" "a";
|
||||
var_assign_var "c" "b";
|
||||
invariant "{ &b -> &a, &c -> &b }";
|
||||
var_assign_var "c" "b";
|
||||
invariant "{ &b -> &a, &c -> &b }"
|
||||
];
|
||||
"no_cycles",
|
||||
[
|
||||
var_assign_var "a" "b";
|
||||
invariant "{ &a -> &b }";
|
||||
var_assign_var "b" "a";
|
||||
invariant "{ &b -> &a }";
|
||||
var_assign_var "a" "a";
|
||||
invariant "{ &b -> &a }"
|
||||
];
|
||||
"conservative_if",
|
||||
[
|
||||
var_assign_var "b" "a";
|
||||
If (unknown_exp,
|
||||
[invariant "{ &b -> &a }";
|
||||
var_assign_var "a" "b";
|
||||
invariant "{ &a -> &b }"],
|
||||
[]
|
||||
);
|
||||
assert_empty
|
||||
];
|
||||
"if1",
|
||||
[
|
||||
var_assign_var "b" "a";
|
||||
var_assign_var "c" "b";
|
||||
If (unknown_exp,
|
||||
[invariant "{ &b -> &a, &c -> &b }";
|
||||
var_assign_var "c" "d"],
|
||||
[invariant "{ &b -> &a, &c -> &b }"]
|
||||
);
|
||||
invariant "{ &b -> &a }"
|
||||
];
|
||||
"if2",
|
||||
[
|
||||
If (unknown_exp,
|
||||
[var_assign_var "a" "b"],
|
||||
[var_assign_var "a" "b"]
|
||||
);
|
||||
invariant "{ &a -> &b }"
|
||||
];
|
||||
"if3",
|
||||
[
|
||||
If (unknown_exp,
|
||||
[var_assign_var "a" "b"],
|
||||
[var_assign_var "a" "c"]
|
||||
);
|
||||
assert_empty
|
||||
];
|
||||
"nested_if",
|
||||
[
|
||||
var_assign_var "b" "a";
|
||||
var_assign_var "c" "b";
|
||||
If (unknown_exp,
|
||||
[If (var_of_str "unknown2",
|
||||
[ invariant "{ &b -> &a, &c -> &b }";
|
||||
var_assign_var "a" "b";
|
||||
invariant "{ &a -> &b, &c -> &b }"],
|
||||
[]
|
||||
)
|
||||
],
|
||||
[]
|
||||
);
|
||||
invariant "{ &c -> &b }"
|
||||
];
|
||||
"loop_as_if",
|
||||
[
|
||||
var_assign_var "b" "a";
|
||||
While (unknown_exp,
|
||||
[var_assign_var "a" "b"]
|
||||
);
|
||||
assert_empty
|
||||
];
|
||||
"easy_loop_invariant",
|
||||
[
|
||||
var_assign_var "b" "a";
|
||||
While (unknown_exp,
|
||||
[var_assign_var "c" "b";
|
||||
invariant "{ &b -> &a, &c -> &b }"]
|
||||
);
|
||||
invariant "{ &b -> &a }"
|
||||
];
|
||||
"empty_loop",
|
||||
[
|
||||
var_assign_var "b" "a";
|
||||
While (unknown_exp, []);
|
||||
var_assign_var "c" "b";
|
||||
invariant "{ &b -> &a, &c -> &b }"
|
||||
];
|
||||
] |> TestInterpreter.create_tests in
|
||||
"copy_propagation_test_suite">:::test_list
|
Loading…
Reference in new issue