package jasmin

  1. Overview
  2. Docs
Compiler for High-Assurance and High-Speed Cryptography

Install

dune-project
 Dependency

Authors

Maintainers

Sources

jasmin-compiler-v2025.06.2.tar.bz2
sha256=aa0d21f532c1560a0939244cfd1c8414ba2b42c9d1403960f458500446cb1ebb

doc/src/jasmin.jasmin/scopeTree.ml.html

Source file scopeTree.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
(** The algorithm in this module locates a suitable location for declaring each
    variable.

    The instructions of a function, represented by their location, are gathered
    in a tree that represents the nesting structure of the lexical scopes: a
    variable declared in a node is visible from all of its children, unless a
    variable with the same name is declared in between. Therefore a variable can
    be declared at the least common ancestor to all of its occurrences.

    The tree data structure that is used remembers the depth of each node to
    support the computation of least common ancestors. *)

open Utils
open Prog

type node = Location.i_loc
type nodeset = Siloc.t

module Tree : sig
  type t = private { parent : t option; depth : int; data : node }

  val root : node -> t
  val make : node -> t -> t
end = struct
  type t = { parent : t option; depth : int; data : node }

  let root ii = { parent = None; depth = 0; data = ii }
  let make ii t = { parent = Some t; data = ii; depth = 1 + t.depth }
end

let make ii = function None -> Tree.root ii | Some t -> Tree.make ii t

type tree = Tree.t Miloc.t

let rec parent_at_depth (d : int) (t : tree) (n : node) : node =
  assert (0 <= d);
  let open Tree in
  let rec loop n =
    if Stdlib.Int.equal d n.depth then n.data else loop (Option.get n.parent)
  in
  loop (Miloc.find n t)

let find_common_ancestor (t : tree) (nodes : nodeset) : node =
  assert (not (Siloc.is_empty nodes));
  (* Ensure all nodes in the set have the same depth *)
  let depth =
    Siloc.fold (fun n -> min (Miloc.find n t).depth) nodes Stdlib.Int.max_int
  in
  let nodes = Siloc.map (parent_at_depth depth t) nodes in
  (* Replace each node by its parent until we get a singleton *)
  let rec loop nodes =
    if Siloc.is_singleton nodes then Siloc.any nodes
    else
      nodes
      |> Siloc.map (fun n -> (Option.get (Miloc.find n t).parent).data)
      |> loop
  in
  loop nodes

(* --------------------------------------------------------------- *)
(* Compute variable occurrences in expressions and instructions *)

let variables_in_ggvar { gs; gv } (acc : Spv.t) : Spv.t =
  match gs with
  | E.Sglob -> acc
  | E.Slocal ->
      let x = L.unloc gv in
      if x.v_kind <> Const then Spv.add x acc else acc

let rec variables_in_pexpr (acc : Spv.t) (e : pexpr) : Spv.t =
  match e with
  | Pconst _ | Pbool _ | Parr_init _ -> acc
  | Pvar x -> variables_in_ggvar x acc
  | Pget (_, _, _, x, e) | Psub (_, _, _, x, e) ->
      variables_in_pexpr (variables_in_ggvar x acc) e
  | Pload (_, _, e) | Papp1 (_, e) -> variables_in_pexpr acc e
  | Papp2 (_, e1, e2) | Pif (_, _, e1, e2) -> variables_in_pexprs acc [ e1; e2 ]
  | PappN (_, es) -> variables_in_pexprs acc es

and variables_in_pexprs (acc : Spv.t) (es : pexpr list) : Spv.t =
  List.fold_left variables_in_pexpr acc es

let variables_in_plval (acc : Spv.t) : plval -> Spv.t = function
  | Lnone _ -> acc
  | Lvar x -> Spv.add (L.unloc x) acc
  | Lmem (_, _, _, e) -> variables_in_pexpr acc e
  | Laset (_, _, _, x, e) | Lasub (_, _, _, x, e) ->
      Spv.add (L.unloc x) (variables_in_pexpr acc e)

let variables_in_plvals (acc : Spv.t) : plvals -> Spv.t =
  List.fold_left variables_in_plval acc

let variables_in_instr_r : _ pinstr_r -> Spv.t = function
  | Cassgn (x, _, _, e) -> variables_in_pexpr (variables_in_plval Spv.empty x) e
  | Copn (xs, _, _, es) | Csyscall (xs, _, es) | Ccall (xs, _, es) ->
      variables_in_pexprs (variables_in_plvals Spv.empty xs) es
  | Cfor (x, (_, e1, e2), _) ->
      variables_in_pexprs (Spv.singleton (L.unloc x)) [ e1; e2 ]
  | Cif (e, _, _) | Cwhile (_, _, e, _, _) -> variables_in_pexpr Spv.empty e

(** Maps each variable to the set of nodes at which it occurs *)
let variable_occurrences (c : _ pstmt) : nodeset Mpv.t =
  let tbl = ref Mpv.empty in
  let insert (n : node) (x : pvar) =
    tbl := Mpv.modify_def Siloc.empty x (Siloc.add n) !tbl
  in
  iter_instr
    (fun i -> i.i_desc |> variables_in_instr_r |> Spv.iter (insert i.i_loc))
    c;
  !tbl

(* --------------------------------------------------------------- *)
(* Compute the scope tree *)

let rec tree_of_instr ((acc : tree), (t : Tree.t option)) (i : _ ginstr) :
    tree * Tree.t option =
  let t = make i.i_loc t in
  let acc = Miloc.add i.i_loc t acc in
  (tree_of_instr_r acc t i.i_desc, Some t)

and tree_of_instr_r (acc : tree) (t : Tree.t) : _ ginstr_r -> tree = function
  | Cassgn _ | Copn _ | Csyscall _ | Ccall _ -> acc
  | Cfor (_, _, c) -> tree_of_stmt acc (Some t) c |> fst
  | Cif (_, c1, c2) | Cwhile (_, c1, _, _, c2) ->
      let acc, _ = tree_of_stmt acc (Some t) c1 in
      let acc, _ = tree_of_stmt acc (Some t) c2 in
      acc

and tree_of_stmt (acc : tree) (t : Tree.t option) (c : _ gstmt) :
    tree * Tree.t option =
  List.fold_left tree_of_instr (acc, t) c

(** Reverse a map from variable to nodes into the corresponding map from nodes
    to sets of variables *)
let reverse (m : node Mpv.t) : Spv.t Miloc.t =
  Mpv.fold (fun x n -> Miloc.modify_def Spv.empty n (Spv.add x)) m Miloc.empty

let get_declaration_sites (fd : _ pfunc) : Spv.t Miloc.t =
  let t, last = tree_of_stmt Miloc.empty None fd.f_body in
  let occ = variable_occurrences fd.f_body in
  (* Add occurrences of returned variables at the last instruction, if any *)
  let occ =
    match last with
    | None -> occ
    | Some t ->
        List.fold_left
          (fun occ x ->
            Mpv.modify_def Siloc.empty (L.unloc x) (Siloc.add t.data) occ)
          occ fd.f_ret
  in
  (* Remove function arguments as they must not be declared *)
  let occ = List.fold_left (fun occ x -> Mpv.remove x occ) occ fd.f_args in
  Mpv.map (find_common_ancestor t) occ |> reverse