Reviewed By: cristianoc Differential Revision: D3080119 fb-gh-sync-id: a11a6ea fbshipit-source-id: a11a6eamaster
parent
1b21869108
commit
967dcec7f1
@ -0,0 +1,74 @@
|
|||||||
|
(*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
(** backward analysis for computing set of maybe-live variables at each program point *)
|
||||||
|
|
||||||
|
module VarSet = PrettyPrintable.MakePPSet(struct
|
||||||
|
type t = CopyPropagation.var
|
||||||
|
let compare = CopyPropagation.var_compare
|
||||||
|
let pp_element = CopyPropagation.pp_var
|
||||||
|
end)
|
||||||
|
|
||||||
|
module Domain = AbstractDomain.FiniteSetDomain(VarSet)
|
||||||
|
|
||||||
|
(* compilers 101-style backward transfer functions for liveness analysis. gen a variable when it is
|
||||||
|
read, kill the variable when it is assigned *)
|
||||||
|
module TransferFunctions = struct
|
||||||
|
|
||||||
|
type astate = Domain.astate
|
||||||
|
|
||||||
|
(* add all of the vars read in [exp] to the live set *)
|
||||||
|
let exp_add_live exp astate =
|
||||||
|
let (ids, pvars) = Sil.exp_get_vars exp in
|
||||||
|
let astate' =
|
||||||
|
IList.fold_left (fun astate_acc id -> Domain.add (LogicalVar id) astate_acc) astate ids in
|
||||||
|
IList.fold_left (fun astate_acc pvar -> Domain.add (ProgramVar pvar) astate_acc) astate' pvars
|
||||||
|
|
||||||
|
let exec_instr astate _ = function
|
||||||
|
| Sil.Letderef (lhs_id, rhs_exp, _, _)
|
||||||
|
| Sil.Set (Sil.Var lhs_id, _, rhs_exp, _) ->
|
||||||
|
Domain.remove (LogicalVar lhs_id) astate
|
||||||
|
|> exp_add_live rhs_exp
|
||||||
|
| Sil.Set (Lvar lhs_pvar, _, rhs_exp, _) ->
|
||||||
|
let astate' =
|
||||||
|
if Pvar.is_global lhs_pvar
|
||||||
|
then astate (* never kill globals *)
|
||||||
|
else Domain.remove (ProgramVar lhs_pvar) astate in
|
||||||
|
exp_add_live rhs_exp astate'
|
||||||
|
| Sil.Set (lhs_exp, _, rhs_exp, _) ->
|
||||||
|
exp_add_live lhs_exp astate
|
||||||
|
|> exp_add_live rhs_exp
|
||||||
|
| Sil.Prune (exp, _, _, _) | Sil.Goto_node (exp, _) ->
|
||||||
|
exp_add_live exp astate
|
||||||
|
| Sil.Call (ret_ids, call_exp, params, _, _) ->
|
||||||
|
IList.fold_right
|
||||||
|
(fun ret_id astate_acc -> Domain.remove (LogicalVar ret_id) astate_acc)
|
||||||
|
ret_ids
|
||||||
|
astate
|
||||||
|
|> exp_add_live call_exp
|
||||||
|
|> IList.fold_right exp_add_live (IList.map fst params)
|
||||||
|
| Sil.Declare_locals _ | Stackop _ ->
|
||||||
|
astate
|
||||||
|
| Sil.Nullify _ | Remove_temps _ | Abstract _ ->
|
||||||
|
failwith
|
||||||
|
"Nullify, Remove_temps, and Abstract instructions should be added after running liveness"
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
module Analyzer =
|
||||||
|
AbstractInterpreter.Make
|
||||||
|
(ProcCfg.Backward(ProcCfg.Forward))
|
||||||
|
(Scheduler.ReversePostorder)
|
||||||
|
(Domain)
|
||||||
|
(TransferFunctions)
|
||||||
|
|
||||||
|
let checker { Callbacks.proc_desc; } = ignore(Analyzer.exec_pdesc proc_desc)
|
@ -0,0 +1,189 @@
|
|||||||
|
(*
|
||||||
|
* 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 TestInterpreter = AnalyzerTester.Make
|
||||||
|
(ProcCfg.Backward (ProcCfg.Forward))
|
||||||
|
(Scheduler.ReversePostorder)
|
||||||
|
(Liveness.Domain)
|
||||||
|
(Liveness.TransferFunctions)
|
||||||
|
|
||||||
|
let tests =
|
||||||
|
let open OUnit2 in
|
||||||
|
let open AnalyzerTester.StructuredSil in
|
||||||
|
let assert_empty = invariant "{ }" in
|
||||||
|
let fun_ptr_typ = Sil.Tptr (Tfun false, Pk_pointer) in
|
||||||
|
let closure_exp captured_pvars =
|
||||||
|
let mk_captured_var str = (Sil.Var (ident_of_str str), pvar_of_str str, dummy_typ) in
|
||||||
|
let captured_vars = IList.map mk_captured_var captured_pvars in
|
||||||
|
let closure = { Sil.name=dummy_procname; captured_vars; } in
|
||||||
|
Sil.Const (Cclosure closure) in
|
||||||
|
let unknown_cond =
|
||||||
|
(* don't want to use AnalyzerTest.unknown_exp because we'll treat it as a live var! *)
|
||||||
|
Sil.exp_zero in
|
||||||
|
let test_list = [
|
||||||
|
"basic_live",
|
||||||
|
[
|
||||||
|
invariant "{ &b }";
|
||||||
|
var_assign_var "a" "b"
|
||||||
|
];
|
||||||
|
"basic_live_then_dead",
|
||||||
|
[
|
||||||
|
assert_empty;
|
||||||
|
var_assign_int "b" 1;
|
||||||
|
invariant "{ &b }";
|
||||||
|
var_assign_var "a" "b"
|
||||||
|
];
|
||||||
|
"iterative_live",
|
||||||
|
[
|
||||||
|
invariant "{ &b, &d, &f }";
|
||||||
|
var_assign_var "e" "f";
|
||||||
|
invariant "{ &b, &d }";
|
||||||
|
var_assign_var "c" "d";
|
||||||
|
invariant "{ &b }";
|
||||||
|
var_assign_var "a" "b"
|
||||||
|
];
|
||||||
|
"live_kill_live",
|
||||||
|
[
|
||||||
|
invariant "{ &b }";
|
||||||
|
var_assign_var "c" "b";
|
||||||
|
assert_empty;
|
||||||
|
var_assign_int "b" 1;
|
||||||
|
invariant "{ &b }";
|
||||||
|
var_assign_var "a" "b"
|
||||||
|
];
|
||||||
|
"basic_live_letderef",
|
||||||
|
[
|
||||||
|
invariant "{ y$0 }";
|
||||||
|
id_assign_id "x" "y"
|
||||||
|
];
|
||||||
|
"basic_live_then_kill_letderef",
|
||||||
|
[
|
||||||
|
invariant "{ z$0 }";
|
||||||
|
id_assign_id "y" "z";
|
||||||
|
invariant "{ y$0 }";
|
||||||
|
id_assign_id "x" "y"
|
||||||
|
];
|
||||||
|
"if_exp_live",
|
||||||
|
[
|
||||||
|
assert_empty;
|
||||||
|
var_assign_int "x" 1;
|
||||||
|
invariant "{ &x }";
|
||||||
|
If (var_of_str "x", [], []);
|
||||||
|
];
|
||||||
|
"while_exp_live",
|
||||||
|
[
|
||||||
|
assert_empty;
|
||||||
|
var_assign_int "x" 1;
|
||||||
|
invariant "{ &x }";
|
||||||
|
While (var_of_str "x", []);
|
||||||
|
];
|
||||||
|
"call_params_live",
|
||||||
|
[
|
||||||
|
invariant "{ &a, &b, &c }";
|
||||||
|
call_unknown_no_ret ["a"; "b"; "c";]
|
||||||
|
];
|
||||||
|
"dead_after_call_with_retval",
|
||||||
|
[
|
||||||
|
assert_empty;
|
||||||
|
call_unknown ["y"] [];
|
||||||
|
invariant "{ y$0 }";
|
||||||
|
id_assign_id "x" "y";
|
||||||
|
];
|
||||||
|
"closure_captured_live",
|
||||||
|
[
|
||||||
|
invariant "{ b$0, c$0 }";
|
||||||
|
var_assign_exp ~rhs_typ:fun_ptr_typ "a" (closure_exp ["b"; "c"])
|
||||||
|
];
|
||||||
|
"if_conservative_live1",
|
||||||
|
[
|
||||||
|
invariant "{ &b }";
|
||||||
|
If (unknown_cond,
|
||||||
|
[var_assign_var "a" "b"],
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
];
|
||||||
|
"if_conservative_live2",
|
||||||
|
[
|
||||||
|
invariant "{ &b, &d }";
|
||||||
|
If (unknown_cond,
|
||||||
|
[var_assign_var "a" "b"],
|
||||||
|
[var_assign_var "c" "d"]
|
||||||
|
)
|
||||||
|
];
|
||||||
|
"if_conservative_kill",
|
||||||
|
[
|
||||||
|
invariant "{ &b }";
|
||||||
|
If (unknown_cond,
|
||||||
|
[var_assign_int "b" 1],
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
invariant "{ &b }";
|
||||||
|
var_assign_var "a" "b"
|
||||||
|
];
|
||||||
|
"if_conservative_kill_live",
|
||||||
|
[
|
||||||
|
invariant "{ &b, &d }";
|
||||||
|
If (unknown_cond,
|
||||||
|
[var_assign_int "b" 1],
|
||||||
|
[var_assign_var "c" "d"]
|
||||||
|
);
|
||||||
|
invariant "{ &b }";
|
||||||
|
var_assign_var "a" "b"
|
||||||
|
];
|
||||||
|
"if_precise1",
|
||||||
|
[
|
||||||
|
assert_empty;
|
||||||
|
If (unknown_cond,
|
||||||
|
[var_assign_int "b" 1;
|
||||||
|
invariant "{ &b }";
|
||||||
|
var_assign_var "a" "b"],
|
||||||
|
[var_assign_int "d" 1;
|
||||||
|
invariant "{ &d }";
|
||||||
|
var_assign_var "c" "d"]
|
||||||
|
)
|
||||||
|
];
|
||||||
|
"if_precise2",
|
||||||
|
[
|
||||||
|
assert_empty;
|
||||||
|
If (unknown_cond,
|
||||||
|
[var_assign_int "b" 2],
|
||||||
|
[var_assign_int "b" 1]
|
||||||
|
);
|
||||||
|
invariant "{ &b }";
|
||||||
|
var_assign_var "a" "b"
|
||||||
|
];
|
||||||
|
"loop_as_if1",
|
||||||
|
[
|
||||||
|
invariant "{ &b }";
|
||||||
|
While (unknown_cond,
|
||||||
|
[var_assign_var "a" "b"]
|
||||||
|
)
|
||||||
|
];
|
||||||
|
"loop_as_if2",
|
||||||
|
[
|
||||||
|
invariant "{ &b }";
|
||||||
|
While (unknown_cond,
|
||||||
|
[var_assign_int "b" 1]
|
||||||
|
);
|
||||||
|
invariant "{ &b }";
|
||||||
|
var_assign_var "a" "b"
|
||||||
|
];
|
||||||
|
"loop_before_after",
|
||||||
|
[
|
||||||
|
invariant "{ &b, &d }";
|
||||||
|
While (unknown_cond,
|
||||||
|
[var_assign_var "b" "d"]
|
||||||
|
);
|
||||||
|
invariant "{ &b }";
|
||||||
|
var_assign_var "a" "b"
|
||||||
|
];
|
||||||
|
] |> TestInterpreter.create_tests in
|
||||||
|
"liveness_test_suite">:::test_list
|
Loading…
Reference in new issue