Source file obj.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
open! Import
include Obj_intf.Definitions

external%template magic
  : ('a : any) ('b : any).
  ('a[@local_opt]) @ c o p u -> ('b[@local_opt]) @ c o p u
  @@ stateless
  = "%obj_magic"
[@@layout_poly]
[@@mode
  c = (uncontended, shared, contended, read, immutable)
  , o = (many, once)
  , p = (nonportable, shareable, portable, reading, stateless)
  , u = (aliased, unique)]

external%template magic_portable
  : ('a : any).
  ('a[@local_opt]) @ c o u -> ('a[@local_opt]) @ c o portable u
  @@ portable
  = "%identity"
[@@layout_poly]
[@@mode
  c = (uncontended, shared, contended, read, immutable)
  , o = (many, once)
  , u = (aliased, unique)]

external%template magic_uncontended
  : ('a : any).
  ('a[@local_opt]) @ contended o p u -> ('a[@local_opt]) @ o p u
  @@ portable
  = "%identity"
[@@layout_poly]
[@@mode
  o = (many, once)
  , p = (nonportable, shareable, portable, reading, stateless)
  , u = (aliased, unique)]

external%template magic_many
  : ('a : any).
  ('a[@local_opt]) @ c once p u -> ('a[@local_opt]) @ c p u
  @@ portable
  = "%identity"
[@@layout_poly]
[@@mode
  c = (uncontended, shared, contended), p = (nonportable, portable), u = (aliased, unique)]

external%template magic_unyielding
  : ('a : any).
  'a @ c local o p u yielding -> 'a @ c local o p u unyielding
  @@ stateless
  = "%identity"
[@@layout_poly]
[@@mode
  c = (uncontended, shared, contended)
  , o = (many, once)
  , p = (nonportable, portable)
  , u = (aliased, unique)]

external%template magic_unique
  : ('a : any).
  ('a[@local_opt]) @ c o p -> ('a[@local_opt]) @ c o p unique
  @@ stateless
  = "%identity"
[@@layout_poly]
[@@mode
  c = (uncontended, shared, contended), o = (many, once), p = (nonportable, portable)]

external%template magic_read_write_uncontended
  : ('a : any).
  ('a[@local_opt]) @ immutable o p u -> ('a[@local_opt]) @ o p u
  @@ portable
  = "%identity"
[@@layout_poly]
[@@mode o = (many, once), p = (nonportable, portable), u = (aliased, unique)]

module%template
  [@inline] [@kind.explicit k = (value, value_or_null)] Make (T : sig
  @@ portable
    type t : k

    val is_immediate : t @ contended local once -> bool [@@zero_alloc]
  end) =
struct
  open T

  external%template repr
    : ('a : k).
    ('a[@local_opt]) @ c o p u -> (t[@local_opt]) @ c o p u
    @@ portable
    = "%obj_magic"
  [@@mode
    c = (uncontended, shared, contended, read, write, immutable)
    , o = (many, once)
    , p = (nonportable, portable)
    , u = (aliased, unique)]

  external dup : t -> t @@ portable = "%obj_dup"
  external tag : t @ contended local once -> int @@ portable = "caml_obj_tag" [@@noalloc]
  external size : t @ contended local once -> int @@ portable = "%obj_size"

  (** Flambda2 attempts to track whether a block is an array, and %obj_size and %obj_field
      may only be used on unspecialized blocks. Hence we wrap them with [opaque_identity],
      which makes Flambda2 forget any additional information about the block. *)
  let%template[@inline always] size t =
    size ((Sys.opaque_identity [@mode contended once nonportable aliased]) t)
  ;;

  let[@inline] is_immediate t = is_immediate t
  let[@inline] is_block t = not (is_immediate t)

  external is_stack : t @ contended local once -> bool @@ portable = "caml_obj_is_stack"
  [@@noalloc]

  let%template stack_or_heap repr : stack_or_heap =
    (* [is_immediate] and [is_stack] do not actually consume [repr]. *)
    let repr = (magic_many [@mode contended nonportable aliased]) repr in
    if is_immediate repr
    then Immediate
    else (
      match Sys.backend_type with
      | Sys.Native -> if is_stack repr then Stack else Heap
      | Sys.Bytecode -> Heap
      | Sys.Other _ -> Heap)
  ;;

  external reserved_header_bits
    :  t @ contended local once
    -> int
    @@ portable
    = "caml_succ_scannable_prefix_len"
  [@@noalloc]

  let%template uniform_or_mixed repr : uniform_or_mixed = exclave_
    (* [is_immediate] and [reserved_header_bits] do not actually consume [repr]. *)
    let repr = (magic_many [@mode contended nonportable aliased]) repr in
    if is_immediate repr
    then Immediate
    else (
      match Sys.backend_type with
      | Sys.Native ->
        (match reserved_header_bits repr with
         | 0 -> Uniform
         | n -> Mixed { scannable_prefix_len = n - 1 })
      | Sys.Bytecode -> Uniform
      | Sys.Other _ -> Uniform)
  ;;

  external reachable_words : t -> int @@ portable = "caml_obj_reachable_words"

  module Expert = struct
    external%template obj
      : ('a : k).
      (t[@local_opt]) @ c o p u -> ('a[@local_opt]) @ c o p u
      @@ portable
      = "%obj_magic"
    [@@mode
      c = (uncontended, shared, contended)
      , o = (many, once)
      , p = (nonportable, portable)
      , u = (aliased, unique)]

    external field : (t[@local_opt]) -> int -> (t[@local_opt]) @@ portable = "%obj_field"

    (* We have to write both implementations because it doesn't make sense to template
       over an allocator, but the local version needs exclave. If [Sys.opaque_identity]
       preserved the fact that [t] is regional rather than local to this function, we
       could instead write
       {[
         let%template[@mode m = (global, local)] [@inline always] field t i =
           let t = Sys.opaque_identity t in
           field i [@exclave_if_local m]
         ;;
       ]} *)
    let[@inline always] [@mode local] field t i = exclave_ field (Sys.opaque_identity t) i
    let[@inline always] field t i = field (Sys.opaque_identity t) i

    external set_field
      :  (t[@local_opt])
      -> int
      -> t
      -> unit
      @@ portable
      = "%obj_set_field"

    let%template[@inline always] set_field t i f = set_field (Sys.opaque_identity t) i f
    [@@mode l = (local, global)]
    ;;

    external raw_field
      :  (t[@local_opt])
      -> int
      -> raw_data
      @@ portable
      = "caml_obj_raw_field"

    external set_raw_field
      :  (t[@local_opt])
      -> int
      -> raw_data
      -> unit
      @@ portable
      = "caml_obj_set_raw_field"
  end
end

type t = Stdlib.Obj.t

external is_int : t @ contended local once -> bool @@ portable = "%obj_is_int"

include%template Make [@kind.explicit value] (struct
    type nonrec t = t

    let[@inline] is_immediate t = is_int t
  end)

external uniquely_reachable_words
  :  t array
  -> int array * int
  @@ portable
  = "caml_obj_uniquely_reachable_words"

let first_non_constant_constructor_tag = Stdlib.Obj.first_non_constant_constructor_tag
let last_non_constant_constructor_tag = Stdlib.Obj.last_non_constant_constructor_tag
let forcing_tag = Stdlib.Obj.forcing_tag
let cont_tag = Stdlib.Obj.cont_tag
let lazy_tag = Stdlib.Obj.lazy_tag
let closure_tag = Stdlib.Obj.closure_tag
let object_tag = Stdlib.Obj.object_tag
let infix_tag = Stdlib.Obj.infix_tag
let forward_tag = Stdlib.Obj.forward_tag
let no_scan_tag = Stdlib.Obj.no_scan_tag
let abstract_tag = Stdlib.Obj.abstract_tag
let string_tag = Stdlib.Obj.string_tag
let double_tag = Stdlib.Obj.double_tag
let double_array_tag = Stdlib.Obj.double_array_tag
let custom_tag = Stdlib.Obj.custom_tag
let int_tag = Stdlib.Obj.int_tag
let out_of_heap_tag = Stdlib.Obj.out_of_heap_tag
let unaligned_tag = Stdlib.Obj.unaligned_tag

module Ephemeron = struct
  include Stdlib.Ephemeron
end

module Expert = struct
  include Stdlib.Obj
  include Expert

  external new_mixed_block
    :  tag:int
    -> total_words:int
    -> scannable_words:int
    -> t
    @@ portable
    = "Base_obj_new_mixed_block"
end

module Nullable = struct
  type t : value_or_null

  external is_int : t @ contended local once -> bool @@ portable = "%obj_is_int"
  external is_null : t @ immutable local once -> bool @@ portable = "%is_null"

  include%template Make [@kind.explicit value_or_null] (struct
      type nonrec t = t

      [%%template
        let is_immediate t =
          let t = (magic_many [@mode contended nonportable aliased]) t in
          is_int t || is_null t
        [@@inline]
        ;;]
    end)

  let null_tag = 1010

  external of_or_null
    :  (Stdlib.Obj.t or_null[@local_opt])
    -> (t[@local_opt])
    @@ portable
    = "%identity"

  external to_or_null
    :  (t[@local_opt])
    -> (Stdlib.Obj.t or_null[@local_opt])
    @@ portable
    = "%identity"
end

external nullable : (t[@local_opt]) -> (Nullable.t[@local_opt]) @@ portable = "%obj_magic"