-
Notifications
You must be signed in to change notification settings - Fork 86
/
Copy pathclosure_conversion.ml
3296 lines (3233 loc) · 129 KB
/
closure_conversion.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
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
(**************************************************************************)
(* *)
(* OCaml *)
(* *)
(* Pierre Chambart, OCamlPro *)
(* Mark Shinwell and Leo White, Jane Street Europe *)
(* *)
(* Copyright 2013--2019 OCamlPro SAS *)
(* Copyright 2014--2019 Jane Street Group LLC *)
(* *)
(* All rights reserved. This file is distributed under the terms of *)
(* the GNU Lesser General Public License version 2.1, with the *)
(* special exception on linking described in the file LICENSE. *)
(* *)
(**************************************************************************)
[@@@ocaml.warning "-fragile-match"]
open! Int_replace_polymorphic_compare
open! Flambda
module BP = Bound_parameter
module IR = Closure_conversion_aux.IR
module Acc = Closure_conversion_aux.Acc
module Env = Closure_conversion_aux.Env
module Expr_with_acc = Closure_conversion_aux.Expr_with_acc
module Apply_cont_with_acc = Closure_conversion_aux.Apply_cont_with_acc
module Let_cont_with_acc = Closure_conversion_aux.Let_cont_with_acc
module Let_with_acc = Closure_conversion_aux.Let_with_acc
module Function_decls = Closure_conversion_aux.Function_decls
module Function_decl = Function_decls.Function_decl
module K = Flambda_kind
module P = Flambda_primitive
module VB = Bound_var
type 'a close_program_metadata =
| Normal : [`Normal] close_program_metadata
| Classic :
(Exported_code.t
* Name_occurrences.t
* Flambda_cmx_format.t option
* Exported_offsets.t)
-> [`Classic] close_program_metadata
type 'a close_program_result =
{ unit : Flambda_unit.t;
metadata : 'a close_program_metadata;
code_slot_offsets : Slot_offsets.t Code_id.Map.t
}
type close_functions_result =
| Lifted of (Symbol.t * Env.value_approximation) Function_slot.Lmap.t
| Dynamic of Set_of_closures.t * Env.value_approximation Function_slot.Map.t
type declare_const_result =
| Field of Field_of_static_block.t
| Unboxed_float of Numeric_types.Float_by_bit_pattern.t
| Unboxed_int32 of Numeric_types.Int32.t
| Unboxed_int64 of Numeric_types.Int64.t
| Unboxed_nativeint of Targetint_32_64.t
let manufacture_symbol acc proposed_name =
let acc, linkage_name =
if Flambda_features.Expert.shorten_symbol_names ()
then Acc.manufacture_symbol_short_name acc
else acc, Linkage_name.of_string proposed_name
in
let symbol =
Symbol.create (Compilation_unit.get_current_exn ()) linkage_name
in
acc, symbol
let declare_symbol_for_function_slot env acc ident function_slot :
Env.t * Acc.t * Symbol.t =
let acc, symbol =
manufacture_symbol acc (Function_slot.to_string function_slot)
in
let env =
Env.add_simple_to_substitute env ident (Simple.symbol symbol)
K.With_subkind.any_value
in
env, acc, symbol
let register_const0 acc constant name =
match Static_const.Map.find constant (Acc.shareable_constants acc) with
| exception Not_found ->
(* Create a variable to ensure uniqueness of the symbol. *)
let var = Variable.create name in
let acc, symbol =
manufacture_symbol acc
(* CR mshinwell: this Variable.rename looks to be redundant *)
(Variable.unique_name (Variable.rename var))
in
let acc = Acc.add_declared_symbol ~symbol ~constant acc in
let acc =
if Static_const.can_share constant
then Acc.add_shareable_constant ~symbol ~constant acc
else acc
in
acc, symbol
| symbol -> acc, symbol
let register_const acc constant name : Acc.t * declare_const_result * string =
let acc, symbol = register_const0 acc constant name in
acc, Field (Symbol symbol), name
let rec declare_const acc (const : Lambda.structured_constant) :
Acc.t * declare_const_result * string =
let module SC = Static_const in
match const with
| Const_base (Const_int c) ->
acc, Field (Tagged_immediate (Targetint_31_63.of_int c)), "int"
| Const_base (Const_char c) ->
acc, Field (Tagged_immediate (Targetint_31_63.of_char c)), "char"
| Const_base (Const_unboxed_float c) ->
let c = Numeric_types.Float_by_bit_pattern.create (float_of_string c) in
acc, Unboxed_float c, "unboxed_float"
| Const_base (Const_string (s, _, _)) ->
register_const acc (SC.immutable_string s) "immstring"
| Const_base (Const_float c) ->
let c = Numeric_types.Float_by_bit_pattern.create (float_of_string c) in
register_const acc (SC.boxed_float (Const c)) "float"
| Const_base (Const_float32 _c) ->
(* CR mslater: (float32) middle end support *)
assert false
| Const_base (Const_int32 c) ->
register_const acc (SC.boxed_int32 (Const c)) "int32"
| Const_base (Const_int64 c) ->
register_const acc (SC.boxed_int64 (Const c)) "int64"
| Const_base (Const_nativeint c) ->
(* CR pchambart: this should be pushed further to lambda *)
let c = Targetint_32_64.of_int64 (Int64.of_nativeint c) in
register_const acc (SC.boxed_nativeint (Const c)) "nativeint"
| Const_base (Const_unboxed_int32 c) -> acc, Unboxed_int32 c, "unboxed_int32"
| Const_base (Const_unboxed_int64 c) -> acc, Unboxed_int64 c, "unboxed_int64"
| Const_base (Const_unboxed_nativeint c) ->
(* CR pchambart: this should be pushed further to lambda *)
let c = Targetint_32_64.of_int64 (Int64.of_nativeint c) in
acc, Unboxed_nativeint c, "unboxed_nativeint"
| Const_immstring c -> register_const acc (SC.immutable_string c) "immstring"
| Const_float_block c ->
register_const acc
(SC.immutable_float_block
(List.map
(fun s ->
let f =
Numeric_types.Float_by_bit_pattern.create (float_of_string s)
in
Or_variable.Const f)
c))
"float_block"
| Const_float_array c ->
register_const acc
(SC.immutable_float_array
(List.map
(fun s ->
let f =
Numeric_types.Float_by_bit_pattern.create (float_of_string s)
in
Or_variable.Const f)
c))
"float_array"
| Const_block (tag, consts) ->
let acc, field_of_blocks =
List.fold_left_map
(fun acc c ->
let acc, f, _ = declare_const acc c in
match f with
| Field f -> acc, f
| Unboxed_float _ | Unboxed_int32 _ | Unboxed_int64 _
| Unboxed_nativeint _ ->
Misc.fatal_errorf
"Unboxed constants are not allowed inside of Const_block: %a"
Printlambda.structured_constant const)
acc consts
in
let const : SC.t =
SC.block (Tag.Scannable.create_exn tag) Immutable field_of_blocks
in
register_const acc const "const_block"
let close_const0 acc (const : Lambda.structured_constant) =
let acc, const, name = declare_const acc const in
match const with
| Field (Tagged_immediate i) ->
( acc,
Simple.const (Reg_width_const.tagged_immediate i),
name,
Flambda_kind.With_subkind.tagged_immediate )
| Unboxed_float f ->
( acc,
Simple.const (Reg_width_const.naked_float f),
name,
Flambda_kind.With_subkind.naked_float )
| Unboxed_int32 i ->
( acc,
Simple.const (Reg_width_const.naked_int32 i),
name,
Flambda_kind.With_subkind.naked_int32 )
| Unboxed_int64 i ->
( acc,
Simple.const (Reg_width_const.naked_int64 i),
name,
Flambda_kind.With_subkind.naked_int64 )
| Unboxed_nativeint i ->
( acc,
Simple.const (Reg_width_const.naked_nativeint i),
name,
Flambda_kind.With_subkind.naked_nativeint )
| Field (Symbol s) ->
acc, Simple.symbol s, name, Flambda_kind.With_subkind.any_value
| Field (Dynamically_computed _) ->
Misc.fatal_errorf "Declaring a computed constant %s" name
let close_const acc const =
let acc, simple, name, _kind = close_const0 acc const in
let named = Named.create_simple simple in
acc, named, name
let find_simple_from_id_with_kind env id =
match Env.find_simple_to_substitute_exn env id with
| simple, kind -> simple, kind
| exception Not_found -> (
match Env.find_var_exn env id with
| exception Not_found ->
Misc.fatal_errorf
"find_simple_from_id: Cannot find [Ident] %a in environment" Ident.print
id
| var, kind -> Simple.var var, kind)
let find_simple_from_id env id = fst (find_simple_from_id_with_kind env id)
(* CR mshinwell: Avoid the double lookup *)
let find_simple acc env (simple : IR.simple) =
match simple with
| Const const ->
let acc, simple, _, _ = close_const0 acc const in
acc, simple
| Var id -> acc, find_simple_from_id env id
let find_simple_with_kind acc env (simple : IR.simple) =
match simple with
| Const const ->
let acc, simple, _, kind = close_const0 acc const in
acc, (simple, kind)
| Var id -> acc, find_simple_from_id_with_kind env id
let find_simples acc env ids =
List.fold_left_map (fun acc id -> find_simple acc env id) acc ids
let find_simples_and_arity acc env ids =
List.fold_left_map (fun acc id -> find_simple_with_kind acc env id) acc ids
let find_value_approximation env simple =
Simple.pattern_match' simple
~var:(fun var ~coercion:_ -> Env.find_var_approximation env var)
~symbol:(fun sym ~coercion:_ -> Value_approximation.Value_symbol sym)
~const:(fun const ->
match Reg_width_const.descr const with
| Tagged_immediate i -> Value_approximation.Value_int i
| Naked_immediate _ | Naked_float _ | Naked_int32 _ | Naked_int64 _
| Naked_vec128 _ | Naked_nativeint _ ->
Value_approximation.Value_unknown)
let find_value_approximation_through_symbol acc env simple =
match find_value_approximation env simple with
| Value_approximation.Value_symbol sym ->
Acc.find_symbol_approximation acc sym
| approx -> approx
module Inlining = struct
include Closure_conversion_aux.Inlining
(* CR keryan: we need to emit warnings *)
let inlinable env apply callee_approx =
let tracker = Env.inlining_history_tracker env in
let are_rebuilding_terms = Are_rebuilding_terms.of_bool true in
let compilation_unit =
Env.inlining_history_tracker env
|> Inlining_history.Tracker.absolute
|> Inlining_history.Absolute.compilation_unit
in
match (callee_approx : Env.value_approximation option) with
| None | Some Value_unknown ->
Inlining_report.record_decision_at_call_site_for_unknown_function ~tracker
~apply ~pass:After_closure_conversion ();
Not_inlinable
| Some (Value_symbol _) | Some (Value_int _) | Some (Block_approximation _)
->
assert false
| Some (Closure_approximation { code; _ }) ->
let metadata = Code_or_metadata.code_metadata code in
let fun_params_length =
Code_metadata.params_arity metadata |> Flambda_arity.num_params
in
if (not (Code_or_metadata.code_present code))
|| fun_params_length > List.length (Apply_expr.args apply)
then (
Inlining_report.record_decision_at_call_site_for_known_function ~tracker
~apply ~pass:After_closure_conversion ~unrolling_depth:None
~callee:(Inlining_history.Absolute.empty compilation_unit)
~are_rebuilding_terms Definition_says_not_to_inline;
Not_inlinable)
else
let code = Code_or_metadata.get_code code in
let inlined_call = Apply_expr.inlined apply in
let decision, res =
match inlined_call with
| Never_inlined ->
( Call_site_inlining_decision_type.Never_inlined_attribute,
Not_inlinable )
| Always_inlined _ | Hint_inlined ->
Call_site_inlining_decision_type.Attribute_always, Inlinable code
| Default_inlined | Unroll _ ->
(* Closure ignores completely [@unrolled] attributes, so it seems
safe to do the same. *)
( Call_site_inlining_decision_type.Definition_says_inline
{ was_inline_always = false },
Inlinable code )
in
Inlining_report.record_decision_at_call_site_for_known_function ~tracker
~apply ~pass:After_closure_conversion ~unrolling_depth:None
~callee:(Code.absolute_history code)
~are_rebuilding_terms decision;
res
let make_inlined_body acc ~callee ~region_inlined_into ~params ~args
~my_closure ~my_region ~my_depth ~body ~free_names_of_body
~exn_continuation ~return_continuation ~apply_exn_continuation
~apply_return_continuation ~apply_depth ~apply_dbg =
let rec_info =
match apply_depth with
| None -> Rec_info_expr.initial
| Some depth -> Rec_info_expr.var depth
in
let bind_params ~params ~args ~body:(acc, body) =
let acc = Acc.with_free_names free_names_of_body acc in
List.fold_left2
(fun (acc, body) param arg ->
Let_with_acc.create acc
(Bound_pattern.singleton (VB.create param Name_mode.normal))
(Named.create_simple arg) ~body)
(acc, body) params args
in
let bind_depth ~my_depth ~rec_info ~body:(acc, body) =
Let_with_acc.create acc
(Bound_pattern.singleton (VB.create my_depth Name_mode.normal))
(Named.create_rec_info rec_info)
~body
in
let apply_renaming (acc, body) renaming =
let acc =
Acc.with_free_names
(Name_occurrences.apply_renaming (Acc.free_names acc) renaming)
acc
in
acc, Expr.apply_renaming body renaming
in
let acc, body =
Inlining_helpers.make_inlined_body ~callee ~region_inlined_into ~params
~args ~my_closure ~my_region ~my_depth ~rec_info ~body:(acc, body)
~exn_continuation ~return_continuation ~apply_exn_continuation
~apply_return_continuation ~bind_params ~bind_depth ~apply_renaming
in
Let_with_acc.create acc
(Bound_pattern.singleton
(VB.create (Variable.create "inlined_dbg") Name_mode.normal))
(Named.create_prim
(Nullary (Enter_inlined_apply { dbg = apply_dbg }))
Debuginfo.none)
~body
let wrap_inlined_body_for_exn_extra_args acc ~extra_args
~apply_exn_continuation ~apply_return_continuation ~result_arity
~make_inlined_body =
let apply_cont_create acc ~trap_action cont ~args ~dbg =
let acc, apply_cont =
Apply_cont_with_acc.create acc ~trap_action cont ~args ~dbg
in
Expr_with_acc.create_apply_cont acc apply_cont
in
let let_cont_create acc cont ~handler_params ~handler ~body ~is_exn_handler
~is_cold =
Let_cont_with_acc.build_non_recursive acc cont ~handler_params ~handler
~body ~is_exn_handler ~is_cold
in
Inlining_helpers.wrap_inlined_body_for_exn_extra_args acc ~extra_args
~apply_exn_continuation ~apply_return_continuation ~result_arity
~make_inlined_body ~apply_cont_create ~let_cont_create
let inline acc ~apply ~apply_depth ~func_desc:code =
let apply_dbg = Apply.dbg apply in
let callee = Apply.callee apply in
let region_inlined_into =
match Apply.call_kind apply with
| Function { alloc_mode; _ } | Method { alloc_mode; _ } -> alloc_mode
| C_call _ ->
Misc.fatal_error
"Trying to call [Closure_conversion.Inlining.inline] on a C call."
in
let args = Apply.args apply in
let apply_return_continuation = Apply.continuation apply in
let apply_exn_continuation = Apply.exn_continuation apply in
let params_and_body = Code.params_and_body code in
let cost_metrics = Code.cost_metrics code in
Function_params_and_body.pattern_match params_and_body
~f:(fun
~return_continuation
~exn_continuation
params
~body
~my_closure
~is_my_closure_used:_
~my_region
~my_depth
~free_names_of_body
->
let free_names_of_body =
match free_names_of_body with
| Unknown ->
Misc.fatal_error
"Params_and_body needs free_names_of_body in [Closure_conversion]"
| Known free_names -> free_names
in
let make_inlined_body =
make_inlined_body ~callee ~region_inlined_into
~params:(Bound_parameters.vars params)
~args ~my_closure ~my_region ~my_depth ~body ~free_names_of_body
~exn_continuation ~return_continuation ~apply_depth ~apply_dbg
in
let acc = Acc.with_free_names Name_occurrences.empty acc in
let acc = Acc.increment_metrics cost_metrics acc in
match Exn_continuation.extra_args apply_exn_continuation with
| [] ->
make_inlined_body acc
~apply_exn_continuation:
(Exn_continuation.exn_handler apply_exn_continuation)
~apply_return_continuation
| extra_args ->
wrap_inlined_body_for_exn_extra_args acc ~extra_args
~apply_exn_continuation ~apply_return_continuation
~result_arity:(Code.result_arity code) ~make_inlined_body)
end
let close_c_call acc env ~loc ~let_bound_ids_with_kinds
(({ prim_name;
prim_arity;
prim_alloc;
prim_c_builtin;
prim_effects;
prim_coeffects;
prim_native_name;
prim_native_repr_args;
prim_native_repr_res;
prim_is_layout_poly
} :
Lambda.external_call_description) as prim_desc)
~(args : Simple.t list list) exn_continuation dbg ~current_region
(k : Acc.t -> Named.t list -> Expr_with_acc.t) : Expr_with_acc.t =
if prim_is_layout_poly
then
Misc.fatal_errorf
"close_c_call: C call primitive %s can't be layout polymorphic." prim_name;
let args =
List.map
(function
| [arg] -> arg
| [] | _ :: _ :: _ ->
Misc.fatal_errorf
"close_c_call: expected only singleton arguments for primitive %s, \
but got: [%a]"
prim_name
(Format.pp_print_list ~pp_sep:Format.pp_print_space (fun ppf args ->
Format.fprintf ppf "[%a]"
(Format.pp_print_list ~pp_sep:Format.pp_print_space
Simple.print)
args))
args)
args
in
let env, let_bound_vars =
List.fold_left_map
(fun env (id, kind) -> Env.add_var_like env id Not_user_visible kind)
env let_bound_ids_with_kinds
in
let let_bound_var =
match let_bound_vars with
| [let_bound_var] -> let_bound_var
| [] | _ :: _ :: _ ->
Misc.fatal_errorf
"close_c_call: expected singleton return for primitive %s, but got: \
[%a]"
prim_name
(Format.pp_print_list ~pp_sep:Format.pp_print_space Variable.print)
let_bound_vars
in
let cost_metrics_of_body, free_names_of_body, acc, body =
Acc.measure_cost_metrics acc ~f:(fun acc ->
k acc
(List.map
(fun var -> Named.create_simple (Simple.var var))
let_bound_vars))
in
let alloc_mode =
match Lambda.alloc_mode_of_primitive_description prim_desc with
| None ->
(* This happens when stack allocation is disabled. *)
Alloc_mode.For_allocations.heap
| Some alloc_mode ->
Alloc_mode.For_allocations.from_lambda alloc_mode ~current_region
in
let box_return_value =
match prim_native_repr_res with
| _, Same_as_ocaml_repr _ -> None
| _, Unboxed_float Pfloat64 -> Some (P.Box_number (Naked_float, alloc_mode))
| _, Unboxed_float Pfloat32 ->
(* CR mslater: (float32) middle end support *)
assert false
| _, Unboxed_integer Pnativeint ->
Some (P.Box_number (Naked_nativeint, alloc_mode))
| _, Unboxed_integer Pint32 -> Some (P.Box_number (Naked_int32, alloc_mode))
| _, Unboxed_integer Pint64 -> Some (P.Box_number (Naked_int64, alloc_mode))
| _, Unboxed_vector (Pvec128 _) ->
Some (P.Box_number (Naked_vec128, alloc_mode))
| _, Untagged_int -> Some P.Tag_immediate
in
let return_continuation, needs_wrapper =
match Expr.descr body with
| Apply_cont apply_cont
when Simple.List.equal
(Apply_cont_expr.args apply_cont)
(Simple.vars let_bound_vars)
&& Option.is_none (Apply_cont_expr.trap_action apply_cont)
&& Option.is_none box_return_value ->
Apply_cont_expr.continuation apply_cont, false
| _ -> Continuation.create (), true
in
let kind_of_primitive_extern_repr
((_, repr) : Primitive.mode * Lambda.extern_repr) =
match repr with
| Same_as_ocaml_repr sort ->
K.With_subkind.(
kind
(from_lambda_values_and_unboxed_numbers_only
(Typeopt.layout_of_const_sort sort)))
| Unboxed_float Pfloat64 -> K.naked_float
| Unboxed_float Pfloat32 ->
(* CR mslater: (float32) middle end support *)
assert false
| Unboxed_integer Pnativeint -> K.naked_nativeint
| Unboxed_integer Pint32 -> K.naked_int32
| Unboxed_integer Pint64 -> K.naked_int64
| Untagged_int -> K.naked_immediate
| Unboxed_vector (Pvec128 _) -> K.naked_vec128
in
let param_arity =
List.map kind_of_primitive_extern_repr prim_native_repr_args
|> List.map K.With_subkind.anything
|> Flambda_arity.create_singletons
in
let return_kind = kind_of_primitive_extern_repr prim_native_repr_res in
let return_arity =
Flambda_arity.create_singletons [K.With_subkind.anything return_kind]
in
let effects = Effects.from_lambda prim_effects in
let coeffects = Coeffects.from_lambda prim_coeffects in
let call_kind =
Call_kind.c_call ~needs_caml_c_call:prim_alloc ~is_c_builtin:prim_c_builtin
~effects ~coeffects alloc_mode
in
let call_symbol =
let prim_name =
if String.equal prim_native_name "" then prim_name else prim_native_name
in
Symbol.create
(Symbol.external_symbols_compilation_unit ())
(Linkage_name.of_string prim_name)
in
let call args acc =
(* Some C primitives have implementations within Flambda itself. *)
match prim_native_name with
| "caml_int64_float_of_bits_unboxed"
(* There is only one case where this operation is not the identity: on
32-bit pre-EABI ARM platforms. It is very unlikely anyone would still be
using one of those, but just in case, we only optimise this primitive on
64-bit systems. (There is no easy way here of detecting just the specific
ARM case in question.) *)
when match Targetint_32_64.num_bits with
| Thirty_two -> false
| Sixty_four -> true -> (
if prim_arity <> 1
then Misc.fatal_errorf "Expected arity one for %s" prim_native_name
else
match prim_native_repr_args, prim_native_repr_res with
| [(_, Unboxed_integer Pint64)], (_, Unboxed_float Pfloat64) -> (
match args with
| [arg] ->
let result = Variable.create "reinterpreted_int64" in
let result' = Bound_var.create result Name_mode.normal in
let bindable = Bound_pattern.singleton result' in
let prim = P.Unary (Reinterpret_int64_as_float, arg) in
let acc, return_result =
Apply_cont_with_acc.create acc return_continuation
~args:[Simple.var result]
~dbg
in
let acc, return_result_expr =
Expr_with_acc.create_apply_cont acc return_result
in
Let_with_acc.create acc bindable
(Named.create_prim prim dbg)
~body:return_result_expr
| [] | _ :: _ ->
Misc.fatal_errorf "Expected one arg for %s" prim_native_name)
| _, _ ->
Misc.fatal_errorf "Wrong argument and/or result kind(s) for %s"
prim_native_name)
| _ ->
let callee = Simple.symbol call_symbol in
let apply =
Apply.create ~callee:(Some callee)
~continuation:(Return return_continuation) exn_continuation ~args
~args_arity:param_arity ~return_arity ~call_kind dbg
~inlined:Default_inlined
~inlining_state:(Inlining_state.default ~round:0)
~probe:None ~position:Normal
~relative_history:(Env.relative_history_from_scoped ~loc env)
in
Expr_with_acc.create_apply acc apply
in
let call : Acc.t -> Expr_with_acc.t =
List.fold_left2
(fun (call : Simple.t list -> Acc.t -> Expr_with_acc.t) arg
(arg_repr : Primitive.mode * Lambda.extern_repr) ->
let unbox_arg : P.unary_primitive option =
match arg_repr with
| _, Same_as_ocaml_repr _ -> None
| _, Unboxed_float Pfloat64 -> Some (P.Unbox_number Naked_float)
| _, Unboxed_float Pfloat32 ->
(* CR mslater: (float32) middle end support *)
assert false
| _, Unboxed_integer Pnativeint ->
Some (P.Unbox_number Naked_nativeint)
| _, Unboxed_integer Pint32 -> Some (P.Unbox_number Naked_int32)
| _, Unboxed_integer Pint64 -> Some (P.Unbox_number Naked_int64)
| _, Untagged_int -> Some P.Untag_immediate
| _, Unboxed_vector (Pvec128 _) -> Some (P.Unbox_number Naked_vec128)
in
match unbox_arg with
| None -> fun args acc -> call (arg :: args) acc
| Some named ->
fun args acc ->
let unboxed_arg = Variable.create "unboxed" in
let unboxed_arg' = VB.create unboxed_arg Name_mode.normal in
let acc, body = call (Simple.var unboxed_arg :: args) acc in
let named = Named.create_prim (Unary (named, arg)) dbg in
Let_with_acc.create acc
(Bound_pattern.singleton unboxed_arg')
named ~body)
call args prim_native_repr_args []
in
let wrap_c_call acc ~handler_param ~code_after_call c_call =
let return_kind = Flambda_kind.With_subkind.create return_kind Anything in
let params =
[BP.create handler_param return_kind] |> Bound_parameters.create
in
Let_cont_with_acc.build_non_recursive acc return_continuation
~handler_params:params ~handler:code_after_call ~body:c_call
~is_exn_handler:false ~is_cold:false
in
let keep_body acc =
( Acc.with_cost_metrics
(Cost_metrics.( + ) (Acc.cost_metrics acc) cost_metrics_of_body)
(Acc.with_free_names free_names_of_body acc),
body )
in
let box_unboxed_returns ~let_bound_var ~box_return_value =
let let_bound_var' = VB.create let_bound_var Name_mode.normal in
let handler_param = Variable.rename let_bound_var in
let body acc =
let acc, body = keep_body acc in
let named =
Named.create_prim
(Unary (box_return_value, Simple.var handler_param))
dbg
in
Let_with_acc.create acc
(Bound_pattern.singleton let_bound_var')
named ~body
in
body, handler_param
in
match box_return_value with
| None ->
if needs_wrapper
then
wrap_c_call acc ~handler_param:let_bound_var ~code_after_call:keep_body
call
else
(* Here the body is discarded. It might be useful to explicitly remove
anything that has been added to the acc while converting the body.
However, as we are hitting this code only when body is a goto
continuation where the only parameter is [let_bound_var] this operation
would be a noop and we can skip it. *)
call acc
| Some box_return_value ->
let code_after_call, handler_param =
box_unboxed_returns ~let_bound_var ~box_return_value
in
wrap_c_call acc ~handler_param ~code_after_call call
let close_exn_continuation acc env (exn_continuation : IR.exn_continuation) =
let acc, extra_args =
List.fold_left_map
(fun acc (simple, kind) ->
let acc, simple = find_simple acc env simple in
acc, (simple, kind))
acc exn_continuation.extra_args
in
( acc,
Exn_continuation.create ~exn_handler:exn_continuation.exn_handler
~extra_args )
let close_raise0 acc env ~raise_kind ~arg ~dbg exn_continuation =
let acc, exn_cont = close_exn_continuation acc env exn_continuation in
let exn_handler = Exn_continuation.exn_handler exn_cont in
let args =
(* CR mshinwell: Share with [Lambda_to_flambda_primitives_helpers] *)
let extra_args =
List.map
(fun (simple, _kind) -> simple)
(Exn_continuation.extra_args exn_cont)
in
arg :: extra_args
in
let raise_kind = Some (Trap_action.Raise_kind.from_lambda raise_kind) in
let trap_action = Trap_action.Pop { exn_handler; raise_kind } in
let acc, apply_cont =
Apply_cont_with_acc.create acc ~trap_action exn_handler ~args ~dbg
in
(* Since raising of an exception doesn't terminate, we don't call [k]. *)
Expr_with_acc.create_apply_cont acc apply_cont
let close_raise acc env ~raise_kind ~arg ~dbg exn_continuation =
let acc, arg = find_simple acc env arg in
close_raise0 acc env ~raise_kind ~arg ~dbg exn_continuation
let close_primitive acc env ~let_bound_ids_with_kinds named
(prim : Lambda.primitive) ~args loc
(exn_continuation : IR.exn_continuation option) ~current_region
(k : Acc.t -> Named.t list -> Expr_with_acc.t) : Expr_with_acc.t =
let orig_exn_continuation = exn_continuation in
let acc, exn_continuation =
match exn_continuation with
| None -> acc, None
| Some exn_continuation ->
let acc, cont = close_exn_continuation acc env exn_continuation in
acc, Some cont
in
let acc, args =
List.fold_left_map (fun acc arg -> find_simples acc env arg) acc args
in
let dbg = Debuginfo.from_location loc in
match prim, args with
| Pccall prim, args ->
let exn_continuation =
match exn_continuation with
| None ->
Misc.fatal_errorf "Pccall is missing exception continuation: %a"
IR.print_named named
| Some exn_continuation -> exn_continuation
in
close_c_call acc env ~loc ~let_bound_ids_with_kinds prim ~args
exn_continuation dbg ~current_region k
| Pgetglobal cu, [] ->
if Compilation_unit.equal cu (Env.current_unit env)
then
Misc.fatal_errorf "Pgetglobal %a in the same unit" Compilation_unit.print
cu;
let symbol =
Flambda2_import.Symbol.for_compilation_unit cu |> Symbol.create_wrapped
in
let named = Named.create_simple (Simple.symbol symbol) in
k acc [named]
| Pgetpredef id, [] ->
let symbol =
Flambda2_import.Symbol.for_predef_ident id |> Symbol.create_wrapped
in
let named = Named.create_simple (Simple.symbol symbol) in
k acc [named]
| Praise raise_kind, [[arg]] ->
let exn_continuation =
match orig_exn_continuation with
| None ->
Misc.fatal_errorf "Praise is missing exception continuation: %a"
IR.print_named named
| Some exn_continuation -> exn_continuation
in
close_raise0 acc env ~raise_kind ~arg ~dbg exn_continuation
| (Pmakeblock _ | Pmakefloatblock _ | Pmakeufloatblock _ | Pmakearray _), []
->
(* Special case for liftable empty block or array *)
let acc, sym =
match prim with
| Pmakeblock (tag, _, _, _mode) ->
if tag <> 0
then
(* There should not be any way to reach this from Ocaml code. *)
Misc.fatal_error
"Non-zero tag on empty block allocation in [Closure_conversion]"
else
register_const0 acc
(Static_const.block Tag.Scannable.zero Immutable [])
"empty_block"
| Pmakefloatblock _ ->
Misc.fatal_error "Unexpected empty float block in [Closure_conversion]"
| Pmakeufloatblock _ ->
Misc.fatal_error "Unexpected empty float# block in [Closure_conversion]"
| Pmakearray (array_kind, _, _mode) ->
let array_kind = Empty_array_kind.of_lambda array_kind in
register_const0 acc (Static_const.empty_array array_kind) "empty_array"
| Pbytes_to_string | Pbytes_of_string | Parray_of_iarray
| Parray_to_iarray | Pignore | Pgetglobal _ | Psetglobal _ | Pgetpredef _
| Pfield _ | Pfield_computed _ | Psetfield _ | Psetfield_computed _
| Pfloatfield _ | Psetfloatfield _ | Pduprecord _ | Pccall _ | Praise _
| Pufloatfield _ | Psetufloatfield _ | Psequand | Psequor | Pnot | Pnegint
| Paddint | Psubint | Pmulint | Pdivint _ | Pmodint _ | Pandint | Porint
| Pxorint | Plslint | Plsrint | Pasrint | Pintcomp _ | Pcompare_ints
| Pcompare_floats _ | Pcompare_bints _ | Poffsetint _ | Poffsetref _
| Pintoffloat _
| Pfloatofint (_, _)
| Pnegfloat (_, _)
| Pabsfloat (_, _)
| Paddfloat (_, _)
| Psubfloat (_, _)
| Pmulfloat (_, _)
| Pdivfloat (_, _)
| Pfloatcomp (_, _)
| Punboxed_float_comp (_, _)
| Pstringlength | Pstringrefu | Pstringrefs | Pbyteslength | Pbytesrefu
| Pbytessetu | Pbytesrefs | Pbytessets | Pduparray _ | Parraylength _
| Parrayrefu _ | Parraysetu _ | Parrayrefs _ | Parraysets _ | Pisint _
| Pisout | Pbintofint _ | Pintofbint _ | Pcvtbint _ | Pnegbint _
| Paddbint _ | Psubbint _ | Pmulbint _ | Pdivbint _ | Pmodbint _
| Pandbint _ | Porbint _ | Pxorbint _ | Plslbint _ | Plsrbint _
| Pasrbint _ | Pbintcomp _ | Punboxed_int_comp _ | Pbigarrayref _
| Pbigarrayset _ | Pbigarraydim _ | Pstring_load_16 _ | Pstring_load_32 _
| Pstring_load_64 _ | Pstring_load_128 _ | Pbytes_load_16 _
| Pbytes_load_32 _ | Pbytes_load_64 _ | Pbytes_load_128 _
| Pbytes_set_16 _ | Pbytes_set_32 _ | Pbytes_set_64 _ | Pbytes_set_128 _
| Pbigstring_load_16 _ | Pbigstring_load_32 _ | Pbigstring_load_64 _
| Pbigstring_load_128 _ | Pbigstring_set_16 _ | Pbigstring_set_32 _
| Pbigstring_set_64 _ | Pbigstring_set_128 _ | Pfloatarray_load_128 _
| Pfloat_array_load_128 _ | Pint_array_load_128 _
| Punboxed_float_array_load_128 _ | Punboxed_int32_array_load_128 _
| Punboxed_int64_array_load_128 _ | Punboxed_nativeint_array_load_128 _
| Pfloatarray_set_128 _ | Pfloat_array_set_128 _ | Pint_array_set_128 _
| Punboxed_float_array_set_128 _ | Punboxed_int32_array_set_128 _
| Punboxed_int64_array_set_128 _ | Punboxed_nativeint_array_set_128 _
| Pctconst _ | Pbswap16 | Pbbswap _ | Pint_as_pointer _ | Popaque _
| Pprobe_is_enabled _ | Pobj_dup | Pobj_magic _ | Punbox_float _
| Pbox_float (_, _)
| Punbox_int _ | Pbox_int _ | Pmake_unboxed_product _
| Punboxed_product_field _ | Pget_header _ | Prunstack | Pperform
| Presume | Preperform | Patomic_exchange | Patomic_cas
| Patomic_fetch_add | Pdls_get | Patomic_load _ ->
(* Inconsistent with outer match *)
assert false
in
k acc [Named.create_simple (Simple.symbol sym)]
| prim, args ->
Lambda_to_flambda_primitives.convert_and_bind acc exn_continuation
~big_endian:(Env.big_endian env) ~register_const0 prim ~args dbg
~current_region k
let close_trap_action_opt trap_action =
Option.map
(fun (trap_action : IR.trap_action) : Trap_action.t ->
match trap_action with
| Push { exn_handler } -> Push { exn_handler }
| Pop { exn_handler } -> Pop { exn_handler; raise_kind = None })
trap_action
let close_named acc env ~let_bound_ids_with_kinds (named : IR.named)
(k : Acc.t -> Named.t list -> Expr_with_acc.t) : Expr_with_acc.t =
match named with
| Simple (Var id) ->
assert (not (Ident.is_global_or_predef id));
let acc, simple = find_simple acc env (Var id) in
let named = Named.create_simple simple in
k acc [named]
| Simple (Const cst) ->
let acc, named, _name = close_const acc cst in
k acc [named]
| Get_tag var ->
let named = find_simple_from_id env var in
let prim : Lambda_to_flambda_primitives_helpers.expr_primitive =
Unary (Tag_immediate, Prim (Unary (Get_tag, Simple named)))
in
Lambda_to_flambda_primitives_helpers.bind_recs acc None ~register_const0
[prim] Debuginfo.none k
| Begin_region { is_try_region } ->
let prim : Lambda_to_flambda_primitives_helpers.expr_primitive =
Nullary (if is_try_region then Begin_try_region else Begin_region)
in
Lambda_to_flambda_primitives_helpers.bind_recs acc None ~register_const0
[prim] Debuginfo.none k
| End_region { is_try_region; region } ->
let named = find_simple_from_id env region in
let prim : Lambda_to_flambda_primitives_helpers.expr_primitive =
Unary
((if is_try_region then End_try_region else End_region), Simple named)
in
Lambda_to_flambda_primitives_helpers.bind_recs acc None ~register_const0
[prim] Debuginfo.none k
| Prim { prim; args; loc; exn_continuation; region } ->
close_primitive acc env ~let_bound_ids_with_kinds named prim ~args loc
exn_continuation
~current_region:(fst (Env.find_var env region))
k
type simplified_block_load =
| Unknown
| Not_a_block
| Block_but_cannot_simplify of Code_or_metadata.t Value_approximation.t
| Field_contents of Simple.t
let simplify_block_load acc body_env ~block ~field : simplified_block_load =
match find_value_approximation_through_symbol acc body_env block with
| Value_unknown -> Unknown
| Closure_approximation _ | Value_symbol _ | Value_int _ -> Not_a_block
| Block_approximation (_tag, approx, _alloc_mode) -> (
let approx =
Simple.pattern_match field
~const:(fun const ->
match Reg_width_const.descr const with
| Tagged_immediate i ->
let i = Targetint_31_63.to_int i in
if i >= Array.length approx then None else Some approx.(i)
| _ -> Some Value_approximation.Value_unknown)
~name:(fun _ ~coercion:_ -> Some Value_approximation.Value_unknown)
in
match approx with
| Some (Value_symbol sym) -> Field_contents (Simple.symbol sym)
| Some (Value_int i) -> Field_contents (Simple.const_int i)
| Some approx -> Block_but_cannot_simplify approx
| None -> Not_a_block)
type block_static_kind =
| Dynamic_block
| Computed_static of Field_of_static_block.t list
| Constant of Field_of_static_block.t list
let classify_fields_of_block env fields alloc_mode =
let is_local =
match (alloc_mode : Alloc_mode.For_allocations.t) with
| Local _ -> true
| Heap -> false
in
let static_fields =
List.fold_left
(fun static_fields f ->
match static_fields with
| None -> None
| Some fields ->
Simple.pattern_match'
~const:(fun c ->
match Reg_width_const.descr c with
| Tagged_immediate imm ->
Some (Field_of_static_block.Tagged_immediate imm :: fields)
| _ -> None)
~symbol:(fun s ~coercion:_ ->
Some (Field_of_static_block.Symbol s :: fields))
~var:(fun v ~coercion:_ ->
if Env.at_toplevel env
&& Flambda_features.classic_mode ()
&& not is_local
then
Some
(Field_of_static_block.Dynamically_computed (v, Debuginfo.none)
:: fields)
else None)
f)
(Some []) fields
|> Option.map List.rev
in
match static_fields with
| None -> Dynamic_block
| Some fields ->
if List.exists
(function
| Field_of_static_block.Dynamically_computed _ -> true | _ -> false)
fields
then Computed_static fields
else Constant fields
let close_let acc env let_bound_ids_with_kinds user_visible defining_expr
~(body : Acc.t -> Env.t -> Expr_with_acc.t) : Expr_with_acc.t =
let rec cont ids_with_kinds env acc (defining_exprs : Named.t list) =
match ids_with_kinds, defining_exprs with
| [], [] -> body acc env