summaryrefslogtreecommitdiff
path: root/src/PureMicroPasses.ml
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/PureMicroPasses.ml94
1 files changed, 79 insertions, 15 deletions
diff --git a/src/PureMicroPasses.ml b/src/PureMicroPasses.ml
index abdeb41e..ceaaa129 100644
--- a/src/PureMicroPasses.ml
+++ b/src/PureMicroPasses.ml
@@ -85,7 +85,9 @@ let get_body_min_var_counter (body : fun_body) : VarId.generator =
(* Find the max id in the input variables - some of them may have been
* filtered from the body *)
let min_input_id =
- List.fold_left (fun id var -> VarId.max id var.id) VarId.zero body.inputs
+ List.fold_left
+ (fun id (var : var) -> VarId.max id var.id)
+ VarId.zero body.inputs
in
let obj =
object
@@ -119,9 +121,12 @@ type pn_ctx = string VarId.Map.t
- whenever we see an rvalue/lvalue which is exactly an unnamed variable,
and this value is linked to some meta-place information which contains
a name and an empty path, we consider we should use this name
+ - for the values given back by backward functions, we link the given back
+ values to the places (identified by unique indices) where the input
+ arguments come from (see examples below)
Something important is that, for every variable we find, the name of this
- variable is influenced by the information we find *below* in the AST.
+ variable can be influenced by the information we find *below* in the AST.
For instance, the following situations happen:
@@ -139,8 +144,8 @@ type pn_ctx = string VarId.Map.t
tmp := discriminant(ls);
switch tmp {
0 => {
- x := (ls as Cons).0;
- hd := (ls as Cons).1;
+ x := (ls as Cons).0; // (i)
+ hd := (ls as Cons).1; // (ii)
...
}
}
@@ -149,8 +154,10 @@ type pn_ctx = string VarId.Map.t
mode, we expand this value upon evaluating `tmp = discriminant(ls)`.
However, at this point, we don't know which should be the names of
the symbolic values we introduce for the fields of `Cons`!
+
Let's imagine we have (for the `Cons` branch): `s0 ~~> Cons s1 s2`.
- The assigments lead to the following binding in the evaluation context:
+ The assigments at (i) and (ii) lead to the following binding in the
+ evaluation context:
```
x -> s1
hd -> s2
@@ -165,7 +172,44 @@ type pn_ctx = string VarId.Map.t
| Cons x hd -> ...
| ...
```
+ - Given back values (introduced by backward functions):
+ Let's say we have the following Rust code:
+ ```
+ let py = id(&mut x);
+ *py = 2;
+ assert!(x == 2);
+ ```
+
+ After desugaring, we get the following MIR:
+ ```
+ ^0 = &mut x; // anonymous variable
+ py = id(move ^0);
+ *py += 2;
+ assert!(x == 2);
+ ```
+
+ We want this to be translated as:
+ ```
+ let py = id_fwd x in
+ let py1 = py + 2 in
+ let x1 = id_back x py1 in // <-- x1 is "given back": doesn't appear in the original MIR
+ assert(x1 = 2);
+ ```
+
+ We want to notice that the value given back by `id_back` is given back for "x",
+ so we should use "x" as the basename (hence the resulting name "x1"). However,
+ this is non-trivial, because after desugaring the input argument given to `id`
+ is not `&mut x` but `move ^0` (i.e., it comes from a temporary, anonymous
+ variable). For this reason, we give unique identifiers to places, link
+ given back values to the places where the input arguments come from, and
+ propagate naming information between the places.
+
+ This way, because of `^0 = &mut x`, we can propagate the name "x" to the place
+ `^0`, then to the given back variable across the function call.
+
- TODO: inputs and end abstraction...
+
+
*)
let compute_pretty_names (def : fun_decl) : fun_decl =
(* Small helpers *)
@@ -217,6 +261,7 @@ let compute_pretty_names (def : fun_decl) : fun_decl =
in
let add_constraint (mp : mplace) (var_id : VarId.id) (ctx : pn_ctx) : pn_ctx =
+ (* Update the variable name *)
match (mp.name, mp.projection) with
| Some name, [] ->
(* Check if the variable already has a name - if not: insert the new name *)
@@ -242,19 +287,39 @@ let compute_pretty_names (def : fun_decl) : fun_decl =
method plus ctx0 ctx1 _ = merge_ctxs (ctx0 ()) (ctx1 ())
- method! visit_Var _ v mdp () =
+ method! visit_Var _ v mp () =
(* Register the variable *)
let ctx = add_var (self#zero ()) v in
(* Register the mplace information if there is such information *)
- match mdp.place with
- | None -> ctx
- | Some mp -> add_constraint mp v.id ctx
+ match mp with Some mp -> add_constraint mp v.id ctx | None -> ctx
end
in
let ctx1 = obj#visit_typed_lvalue () lv () in
merge_ctxs ctx ctx1
in
+ (* This is used to propagate constraint information about places in case of
+ * assignments *)
+ let add_left_right_constraint (lv : typed_lvalue) (re : texpression)
+ (ctx : pn_ctx) : pn_ctx =
+ (* We propagate constraints across variable reassignments: `^0 = x` *)
+ match (lv.value, re.e) with
+ | ( LvVar (Var (lvar, (Some { name = None; projection = [] } | None))),
+ Value (_, Some ({ name = Some _; projection = [] } as rmp)) ) ->
+ (* Use the meta-information on the right *)
+ add_constraint rmp lvar.id ctx
+ | ( LvVar (Var (lvar, (Some { name = None; projection = [] } | None))),
+ Value ({ value = RvPlace { var = rvar_id; projection = [] }; ty = _ }, _)
+ ) -> (
+ (* Use the variable name *)
+ match
+ (VarId.Map.find_opt lvar.id ctx, VarId.Map.find_opt rvar_id ctx)
+ with
+ | None, Some name -> VarId.Map.add lvar.id name ctx
+ | _ -> ctx)
+ | _ -> (* Nothing to propagate *) ctx
+ in
+
(* *)
let rec update_texpression (e : texpression) (ctx : pn_ctx) :
pn_ctx * texpression =
@@ -286,6 +351,7 @@ let compute_pretty_names (def : fun_decl) : fun_decl =
and update_let (monadic : bool) (lv : typed_lvalue) (re : texpression)
(e : texpression) (ctx : pn_ctx) : pn_ctx * expression =
let ctx = add_left_constraint lv ctx in
+ let ctx = add_left_right_constraint lv re ctx in
let ctx, re = update_texpression re ctx in
let ctx, e = update_texpression e ctx in
let lv = update_typed_lvalue ctx lv in
@@ -822,7 +888,7 @@ let to_monadic (config : config) (def : fun_decl) : fun_decl =
let id, _ = VarId.fresh var_cnt in
let var = { id; basename = None; ty = unit_ty } in
let inputs = [ var ] in
- let input_lv = mk_typed_lvalue_from_var var None None in
+ let input_lv = mk_typed_lvalue_from_var var None in
let inputs_lvs = [ input_lv ] in
Some { body with inputs; inputs_lvs }
in
@@ -980,7 +1046,7 @@ let decompose_monadic_let_bindings (_ctx : trans_ctx) (def : fun_decl) :
* monadic binding *)
let vid = fresh_id () in
let tmp : var = { id = vid; basename = None; ty = lv.ty } in
- let ltmp = mk_typed_lvalue_from_var tmp None None in
+ let ltmp = mk_typed_lvalue_from_var tmp None in
let rtmp = mk_typed_rvalue_from_var tmp in
let rtmp = mk_value_expression rtmp None in
(* Visit the next expression *)
@@ -1058,9 +1124,7 @@ let unfold_monadic_let_bindings (config : config) (_ctx : trans_ctx)
in
(* The `Success` branch introduces a fresh state variable *)
let state_var = fresh_state_var () in
- let state_value =
- mk_typed_lvalue_from_var state_var None None
- in
+ let state_value = mk_typed_lvalue_from_var state_var None in
let success_pat =
mk_result_return_lvalue
(mk_simpl_tuple_lvalue [ state_value; lv ])
@@ -1131,7 +1195,7 @@ let unfold_monadic_let_bindings (config : config) (_ctx : trans_ctx)
let sg = { sg with inputs = sg_inputs; outputs = sg_outputs } in
(* Update the inputs list *)
let inputs = body.inputs @ [ input_state_var ] in
- let input_lv = mk_typed_lvalue_from_var input_state_var None None in
+ let input_lv = mk_typed_lvalue_from_var input_state_var None in
let inputs_lvs = body.inputs_lvs @ [ input_lv ] in
(* Update the body *)
let body = { body with inputs; inputs_lvs } in