@@ -4,6 +4,17 @@ open Util
44
55open 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+ *)
718module 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
0 commit comments