Skip to content

Commit 28f1321

Browse files
gascheclef-men
andcommitted
lambda: add support for more 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 6f05b70 commit 28f1321

File tree

6 files changed

+127
-16
lines changed

6 files changed

+127
-16
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: 109 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,20 @@ type loc_kind =
7777
| Loc_POS
7878
| Loc_FUNCTION
7979

80+
type atomic_kind =
81+
| Ref (* operation on an atomic reference
82+
(takes only a pointer) *)
83+
| Record (* operation on an atomic record
84+
(takes a pointer and an offset) *)
85+
| Loc (* operation on a first-class field
86+
(takes a (pointer, offset) pair *)
87+
88+
type atomic_op =
89+
| Load
90+
| Exchange
91+
| Cas
92+
| Fetch_add
93+
8094
type prim =
8195
| Primitive of Lambda.primitive * int
8296
| External of Primitive.description
@@ -92,6 +106,7 @@ type prim =
92106
| Identity
93107
| Apply
94108
| Revapply
109+
| Atomic of atomic_op * atomic_kind
95110

96111
let used_primitives = Hashtbl.create 7
97112
let add_used_primitive loc env path =
@@ -114,12 +129,11 @@ let prim_sys_argv =
114129
Primitive.simple ~name:"caml_sys_argv" ~arity:1 ~alloc:true
115130

116131
let prim_atomic_exchange =
117-
Primitive.simple ~name:"caml_atomic_exchange" ~arity:2 ~alloc:false
132+
Primitive.simple ~name:"caml_atomic_exchange_field" ~arity:3 ~alloc:false
118133
let prim_atomic_cas =
119-
Primitive.simple ~name:"caml_atomic_cas" ~arity:3 ~alloc:false
134+
Primitive.simple ~name:"caml_atomic_cas_field" ~arity:4 ~alloc:false
120135
let prim_atomic_fetch_add =
121-
Primitive.simple ~name:"caml_atomic_fetch_add" ~arity:2 ~alloc:false
122-
136+
Primitive.simple ~name:"caml_atomic_fetch_add_field" ~arity:3 ~alloc:false
123137

124138
let primitives_table =
125139
create_hashtable 57 [
@@ -372,10 +386,18 @@ let primitives_table =
372386
"%greaterequal", Comparison(Greater_equal, Compare_generic);
373387
"%greaterthan", Comparison(Greater_than, Compare_generic);
374388
"%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;
389+
"%atomic_load", Atomic(Load, Ref);
390+
"%atomic_exchange", Atomic(Exchange, Ref);
391+
"%atomic_cas", Atomic(Cas, Ref);
392+
"%atomic_fetch_add", Atomic(Fetch_add, Ref);
393+
"%atomic_load_field", Atomic(Load, Record);
394+
"%atomic_exchange_field", Atomic(Exchange, Record);
395+
"%atomic_cas_field", Atomic(Cas, Record);
396+
"%atomic_fetch_add_field", Atomic(Fetch_add, Record);
397+
"%atomic_load_loc", Atomic(Load, Loc);
398+
"%atomic_exchange_loc", Atomic(Exchange, Loc);
399+
"%atomic_cas_loc", Atomic(Cas, Loc);
400+
"%atomic_fetch_add_loc", Atomic(Fetch_add, Loc);
379401
"%runstack", Primitive (Prunstack, 3);
380402
"%reperform", Primitive (Preperform, 3);
381403
"%perform", Primitive (Pperform, 1);
@@ -658,6 +680,75 @@ let lambda_of_loc kind sloc =
658680
let scope_name = Debuginfo.Scoped_location.string_of_scoped_location sloc in
659681
Lconst (Const_immstring scope_name)
660682

683+
let atomic_arity (op : atomic_op) (kind : atomic_kind) =
684+
let arity_of_op = match op with
685+
| Load -> 1
686+
| Exchange -> 2
687+
| Cas -> 3
688+
| Fetch_add -> 2
689+
in
690+
let extra_kind_arity = match kind with
691+
| Ref | Loc -> 0
692+
| Record -> 1
693+
in
694+
arity_of_op + extra_kind_arity
695+
696+
let lambda_of_atomic prim_name loc op kind args =
697+
if List.length args <> atomic_arity op kind then
698+
raise(Error(to_location loc,
699+
Wrong_arity_builtin_primitive prim_name));
700+
let split = function
701+
| [] ->
702+
(* split is only called when [arity >= 1] *)
703+
assert false
704+
| first :: rest -> first, rest
705+
in
706+
let prim = match (op : atomic_op) with
707+
| Load -> Patomic_load
708+
| Exchange -> Pccall prim_atomic_exchange
709+
| Cas -> Pccall prim_atomic_cas
710+
| Fetch_add -> Pccall prim_atomic_fetch_add
711+
in
712+
match (kind : atomic_kind) with
713+
| Record ->
714+
(* the primitive application
715+
[%atomic_exchange_field ptr ofs v]
716+
becomes (in pseudo-code mixing C calls and OCaml expressions)
717+
[caml_atomic_exchange_field(ptr, ofs, v)] *)
718+
Lprim(prim, args, loc)
719+
| Ref ->
720+
(* the primitive application
721+
[%atomic_exchange ref v]
722+
becomes
723+
[caml_atomic_exchange_field(ref, Val_long(0), v)] *)
724+
let ref_arg, rest = split args in
725+
let args = ref_arg :: Lconst (Lambda.const_int 0) :: rest in
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),
745+
[Lvar varg], loc) in
746+
let ofs = Lprim(Pfield(1, Immediate, Immutable),
747+
[Lvar varg], loc) in
748+
let args = ptr :: ofs :: rest in
749+
Llet(Strict, Pgenval, varg, loc_arg,
750+
Lprim(prim, args, loc))
751+
661752
let caml_restore_raw_backtrace =
662753
Primitive.simple ~name:"caml_restore_raw_backtrace" ~arity:2 ~alloc:false
663754

@@ -744,10 +835,13 @@ let lambda_of_prim prim_name prim loc args arg_exps =
744835
ap_inlined = Default_inline;
745836
ap_specialised = Default_specialise;
746837
}
838+
| Atomic (op, kind), args ->
839+
lambda_of_atomic prim_name loc op kind args
747840
| (Raise _ | Raise_with_backtrace
748841
| Lazy_force | Loc _ | Primitive _ | Comparison _
749842
| Send | Send_self | Send_cache | Frame_pointers | Identity
750-
| Apply | Revapply), _ ->
843+
| Apply | Revapply
844+
), _ ->
751845
raise(Error(to_location loc, Wrong_arity_builtin_primitive prim_name))
752846

753847
let check_primitive_arity loc p =
@@ -766,6 +860,7 @@ let check_primitive_arity loc p =
766860
| Frame_pointers -> p.prim_arity = 0
767861
| Identity -> p.prim_arity = 1
768862
| Apply | Revapply -> p.prim_arity = 2
863+
| Atomic (op, kind) -> p.prim_arity = atomic_arity op kind
769864
in
770865
if not ok then raise(Error(loc, Wrong_arity_builtin_primitive p.prim_name))
771866

@@ -838,7 +933,11 @@ let primitive_needs_event_after = function
838933
lambda_primitive_needs_event_after (comparison_primitive comp knd)
839934
| Lazy_force | Send | Send_self | Send_cache
840935
| Apply | Revapply -> true
841-
| Raise _ | Raise_with_backtrace | Loc _ | Frame_pointers | Identity -> false
936+
| Raise _ | Raise_with_backtrace
937+
| Loc _
938+
| Frame_pointers | Identity
939+
| Atomic (_, _)
940+
-> false
842941

843942
let transl_primitive_application loc p env ty path exp args arg_exps =
844943
let prim =

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_boxed" 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)