Skip to content

Commit 0205ea9

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 277985f commit 0205ea9

File tree

7 files changed

+127
-17
lines changed

7 files changed

+127
-17
lines changed

asmcomp/cmm_helpers.ml

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

744+
let field_address_computed ptr ofs dbg =
745+
array_indexing log2_size_addr ptr ofs dbg
746+
744747
let addr_array_ref arr ofs dbg =
745748
Cop(mk_load_mut Word_val,
746749
[array_indexing log2_size_addr arr ofs dbg], dbg)

asmcomp/cmm_helpers.mli

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,10 @@ val set_field :
214214
expression -> int -> expression -> Lambda.initialization_or_assignment ->
215215
Debuginfo.t -> expression
216216

217+
(** [field_address_computed ptr ofs dbg] returns an expression for the address
218+
at offset [ofs] of the block pointed to by [ptr]. *)
219+
val field_address_computed : expression -> expression -> Debuginfo.t -> expression
220+
217221
(** Load a block's header *)
218222
val get_header : expression -> Debuginfo.t -> expression
219223

asmcomp/cmmgen.ml

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -831,8 +831,6 @@ and transl_prim_1 env p arg dbg =
831831
dbg)
832832
| Pdls_get ->
833833
Cop(Cdls_get, [transl env arg], dbg)
834-
| Patomic_load ->
835-
Cop(mk_load_atomic Word_val, [transl env arg], dbg)
836834
| Ppoll ->
837835
(Csequence (remove_unit (transl env arg),
838836
return_unit dbg (Cop(Cpoll, [], dbg))))
@@ -853,7 +851,9 @@ and transl_prim_1 env p arg dbg =
853851
| Plslbint _ | Plsrbint _ | Pasrbint _ | Pbintcomp (_, _)
854852
| Pbigarrayref (_, _, _, _) | Pbigarrayset (_, _, _, _)
855853
| Pbigarraydim _ | Pstring_load _ | Pbytes_load _ | Pbytes_set _
856-
| Pbigstring_load _ | Pbigstring_set _)
854+
| Pbigstring_load _ | Pbigstring_set _
855+
| Patomic_load
856+
)
857857
->
858858
fatal_errorf "Cmmgen.transl_prim_1: %a"
859859
Printclambda_primitives.primitive p
@@ -870,6 +870,12 @@ and transl_prim_2 env p arg1 arg2 dbg =
870870
let float_val = transl_unbox_float dbg env arg2 in
871871
setfloatfield n init ptr float_val dbg
872872

873+
| Patomic_load ->
874+
let ptr = transl env arg1 in
875+
let ofs = transl env arg2 in
876+
Cop(mk_load_atomic Word_val,
877+
[field_address_computed ptr ofs dbg], dbg)
878+
873879
(* Boolean operations *)
874880
| Psequand ->
875881
let dbg' = Debuginfo.none in
@@ -1022,7 +1028,6 @@ and transl_prim_2 env p arg1 arg2 dbg =
10221028
[transl_unbox_int dbg env bi arg1;
10231029
transl_unbox_int dbg env bi arg2], dbg)) dbg
10241030
| Prunstack | Pperform | Presume | Preperform | Pdls_get
1025-
| Patomic_load
10261031
| Pnot | Pnegint | Pintoffloat | Pfloatofint | Pnegfloat
10271032
| Pabsfloat | Pstringlength | Pbyteslength | Pbytessetu | Pbytessets
10281033
| 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: 108 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,17 @@ type loc_kind =
7777
| Loc_POS
7878
| Loc_FUNCTION
7979

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

96108
let used_primitives = Hashtbl.create 7
97109
let add_used_primitive loc env path =
@@ -114,12 +126,11 @@ let prim_sys_argv =
114126
Primitive.simple ~name:"caml_sys_argv" ~arity:1 ~alloc:true
115127

116128
let prim_atomic_exchange =
117-
Primitive.simple ~name:"caml_atomic_exchange" ~arity:2 ~alloc:false
129+
Primitive.simple ~name:"caml_atomic_exchange_field" ~arity:3 ~alloc:false
118130
let prim_atomic_cas =
119-
Primitive.simple ~name:"caml_atomic_cas" ~arity:3 ~alloc:false
131+
Primitive.simple ~name:"caml_atomic_cas_field" ~arity:4 ~alloc:false
120132
let prim_atomic_fetch_add =
121-
Primitive.simple ~name:"caml_atomic_fetch_add" ~arity:2 ~alloc:false
122-
133+
Primitive.simple ~name:"caml_atomic_fetch_add_field" ~arity:3 ~alloc:false
123134

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

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

@@ -744,10 +834,13 @@ let lambda_of_prim prim_name prim loc args arg_exps =
744834
ap_inlined = Default_inline;
745835
ap_specialised = Default_specialise;
746836
}
837+
| Atomic (op, kind), args ->
838+
lambda_of_atomic prim_name loc op kind args
747839
| (Raise _ | Raise_with_backtrace
748840
| Lazy_force | Loc _ | Primitive _ | Comparison _
749841
| Send | Send_self | Send_cache | Frame_pointers | Identity
750-
| Apply | Revapply), _ ->
842+
| Apply | Revapply
843+
), _ ->
751844
raise(Error(to_location loc, Wrong_arity_builtin_primitive prim_name))
752845

753846
let check_primitive_arity loc p =
@@ -766,6 +859,7 @@ let check_primitive_arity loc p =
766859
| Frame_pointers -> p.prim_arity = 0
767860
| Identity -> p.prim_arity = 1
768861
| Apply | Revapply -> p.prim_arity = 2
862+
| Atomic (op, kind) -> p.prim_arity = atomic_arity op kind
769863
in
770864
if not ok then raise(Error(loc, Wrong_arity_builtin_primitive p.prim_name))
771865

@@ -838,7 +932,11 @@ let primitive_needs_event_after = function
838932
lambda_primitive_needs_event_after (comparison_primitive comp knd)
839933
| Lazy_force | Send | Send_self | Send_cache
840934
| Apply | Revapply -> true
841-
| Raise _ | Raise_with_backtrace | Loc _ | Frame_pointers | Identity -> false
935+
| Raise _ | Raise_with_backtrace
936+
| Loc _
937+
| Frame_pointers | Identity
938+
| Atomic (_, _)
939+
-> false
842940

843941
let transl_primitive_application loc p env ty path exp args arg_exps =
844942
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_270 (r: val) (load_mut_atomic val r))
1818

1919
(function camlCmm.standard_atomic_cas_293 (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)