Skip to content

Commit 92de452

Browse files
gascheclef-men
andcommitted
lambda: Add support for new atomic primitives.
Uses of existing atomic primitives %atomic_foo, which act on single-field references, are now translated into %atomic_foo_field, which act on a pointer and an offset -- passed as separate arguments. In particular, note that the arity of the internal Lambda primitive Patomic_load increases by one with this patchset. (Initially we renamed it into Patomic_load_field but this creates a lot of churn for no clear benefits.) We also support primitives of the form %atomic_foo_loc, which expects a pair of a pointer and an offset (as a single argument), as we proposed in the RFC on atomic fields ocaml/RFCs#39 (but there is no language-level support for atomic record fields yet) Co-authored-by: Clément Allain <[email protected]>
1 parent 6c32a32 commit 92de452

File tree

7 files changed

+131
-17
lines changed

7 files changed

+131
-17
lines changed

asmcomp/cmm_helpers.ml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,9 @@ let array_indexing ?typ log2size ptr ofs dbg =
744744
Cop(add, [Cop(add, [ptr; lsl_const ofs (log2size - 1) dbg], dbg);
745745
Cconst_int((-1) lsl (log2size - 1), dbg)], dbg)
746746

747+
let field_address_computed ptr ofs dbg =
748+
array_indexing log2_size_addr ptr ofs dbg
749+
747750
let addr_array_ref arr ofs dbg =
748751
Cop(mk_load_mut Word_val,
749752
[array_indexing log2_size_addr arr ofs dbg], dbg)

asmcomp/cmm_helpers.mli

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,12 @@ val set_field :
211211
expression -> int -> expression -> Lambda.initialization_or_assignment ->
212212
Debuginfo.t -> expression
213213

214+
(** [field_address_computed ptr ofs dbg] returns an expression for the address
215+
at offset [ofs] (in machine words) of the block pointed to by [ptr].
216+
The resulting expression is a derived pointer of type [Addr]. *)
217+
val field_address_computed :
218+
expression -> expression -> Debuginfo.t -> expression
219+
214220
(** Load a block's header *)
215221
val get_header : expression -> Debuginfo.t -> expression
216222

asmcomp/cmmgen.ml

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -897,8 +897,6 @@ and transl_prim_1 env p arg dbg =
897897
dbg)
898898
| Pdls_get ->
899899
Cop(Cdls_get, [transl env arg], dbg)
900-
| Patomic_load ->
901-
Cop(mk_load_atomic Word_val, [transl env arg], dbg)
902900
| Ppoll ->
903901
(Csequence (remove_unit (transl env arg),
904902
return_unit dbg (Cop(Cpoll, [], dbg))))
@@ -919,7 +917,9 @@ and transl_prim_1 env p arg dbg =
919917
| Plslbint _ | Plsrbint _ | Pasrbint _ | Pbintcomp (_, _)
920918
| Pbigarrayref (_, _, _, _) | Pbigarrayset (_, _, _, _)
921919
| Pbigarraydim _ | Pstring_load _ | Pbytes_load _ | Pbytes_set _
922-
| Pbigstring_load _ | Pbigstring_set _)
920+
| Pbigstring_load _ | Pbigstring_set _
921+
| Patomic_load
922+
)
923923
->
924924
fatal_errorf "Cmmgen.transl_prim_1: %a"
925925
Printclambda_primitives.primitive p
@@ -936,6 +936,12 @@ and transl_prim_2 env p arg1 arg2 dbg =
936936
let float_val = transl_unbox_float dbg env arg2 in
937937
setfloatfield n init ptr float_val dbg
938938

939+
| Patomic_load ->
940+
let ptr = transl env arg1 in
941+
let ofs = transl env arg2 in
942+
Cop(mk_load_atomic Word_val,
943+
[field_address_computed ptr ofs dbg], dbg)
944+
939945
(* Boolean operations *)
940946
| Psequand ->
941947
let dbg' = Debuginfo.none in
@@ -1088,7 +1094,6 @@ and transl_prim_2 env p arg1 arg2 dbg =
10881094
[transl_unbox_int dbg env bi arg1;
10891095
transl_unbox_int dbg env bi arg2], dbg)) dbg
10901096
| Prunstack | Pperform | Presume | Preperform | Pdls_get
1091-
| Patomic_load
10921097
| Pnot | Pnegint | Pintoffloat | Pfloatofint | Pnegfloat
10931098
| Pabsfloat | Pstringlength | Pbyteslength | Pbytessetu | Pbytessets
10941099
| Pisint | Pbswap16 | Pint_as_pointer | Popaque | Pread_symbol _

bytecomp/bytegen.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ let comp_primitive stack_info p sz args =
488488
| Pint_as_pointer -> Kccall("caml_int_as_pointer", 1)
489489
| Pbytes_to_string -> Kccall("caml_string_of_bytes", 1)
490490
| Pbytes_of_string -> Kccall("caml_bytes_of_string", 1)
491-
| Patomic_load -> Kccall("caml_atomic_load", 1)
491+
| Patomic_load -> Kccall("caml_atomic_load_field", 2)
492492
| Pdls_get -> Kccall("caml_domain_dls_get", 1)
493493
| Ppoll -> Kccall("caml_process_pending_actions_with_root", 1)
494494
(* The cases below are handled in [comp_expr] before the [comp_primitive] call

lambda/translprim.ml

Lines changed: 110 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,17 @@ type loc_kind =
7676
| Loc_POS
7777
| Loc_FUNCTION
7878

79+
type atomic_kind =
80+
| Ref (* operation on an atomic reference (takes only a pointer) *)
81+
| Field (* operation on an atomic field (takes a pointer and an offset) *)
82+
| Loc (* operation on a first-class field (takes a (pointer, offset) pair *)
83+
84+
type atomic_op =
85+
| Load
86+
| Exchange
87+
| Cas
88+
| Faa
89+
7990
type prim =
8091
| Primitive of Lambda.primitive * int
8192
| External of Primitive.description
@@ -91,6 +102,7 @@ type prim =
91102
| Identity
92103
| Apply
93104
| Revapply
105+
| Atomic of atomic_op * atomic_kind
94106

95107
let used_primitives = Hashtbl.create 7
96108
let add_used_primitive loc env path =
@@ -113,12 +125,11 @@ let prim_sys_argv =
113125
Primitive.simple ~name:"caml_sys_argv" ~arity:1 ~alloc:true
114126

115127
let prim_atomic_exchange =
116-
Primitive.simple ~name:"caml_atomic_exchange" ~arity:2 ~alloc:false
128+
Primitive.simple ~name:"caml_atomic_exchange_field" ~arity:3 ~alloc:false
117129
let prim_atomic_cas =
118-
Primitive.simple ~name:"caml_atomic_cas" ~arity:3 ~alloc:false
130+
Primitive.simple ~name:"caml_atomic_cas_field" ~arity:4 ~alloc:false
119131
let prim_atomic_fetch_add =
120-
Primitive.simple ~name:"caml_atomic_fetch_add" ~arity:2 ~alloc:false
121-
132+
Primitive.simple ~name:"caml_atomic_fetch_add_field" ~arity:3 ~alloc:false
122133

123134
let primitives_table =
124135
create_hashtable 57 [
@@ -371,10 +382,18 @@ let primitives_table =
371382
"%greaterequal", Comparison(Greater_equal, Compare_generic);
372383
"%greaterthan", Comparison(Greater_than, Compare_generic);
373384
"%compare", Comparison(Compare, Compare_generic);
374-
"%atomic_load", Primitive (Patomic_load, 1);
375-
"%atomic_exchange", External prim_atomic_exchange;
376-
"%atomic_cas", External prim_atomic_cas;
377-
"%atomic_fetch_add", External prim_atomic_fetch_add;
385+
"%atomic_load", Atomic(Load, Ref);
386+
"%atomic_exchange", Atomic(Exchange, Ref);
387+
"%atomic_cas", Atomic(Cas, Ref);
388+
"%atomic_fetch_add", Atomic(Faa, Ref);
389+
"%atomic_load_field", Atomic(Load, Field);
390+
"%atomic_exchange_field", Atomic(Exchange, Field);
391+
"%atomic_cas_field", Atomic(Cas, Field);
392+
"%atomic_fetch_add_field", Atomic(Faa, Field);
393+
"%atomic_load_loc", Atomic(Load, Loc);
394+
"%atomic_exchange_loc", Atomic(Exchange, Loc);
395+
"%atomic_cas_loc", Atomic(Cas, Loc);
396+
"%atomic_fetch_add_loc", Atomic(Faa, Loc);
378397
"%runstack", Primitive (Prunstack, 3);
379398
"%reperform", Primitive (Preperform, 3);
380399
"%perform", Primitive (Pperform, 1);
@@ -657,6 +676,79 @@ let lambda_of_loc kind sloc =
657676
let scope_name = Debuginfo.Scoped_location.string_of_scoped_location sloc in
658677
Lconst (Const_immstring scope_name)
659678

679+
let atomic_arity op (kind : atomic_kind) =
680+
let arity_of_op =
681+
match op with
682+
| Load -> 1
683+
| Exchange -> 2
684+
| Cas -> 3
685+
| Faa -> 2
686+
in
687+
let extra_kind_arity =
688+
match kind with
689+
| Ref | Loc -> 0
690+
| Field -> 1
691+
in
692+
arity_of_op + extra_kind_arity
693+
694+
let lambda_of_atomic prim_name loc op (kind : atomic_kind) args =
695+
if List.length args <> atomic_arity op kind then
696+
raise (Error (to_location loc, Wrong_arity_builtin_primitive prim_name)) ;
697+
let split = function
698+
| [] ->
699+
(* split is only called when [arity >= 1] *)
700+
assert false
701+
| first :: rest ->
702+
first, rest
703+
in
704+
let prim =
705+
match op with
706+
| Load -> Patomic_load
707+
| Exchange -> Pccall prim_atomic_exchange
708+
| Cas -> Pccall prim_atomic_cas
709+
| Faa -> Pccall prim_atomic_fetch_add
710+
in
711+
match kind with
712+
| Ref ->
713+
(* the primitive application
714+
[Lprim(%atomic_exchange, [ref; v])]
715+
becomes
716+
[Lprim(caml_atomic_exchange_field, [ref; 0; v])]
717+
*)
718+
let ref_arg, rest = split args in
719+
let args = ref_arg :: Lconst (Lambda.const_int 0) :: rest in
720+
Lprim (prim, args, loc)
721+
| Field ->
722+
(* the primitive application
723+
[Lprim(%atomic_exchange_field, [ptr; ofs; v])]
724+
becomes
725+
[Lprim(caml_atomic_exchange_field, [ptr; ofs; v])] *)
726+
Lprim (prim, args, loc)
727+
| Loc ->
728+
(* the primitive application
729+
[Lprim(%atomic_exchange_loc, [(ptr, ofs); v])]
730+
becomes
731+
[Lprim(caml_atomic_exchange_field, [ptr; ofs; v])]
732+
and in the general case of a non-tuple expression <loc>
733+
[Lprim(%atomic_exchange_loc, [loc; v])]
734+
becomes
735+
[Llet(p, loc,
736+
Lprim(caml_atomic_exchange_field, [Field(p, 0); Field(p, 1); v]))]
737+
*)
738+
let loc_arg, rest = split args in
739+
match loc_arg with
740+
| Lprim (Pmakeblock _, [ptr; ofs], _argloc) ->
741+
let args = ptr :: ofs :: rest in
742+
Lprim (prim, args, loc)
743+
| _ ->
744+
let varg = Ident.create_local "atomic_arg" in
745+
let ptr = Lprim (Pfield (0, Pointer, Immutable), [Lvar varg], loc) in
746+
let ofs =
747+
Lprim (Pfield (1, Immediate, Immutable), [Lvar varg], loc)
748+
in
749+
let args = ptr :: ofs :: rest in
750+
Llet (Strict, Pgenval, varg, loc_arg, Lprim (prim, args, loc))
751+
660752
let caml_restore_raw_backtrace =
661753
Primitive.simple ~name:"caml_restore_raw_backtrace" ~arity:2 ~alloc:false
662754

@@ -743,10 +835,13 @@ let lambda_of_prim prim_name prim loc args arg_exps =
743835
ap_inlined = Default_inline;
744836
ap_specialised = Default_specialise;
745837
}
838+
| Atomic (op, kind), args ->
839+
lambda_of_atomic prim_name loc op kind args
746840
| (Raise _ | Raise_with_backtrace
747841
| Lazy_force | Loc _ | Primitive _ | Comparison _
748842
| Send | Send_self | Send_cache | Frame_pointers | Identity
749-
| Apply | Revapply), _ ->
843+
| Apply | Revapply
844+
), _ ->
750845
raise(Error(to_location loc, Wrong_arity_builtin_primitive prim_name))
751846

752847
let check_primitive_arity loc p =
@@ -765,6 +860,7 @@ let check_primitive_arity loc p =
765860
| Frame_pointers -> p.prim_arity = 0
766861
| Identity -> p.prim_arity = 1
767862
| Apply | Revapply -> p.prim_arity = 2
863+
| Atomic (op, kind) -> p.prim_arity = atomic_arity op kind
768864
in
769865
if not ok then raise(Error(loc, Wrong_arity_builtin_primitive p.prim_name))
770866

@@ -838,7 +934,11 @@ let primitive_needs_event_after = function
838934
lambda_primitive_needs_event_after (comparison_primitive comp knd)
839935
| Lazy_force | Send | Send_self | Send_cache
840936
| Apply | Revapply -> true
841-
| Raise _ | Raise_with_backtrace | Loc _ | Frame_pointers | Identity -> false
937+
| Raise _ | Raise_with_backtrace
938+
| Loc _
939+
| Frame_pointers | Identity
940+
| Atomic (_, _)
941+
-> false
842942

843943
let transl_primitive_application loc p env ty path exp args arg_exps =
844944
let prim =

runtime/memory.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ CAMLprim value caml_atomic_exchange (value ref, value v)
352352
return caml_atomic_exchange_field(ref, Val_long(0), v);
353353
}
354354

355-
CAMLexport value caml_atomic_cas_field (
355+
CAMLprim value caml_atomic_cas_field (
356356
value obj, value vfield, value oldval, value newval)
357357
{
358358
intnat field = Long_val(vfield);

testsuite/tests/atomic-locs/cmm.compilers.reference

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ cmm:
1717
(function camlCmm.standard_atomic_get_271 (r: val) (load_mut_atomic val r))
1818

1919
(function camlCmm.standard_atomic_cas_294 (r: val oldv: val newv: val)
20-
(extcall "caml_atomic_cas" r oldv newv int,int,int->val))
20+
(extcall "caml_atomic_cas_field" r 1 oldv newv int,int,int,int->val))
2121

2222
(function camlCmm.entry ()
2323
(let standard_atomic_get "camlCmm.2"

0 commit comments

Comments
 (0)