|
|
|
(*
|
|
|
|
* Copyright (c) 2009-2013, Monoidics ltd.
|
|
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
|
|
|
*)
|
|
|
|
|
|
|
|
(** The Smallfoot Intermediate Language *)
|
|
|
|
|
|
|
|
open! IStd
|
|
|
|
module F = Format
|
|
|
|
module L = Logging
|
|
|
|
|
|
|
|
(** {2 Programs and Types} *)
|
|
|
|
|
|
|
|
(** Kind of prune instruction *)
|
|
|
|
type if_kind =
|
|
|
|
| Ik_bexp (** boolean expressions, and exp ? exp : exp *)
|
|
|
|
| Ik_dowhile
|
|
|
|
| Ik_for
|
|
|
|
| Ik_if
|
|
|
|
| Ik_land_lor (** obtained from translation of && or || *)
|
|
|
|
| Ik_while
|
|
|
|
| Ik_switch
|
|
|
|
[@@deriving compare, equal]
|
|
|
|
|
|
|
|
type instr_metadata =
|
|
|
|
| Abstract of Location.t
|
|
|
|
(** a good place to apply abstraction, mostly used in the biabduction analysis *)
|
|
|
|
| ExitScope of Var.t list * Location.t (** remove temporaries and dead program variables *)
|
|
|
|
| Nullify of Pvar.t * Location.t (** nullify stack variable *)
|
|
|
|
| Skip (** no-op *)
|
|
|
|
| VariableLifetimeBegins of Pvar.t * Typ.t * Location.t (** stack variable declared *)
|
|
|
|
[@@deriving compare]
|
|
|
|
|
|
|
|
(** An instruction. *)
|
|
|
|
type instr =
|
|
|
|
(* Note for frontend writers:
|
|
|
|
[x] must be used in a subsequent instruction, otherwise the entire
|
|
|
|
`Load` instruction may be eliminated by copy-propagation. *)
|
|
|
|
| Load of {id: Ident.t; e: Exp.t; root_typ: Typ.t; typ: Typ.t; loc: Location.t}
|
|
|
|
(** Load a value from the heap into an identifier.
|
|
|
|
|
|
|
|
[id = *exp:typ(root_typ)] where
|
|
|
|
|
|
|
|
- [exp] is an expression denoting a heap address
|
|
|
|
- [typ] is typ of [exp] and [id]
|
|
|
|
- [root_typ] is the root type of [exp]
|
|
|
|
|
|
|
|
The [root_typ] is deprecated: it is broken in C/C++. We are removing [root_typ] in the
|
|
|
|
future, so please use [typ] instead. *)
|
|
|
|
| Store of {e1: Exp.t; root_typ: Typ.t; typ: Typ.t; e2: Exp.t; loc: Location.t}
|
|
|
|
(** Store the value of an expression into the heap.
|
|
|
|
|
|
|
|
[*exp1:typ(root_typ) = exp2] where
|
|
|
|
|
|
|
|
- [exp1] is an expression denoting a heap address
|
|
|
|
- [typ] is typ of [*exp1] and [exp2]
|
|
|
|
- [root_typ] is the root type of [exp1]
|
|
|
|
- [exp2] is the expression whose value is stored.
|
|
|
|
|
|
|
|
The [root_typ] is deprecated: it is broken in C/C++. We are removing [root_typ] in the
|
|
|
|
future, so please use [typ] instead. *)
|
|
|
|
| Prune of Exp.t * Location.t * bool * if_kind
|
|
|
|
(** prune the state based on [exp=1], the boolean indicates whether true branch *)
|
|
|
|
| Call of (Ident.t * Typ.t) * Exp.t * (Exp.t * Typ.t) list * Location.t * CallFlags.t
|
|
|
|
(** [Call ((ret_id, ret_typ), e_fun, arg_ts, loc, call_flags)] represents an instruction
|
|
|
|
[ret_id = e_fun(arg_ts);] *)
|
|
|
|
| Metadata of instr_metadata
|
|
|
|
(** hints about the program that are not strictly needed to understand its semantics, for
|
|
|
|
instance information about its original syntactic structure *)
|
|
|
|
[@@deriving compare]
|
|
|
|
|
|
|
|
let equal_instr = [%compare.equal: instr]
|
|
|
|
|
|
|
|
let skip_instr = Metadata Skip
|
|
|
|
|
|
|
|
(** Check if an instruction is auxiliary, or if it comes from source instructions. *)
|
|
|
|
let instr_is_auxiliary = function
|
|
|
|
| Load _ | Store _ | Prune _ | Call _ ->
|
|
|
|
false
|
|
|
|
| Metadata _ ->
|
|
|
|
true
|
|
|
|
|
|
|
|
|
|
|
|
(** {2 Pretty Printing} *)
|
|
|
|
|
|
|
|
let color_wrapper ~f = if Config.print_using_diff then Pp.color_wrapper ~f else f
|
|
|
|
|
|
|
|
let pp_exp_typ pe f (e, t) = F.fprintf f "%a:%a" (Exp.pp_diff pe) e (Typ.pp pe) t
|
|
|
|
|
|
|
|
let location_of_instr_metadata = function
|
|
|
|
| Abstract loc | ExitScope (_, loc) | Nullify (_, loc) | VariableLifetimeBegins (_, _, loc) ->
|
|
|
|
loc
|
|
|
|
| Skip ->
|
|
|
|
Location.dummy
|
|
|
|
|
|
|
|
|
|
|
|
(** Get the location of the instruction *)
|
|
|
|
let location_of_instr = function
|
|
|
|
| Load {loc} | Store {loc} | Prune (_, loc, _, _) | Call (_, _, _, loc, _) ->
|
|
|
|
loc
|
|
|
|
| Metadata metadata ->
|
|
|
|
location_of_instr_metadata metadata
|
|
|
|
|
|
|
|
|
|
|
|
let exps_of_instr_metadata = function
|
|
|
|
| Abstract _ ->
|
|
|
|
[]
|
|
|
|
| ExitScope (vars, _) ->
|
|
|
|
List.map ~f:Var.to_exp vars
|
|
|
|
| Nullify (pvar, _) ->
|
|
|
|
[Exp.Lvar pvar]
|
|
|
|
| Skip ->
|
|
|
|
[]
|
|
|
|
| VariableLifetimeBegins (pvar, _, _) ->
|
|
|
|
[Exp.Lvar pvar]
|
|
|
|
|
|
|
|
|
|
|
|
(** get the expressions occurring in the instruction *)
|
|
|
|
let exps_of_instr = function
|
|
|
|
| Load {id; e} ->
|
|
|
|
[Exp.Var id; e]
|
|
|
|
| Store {e1; e2} ->
|
|
|
|
[e1; e2]
|
|
|
|
| Prune (cond, _, _, _) ->
|
|
|
|
[cond]
|
|
|
|
| Call ((id, _), e, exps, _, _) ->
|
|
|
|
let exps = List.map ~f:fst exps in
|
|
|
|
e :: Exp.Var id :: exps
|
|
|
|
| Metadata metadata ->
|
|
|
|
exps_of_instr_metadata metadata
|
|
|
|
|
|
|
|
|
|
|
|
(** Convert an if_kind to string *)
|
|
|
|
let if_kind_to_string = function
|
|
|
|
| Ik_bexp ->
|
|
|
|
"boolean exp"
|
|
|
|
| Ik_dowhile ->
|
|
|
|
"do while"
|
|
|
|
| Ik_for ->
|
|
|
|
"for loop"
|
|
|
|
| Ik_if ->
|
|
|
|
"if"
|
|
|
|
| Ik_land_lor ->
|
|
|
|
"obtained from && or ||"
|
|
|
|
| Ik_while ->
|
|
|
|
"while"
|
|
|
|
| Ik_switch ->
|
|
|
|
"switch"
|
|
|
|
|
|
|
|
|
|
|
|
let pp_instr_metadata pe f = function
|
|
|
|
| Abstract loc ->
|
|
|
|
F.fprintf f "APPLY_ABSTRACTION; [%a]" Location.pp loc
|
|
|
|
| ExitScope (vars, loc) ->
|
|
|
|
F.fprintf f "EXIT_SCOPE(%a); [%a]" (Pp.seq ~sep:"," Var.pp) vars Location.pp loc
|
|
|
|
| Nullify (pvar, loc) ->
|
|
|
|
F.fprintf f "NULLIFY(%a); [%a]" (Pvar.pp pe) pvar Location.pp loc
|
|
|
|
| Skip ->
|
|
|
|
F.pp_print_string f "SKIP"
|
|
|
|
| VariableLifetimeBegins (pvar, typ, loc) ->
|
|
|
|
F.fprintf f "VARIABLE_DECLARED(%a:%a); [%a]" Pvar.pp_value pvar (Typ.pp_full pe) typ
|
|
|
|
Location.pp loc
|
|
|
|
|
|
|
|
|
|
|
|
let pp_instr ~print_types pe0 f instr =
|
|
|
|
let pp_typ = if print_types then Typ.pp_full else Typ.pp in
|
|
|
|
let pp_root ~typ ~root_typ f =
|
|
|
|
if not (Typ.equal typ root_typ) then F.fprintf f "(root %a)" (pp_typ pe0) root_typ
|
|
|
|
in
|
|
|
|
color_wrapper pe0 f instr ~f:(fun pe f instr ->
|
|
|
|
match instr with
|
|
|
|
| Load {id; e; root_typ; typ; loc} ->
|
|
|
|
F.fprintf f "%a=*%a:%a%t [%a]" Ident.pp id (Exp.pp_diff ~print_types pe) e (pp_typ pe0)
|
|
|
|
typ (pp_root ~typ ~root_typ) Location.pp loc
|
|
|
|
| Store {e1; root_typ; typ; e2; loc} ->
|
|
|
|
F.fprintf f "*%a:%a%t=%a [%a]" (Exp.pp_diff ~print_types pe) e1 (pp_typ pe0) root_typ
|
|
|
|
(pp_root ~typ ~root_typ) (Exp.pp_diff ~print_types pe) e2 Location.pp loc
|
|
|
|
| Prune (cond, loc, true_branch, _) ->
|
|
|
|
F.fprintf f "PRUNE(%a, %b); [%a]" (Exp.pp_diff ~print_types pe) cond true_branch
|
|
|
|
Location.pp loc
|
|
|
|
| Call ((id, _), e, arg_ts, loc, cf) ->
|
|
|
|
F.fprintf f "%a=" Ident.pp id ;
|
|
|
|
F.fprintf f "%a(%a)%a [%a]" (Exp.pp_diff ~print_types pe) e
|
|
|
|
(Pp.comma_seq (pp_exp_typ pe))
|
|
|
|
arg_ts CallFlags.pp cf Location.pp loc
|
|
|
|
| Metadata metadata ->
|
|
|
|
pp_instr_metadata pe0 f metadata )
|
|
|
|
|
|
|
|
|
|
|
|
(** Dump an instruction. *)
|
|
|
|
let d_instr (i : instr) = L.d_pp_with_pe ~color:Pp.Green (pp_instr ~print_types:true) i
|