Skip to content

Commit 55f0a27

Browse files
value_or_null atomics with correct result kind inference (#3807)
* Infer or_null result values for atomic ops * stdlib changes * tests * `prove_is_immediate` for atomic comparsion arguments * line limit * rename to `prove_is_not_a_pointer` --------- Co-authored-by: Diana Kalinichenko <[email protected]>
1 parent 6fa55a1 commit 55f0a27

File tree

11 files changed

+207
-39
lines changed

11 files changed

+207
-39
lines changed

lambda/lambda.ml

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -994,6 +994,7 @@ let nullable_value raw_kind =
994994

995995
let layout_unit = non_null_value Pintval
996996
let layout_int = non_null_value Pintval
997+
let layout_int_or_null = nullable_value Pintval
997998
let layout_array kind = non_null_value (Parrayval kind)
998999
let layout_block = non_null_value Pgenval
9991000
let layout_list =
@@ -2389,13 +2390,19 @@ let primitive_result_layout (p : primitive) =
23892390
| (Parray_to_iarray | Parray_of_iarray) -> layout_any_value
23902391
| Pget_header _ -> layout_boxed_int Boxed_nativeint
23912392
| Prunstack | Presume | Pperform | Preperform -> layout_any_value
2392-
| Patomic_load { immediate_or_pointer = Immediate } -> layout_int
2393-
| Patomic_load { immediate_or_pointer = Pointer } -> layout_any_value
2393+
| Patomic_load { immediate_or_pointer = Immediate } ->
2394+
layout_int_or_null
2395+
| Patomic_load { immediate_or_pointer = Pointer } ->
2396+
layout_any_value
23942397
| Patomic_set _ -> layout_unit
2395-
| Patomic_exchange { immediate_or_pointer = Immediate } -> layout_int
2396-
| Patomic_exchange { immediate_or_pointer = Pointer } -> layout_any_value
2397-
| Patomic_compare_exchange { immediate_or_pointer = Immediate } -> layout_int
2398-
| Patomic_compare_exchange { immediate_or_pointer = Pointer } -> layout_any_value
2398+
| Patomic_exchange { immediate_or_pointer = Immediate } ->
2399+
layout_int_or_null
2400+
| Patomic_exchange { immediate_or_pointer = Pointer } ->
2401+
layout_any_value
2402+
| Patomic_compare_exchange { immediate_or_pointer = Immediate } ->
2403+
layout_int_or_null
2404+
| Patomic_compare_exchange { immediate_or_pointer = Pointer } ->
2405+
layout_any_value
23992406
| Patomic_compare_set _
24002407
| Patomic_fetch_add -> layout_int
24012408
| Pdls_get -> layout_any_value

middle_end/flambda2/simplify/simplify_ternary_primitive.ml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,9 @@ let simplify_atomic_compare_and_set_or_exchange_args
8787
also because the old value cannot be a pointer if the operation is to
8888
perform a write. *)
8989
let is_immediate ty =
90-
match T.prove_is_a_tagged_immediate (DA.typing_env dacc) ty with
91-
| Proved () -> true
92-
| Unknown -> false
90+
match T.prove_is_not_a_pointer (DA.typing_env dacc) ty with
91+
| Proved true -> true
92+
| Proved false | Unknown -> false
9393
in
9494
if is_immediate comparison_value_ty && is_immediate new_value_ty
9595
then Immediate
@@ -139,7 +139,7 @@ let simplify_atomic_compare_exchange
139139
in
140140
let result_var_ty =
141141
match atomic_kind (* N.B. not [args_kind] *) with
142-
| Immediate -> T.any_tagged_immediate
142+
| Immediate -> T.any_tagged_immediate_or_null
143143
| Any_value -> T.any_value
144144
in
145145
let dacc = DA.add_variable dacc result_var result_var_ty in

middle_end/flambda2/simplify/simplify_unary_primitive.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -769,7 +769,7 @@ let simplify_atomic_load (block_access_field_kind : P.Block_access_field_kind.t)
769769
~original_prim dacc ~original_term ~arg:_ ~arg_ty:_ ~result_var =
770770
match block_access_field_kind with
771771
| Immediate ->
772-
let dacc = DA.add_variable dacc result_var T.any_tagged_immediate in
772+
let dacc = DA.add_variable dacc result_var T.any_tagged_immediate_or_null in
773773
SPR.create original_term ~try_reify:false dacc
774774
| Any_value ->
775775
SPR.create_unknown dacc ~result_var

middle_end/flambda2/types/flambda2_types.mli

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,8 @@ val any_value : t
327327

328328
val any_tagged_immediate : t
329329

330+
val any_tagged_immediate_or_null : t
331+
330332
val any_tagged_bool : t
331333

332334
val any_boxed_float32 : t
@@ -646,6 +648,9 @@ val prove_unique_fully_constructed_immutable_heap_block :
646648

647649
val prove_is_int : Typing_env.t -> t -> bool proof_of_property
648650

651+
(* Either a tagged integer or a null poitner. *)
652+
val prove_is_not_a_pointer : Typing_env.t -> t -> bool proof_of_property
653+
649654
(* Returns the result of [Is_flat_float_array] *)
650655
val meet_is_flat_float_array : Typing_env.t -> t -> bool meet_shortcut
651656

middle_end/flambda2/types/grammar/more_type_creators.ml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ let any_tagged_immediate_non_null =
7474
~immediates:Unknown ~blocks:(Known TG.Row_like_for_blocks.bottom)
7575
~extensions:No_extensions
7676

77+
let any_tagged_immediate_or_null =
78+
TG.create_from_head_value
79+
{ non_null = Ok any_tagged_immediate_non_null; is_null = Maybe_null }
80+
7781
let these_tagged_immediates0 imms =
7882
match Targetint_31_63.Set.get_singleton imms with
7983
| Some imm -> TG.this_tagged_immediate imm

middle_end/flambda2/types/grammar/more_type_creators.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ val these_naked_vec128s :
4343

4444
val any_tagged_immediate : Type_grammar.t
4545

46+
val any_tagged_immediate_or_null : Type_grammar.t
47+
4648
val these_tagged_immediates0 : Targetint_31_63.Set.t -> Type_grammar.t
4749

4850
val these_tagged_immediates : Targetint_31_63.Set.t -> Type_grammar.t

middle_end/flambda2/types/provers.ml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,25 @@ let prove_is_int env t =
153153
let meet_is_int_variant_only env t =
154154
gen_value_to_meet (prove_is_int_generic_value ~variant_only:true) env t
155155

156+
let prove_is_not_a_pointer_generic_value env t =
157+
match expand_head env t with
158+
| Value Unknown -> Unknown
159+
| Value Bottom -> Invalid
160+
| Value (Ok { is_null = Maybe_null; non_null = Bottom }) -> Proved true
161+
| Value (Ok { is_null = Not_null; non_null = Bottom }) -> Invalid
162+
| Value (Ok { is_null = Maybe_null | Not_null; non_null = Unknown }) ->
163+
Unknown
164+
| Value (Ok { is_null; non_null = Ok head }) -> (
165+
match prove_is_int_generic_value ~variant_only:false env head, is_null with
166+
| Proved true, (Maybe_null | Not_null) -> Proved true
167+
| (Proved false | Unknown), Maybe_null | Unknown, Not_null -> Unknown
168+
| Proved false, Not_null -> Proved false
169+
| Invalid, _ -> Invalid (* Ought to be impossible. *))
170+
| _ -> wrong_kind "Value" t Invalid
171+
172+
let prove_is_not_a_pointer env t =
173+
as_property (prove_is_not_a_pointer_generic_value env t)
174+
156175
(* Note: this function returns a generic proof because we want to propagate the
157176
Invalid cases to prove_naked_immediates_generic, but it's not suitable for
158177
implementing [meet_get_tag] because it doesn't ignore the immediates part of

middle_end/flambda2/types/provers.mli

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ val prove_unique_tag_and_size :
123123

124124
val prove_is_int : Typing_env.t -> Type_grammar.t -> bool proof_of_property
125125

126+
val prove_is_not_a_pointer :
127+
Typing_env.t -> Type_grammar.t -> bool proof_of_property
128+
126129
val meet_is_int_variant_only :
127130
Typing_env.t -> Type_grammar.t -> bool meet_shortcut
128131

stdlib/atomic.ml

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,25 @@
1212
(* *)
1313
(**************************************************************************)
1414

15-
type !'a t : mutable_data with 'a
15+
type (!'a : value_or_null) t : mutable_data with 'a
1616

17-
external make : 'a -> ('a t[@local_opt]) @@ portable = "%makemutable"
18-
external make_contended : 'a -> ('a t[@local_opt]) @@ portable = "caml_atomic_make_contended"
19-
external get : 'a t @ local -> 'a @@ portable = "%atomic_load"
20-
external set : 'a t @ local -> 'a -> unit @@ portable = "%atomic_set"
21-
external exchange : 'a t @ local -> 'a -> 'a @@ portable = "%atomic_exchange"
22-
external compare_and_set : 'a t @ local -> 'a -> 'a -> bool @@ portable = "%atomic_cas"
23-
external compare_exchange : 'a t @ local -> 'a -> 'a -> 'a @@ portable = "%atomic_compare_exchange"
17+
external make : ('a : value_or_null).
18+
'a -> ('a t[@local_opt]) @@ portable = "%makemutable"
19+
external make_contended : ('a : value_or_null).
20+
'a -> ('a t[@local_opt]) @@ portable = "caml_atomic_make_contended"
21+
external get : ('a : value_or_null).
22+
'a t @ local -> 'a @@ portable = "%atomic_load"
23+
external set : ('a : value_or_null).
24+
'a t @ local -> 'a -> unit @@ portable = "%atomic_set"
25+
external exchange : ('a : value_or_null).
26+
'a t @ local -> 'a -> 'a @@ portable =
27+
"%atomic_exchange"
28+
external compare_and_set : ('a : value_or_null).
29+
'a t @ local -> 'a -> 'a -> bool @@ portable =
30+
"%atomic_cas"
31+
external compare_exchange : ('a : value_or_null).
32+
'a t @ local -> 'a -> 'a -> 'a @@ portable =
33+
"%atomic_compare_exchange"
2434
external fetch_and_add : int t @ contended local -> int -> int @@ portable = "%atomic_fetch_add"
2535
external add : int t @ contended local -> int -> unit @@ portable = "%atomic_add"
2636
external sub : int t @ contended local -> int -> unit @@ portable = "%atomic_sub"
@@ -32,9 +42,19 @@ let incr r = add r 1
3242
let decr r = sub r 1
3343

3444
module Contended = struct
35-
external get : ('a : value mod contended). 'a t @ contended local -> 'a @@ portable = "%atomic_load"
36-
external set : ('a : value mod portable). 'a t @ contended local -> 'a -> unit @@ portable = "%atomic_set"
37-
external exchange : ('a : value mod contended portable). 'a t @ contended local -> 'a -> 'a @@ portable = "%atomic_exchange"
38-
external compare_and_set : ('a : value mod portable). 'a t @ contended local -> 'a -> 'a -> bool @@ portable = "%atomic_cas"
39-
external compare_exchange : ('a : value mod contended portable). 'a t @ contended local -> 'a -> 'a -> 'a @@ portable = "%atomic_compare_exchange"
45+
external get : ('a : value_or_null mod contended).
46+
'a t @ contended local -> 'a @@ portable =
47+
"%atomic_load"
48+
external set : ('a : value_or_null mod portable).
49+
'a t @ contended local -> 'a -> unit @@ portable =
50+
"%atomic_set"
51+
external exchange : ('a : value_or_null mod contended portable).
52+
'a t @ contended local -> 'a -> 'a @@ portable =
53+
"%atomic_exchange"
54+
external compare_and_set : ('a : value_or_null mod portable).
55+
'a t @ contended local -> 'a -> 'a -> bool @@ portable =
56+
"%atomic_cas"
57+
external compare_exchange : ('a : value_or_null mod contended portable).
58+
'a t @ contended local -> 'a -> 'a -> 'a @@ portable =
59+
"%atomic_compare_exchange"
4060
end

stdlib/atomic.mli

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@
2525
*)
2626

2727
(** An atomic (mutable) reference to a value of type ['a]. *)
28-
type !'a t : mutable_data with 'a
28+
type (!'a : value_or_null) t : mutable_data with 'a
2929

3030
(** Create an atomic reference. *)
31-
external make : 'a -> ('a t[@local_opt]) = "%makemutable"
31+
external make : ('a : value_or_null) .
32+
'a -> ('a t[@local_opt]) = "%makemutable"
3233

3334
(** Create an atomic reference that is alone on a cache line. It occupies 4-16x
3435
the memory of one allocated with [make v].
@@ -44,28 +45,33 @@ external make : 'a -> ('a t[@local_opt]) = "%makemutable"
4445
4546
CR ocaml 5 all-runtime5: does not support runtime4 *)
4647

47-
external make_contended : 'a -> ('a t[@local_opt]) = "caml_atomic_make_contended"
48+
external make_contended : ('a : value_or_null).
49+
'a -> ('a t[@local_opt]) = "caml_atomic_make_contended"
4850

4951
(** Get the current value of the atomic reference. *)
50-
val get : 'a t @ local -> 'a
52+
val get : ('a : value_or_null). 'a t @ local -> 'a
5153

5254
(** Set a new value for the atomic reference. *)
53-
external set : 'a t @ local -> 'a -> unit = "%atomic_set"
55+
external set : ('a : value_or_null).
56+
'a t @ local -> 'a -> unit = "%atomic_set"
5457

5558
(** Set a new value for the atomic reference, and return the current value. *)
56-
external exchange : 'a t @ local -> 'a -> 'a = "%atomic_exchange"
59+
external exchange : ('a : value_or_null).
60+
'a t @ local -> 'a -> 'a = "%atomic_exchange"
5761

5862
(** [compare_and_set r seen v] sets the new value of [r] to [v] only
5963
if its current value is physically equal to [seen] -- the
6064
comparison and the set occur atomically. Returns [true] if the
6165
comparison succeeded (so the set happened) and [false]
6266
otherwise. *)
63-
external compare_and_set : 'a t @ local -> 'a -> 'a -> bool = "%atomic_cas"
67+
external compare_and_set : ('a : value_or_null).
68+
'a t @ local -> 'a -> 'a -> bool = "%atomic_cas"
6469

6570
(** [compare_exchange r seen v] sets the new value of [r] to [v] only
6671
if its current value is physically equal to [seen] -- the comparison
6772
and the set occur atomically. Returns the previous value. *)
68-
external compare_exchange : 'a t @ local -> 'a -> 'a -> 'a = "%atomic_compare_exchange"
73+
external compare_exchange : ('a : value_or_null).
74+
'a t @ local -> 'a -> 'a -> 'a = "%atomic_compare_exchange"
6975

7076
(** [fetch_and_add r n] atomically increments the value of [r] by [n],
7177
and returns the current value (before the increment). *)
@@ -96,24 +102,28 @@ val decr : int t @ contended local -> unit
96102
via modes. *)
97103
module Contended : sig
98104
(** Like {!get}, but can be called on an atomic that came from another domain. *)
99-
val get : ('a : value mod contended). 'a t @ contended local -> 'a
105+
val get : ('a : value_or_null mod contended).
106+
'a t @ contended local -> 'a
100107

101108
(** Like {!set}, but can be called on an atomic that came from another domain. *)
102109
external set
103-
: ('a : value mod portable). 'a t @ contended local -> 'a -> unit = "%atomic_set"
110+
: ('a : value_or_null mod portable).
111+
'a t @ contended local -> 'a -> unit ="%atomic_set"
104112

105113
(** Like {!exchange}, but can be called on an atomic that came from another domain. *)
106-
external exchange : ('a : value mod contended portable). 'a t @ contended local -> 'a -> 'a
107-
= "%atomic_exchange"
114+
external exchange :
115+
('a : value_or_null mod contended portable).
116+
'a t @ contended local -> 'a -> 'a = "%atomic_exchange"
108117

109118
(** Like {!compare_and_set}, but can be called on an atomic that came from another domain. *)
110119
external compare_and_set
111-
: ('a : value mod portable). 'a t @ contended local -> 'a -> 'a -> bool = "%atomic_cas"
120+
: ('a : value_or_null mod portable).
121+
'a t @ contended local -> 'a -> 'a -> bool = "%atomic_cas"
112122

113123
(** Like {!compare_exchange}, but can be called on an atomic that came from another domain. *)
114124
external compare_exchange
115-
: ('a : value mod contended portable). 'a t @ contended local -> 'a -> 'a -> 'a
116-
= "%atomic_compare_exchange"
125+
: ('a : value_or_null mod contended portable).
126+
'a t @ contended local -> 'a -> 'a -> 'a = "%atomic_compare_exchange"
117127
end
118128

119129
(** {1:examples Examples}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
(* TEST
2+
flambda2;
3+
{
4+
native;
5+
} {
6+
flags = "-O3";
7+
native;
8+
} {
9+
flags = "-Oclassic";
10+
native;
11+
} {
12+
bytecode;
13+
}
14+
*)
15+
16+
external get_int : int or_null Atomic.t -> int or_null = "%atomic_load"
17+
18+
external exchange_int : int or_null Atomic.t -> int or_null -> int or_null
19+
= "%atomic_exchange"
20+
21+
external compare_and_exchange_int
22+
: int or_null Atomic.t -> int or_null -> int or_null -> int or_null
23+
= "%atomic_compare_exchange"
24+
25+
let () =
26+
let x = Sys.opaque_identity (Atomic.make Null) in
27+
match get_int x with
28+
| Null -> ()
29+
| This _ -> assert false
30+
;;
31+
32+
let () =
33+
let x = Sys.opaque_identity (Atomic.make (This 47)) in
34+
match get_int x with
35+
| This 47 -> ()
36+
| This _ -> assert false
37+
| Null -> assert false
38+
;;
39+
40+
let () =
41+
let x = Sys.opaque_identity (Atomic.make Null) in
42+
Atomic.set x (This 5);
43+
Atomic.set x (This 7);
44+
match get_int x with
45+
| This 7 -> ()
46+
| Null -> assert false
47+
| This _ -> assert false
48+
;;
49+
50+
let () =
51+
let x = Sys.opaque_identity (Atomic.make Null) in
52+
match exchange_int x (This 11) with
53+
| Null -> ()
54+
| This _ -> assert false
55+
;;
56+
57+
let () =
58+
let x = Sys.opaque_identity (Atomic.make (This 11)) in
59+
(match exchange_int x Null with
60+
| This 11 -> ()
61+
| This _ -> assert false
62+
| Null -> assert false);
63+
match exchange_int x (This 0) with
64+
| Null -> ()
65+
| This _ -> assert false
66+
;;
67+
68+
let () =
69+
let x = Sys.opaque_identity (Atomic.make Null) in
70+
match compare_and_exchange_int x (This 5) (This 8) with
71+
| Null -> ()
72+
| This _ -> assert false
73+
;;
74+
75+
let () =
76+
let x = Sys.opaque_identity (Atomic.make Null) in
77+
(match compare_and_exchange_int x Null (This 42) with
78+
| Null -> ()
79+
| This _ -> assert false);
80+
match compare_and_exchange_int x Null (This 45) with
81+
| This 42 -> ()
82+
| This _ -> assert false
83+
| Null -> assert false
84+
;;
85+
86+
let () =
87+
let aaa = This (ref "aaa") in
88+
let bb = Sys.opaque_identity (This (ref "bb")) in
89+
let zzzzz = This (Sys.opaque_identity (ref "zzzzz")) in
90+
let arr = This (ref "arr") in
91+
let y = Atomic.make aaa in
92+
assert (Atomic.exchange y Null == aaa);
93+
assert (Atomic.exchange y bb == Null);
94+
assert (Atomic.compare_exchange y aaa zzzzz == bb);
95+
assert (Atomic.compare_exchange y aaa Null == bb);
96+
assert (Atomic.compare_exchange y bb arr == bb);
97+
assert (Atomic.compare_and_set y arr Null)
98+
;;

0 commit comments

Comments
 (0)