Skip to content

Commit 5598650

Browse files
Added more documentation for Extensions code, to facilitate new developers.
1 parent 1936e93 commit 5598650

File tree

7 files changed

+453
-23
lines changed

7 files changed

+453
-23
lines changed

lib/ext/atomicExt/AtomicExt.ml

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ open Util
44

55
open ExtApi
66

7+
8+
(** Here we implement an extension that adds support for custom hardware-provided atomic primitives. This is a core Raven extension that is enabled by default.
9+
10+
At present this adds the following atomic primitives:
11+
- `cas`: Compare-and-swap
12+
- `faa`: Fetch-and-add
13+
- `xchg`: Exchange
14+
- `cmpxchg`: Compare-and-exchange
15+
16+
The difference between `cas` and `cmpxchg` is that `cas` only returns a bool indicating whether the swap succeeded, whereas `cmpxchg` returns the initial value at the location in addition to whether the swap succeeded or not.
17+
*)
718
module AtomicExt (Cont : ListApi) = struct
819
let lib_source = None
920
let local_vars = []
@@ -20,6 +31,7 @@ module AtomicExt (Cont : ListApi) = struct
2031
| Xchg -> "xchg"
2132
| CmpXchg -> "cmpxchg"
2233

34+
(* No new types or expressions, only new statements. *)
2335
type Stmt.stmt_ext +=
2436
| AtomicInbuiltInit of atomic_inbuilt_kind
2537
| AtomicInbuiltNonInit of atomic_inbuilt_kind
@@ -43,12 +55,14 @@ module AtomicExt (Cont : ListApi) = struct
4355
let stmt_ext_local_vars_modified stmt_ext exprs =
4456
match stmt_ext, exprs with
4557
| (AtomicInbuiltInit ais | AtomicInbuiltNonInit ais), (lhs_expr :: field_expr :: ref_expr :: args) ->
58+
(* Only the `lhs_expr` is modified; if it is local, it is returned. *)
4659
if QualIdent.is_local (Expr.to_qual_ident lhs_expr) then
4760
[QualIdent.to_ident (Expr.to_qual_ident lhs_expr)]
4861
else
4962
[]
5063
| _ -> Cont.stmt_ext_local_vars_modified stmt_ext exprs
5164

65+
(* These commands pretty explicitly access the `field_expr` field. *)
5266
let stmt_ext_fields_accessed stmt_ext exprs =
5367
match stmt_ext, exprs with
5468
| (AtomicInbuiltInit ais | AtomicInbuiltNonInit ais), (lhs_expr :: field_expr :: ref_expr :: args) ->
@@ -67,6 +81,12 @@ module AtomicExt (Cont : ListApi) = struct
6781

6882
let type_check_expr = Cont.type_check_expr
6983

84+
(** Here we define a custom logic to decide whether a type is "word-sized" (ie operable in an atomic hardware step), or not.
85+
86+
At present, we assume 2 bits for storing any tags, and the remaining 62 bits to store the object itself, be it an `Int`, `Ref`, or `Bool`.
87+
88+
This logic can ready be updated to fit specific domains. That is one advantage of implementing this feature as an extension.
89+
*)
7090
let is_type_word_sized (typ: type_expr) : bool Rewriter.t =
7191
let open Rewriter.Syntax in
7292
Logs.debug (fun m -> m "[EXT] AtomicExt: is_type_word_sized: input=%a" Type.pr typ);
@@ -106,6 +126,7 @@ module AtomicExt (Cont : ListApi) = struct
106126

107127
| _ -> Rewriter.return false
108128

129+
(* type-check each statement *)
109130
let type_check_stmt call_decl (stmt_ext : Stmt.stmt_ext) (expr_list: expr list) (stmt_loc: Loc.t) (disam_tbl : ProgUtils.DisambiguationTbl.t)
110131
(type_check_stmt_functs : ExtApi.type_check_stmt_functs)
111132
:
@@ -124,9 +145,11 @@ module AtomicExt (Cont : ListApi) = struct
124145
if is_ghost_scope then
125146
Error.type_error stmt_loc "Cannot use atomic operations in a ghost context"
126147
in
148+
(* use `get_assign_lhs` to get the var_decl for `lhs_expr`. *)
127149
let* atomic_inbuilt_lhs, var_decl = type_check_stmt_functs.get_assign_lhs (Expr.to_qual_ident lhs_expr) ~is_init:is_init in
128150
Logs.debug (fun m -> m "[EXT] AtomicExt.type_check_stmt lhs_expr: %a; atomic_inbuilt_lhs: %a" Expr.pr lhs_expr QualIdent.pr atomic_inbuilt_lhs);
129151

152+
(* Use `Rewriter.resolve_and_find` to find the field. This can very well be replaced by the more compact `Rewriter.find_and_reify_field`; that would be equivalent. *)
130153
let* atomic_inbuilt_field, symbol =
131154
Rewriter.resolve_and_find (Expr.to_qual_ident field_expr)
132155
in
@@ -151,53 +174,79 @@ module AtomicExt (Cont : ListApi) = struct
151174
("Expected field identifier, but found "
152175
^ Expr.to_string field_expr)
153176
in
177+
(* type-checking `ref_expr` *)
154178
let* atomic_inbuilt_ref = type_check_stmt_functs.disambiguate_process_expr ref_expr Type.ref disam_tbl in
155179
let+ args = match ais with
180+
(* type-checking arguments differently according to each atomic primitive. *)
156181
| Cas -> begin
157182
match args with
158183
| old_val_expr :: new_val_expr :: [] ->
184+
(* for `cas`, the old_val_expr and new_val_expr have the same type has the field `ais_fld`. *)
159185
let* cas_old_val = type_check_stmt_functs.disambiguate_process_expr old_val_expr ais_fld_type disam_tbl in
160186
let* cas_new_val = type_check_stmt_functs.disambiguate_process_expr new_val_expr ais_fld_type disam_tbl in
161187
let+ _ = type_check_stmt_functs.disambiguate_process_expr (Expr.mk_var ~typ:var_decl.var_type (Expr.to_qual_ident lhs_expr)) (Type.bool |> Type.set_ghost_to var_decl.var_type) disam_tbl in
162188
[ cas_old_val; cas_new_val ]
189+
190+
(* type error *)
163191
| _ -> Error.type_error stmt_loc "Wrong number of arguments for CAS"
164192
end
193+
165194
| Faa -> begin
166195
match args with
167196
| faa_val :: [] ->
197+
(* For `faa`, the underlying field must be an `Int` field, having a type_expr of `Field[Int]`. *)
168198
let* _ = type_check_stmt_functs.disambiguate_process_expr (Expr.mk_var ~typ:ais_fld_type_full atomic_inbuilt_field)
169199
(Type.mk_fld (QualIdent.to_loc atomic_inbuilt_field) Type.int) disam_tbl in
200+
(* make sure `faa_val` is well_typed. *)
170201
let* faa_val = type_check_stmt_functs.disambiguate_process_expr faa_val ais_fld_type disam_tbl in
202+
203+
(* make sure `lhs_expr` is of the right type. *)
171204
let+ _ = type_check_stmt_functs.disambiguate_process_expr (Expr.mk_var ~typ:var_decl.var_type (Expr.to_qual_ident lhs_expr)) (ais_fld_type |> Type.set_ghost_to var_decl.var_type) disam_tbl in
172205
[ faa_val ]
206+
207+
(* type error *)
173208
| _ -> Error.type_error stmt_loc "Wrong number of arguments for FAA"
174209
end
210+
175211
| Xchg -> begin
176212
match args with
177213
| xchg_new_val :: [] ->
178-
214+
(* make sure `xch_new_val` is well-typed *)
179215
let* xchg_new_val = type_check_stmt_functs.disambiguate_process_expr xchg_new_val ais_fld_type disam_tbl in
216+
217+
(* make sure `lhs_expr` is well-typed *)
180218
let+ _ = type_check_stmt_functs.disambiguate_process_expr (Expr.mk_var ~typ:var_decl.var_type (Expr.to_qual_ident lhs_expr)) (ais_fld_type |> Type.set_ghost_to var_decl.var_type) disam_tbl in
181219
[ xchg_new_val ]
220+
221+
(* type error *)
182222
| _ -> Error.type_error stmt_loc "Wrong number of arguments for XCHG"
183223
end
224+
184225
| CmpXchg -> begin
185226
match args with
186227
| old_val_expr :: new_val_expr :: [] ->
228+
(* make sure `cmxchg_old_val` is well-typed *)
187229
let* cmpxchg_old_val = type_check_stmt_functs.disambiguate_process_expr old_val_expr ais_fld_type disam_tbl in
230+
231+
(* make sure `cmxchg_new_val` is well-typed *)
188232
let* cmpxchg_new_val = type_check_stmt_functs.disambiguate_process_expr new_val_expr ais_fld_type disam_tbl in
233+
234+
(* make sure `lhs_expr` is well-typed *)
189235
let+ _ = type_check_stmt_functs.disambiguate_process_expr
190236
(Expr.mk_var ~typ:var_decl.var_type (Expr.to_qual_ident lhs_expr))
191237
(Type.mk_prod stmt_loc [ais_fld_type; Type.bool] |> Type.set_ghost_to var_decl.var_type)
192238
disam_tbl
193239
in
194240

195241
[ cmpxchg_old_val; cmpxchg_new_val ]
242+
243+
(* type error *)
196244
| _ -> Error.type_error stmt_loc "Wrong number of arguments for cmpxchg"
197245
end
198246

199247
in
200248

249+
(* Finally, rebuilt the `Stmt.basic_stmt_desc`. *)
201250
let ais_desc = (ext,
202251
Expr.from_var_decl var_decl ::
203252
(Expr.mk_app ~loc:(Expr.to_loc field_expr) ~typ:ais_fld_type_full (Expr.Var atomic_inbuilt_field) []) ::
@@ -207,19 +256,23 @@ module AtomicExt (Cont : ListApi) = struct
207256
Logs.debug (fun m -> m "AtomicExt.type_check_stmt FINISHES");
208257
(Stmt.StmtExt ais_desc, disam_tbl)
209258

259+
(* Type error *)
210260
| (AtomicInbuiltInit ais | AtomicInbuiltNonInit ais), _ ->
211261
Error.type_error stmt_loc "Wrong number of arguments for atomic commands"
212-
| stmt_ext, expr_list -> Cont.type_check_stmt call_decl stmt_ext expr_list stmt_loc disam_tbl type_check_stmt_functs
262+
263+
| _ -> Cont.type_check_stmt call_decl stmt_ext expr_list stmt_loc disam_tbl type_check_stmt_functs
213264

214265

215266
(* Rewrites *)
267+
(* Rewriting these atomic commands in equivalent simpler Raven commands. *)
216268
let rewrite_type_ext = Cont.rewrite_type_ext
217269
let rewrite_expr_ext = Cont.rewrite_expr_ext
218270

219271
let rewrite_stmt_ext (stmt_ext: Stmt.stmt_ext) (expr_list: expr list) loc: Stmt.t Rewriter.t =
220272
let open Rewriter.Syntax in
221273
match stmt_ext, expr_list with
222274
| (AtomicInbuiltInit ais | AtomicInbuiltNonInit ais), (lhs_expr :: field_expr :: ref_expr :: args) ->
275+
(* define a new local variable to store field value *)
223276
let new_var_name =
224277
Ident.fresh loc (QualIdent.to_string (Expr.to_qual_ident lhs_expr) ^ "$" ^ atomic_inbuilt_string ais)
225278
in
@@ -234,17 +287,20 @@ module AtomicExt (Cont : ListApi) = struct
234287
Type.mk_var_decl ~loc:loc ~ghost:true new_var_name
235288
new_var_decl_typ
236289
in
290+
(* add the new local variable *)
237291
let+ _ =
238292
Rewriter.introduce_symbol
239293
(Module.VarDef { var_decl = new_var_decl; var_init = None })
240294
in
241295
let new_var_qualident = QualIdent.from_ident new_var_decl.var_name in
296+
(* ```rd_var := ref.field;``` *)
242297
let read_stmt =
243298
Stmt.mk_field_read ~loc:loc new_var_qualident
244299
(Expr.to_qual_ident field_expr) ref_expr
245300
in
246301
let ais_stmts =
247302
match ais, args with
303+
(* CAS *)
248304
| Cas, [old_val; new_val] ->
249305
let test_ =
250306
Some
@@ -262,15 +318,48 @@ module AtomicExt (Cont : ListApi) = struct
262318
let else_ =
263319
Stmt.mk_assign ~loc:loc [ Expr.to_qual_ident lhs_expr ] (Expr.mk_bool false)
264320
in
321+
(* ```
322+
if (rd_var == old_val) {
323+
lhs := true;
324+
ref.field := new_val;
325+
} else {
326+
lhs := false;
327+
}
328+
```
329+
*)
265330
[Stmt.mk_cond ~loc:loc test_ then_ else_]
331+
332+
(* FAA *)
266333
| Faa, [faa_val] ->
334+
(* ```
335+
ref.field := rd_var + faa_val;
336+
lhs_expr := rd_var;
337+
```
338+
*)
267339
[ Stmt.mk_field_write ~loc:loc ref_expr (Expr.to_qual_ident field_expr)
268340
(Expr.mk_app ~typ:Type.int Expr.Plus [Expr.from_var_decl new_var_decl; faa_val]);
269341
Stmt.mk_assign ~loc:loc [ Expr.to_qual_ident lhs_expr ] (Expr.from_var_decl new_var_decl) ]
342+
343+
(* XCHG *)
270344
| Xchg, [xchg_new_val] ->
345+
(* ```
346+
ref.field := xchg_new_val;
347+
lhs_expr := rd_var;
348+
```
349+
*)
271350
[ Stmt.mk_field_write ~loc:loc ref_expr (Expr.to_qual_ident field_expr)
272351
xchg_new_val;
273352
Stmt.mk_assign ~loc:loc [ Expr.to_qual_ident lhs_expr ] (Expr.from_var_decl new_var_decl) ]
353+
354+
(* ```
355+
if (rd_var == old_val) {
356+
lhs_expr := (rd_var, true);
357+
ref.field := new_val;
358+
} else {
359+
lhs_expr := (rd_var, false);
360+
}
361+
```
362+
*)
274363
| CmpXchg, [cmpxchg_old_val; cmpxchg_new_val] ->
275364
let test_ =
276365
Some
@@ -295,12 +384,14 @@ module AtomicExt (Cont : ListApi) = struct
295384
])
296385
in
297386
[Stmt.mk_cond ~loc:loc test_ then_ else_]
387+
298388
| _ -> assert false
299389
in
300390
let new_stmts =
301391
Stmt.mk_block_stmt ~loc:loc (read_stmt :: ais_stmts)
302392
in
303393
new_stmts
394+
304395
| _ -> Cont.rewrite_stmt_ext stmt_ext expr_list loc
305396

306397

lib/ext/defaultExt/defaultExt.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ open Ast
33
open ExtApi
44
open Util
55

6+
(* This is the blank extension used to close the "chain" of extensions. If the code reaches this extension then that means that previous extensions did not do their job. This function mostly just raises errors for everything. *)
7+
68
module DefaultExt = struct
79
let lib_source = None
810
let local_vars = []

0 commit comments

Comments
 (0)