Skip to content

Commit 5eb001a

Browse files
gascheclef-men
authored 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 fb0929d commit 5eb001a

File tree

4 files changed

+130
-20
lines changed

4 files changed

+130
-20
lines changed

bytecomp/bytegen.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ let comp_primitive stack_info p sz args =
656656
| Parray_of_iarray -> Kccall("caml_array_of_iarray", 1)
657657
| Pget_header _ -> Kccall("caml_get_header", 1)
658658
| Pobj_dup -> Kccall("caml_obj_dup", 1)
659-
| Patomic_load _ -> Kccall("caml_atomic_load", 1)
659+
| Patomic_load _ -> Kccall("caml_atomic_load_field", 2)
660660
| Patomic_set _
661661
| Patomic_exchange _ -> Kccall("caml_atomic_exchange", 2)
662662
| Patomic_compare_exchange _ -> Kccall("caml_atomic_compare_exchange", 3)

lambda/translprim.ml

Lines changed: 127 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,24 @@ type loc_kind =
114114
| Loc_POS
115115
| Loc_FUNCTION
116116

117+
type atomic_kind =
118+
| Ref (* operation on an atomic reference (takes only a pointer) *)
119+
| Field (* operation on an atomic field (takes a pointer and an offset) *)
120+
| Loc (* operation on a first-class field (takes a (pointer, offset) pair *)
121+
122+
type atomic_op =
123+
| Load
124+
| Set
125+
| Exchange
126+
| Compare_exchange
127+
| Cas
128+
| Faa
129+
| Add
130+
| Sub
131+
| Lxor
132+
| Land
133+
| Lor
134+
117135
type prim =
118136
| Primitive of Lambda.primitive * int
119137
| External of Lambda.external_call_description
@@ -134,6 +152,7 @@ type prim =
134152
| Poke of Lambda.peek_or_poke option
135153
(* For [Peek] and [Poke] the [option] is [None] until the primitive
136154
specialization code (below) has been run. *)
155+
| Atomic of atomic_op * atomic_kind
137156
| Unsupported of Lambda.primitive
138157

139158
let units_with_used_primitives = Hashtbl.create 7
@@ -897,22 +916,26 @@ let lookup_primitive loc ~poly_mode ~poly_sort pos p =
897916
| "%unbox_vec128" -> Primitive(Punbox_vector Boxed_vec128, 1)
898917
| "%box_vec128" -> Primitive(Pbox_vector (Boxed_vec128, mode), 1)
899918
| "%get_header" -> Primitive (Pget_header mode, 1)
900-
| "%atomic_load" ->
901-
Primitive ((Patomic_load {immediate_or_pointer=Pointer}), 1)
902-
| "%atomic_set" ->
903-
Primitive (Patomic_set {immediate_or_pointer=Pointer}, 2)
904-
| "%atomic_exchange" ->
905-
Primitive (Patomic_exchange {immediate_or_pointer=Pointer}, 2)
906-
| "%atomic_compare_exchange" ->
907-
Primitive (Patomic_compare_exchange {immediate_or_pointer=Pointer}, 3)
908-
| "%atomic_cas" ->
909-
Primitive (Patomic_compare_set {immediate_or_pointer=Pointer}, 3)
910-
| "%atomic_fetch_add" -> Primitive (Patomic_fetch_add, 2)
911-
| "%atomic_add" -> Primitive (Patomic_add, 2)
912-
| "%atomic_sub" -> Primitive (Patomic_sub, 2)
913-
| "%atomic_land" -> Primitive (Patomic_land, 2)
914-
| "%atomic_lor" -> Primitive (Patomic_lor, 2)
915-
| "%atomic_lxor" -> Primitive (Patomic_lxor, 2)
919+
(* CR melse: field/loc variants of other primitives *)
920+
| "%atomic_load" -> Atomic (Load, Ref)
921+
| "%atomic_load_field" -> Atomic (Load, Field)
922+
| "%atomic_load_loc" -> Atomic (Load, Loc)
923+
| "%atomic_set" -> Atomic (Set, Ref)
924+
| "%atomic_exchange" -> Atomic (Exchange, Ref)
925+
| "%atomic_exchange_field" -> Atomic (Exchange, Field)
926+
| "%atomic_exchange_loc" -> Atomic (Exchange, Loc)
927+
| "%atomic_compare_exchange" -> Atomic (Compare_exchange, Ref)
928+
| "%atomic_cas" -> Atomic (Cas, Ref)
929+
| "%atomic_cas_field" -> Atomic (Cas, Field)
930+
| "%atomic_cas_loc" -> Atomic (Cas, Loc)
931+
| "%atomic_fetch_add" -> Atomic (Faa, Ref)
932+
| "%atomic_fetch_add_field" -> Atomic (Faa, Field)
933+
| "%atomic_fetch_add_loc" -> Atomic (Faa, Loc)
934+
| "%atomic_add" -> Atomic (Add, Ref)
935+
| "%atomic_sub" -> Atomic (Sub, Ref)
936+
| "%atomic_land" -> Atomic (Land, Ref)
937+
| "%atomic_lor" -> Atomic (Lor, Ref)
938+
| "%atomic_lxor" -> Atomic (Lxor, Ref)
916939
| "%runstack" ->
917940
if runtime5 then Primitive (Prunstack, 3) else Unsupported Prunstack
918941
| "%reperform" ->
@@ -1624,6 +1647,90 @@ let lambda_of_loc kind sloc =
16241647
~include_zero_alloc:false sloc in
16251648
Lconst (Const_immstring scope_name)
16261649

1650+
let atomic_arity op (kind : atomic_kind) =
1651+
let arity_of_op =
1652+
match op with
1653+
| Load -> 1
1654+
| Set -> 2
1655+
| Exchange -> 2
1656+
| Compare_exchange -> 3
1657+
| Cas -> 3
1658+
| Faa -> 2
1659+
| Add | Sub | Lxor | Land | Lor -> 2
1660+
in
1661+
let extra_kind_arity =
1662+
match kind with
1663+
| Ref | Loc -> 0
1664+
| Field -> 1
1665+
in
1666+
arity_of_op + extra_kind_arity
1667+
1668+
let lambda_of_atomic prim_name loc op (kind : atomic_kind) args =
1669+
if List.length args <> atomic_arity op kind then
1670+
raise (Error (to_location loc, Wrong_arity_builtin_primitive prim_name)) ;
1671+
let split = function
1672+
| [] ->
1673+
(* split is only called when [arity >= 1] *)
1674+
assert false
1675+
| first :: rest ->
1676+
first, rest
1677+
in
1678+
let prim =
1679+
match op with
1680+
| Load -> Patomic_load {immediate_or_pointer = Pointer}
1681+
| Set -> Patomic_set {immediate_or_pointer = Pointer}
1682+
| Compare_exchange -> Patomic_compare_exchange {immediate_or_pointer=Pointer}
1683+
| Exchange -> Patomic_exchange {immediate_or_pointer=Pointer}
1684+
| Cas -> Patomic_compare_set {immediate_or_pointer=Pointer}
1685+
| Faa -> Patomic_fetch_add
1686+
| Add -> Patomic_add
1687+
| Sub -> Patomic_sub
1688+
| Lor -> Patomic_lor
1689+
| Lxor -> Patomic_lxor
1690+
| Land -> Patomic_land
1691+
in
1692+
match kind with
1693+
| Ref ->
1694+
(* the primitive application
1695+
[Lprim(%atomic_exchange, [ref; v])]
1696+
becomes
1697+
[Lprim(caml_atomic_exchange_field, [ref; 0; v])]
1698+
*)
1699+
let ref_arg, rest = split args in
1700+
let args = ref_arg :: Lconst (Lambda.const_int 0) :: rest in
1701+
Lprim (prim, args, loc)
1702+
| Field ->
1703+
(* the primitive application
1704+
[Lprim(%atomic_exchange_field, [ptr; ofs; v])]
1705+
becomes
1706+
[Lprim(caml_atomic_exchange_field, [ptr; ofs; v])] *)
1707+
Lprim (prim, args, loc)
1708+
| Loc ->
1709+
(* CR melse: unboxed tuple? *)
1710+
(* the primitive application
1711+
[Lprim(%atomic_exchange_loc, [(ptr, ofs); v])]
1712+
becomes
1713+
[Lprim(caml_atomic_exchange_field, [ptr; ofs; v])]
1714+
and in the general case of a non-tuple expression <loc>
1715+
[Lprim(%atomic_exchange_loc, [loc; v])]
1716+
becomes
1717+
[Llet(p, loc,
1718+
Lprim(caml_atomic_exchange_field, [Field(p, 0); Field(p, 1); v]))]
1719+
*)
1720+
let loc_arg, rest = split args in
1721+
match loc_arg with
1722+
| Lprim (Pmakeblock _, [ptr; ofs], _argloc) ->
1723+
let args = ptr :: ofs :: rest in
1724+
Lprim (prim, args, loc)
1725+
| _ ->
1726+
let varg = Ident.create_local "atomic_arg" in
1727+
let ptr = Lprim (Pfield (0, Pointer, Reads_agree), [Lvar varg], loc) in
1728+
let ofs =
1729+
Lprim (Pfield (1, Immediate, Reads_agree), [Lvar varg], loc)
1730+
in
1731+
let args = ptr :: ofs :: rest in
1732+
Llet (Strict, Pvalue { raw_kind = Pgenval; nullable = Non_nullable }, varg, loc_arg, Lprim (prim, args, loc))
1733+
16271734
let caml_restore_raw_backtrace =
16281735
Lambda.simple_prim_on_values ~name:"caml_restore_raw_backtrace" ~arity:2
16291736
~alloc:false
@@ -1721,6 +1828,8 @@ let lambda_of_prim prim_name prim loc args arg_exps =
17211828
Lprim (Ppeek layout, [ptr], loc)
17221829
| Poke (Some layout), [ptr; new_value] ->
17231830
Lprim (Ppoke layout, [ptr; new_value], loc)
1831+
| Atomic (op, kind), args ->
1832+
lambda_of_atomic prim_name loc op kind args
17241833
| Unsupported prim, _ ->
17251834
let exn =
17261835
transl_extension_path loc (Lazy.force Env.initial)
@@ -1773,6 +1882,7 @@ let check_primitive_arity loc p =
17731882
| Frame_pointers -> p.prim_arity = 0
17741883
| Identity | Peek _ -> p.prim_arity = 1
17751884
| Apply _ | Revapply _ | Poke _ -> p.prim_arity = 2
1885+
| Atomic (op, kind) -> p.prim_arity = atomic_arity op kind
17761886
| Unsupported _ -> true
17771887
in
17781888
if not ok then raise(Error(loc, Wrong_arity_builtin_primitive p.prim_name))
@@ -1972,7 +2082,7 @@ let primitive_needs_event_after = function
19722082
| Lazy_force _ | Send _ | Send_self _ | Send_cache _
19732083
| Apply _ | Revapply _ -> true
19742084
| Raise _ | Raise_with_backtrace | Loc _ | Frame_pointers | Identity
1975-
| Peek _ | Poke _ | Unsupported _ -> false
2085+
| Peek _ | Poke _ | Atomic _ | Unsupported _ -> false
19762086

19772087
let transl_primitive_application loc p env ty ~poly_mode ~stack ~poly_sort
19782088
path exp args arg_exps pos =

runtime/memory.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ CAMLprim value caml_atomic_compare_exchange_field (value obj, intnat field, valu
372372
}
373373
}
374374

375-
CAMLexport value caml_atomic_cas_field (value obj, value vfield, value oldval, value newval)
375+
CAMLprim value caml_atomic_cas_field (value obj, value vfield, value oldval, value newval)
376376
{
377377
intnat field = Long_val(vfield);
378378
if (caml_atomic_compare_exchange_field(obj, field, oldval, newval) == oldval) {

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)