You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

406 lines
12 KiB

(*
* Copyright (c) 2016-present, Programming Research Laboratory (ROPAS)
* Seoul National University, Korea
* 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.
*)
(* Abstract Array Block *)
open! IStd
open AbsLoc
open! AbstractDomain.Types
module Bound = Bounds.Bound
module F = Format
module L = Logging
module ArrInfo = struct
type t = C of {offset: Itv.t; size: Itv.t; stride: Itv.t} | Java of {length: Itv.t} | Top
[@@deriving compare]
let top : t = Top
let make_c : offset:Itv.t -> size:Itv.t -> stride:Itv.t -> t =
fun ~offset ~size ~stride -> C {offset; size; stride}
let make_java : length:Itv.t -> t = fun ~length -> Java {length}
let join : t -> t -> t =
fun a1 a2 ->
if phys_equal a1 a2 then a2
else
match (a1, a2) with
| ( C {offset= offset1; size= size1; stride= stride1}
, C {offset= offset2; size= size2; stride= stride2} ) ->
C
{ offset= Itv.join offset1 offset2
; size= Itv.join size1 size2
; stride= Itv.join stride1 stride2 }
| Java {length= length1}, Java {length= length2} ->
Java {length= Itv.join length1 length2}
| _ ->
Top
let widen : prev:t -> next:t -> num_iters:int -> t =
fun ~prev ~next ~num_iters ->
if phys_equal prev next then next
else
match (prev, next) with
| ( C {offset= offset1; size= size1; stride= stride1}
, C {offset= offset2; size= size2; stride= stride2} ) ->
let offset =
let thresholds =
if Itv.eq size1 size2 then Option.to_list (Itv.get_const size1) else []
in
Itv.widen_thresholds ~thresholds ~prev:offset1 ~next:offset2 ~num_iters
in
C
{ offset
; size= Itv.widen ~prev:size1 ~next:size2 ~num_iters
; stride= Itv.widen ~prev:stride1 ~next:stride2 ~num_iters }
| Java {length= length1}, Java {length= length2} ->
Java {length= Itv.widen ~prev:length1 ~next:length2 ~num_iters}
| _ ->
Top
let leq : lhs:t -> rhs:t -> bool =
fun ~lhs ~rhs ->
if phys_equal lhs rhs then true
else
match (lhs, rhs) with
| ( C {offset= offset1; size= size1; stride= stride1}
, C {offset= offset2; size= size2; stride= stride2} ) ->
Itv.le ~lhs:offset1 ~rhs:offset2 && Itv.le ~lhs:size1 ~rhs:size2
&& Itv.le ~lhs:stride1 ~rhs:stride2
| Java {length= length1}, Java {length= length2} ->
Itv.le ~lhs:length1 ~rhs:length2
| _, Top ->
true
| _ ->
false
let map_offset : f:(Itv.t -> Itv.t) -> t -> t =
fun ~f arr ->
match arr with
| C {offset; size; stride} ->
C {offset= f offset; size; stride}
| Java _ ->
L.(die InternalError) "Unexpected pointer arithmetics on Java array"
| Top ->
Top
let plus_offset : t -> Itv.t -> t = fun arr i -> map_offset arr ~f:(Itv.plus i)
let minus_offset : t -> Itv.t -> t =
fun arr i -> map_offset arr ~f:(fun offset -> Itv.minus offset i)
let diff : t -> t -> Itv.t =
fun arr1 arr2 ->
match (arr1, arr2) with
| C {offset= offset1}, C {offset= offset2} ->
Itv.minus offset1 offset2
| Java _, _ | _, Java _ ->
L.(die InternalError) "Unexpected pointer arithmetics on Java array"
| Top, _ | _, Top ->
Itv.top
let subst : t -> Bound.eval_sym -> t =
fun arr eval_sym ->
match arr with
| C {offset; size; stride} ->
C {offset= Itv.subst offset eval_sym; size= Itv.subst size eval_sym; stride}
| Java {length} ->
Java {length= Itv.subst length eval_sym}
| Top ->
Top
let pp : F.formatter -> t -> unit =
fun f arr ->
match arr with
| C {offset; size} ->
F.fprintf f "offset : %a, size : %a" Itv.pp offset Itv.pp size
| Java {length} ->
F.fprintf f "length : %a" Itv.pp length
| Top ->
AbstractDomain.TopLiftedUtils.pp_top f
let is_pointer : Symb.SymbolPath.partial -> t -> bool =
fun path arr ->
match (path, arr) with
| Deref ((Deref_COneValuePointer | Deref_CPointer), path), C {offset; size} ->
Itv.is_offset_path_of path offset && Itv.is_length_path_of path size
| Deref (Deref_JavaPointer, path), Java {length} ->
Itv.is_length_path_of path length
| _, _ ->
false
let is_symbolic : t -> bool =
fun arr ->
match arr with
| C {offset; size; stride} ->
Itv.is_symbolic offset || Itv.is_symbolic size || Itv.is_symbolic stride
| Java {length} ->
Itv.is_symbolic length
| Top ->
false
let get_symbols : t -> Itv.SymbolSet.t =
fun arr ->
match arr with
| C {offset; size; stride} ->
let s1 = Itv.get_symbols offset in
let s2 = Itv.get_symbols size in
let s3 = Itv.get_symbols stride in
Itv.SymbolSet.union3 s1 s2 s3
| Java {length} ->
Itv.get_symbols length
| Top ->
Itv.SymbolSet.empty
let normalize : t -> t =
fun arr ->
match arr with
| C {offset; size; stride} ->
C {offset= Itv.normalize offset; size= Itv.normalize size; stride= Itv.normalize stride}
| Java {length} ->
Java {length= Itv.normalize length}
| Top ->
Top
let prune_offset : f:(Itv.t -> Itv.t -> Itv.t) -> t -> t -> t =
fun ~f arr1 arr2 ->
match arr2 with
| C {offset= offset2} ->
map_offset arr1 ~f:(fun offset1 -> f offset1 offset2)
| Java _ | Top ->
arr1
let prune_binop : Binop.t -> t -> t -> t =
fun c arr1 arr2 -> prune_offset arr1 arr2 ~f:(Itv.prune_binop c)
let prune_eq : t -> t -> t = fun arr1 arr2 -> prune_offset arr1 arr2 ~f:Itv.prune_eq
let prune_ne : t -> t -> t = fun arr1 arr2 -> prune_offset arr1 arr2 ~f:Itv.prune_ne
let set_length : Itv.t -> t -> t =
fun size arr ->
match arr with
| C {offset; stride} ->
C {offset; size; stride}
| Java _ ->
Java {length= size}
| Top ->
Top
let transform_length : f:(Itv.t -> Itv.t) -> t -> t =
fun ~f arr ->
match arr with
| C {offset; size; stride} ->
C {offset; size= f size; stride}
| Java {length} ->
Java {length= f length}
| Top ->
Top
(* Set new stride only when the previous stride is a constant interval. *)
let set_stride : Z.t -> t -> t =
fun new_stride arr ->
match arr with
| C {offset; size; stride} ->
Option.value_map (Itv.get_const stride) ~default:arr ~f:(fun stride ->
assert ((not Z.(equal stride zero)) && not Z.(equal new_stride zero)) ;
if Z.equal new_stride stride then arr
else
let set itv = Itv.div_const (Itv.mult_const itv stride) new_stride in
C {offset= set offset; size= set size; stride= Itv.of_big_int new_stride} )
| Java _ ->
L.(die InternalError) "Unexpected cast on Java array"
| Top ->
Top
let set_offset : Itv.t -> t -> t =
fun offset arr ->
match arr with
| C {size; stride} ->
C {offset; size; stride}
| Java _ ->
L.(die InternalError) "Unexpected offset setting on Java array"
| Top ->
Top
let get_offset = function C {offset} -> offset | Java _ -> Itv.zero | Top -> Itv.top
let get_size = function C {size} -> size | Java {length} -> length | Top -> Itv.top
let byte_size = function
| C {size; stride} ->
Itv.mult size stride
| Java _ ->
L.(die InternalError) "Unexpected byte-size operation on Java array"
| Top ->
Itv.top
let lift_cmp_itv cmp_itv arr1 arr2 =
match (arr1, arr2) with
| ( C {offset= offset1; size= size1; stride= stride1}
, C {offset= offset2; size= size2; stride= stride2} )
when Itv.eq stride1 stride2 && Itv.eq size1 size2 ->
cmp_itv offset1 offset2
| Java {length= length1}, Java {length= length2} when Itv.eq length1 length2 ->
cmp_itv Itv.zero Itv.zero
| _ ->
Boolean.Top
let is_symbolic_length_of_path path info =
match (path, info) with
| Symb.SymbolPath.Deref (_, prefix), Java {length} ->
Itv.is_length_path_of prefix length
| _ ->
false
end
include AbstractDomain.Map (Allocsite) (ArrInfo)
let compare = compare ArrInfo.compare
let bot : t = empty
let unknown : t = add Allocsite.unknown ArrInfo.top bot
let is_bot : t -> bool = is_empty
let make_c : Allocsite.t -> offset:Itv.t -> size:Itv.t -> stride:Itv.t -> t =
fun a ~offset ~size ~stride -> singleton a (ArrInfo.make_c ~offset ~size ~stride)
let make_java : Allocsite.t -> length:Itv.t -> t =
fun a ~length -> singleton a (ArrInfo.make_java ~length)
let join_itv : cost_mode:bool -> f:(ArrInfo.t -> Itv.t) -> t -> Itv.t =
fun ~cost_mode ~f a ->
let join, init = if cost_mode then (Itv.plus, Itv.zero) else (Itv.join, Itv.bot) in
fold (fun _ arr -> join (f arr)) a init
let get_offset ?(cost_mode = false) = join_itv ~cost_mode ~f:ArrInfo.get_offset
let get_size ?(cost_mode = false) = join_itv ~cost_mode ~f:ArrInfo.get_size
let plus_offset : t -> Itv.t -> t = fun arr i -> map (fun a -> ArrInfo.plus_offset a i) arr
let minus_offset : t -> Itv.t -> t = fun arr i -> map (fun a -> ArrInfo.minus_offset a i) arr
let diff : t -> t -> Itv.t =
fun arr1 arr2 ->
let diff_join k a2 acc =
match find k arr1 with
| a1 ->
Itv.join acc (ArrInfo.diff a1 a2)
| exception Caml.Not_found ->
Itv.top
in
fold diff_join arr2 Itv.bot
let get_pow_loc : t -> PowLoc.t =
fun array ->
let pow_loc_of_allocsite k _ acc = PowLoc.add (Loc.of_allocsite k) acc in
fold pow_loc_of_allocsite array PowLoc.bot
let subst : t -> Bound.eval_sym -> PowLoc.eval_locpath -> PowLoc.t * t =
fun a eval_sym eval_locpath ->
let subst1 l info (powloc_acc, acc) =
let info' = ArrInfo.subst info eval_sym in
match Allocsite.get_param_path l with
| None ->
(powloc_acc, add l info' acc)
| Some path ->
let locs = eval_locpath path in
let add_allocsite l (powloc_acc, acc) =
match l with
| Loc.Allocsite (Symbol (Symb.SymbolPath.Deref (_, prefix)) as a)
when ArrInfo.is_symbolic_length_of_path path info ->
let length = Itv.of_length_path ~is_void:false prefix in
(powloc_acc, add a (ArrInfo.make_java ~length) acc)
| Loc.Allocsite a ->
(powloc_acc, add a info' acc)
| _ ->
if ArrInfo.is_pointer path info then (PowLoc.add l powloc_acc, acc)
else (
if Config.bo_debug >= 3 then
L.d_printfln_escaped "Substitution of array block failed: %a -> %a" Loc.pp l
ArrInfo.pp info ;
(powloc_acc, acc) )
in
PowLoc.fold add_allocsite locs (powloc_acc, acc)
in
fold subst1 a (PowLoc.empty, empty)
let is_symbolic : t -> bool = fun a -> exists (fun _ ai -> ArrInfo.is_symbolic ai) a
let get_symbols : t -> Itv.SymbolSet.t =
fun a ->
fold (fun _ ai acc -> Itv.SymbolSet.union acc (ArrInfo.get_symbols ai)) a Itv.SymbolSet.empty
let normalize : t -> t = fun a -> map ArrInfo.normalize a
let do_prune : (ArrInfo.t -> ArrInfo.t -> ArrInfo.t) -> t -> t -> t =
fun arr_info_prune a1 a2 ->
match is_singleton_or_more a2 with
| IContainer.Singleton (k, v2) ->
update k (Option.map ~f:(fun v -> arr_info_prune v v2)) a1
| _ ->
a1
let prune_binop : Binop.t -> t -> t -> t = fun c a1 a2 -> do_prune (ArrInfo.prune_binop c) a1 a2
let prune_eq : t -> t -> t = fun a1 a2 -> do_prune ArrInfo.prune_eq a1 a2
let prune_ne : t -> t -> t = fun a1 a2 -> do_prune ArrInfo.prune_ne a1 a2
let set_length : Itv.t -> t -> t = fun length a -> map (ArrInfo.set_length length) a
let set_stride : Z.t -> t -> t = fun stride a -> map (ArrInfo.set_stride stride) a
let set_offset : Itv.t -> t -> t = fun offset a -> map (ArrInfo.set_offset offset) a
let lift_cmp_itv cmp_itv cmp_loc arr1 arr2 =
match (is_singleton_or_more arr1, is_singleton_or_more arr2) with
| IContainer.Singleton (as1, ai1), IContainer.Singleton (as2, ai2) ->
Boolean.EqualOrder.(
of_equal
{on_equal= ArrInfo.lift_cmp_itv cmp_itv ai1 ai2; on_not_equal= cmp_loc.on_not_equal}
(Allocsite.eq as1 as2))
| _ ->
Boolean.Top
let transform_length : f:(Itv.t -> Itv.t) -> t -> t =
fun ~f a -> map (ArrInfo.transform_length ~f) a