summaryrefslogtreecommitdiff
path: root/lambda/translprim.ml
blob: 4b380ca7c8c4e0cf483887161fe6f39f3eaae4b4 (plain)
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
(**************************************************************************)
(*                                                                        *)
(*                                 OCaml                                  *)
(*                                                                        *)
(*             Xavier Leroy, projet Cristal, INRIA Rocquencourt           *)
(*                                                                        *)
(*   Copyright 1996 Institut National de Recherche en Informatique et     *)
(*     en Automatique.                                                    *)
(*                                                                        *)
(*   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.          *)
(*                                                                        *)
(**************************************************************************)

(* Translation of primitives *)

open Misc
open Asttypes
open Primitive
open Types
open Typedtree
open Typeopt
open Lambda
open Debuginfo.Scoped_location

type error =
  | Unknown_builtin_primitive of string
  | Wrong_arity_builtin_primitive of string

exception Error of Location.t * error

(* Insertion of debugging events *)

let event_before loc exp lam = match lam with
| Lstaticraise (_,_) -> lam
| _ ->
  if !Clflags.debug && not !Clflags.native_code
  then Levent(lam, {lev_loc = loc;
                    lev_kind = Lev_before;
                    lev_repr = None;
                    lev_env = exp.exp_env})
  else lam

let event_after loc exp lam =
  if !Clflags.debug && not !Clflags.native_code
  then Levent(lam, {lev_loc = loc;
                    lev_kind = Lev_after exp.exp_type;
                    lev_repr = None;
                    lev_env = exp.exp_env})
  else lam

type comparison =
  | Equal
  | Not_equal
  | Less_equal
  | Less_than
  | Greater_equal
  | Greater_than
  | Compare

type comparison_kind =
  | Compare_generic
  | Compare_ints
  | Compare_floats
  | Compare_strings
  | Compare_bytes
  | Compare_nativeints
  | Compare_int32s
  | Compare_int64s

type loc_kind =
  | Loc_FILE
  | Loc_LINE
  | Loc_MODULE
  | Loc_LOC
  | Loc_POS
  | Loc_FUNCTION

type prim =
  | Primitive of Lambda.primitive * int
  | External of Primitive.description
  | Comparison of comparison * comparison_kind
  | Raise of Lambda.raise_kind
  | Raise_with_backtrace
  | Lazy_force
  | Loc of loc_kind
  | Send
  | Send_self
  | Send_cache

let used_primitives = Hashtbl.create 7
let add_used_primitive loc env path =
  match path with
    Some (Path.Pdot _ as path) ->
      let path = Env.normalize_path_prefix (Some loc) env path in
      let unit = Path.head path in
      if Ident.global unit && not (Hashtbl.mem used_primitives path)
      then Hashtbl.add used_primitives path loc
  | _ -> ()

let clear_used_primitives () = Hashtbl.clear used_primitives
let get_used_primitives () =
  Hashtbl.fold (fun path _ acc -> path :: acc) used_primitives []

let gen_array_kind =
  if Config.flat_float_array then Pgenarray else Paddrarray

let prim_sys_argv =
  Primitive.simple ~name:"caml_sys_argv" ~arity:1 ~alloc:true

let primitives_table =
  create_hashtable 57 [
    "%identity", Primitive (Pidentity, 1);
    "%bytes_to_string", Primitive (Pbytes_to_string, 1);
    "%bytes_of_string", Primitive (Pbytes_of_string, 1);
    "%ignore", Primitive (Pignore, 1);
    "%revapply", Primitive (Prevapply, 2);
    "%apply", Primitive (Pdirapply, 2);
    "%loc_LOC", Loc Loc_LOC;
    "%loc_FILE", Loc Loc_FILE;
    "%loc_LINE", Loc Loc_LINE;
    "%loc_POS", Loc Loc_POS;
    "%loc_MODULE", Loc Loc_MODULE;
    "%loc_FUNCTION", Loc Loc_FUNCTION;
    "%field0", Primitive (Pfield(0, Pointer, Mutable), 1);
    "%field1", Primitive (Pfield(1, Pointer, Mutable), 1);
    "%setfield0", Primitive ((Psetfield(0, Pointer, Assignment)), 2);
    "%makeblock", Primitive ((Pmakeblock(0, Immutable, None)), 1);
    "%makemutable", Primitive ((Pmakeblock(0, Mutable, None)), 1);
    "%raise", Raise Raise_regular;
    "%reraise", Raise Raise_reraise;
    "%raise_notrace", Raise Raise_notrace;
    "%raise_with_backtrace", Raise_with_backtrace;
    "%sequand", Primitive (Psequand, 2);
    "%sequor", Primitive (Psequor, 2);
    "%boolnot", Primitive (Pnot, 1);
    "%big_endian", Primitive ((Pctconst Big_endian), 1);
    "%backend_type", Primitive ((Pctconst Backend_type), 1);
    "%word_size", Primitive ((Pctconst Word_size), 1);
    "%int_size", Primitive ((Pctconst Int_size), 1);
    "%max_wosize", Primitive ((Pctconst Max_wosize), 1);
    "%ostype_unix", Primitive ((Pctconst Ostype_unix), 1);
    "%ostype_win32", Primitive ((Pctconst Ostype_win32), 1);
    "%ostype_cygwin", Primitive ((Pctconst Ostype_cygwin), 1);
    "%negint", Primitive (Pnegint, 1);
    "%succint", Primitive ((Poffsetint 1), 1);
    "%predint", Primitive ((Poffsetint(-1)), 1);
    "%addint", Primitive (Paddint, 2);
    "%subint", Primitive (Psubint, 2);
    "%mulint", Primitive (Pmulint, 2);
    "%divint", Primitive ((Pdivint Safe), 2);
    "%modint", Primitive ((Pmodint Safe), 2);
    "%andint", Primitive (Pandint, 2);
    "%orint", Primitive (Porint, 2);
    "%xorint", Primitive (Pxorint, 2);
    "%lslint", Primitive (Plslint, 2);
    "%lsrint", Primitive (Plsrint, 2);
    "%asrint", Primitive (Pasrint, 2);
    "%eq", Primitive ((Pintcomp Ceq), 2);
    "%noteq", Primitive ((Pintcomp Cne), 2);
    "%ltint", Primitive ((Pintcomp Clt), 2);
    "%leint", Primitive ((Pintcomp Cle), 2);
    "%gtint", Primitive ((Pintcomp Cgt), 2);
    "%geint", Primitive ((Pintcomp Cge), 2);
    "%incr", Primitive ((Poffsetref(1)), 1);
    "%decr", Primitive ((Poffsetref(-1)), 1);
    "%intoffloat", Primitive (Pintoffloat, 1);
    "%floatofint", Primitive (Pfloatofint, 1);
    "%negfloat", Primitive (Pnegfloat, 1);
    "%absfloat", Primitive (Pabsfloat, 1);
    "%addfloat", Primitive (Paddfloat, 2);
    "%subfloat", Primitive (Psubfloat, 2);
    "%mulfloat", Primitive (Pmulfloat, 2);
    "%divfloat", Primitive (Pdivfloat, 2);
    "%eqfloat", Primitive ((Pfloatcomp CFeq), 2);
    "%noteqfloat", Primitive ((Pfloatcomp CFneq), 2);
    "%ltfloat", Primitive ((Pfloatcomp CFlt), 2);
    "%lefloat", Primitive ((Pfloatcomp CFle), 2);
    "%gtfloat", Primitive ((Pfloatcomp CFgt), 2);
    "%gefloat", Primitive ((Pfloatcomp CFge), 2);
    "%string_length", Primitive (Pstringlength, 1);
    "%string_safe_get", Primitive (Pstringrefs, 2);
    "%string_safe_set", Primitive (Pbytessets, 3);
    "%string_unsafe_get", Primitive (Pstringrefu, 2);
    "%string_unsafe_set", Primitive (Pbytessetu, 3);
    "%bytes_length", Primitive (Pbyteslength, 1);
    "%bytes_safe_get", Primitive (Pbytesrefs, 2);
    "%bytes_safe_set", Primitive (Pbytessets, 3);
    "%bytes_unsafe_get", Primitive (Pbytesrefu, 2);
    "%bytes_unsafe_set", Primitive (Pbytessetu, 3);
    "%array_length", Primitive ((Parraylength gen_array_kind), 1);
    "%array_safe_get", Primitive ((Parrayrefs gen_array_kind), 2);
    "%array_safe_set", Primitive ((Parraysets gen_array_kind), 3);
    "%array_unsafe_get", Primitive ((Parrayrefu gen_array_kind), 2);
    "%array_unsafe_set", Primitive ((Parraysetu gen_array_kind), 3);
    "%obj_size", Primitive ((Parraylength gen_array_kind), 1);
    "%obj_field", Primitive ((Parrayrefu gen_array_kind), 2);
    "%obj_set_field", Primitive ((Parraysetu gen_array_kind), 3);
    "%floatarray_length", Primitive ((Parraylength Pfloatarray), 1);
    "%floatarray_safe_get", Primitive ((Parrayrefs Pfloatarray), 2);
    "%floatarray_safe_set", Primitive ((Parraysets Pfloatarray), 3);
    "%floatarray_unsafe_get", Primitive ((Parrayrefu Pfloatarray), 2);
    "%floatarray_unsafe_set", Primitive ((Parraysetu Pfloatarray), 3);
    "%obj_is_int", Primitive (Pisint, 1);
    "%lazy_force", Lazy_force;
    "%nativeint_of_int", Primitive ((Pbintofint Pnativeint), 1);
    "%nativeint_to_int", Primitive ((Pintofbint Pnativeint), 1);
    "%nativeint_neg", Primitive ((Pnegbint Pnativeint), 1);
    "%nativeint_add", Primitive ((Paddbint Pnativeint), 2);
    "%nativeint_sub", Primitive ((Psubbint Pnativeint), 2);
    "%nativeint_mul", Primitive ((Pmulbint Pnativeint), 2);
    "%nativeint_div",
    Primitive ((Pdivbint { size = Pnativeint; is_safe = Safe }), 2);
    "%nativeint_mod",
    Primitive ((Pmodbint { size = Pnativeint; is_safe = Safe }), 2);
    "%nativeint_and", Primitive ((Pandbint Pnativeint), 2);
    "%nativeint_or", Primitive ( (Porbint Pnativeint), 2);
    "%nativeint_xor", Primitive ((Pxorbint Pnativeint), 2);
    "%nativeint_lsl", Primitive ((Plslbint Pnativeint), 2);
    "%nativeint_lsr", Primitive ((Plsrbint Pnativeint), 2);
    "%nativeint_asr", Primitive ((Pasrbint Pnativeint), 2);
    "%int32_of_int", Primitive ((Pbintofint Pint32), 1);
    "%int32_to_int", Primitive ((Pintofbint Pint32), 1);
    "%int32_neg", Primitive ((Pnegbint Pint32), 1);
    "%int32_add", Primitive ((Paddbint Pint32), 2);
    "%int32_sub", Primitive ((Psubbint Pint32), 2);
    "%int32_mul", Primitive ((Pmulbint Pint32), 2);
    "%int32_div", Primitive ((Pdivbint { size = Pint32; is_safe = Safe }), 2);
    "%int32_mod", Primitive ((Pmodbint { size = Pint32; is_safe = Safe }), 2);
    "%int32_and", Primitive ((Pandbint Pint32), 2);
    "%int32_or", Primitive ( (Porbint Pint32), 2);
    "%int32_xor", Primitive ((Pxorbint Pint32), 2);
    "%int32_lsl", Primitive ((Plslbint Pint32), 2);
    "%int32_lsr", Primitive ((Plsrbint Pint32), 2);
    "%int32_asr", Primitive ((Pasrbint Pint32), 2);
    "%int64_of_int", Primitive ((Pbintofint Pint64), 1);
    "%int64_to_int", Primitive ((Pintofbint Pint64), 1);
    "%int64_neg", Primitive ((Pnegbint Pint64), 1);
    "%int64_add", Primitive ((Paddbint Pint64), 2);
    "%int64_sub", Primitive ((Psubbint Pint64), 2);
    "%int64_mul", Primitive ((Pmulbint Pint64), 2);
    "%int64_div", Primitive ((Pdivbint { size = Pint64; is_safe = Safe }), 2);
    "%int64_mod", Primitive ((Pmodbint { size = Pint64; is_safe = Safe }), 2);
    "%int64_and", Primitive ((Pandbint Pint64), 2);
    "%int64_or", Primitive ( (Porbint Pint64), 2);
    "%int64_xor", Primitive ((Pxorbint Pint64), 2);
    "%int64_lsl", Primitive ((Plslbint Pint64), 2);
    "%int64_lsr", Primitive ((Plsrbint Pint64), 2);
    "%int64_asr", Primitive ((Pasrbint Pint64), 2);
    "%nativeint_of_int32", Primitive ((Pcvtbint(Pint32, Pnativeint)), 1);
    "%nativeint_to_int32", Primitive ((Pcvtbint(Pnativeint, Pint32)), 1);
    "%int64_of_int32", Primitive ((Pcvtbint(Pint32, Pint64)), 1);
    "%int64_to_int32", Primitive ((Pcvtbint(Pint64, Pint32)), 1);
    "%int64_of_nativeint", Primitive ((Pcvtbint(Pnativeint, Pint64)), 1);
    "%int64_to_nativeint", Primitive ((Pcvtbint(Pint64, Pnativeint)), 1);
    "%caml_ba_ref_1",
    Primitive
      ((Pbigarrayref(false, 1, Pbigarray_unknown, Pbigarray_unknown_layout)),
       2);
    "%caml_ba_ref_2",
    Primitive
      ((Pbigarrayref(false, 2, Pbigarray_unknown, Pbigarray_unknown_layout)),
       3);
    "%caml_ba_ref_3",
    Primitive
      ((Pbigarrayref(false, 3, Pbigarray_unknown, Pbigarray_unknown_layout)),
       4);
    "%caml_ba_set_1",
    Primitive
      ((Pbigarrayset(false, 1, Pbigarray_unknown, Pbigarray_unknown_layout)),
       3);
    "%caml_ba_set_2",
    Primitive
      ((Pbigarrayset(false, 2, Pbigarray_unknown, Pbigarray_unknown_layout)),
       4);
    "%caml_ba_set_3",
    Primitive
      ((Pbigarrayset(false, 3, Pbigarray_unknown, Pbigarray_unknown_layout)),
       5);
    "%caml_ba_unsafe_ref_1",
    Primitive
      ((Pbigarrayref(true, 1, Pbigarray_unknown, Pbigarray_unknown_layout)),
       2);
    "%caml_ba_unsafe_ref_2",
    Primitive
      ((Pbigarrayref(true, 2, Pbigarray_unknown, Pbigarray_unknown_layout)),
       3);
    "%caml_ba_unsafe_ref_3",
    Primitive
      ((Pbigarrayref(true, 3, Pbigarray_unknown, Pbigarray_unknown_layout)),
       4);
    "%caml_ba_unsafe_set_1",
    Primitive
      ((Pbigarrayset(true, 1, Pbigarray_unknown, Pbigarray_unknown_layout)),
       3);
    "%caml_ba_unsafe_set_2",
    Primitive
      ((Pbigarrayset(true, 2, Pbigarray_unknown, Pbigarray_unknown_layout)),
       4);
    "%caml_ba_unsafe_set_3",
    Primitive
      ((Pbigarrayset(true, 3, Pbigarray_unknown, Pbigarray_unknown_layout)),
       5);
    "%caml_ba_dim_1", Primitive ((Pbigarraydim(1)), 1);
    "%caml_ba_dim_2", Primitive ((Pbigarraydim(2)), 1);
    "%caml_ba_dim_3", Primitive ((Pbigarraydim(3)), 1);
    "%caml_string_get16", Primitive ((Pstring_load_16(false)), 2);
    "%caml_string_get16u", Primitive ((Pstring_load_16(true)), 2);
    "%caml_string_get32", Primitive ((Pstring_load_32(false)), 2);
    "%caml_string_get32u", Primitive ((Pstring_load_32(true)), 2);
    "%caml_string_get64", Primitive ((Pstring_load_64(false)), 2);
    "%caml_string_get64u", Primitive ((Pstring_load_64(true)), 2);
    "%caml_string_set16", Primitive ((Pbytes_set_16(false)), 3);
    "%caml_string_set16u", Primitive ((Pbytes_set_16(true)), 3);
    "%caml_string_set32", Primitive ((Pbytes_set_32(false)), 3);
    "%caml_string_set32u", Primitive ((Pbytes_set_32(true)), 3);
    "%caml_string_set64", Primitive ((Pbytes_set_64(false)), 3);
    "%caml_string_set64u", Primitive ((Pbytes_set_64(true)), 3);
    "%caml_bytes_get16", Primitive ((Pbytes_load_16(false)), 2);
    "%caml_bytes_get16u", Primitive ((Pbytes_load_16(true)), 2);
    "%caml_bytes_get32", Primitive ((Pbytes_load_32(false)), 2);
    "%caml_bytes_get32u", Primitive ((Pbytes_load_32(true)), 2);
    "%caml_bytes_get64", Primitive ((Pbytes_load_64(false)), 2);
    "%caml_bytes_get64u", Primitive ((Pbytes_load_64(true)), 2);
    "%caml_bytes_set16", Primitive ((Pbytes_set_16(false)), 3);
    "%caml_bytes_set16u", Primitive ((Pbytes_set_16(true)), 3);
    "%caml_bytes_set32", Primitive ((Pbytes_set_32(false)), 3);
    "%caml_bytes_set32u", Primitive ((Pbytes_set_32(true)), 3);
    "%caml_bytes_set64", Primitive ((Pbytes_set_64(false)), 3);
    "%caml_bytes_set64u", Primitive ((Pbytes_set_64(true)), 3);
    "%caml_bigstring_get16", Primitive ((Pbigstring_load_16(false)), 2);
    "%caml_bigstring_get16u", Primitive ((Pbigstring_load_16(true)), 2);
    "%caml_bigstring_get32", Primitive ((Pbigstring_load_32(false)), 2);
    "%caml_bigstring_get32u", Primitive ((Pbigstring_load_32(true)), 2);
    "%caml_bigstring_get64", Primitive ((Pbigstring_load_64(false)), 2);
    "%caml_bigstring_get64u", Primitive ((Pbigstring_load_64(true)), 2);
    "%caml_bigstring_set16", Primitive ((Pbigstring_set_16(false)), 3);
    "%caml_bigstring_set16u", Primitive ((Pbigstring_set_16(true)), 3);
    "%caml_bigstring_set32", Primitive ((Pbigstring_set_32(false)), 3);
    "%caml_bigstring_set32u", Primitive ((Pbigstring_set_32(true)), 3);
    "%caml_bigstring_set64", Primitive ((Pbigstring_set_64(false)), 3);
    "%caml_bigstring_set64u", Primitive ((Pbigstring_set_64(true)), 3);
    "%bswap16", Primitive (Pbswap16, 1);
    "%bswap_int32", Primitive ((Pbbswap(Pint32)), 1);
    "%bswap_int64", Primitive ((Pbbswap(Pint64)), 1);
    "%bswap_native", Primitive ((Pbbswap(Pnativeint)), 1);
    "%int_as_pointer", Primitive (Pint_as_pointer, 1);
    "%opaque", Primitive (Popaque, 1);
    "%sys_argv", External prim_sys_argv;
    "%send", Send;
    "%sendself", Send_self;
    "%sendcache", Send_cache;
    "%equal", Comparison(Equal, Compare_generic);
    "%notequal", Comparison(Not_equal, Compare_generic);
    "%lessequal", Comparison(Less_equal, Compare_generic);
    "%lessthan", Comparison(Less_than, Compare_generic);
    "%greaterequal", Comparison(Greater_equal, Compare_generic);
    "%greaterthan", Comparison(Greater_than, Compare_generic);
    "%compare", Comparison(Compare, Compare_generic);
    "%atomic_load", Primitive ((Patomic_load {immediate_or_pointer=Pointer}), 1);
    "%atomic_exchange", Primitive (Patomic_exchange, 2);
    "%atomic_cas", Primitive (Patomic_cas, 3);
    "%atomic_fetch_add", Primitive (Patomic_fetch_add, 2);
    "%perform", Primitive (Pperform, 1);
    "%resume", Primitive (Presume, 3);
    "%poll", Primitive (Ppoll, 1);
    "%nop", Primitive (Pnop, 1);
  ]


let lookup_primitive loc p =
  match Hashtbl.find primitives_table p.prim_name with
  | prim -> prim
  | exception Not_found ->
      if String.length p.prim_name > 0 && p.prim_name.[0] = '%' then
        raise(Error(loc, Unknown_builtin_primitive p.prim_name));
      External p

let lookup_primitive_and_mark_used loc p env path =
  match lookup_primitive loc p with
  | External _ as e -> add_used_primitive loc env path; e
  | x -> x

let simplify_constant_constructor = function
  | Equal -> true
  | Not_equal -> true
  | Less_equal -> false
  | Less_than -> false
  | Greater_equal -> false
  | Greater_than -> false
  | Compare -> false

(* The following function computes the greatest lower bound in the
   semilattice of array kinds:
          gen
         /   \
      addr   float
       |
      int
   Note that the GLB is not guaranteed to exist, in which case we return
   our first argument instead of raising a fatal error because, although
   it cannot happen in a well-typed program, (ab)use of Obj.magic can
   probably trigger it.
*)
let glb_array_type t1 t2 =
  match t1, t2 with
  | Pfloatarray, (Paddrarray | Pintarray)
  | (Paddrarray | Pintarray), Pfloatarray -> t1

  | Pgenarray, x | x, Pgenarray -> x
  | Paddrarray, x | x, Paddrarray -> x
  | Pintarray, Pintarray -> Pintarray
  | Pfloatarray, Pfloatarray -> Pfloatarray

(* Specialize a primitive from available type information. *)

let specialize_primitive env ty ~has_constant_constructor prim =
  let param_tys =
    match is_function_type env ty with
    | None -> []
    | Some (p1, rhs) ->
      match is_function_type env rhs with
      | None -> [p1]
      | Some (p2, _) -> [p1;p2]
  in
  match prim, param_tys with
  | Primitive (Psetfield(n, Pointer, init), arity), [_; p2] -> begin
      match maybe_pointer_type env p2 with
      | Pointer -> None
      | Immediate -> Some (Primitive (Psetfield(n, Immediate, init), arity))
    end
  | Primitive (Pfield (n, Pointer, mut), arity), _ ->
      (* try strength reduction based on the *result type* *)
      let is_int = match is_function_type env ty with
        | None -> Pointer
        | Some (_p1, rhs) -> maybe_pointer_type env rhs in
      Some (Primitive (Pfield (n, is_int, mut), arity))
  | Primitive (Parraylength t, arity), [p] -> begin
      let array_type = glb_array_type t (array_type_kind env p) in
      if t = array_type then None
      else Some (Primitive (Parraylength array_type, arity))
    end
  | Primitive (Parrayrefu t, arity), p1 :: _ -> begin
      let array_type = glb_array_type t (array_type_kind env p1) in
      if t = array_type then None
      else Some (Primitive (Parrayrefu array_type, arity))
    end
  | Primitive (Parraysetu t, arity), p1 :: _ -> begin
      let array_type = glb_array_type t (array_type_kind env p1) in
      if t = array_type then None
      else Some (Primitive (Parraysetu array_type, arity))
    end
  | Primitive (Parrayrefs t, arity), p1 :: _ -> begin
      let array_type = glb_array_type t (array_type_kind env p1) in
      if t = array_type then None
      else Some (Primitive (Parrayrefs array_type, arity))
    end
  | Primitive (Parraysets t, arity), p1 :: _ -> begin
      let array_type = glb_array_type t (array_type_kind env p1) in
      if t = array_type then None
      else Some (Primitive (Parraysets array_type, arity))
    end
  | Primitive (Pbigarrayref(unsafe, n, Pbigarray_unknown,
                            Pbigarray_unknown_layout), arity), p1 :: _ -> begin
      let (k, l) = bigarray_type_kind_and_layout env p1 in
      match k, l with
      | Pbigarray_unknown, Pbigarray_unknown_layout -> None
      | _, _ -> Some (Primitive (Pbigarrayref(unsafe, n, k, l), arity))
    end
  | Primitive (Pbigarrayset(unsafe, n, Pbigarray_unknown,
                            Pbigarray_unknown_layout), arity), p1 :: _ -> begin
      let (k, l) = bigarray_type_kind_and_layout env p1 in
      match k, l with
      | Pbigarray_unknown, Pbigarray_unknown_layout -> None
      | _, _ -> Some (Primitive (Pbigarrayset(unsafe, n, k, l), arity))
    end
  | Primitive (Pmakeblock(tag, mut, None), arity), fields -> begin
      let shape = List.map (Typeopt.value_kind env) fields in
      let useful = List.exists (fun knd -> knd <> Pgenval) shape in
      if useful then Some (Primitive (Pmakeblock(tag, mut, Some shape), arity))
      else None
    end
  | Primitive (Patomic_load { immediate_or_pointer = Pointer }, arity), _ -> begin
      let is_int = match is_function_type env ty with
        | None -> Pointer
        | Some (_p1, rhs) -> maybe_pointer_type env rhs in
      Some (Primitive (Patomic_load {immediate_or_pointer = is_int}, arity))
    end
  | Comparison(comp, Compare_generic), p1 :: _ ->
    if (has_constant_constructor
        && simplify_constant_constructor comp) then begin
      Some (Comparison(comp, Compare_ints))
    end else if (is_base_type env p1 Predef.path_int
        || is_base_type env p1 Predef.path_char
        || (maybe_pointer_type env p1 = Immediate)) then begin
      Some (Comparison(comp, Compare_ints))
    end else if is_base_type env p1 Predef.path_float then begin
      Some (Comparison(comp, Compare_floats))
    end else if is_base_type env p1 Predef.path_string then begin
      Some (Comparison(comp, Compare_strings))
    end else if is_base_type env p1 Predef.path_bytes then begin
      Some (Comparison(comp, Compare_bytes))
    end else if is_base_type env p1 Predef.path_nativeint then begin
      Some (Comparison(comp, Compare_nativeints))
    end else if is_base_type env p1 Predef.path_int32 then begin
      Some (Comparison(comp, Compare_int32s))
    end else if is_base_type env p1 Predef.path_int64 then begin
      Some (Comparison(comp, Compare_int64s))
    end else begin
      None
    end
  | _ -> None

let caml_equal =
  Primitive.simple ~name:"caml_equal" ~arity:2 ~alloc:true
let caml_string_equal =
  Primitive.simple ~name:"caml_string_equal" ~arity:2 ~alloc:false
let caml_bytes_equal =
  Primitive.simple ~name:"caml_bytes_equal" ~arity:2 ~alloc:false
let caml_notequal =
  Primitive.simple ~name:"caml_notequal" ~arity:2 ~alloc:true
let caml_string_notequal =
  Primitive.simple ~name:"caml_string_notequal" ~arity:2 ~alloc:false
let caml_bytes_notequal =
  Primitive.simple ~name:"caml_bytes_notequal" ~arity:2 ~alloc:false
let caml_lessequal =
  Primitive.simple ~name:"caml_lessequal" ~arity:2 ~alloc:true
let caml_string_lessequal =
  Primitive.simple ~name:"caml_string_lessequal" ~arity:2 ~alloc:false
let caml_bytes_lessequal =
  Primitive.simple ~name:"caml_bytes_lessequal" ~arity:2 ~alloc:false
let caml_lessthan =
  Primitive.simple ~name:"caml_lessthan" ~arity:2 ~alloc:true
let caml_string_lessthan =
  Primitive.simple ~name:"caml_string_lessthan" ~arity:2 ~alloc:false
let caml_bytes_lessthan =
  Primitive.simple ~name:"caml_bytes_lessthan" ~arity:2 ~alloc:false
let caml_greaterequal =
  Primitive.simple ~name:"caml_greaterequal" ~arity:2 ~alloc:true
let caml_string_greaterequal =
  Primitive.simple ~name:"caml_string_greaterequal" ~arity:2 ~alloc:false
let caml_bytes_greaterequal =
  Primitive.simple ~name:"caml_bytes_greaterequal" ~arity:2 ~alloc:false
let caml_greaterthan =
  Primitive.simple ~name:"caml_greaterthan" ~arity:2 ~alloc:true
let caml_string_greaterthan =
  Primitive.simple ~name:"caml_string_greaterthan" ~arity:2 ~alloc: false
let caml_bytes_greaterthan =
  Primitive.simple ~name:"caml_bytes_greaterthan" ~arity:2 ~alloc: false
let caml_compare =
  Primitive.simple ~name:"caml_compare" ~arity:2 ~alloc:true
let caml_string_compare =
  Primitive.simple ~name:"caml_string_compare" ~arity:2 ~alloc:false
let caml_bytes_compare =
  Primitive.simple ~name:"caml_bytes_compare" ~arity:2 ~alloc:false

let comparison_primitive comparison comparison_kind =
  match comparison, comparison_kind with
  | Equal, Compare_generic -> Pccall caml_equal
  | Equal, Compare_ints -> Pintcomp Ceq
  | Equal, Compare_floats -> Pfloatcomp CFeq
  | Equal, Compare_strings -> Pccall caml_string_equal
  | Equal, Compare_bytes -> Pccall caml_bytes_equal
  | Equal, Compare_nativeints -> Pbintcomp(Pnativeint, Ceq)
  | Equal, Compare_int32s -> Pbintcomp(Pint32, Ceq)
  | Equal, Compare_int64s -> Pbintcomp(Pint64, Ceq)
  | Not_equal, Compare_generic -> Pccall caml_notequal
  | Not_equal, Compare_ints -> Pintcomp Cne
  | Not_equal, Compare_floats -> Pfloatcomp CFneq
  | Not_equal, Compare_strings -> Pccall caml_string_notequal
  | Not_equal, Compare_bytes -> Pccall caml_bytes_notequal
  | Not_equal, Compare_nativeints -> Pbintcomp(Pnativeint, Cne)
  | Not_equal, Compare_int32s -> Pbintcomp(Pint32, Cne)
  | Not_equal, Compare_int64s -> Pbintcomp(Pint64, Cne)
  | Less_equal, Compare_generic -> Pccall caml_lessequal
  | Less_equal, Compare_ints -> Pintcomp Cle
  | Less_equal, Compare_floats -> Pfloatcomp CFle
  | Less_equal, Compare_strings -> Pccall caml_string_lessequal
  | Less_equal, Compare_bytes -> Pccall caml_bytes_lessequal
  | Less_equal, Compare_nativeints -> Pbintcomp(Pnativeint, Cle)
  | Less_equal, Compare_int32s -> Pbintcomp(Pint32, Cle)
  | Less_equal, Compare_int64s -> Pbintcomp(Pint64, Cle)
  | Less_than, Compare_generic -> Pccall caml_lessthan
  | Less_than, Compare_ints -> Pintcomp Clt
  | Less_than, Compare_floats -> Pfloatcomp CFlt
  | Less_than, Compare_strings -> Pccall caml_string_lessthan
  | Less_than, Compare_bytes -> Pccall caml_bytes_lessthan
  | Less_than, Compare_nativeints -> Pbintcomp(Pnativeint, Clt)
  | Less_than, Compare_int32s -> Pbintcomp(Pint32, Clt)
  | Less_than, Compare_int64s -> Pbintcomp(Pint64, Clt)
  | Greater_equal, Compare_generic -> Pccall caml_greaterequal
  | Greater_equal, Compare_ints -> Pintcomp Cge
  | Greater_equal, Compare_floats -> Pfloatcomp CFge
  | Greater_equal, Compare_strings -> Pccall caml_string_greaterequal
  | Greater_equal, Compare_bytes -> Pccall caml_bytes_greaterequal
  | Greater_equal, Compare_nativeints -> Pbintcomp(Pnativeint, Cge)
  | Greater_equal, Compare_int32s -> Pbintcomp(Pint32, Cge)
  | Greater_equal, Compare_int64s -> Pbintcomp(Pint64, Cge)
  | Greater_than, Compare_generic -> Pccall caml_greaterthan
  | Greater_than, Compare_ints -> Pintcomp Cgt
  | Greater_than, Compare_floats -> Pfloatcomp CFgt
  | Greater_than, Compare_strings -> Pccall caml_string_greaterthan
  | Greater_than, Compare_bytes -> Pccall caml_bytes_greaterthan
  | Greater_than, Compare_nativeints -> Pbintcomp(Pnativeint, Cgt)
  | Greater_than, Compare_int32s -> Pbintcomp(Pint32, Cgt)
  | Greater_than, Compare_int64s -> Pbintcomp(Pint64, Cgt)
  | Compare, Compare_generic -> Pccall caml_compare
  | Compare, Compare_ints -> Pcompare_ints
  | Compare, Compare_floats -> Pcompare_floats
  | Compare, Compare_strings -> Pccall caml_string_compare
  | Compare, Compare_bytes -> Pccall caml_bytes_compare
  | Compare, Compare_nativeints -> Pcompare_bints Pnativeint
  | Compare, Compare_int32s -> Pcompare_bints Pint32
  | Compare, Compare_int64s -> Pcompare_bints Pint64

let lambda_of_loc kind sloc =
  let loc = to_location sloc in
  let loc_start = loc.Location.loc_start in
  let (file, lnum, cnum) = Location.get_pos_info loc_start in
  let file =
    if Filename.is_relative file then
      file
    else
      Location.rewrite_absolute_path file in
  let enum = loc.Location.loc_end.Lexing.pos_cnum -
      loc_start.Lexing.pos_cnum + cnum in
  match kind with
  | Loc_POS ->
    Lconst (Const_block (0, [
          Const_immstring file;
          Const_base (Const_int lnum);
          Const_base (Const_int cnum);
          Const_base (Const_int enum);
        ]))
  | Loc_FILE -> Lconst (Const_immstring file)
  | Loc_MODULE ->
    let filename = Filename.basename file in
    let name = Env.get_unit_name () in
    let module_name = if name = "" then "//"^filename^"//" else name in
    Lconst (Const_immstring module_name)
  | Loc_LOC ->
    let loc = Printf.sprintf "File %S, line %d, characters %d-%d"
        file lnum cnum enum in
    Lconst (Const_immstring loc)
  | Loc_LINE -> Lconst (Const_base (Const_int lnum))
  | Loc_FUNCTION ->
    let scope_name = Debuginfo.Scoped_location.string_of_scoped_location sloc in
    Lconst (Const_immstring scope_name)

let caml_restore_raw_backtrace =
  Primitive.simple ~name:"caml_restore_raw_backtrace" ~arity:2 ~alloc:false

let try_ids = Hashtbl.create 8

let add_exception_ident id =
  Hashtbl.replace try_ids id ()

let remove_exception_ident id =
  Hashtbl.remove try_ids id

let lambda_of_prim prim_name prim loc args arg_exps =
  match prim, args with
  | Primitive (prim, arity), args when arity = List.length args ->
      Lprim(prim, args, loc)
  | External prim, args when prim = prim_sys_argv ->
      Lprim(Pccall prim, Lconst (const_int 0) :: args, loc)
  | External prim, args ->
      Lprim(Pccall prim, args, loc)
  | Comparison(comp, knd), ([_;_] as args) ->
      let prim = comparison_primitive comp knd in
      Lprim(prim, args, loc)
  | Raise kind, [arg] ->
      let kind =
        match kind, arg with
        | Raise_regular, Lvar argv when Hashtbl.mem try_ids argv ->
            Raise_reraise
        | _, _ ->
            kind
      in
      let arg =
        match arg_exps with
        | None -> arg
        | Some [arg_exp] -> event_after loc arg_exp arg
        | Some _ -> assert false
      in
      Lprim(Praise kind, [arg], loc)
  | Raise_with_backtrace, [exn; bt] ->
      let vexn = Ident.create_local "exn" in
      let raise_arg =
        match arg_exps with
        | None -> Lvar vexn
        | Some [exn_exp; _] -> event_after loc exn_exp (Lvar vexn)
        | Some _ -> assert false
      in
      Llet(Strict, Pgenval, vexn, exn,
           Lsequence(Lprim(Pccall caml_restore_raw_backtrace,
                           [Lvar vexn; bt],
                           loc),
                     Lprim(Praise Raise_reraise, [raise_arg], loc)))
  | Lazy_force, [arg] ->
      Matching.inline_lazy_force arg Loc_unknown
  | Loc kind, [] ->
      lambda_of_loc kind loc
  | Loc kind, [arg] ->
      let lam = lambda_of_loc kind loc in
      Lprim(Pmakeblock(0, Immutable, None), [lam; arg], loc)
  | Send, [obj; meth] ->
      Lsend(Public, meth, obj, [], loc)
  | Send_self, [obj; meth] ->
      Lsend(Self, meth, obj, [], loc)
  | Send_cache, [obj; meth; cache; pos] ->
      Lsend(Cached, meth, obj, [cache; pos], loc)
  | (Raise _ | Raise_with_backtrace
    | Lazy_force | Loc _ | Primitive _ | Comparison _
    | Send | Send_self | Send_cache), _ ->
      raise(Error(to_location loc, Wrong_arity_builtin_primitive prim_name))

let check_primitive_arity loc p =
  let prim = lookup_primitive loc p in
  let ok =
    match prim with
    | Primitive (_,arity) -> arity = p.prim_arity
    | External _ -> true
    | Comparison _ -> p.prim_arity = 2
    | Raise _ -> p.prim_arity = 1
    | Raise_with_backtrace -> p.prim_arity = 2
    | Lazy_force -> p.prim_arity = 1
    | Loc _ -> p.prim_arity = 1 || p.prim_arity = 0
    | Send | Send_self -> p.prim_arity = 2
    | Send_cache -> p.prim_arity = 4
  in
  if not ok then raise(Error(loc, Wrong_arity_builtin_primitive p.prim_name))

(* Eta-expand a primitive *)

let transl_primitive loc p env ty path =
  let prim = lookup_primitive_and_mark_used (to_location loc) p env path in
  let has_constant_constructor = false in
  let prim =
    match specialize_primitive env ty ~has_constant_constructor prim with
    | None -> prim
    | Some prim -> prim
  in
  let rec make_params n =
    if n <= 0 then []
    else (Ident.create_local "prim", Pgenval) :: make_params (n-1)
  in
  let params = make_params p.prim_arity in
  let args = List.map (fun (id, _) -> Lvar id) params in
  let body = lambda_of_prim p.prim_name prim loc args None in
  match params with
  | [] -> body
  | _ ->
      Lfunction{ kind = Curried;
                 params;
                 return = Pgenval;
                 attr = default_stub_attribute;
                 loc;
                 body; }

let lambda_primitive_needs_event_after = function
  | Prevapply | Pdirapply (* PR#6920 *)
  (* We add an event after any primitive resulting in a C call that
     may raise an exception or allocate. These are places where we may
     collect the call stack. *)
  | Pduprecord _ | Pccall _ | Pfloatofint | Pnegfloat | Pabsfloat
  | Paddfloat | Psubfloat | Pmulfloat | Pdivfloat | Pstringrefs | Pbytesrefs
  | Pbytessets | Pmakearray (Pgenarray, _) | Pduparray _
  | Parrayrefu (Pgenarray | Pfloatarray) | Parraysetu (Pgenarray | Pfloatarray)
  | Parrayrefs _ | Parraysets _ | Pbintofint _ | Pcvtbint _ | Pnegbint _
  | Paddbint _ | Psubbint _ | Pmulbint _ | Pdivbint _ | Pmodbint _ | Pandbint _
  | Porbint _ | Pxorbint _ | Plslbint _ | Plsrbint _ | Pasrbint _ | Pbintcomp _
  | Pcompare_bints _
  | Pbigarrayref _ | Pbigarrayset _ | Pbigarraydim _ | Pstring_load_16 _
  | Pstring_load_32 _ | Pstring_load_64 _ | Pbytes_load_16 _ | Pbytes_load_32 _
  | Pbytes_load_64 _ | Pbytes_set_16 _ | Pbytes_set_32 _ | Pbytes_set_64 _
  | Pbigstring_load_16 _ | Pbigstring_load_32 _ | Pbigstring_load_64 _
  | Pbigstring_set_16 _ | Pbigstring_set_32 _ | Pbigstring_set_64 _
  | Prunstack | Pperform | Preperform | Presume | Ppoll
  | Pbbswap _ -> true

  | Pidentity | Pbytes_to_string | Pbytes_of_string | Pignore | Psetglobal _
  | Pgetglobal _ | Pmakeblock _ | Pfield _ | Pfield_computed | Psetfield _
  | Psetfield_computed _ | Pfloatfield _ | Psetfloatfield _ | Praise _
  | Psequor | Psequand | Pnot | Pnegint | Paddint | Psubint | Pmulint
  | Pdivint _ | Pmodint _ | Pandint | Porint | Pxorint | Plslint | Plsrint
  | Pasrint | Pintcomp _ | Poffsetint _ | Poffsetref _ | Pintoffloat
  | Pcompare_ints | Pcompare_floats
  | Pfloatcomp _ | Pstringlength | Pstringrefu | Pbyteslength | Pbytesrefu
  | Pbytessetu | Pmakearray ((Pintarray | Paddrarray | Pfloatarray), _)
  | Parraylength _ | Parrayrefu _ | Parraysetu _ | Pisint | Pisout
  | Patomic_exchange | Patomic_cas | Patomic_fetch_add | Patomic_load _ | Pnop
  | Pintofbint _ | Pctconst _ | Pbswap16 | Pint_as_pointer | Popaque -> false

(* Determine if a primitive should be surrounded by an "after" debug event *)
let primitive_needs_event_after = function
  | Primitive (prim,_) -> lambda_primitive_needs_event_after prim
  | External _ -> true
  | Comparison(comp, knd) ->
      lambda_primitive_needs_event_after (comparison_primitive comp knd)
  | Lazy_force | Send | Send_self | Send_cache -> true
  | Raise _ | Raise_with_backtrace | Loc _ -> false

let transl_primitive_application loc p env ty path exp args arg_exps =
  let prim =
    lookup_primitive_and_mark_used (to_location loc) p env (Some path) in
  let has_constant_constructor =
    match arg_exps with
    | [_; {exp_desc = Texp_construct(_, {cstr_tag = Cstr_constant _}, _)}]
    | [{exp_desc = Texp_construct(_, {cstr_tag = Cstr_constant _}, _)}; _]
    | [_; {exp_desc = Texp_variant(_, None)}]
    | [{exp_desc = Texp_variant(_, None)}; _] -> true
    | _ -> false
  in
  let prim =
    match specialize_primitive env ty ~has_constant_constructor prim with
    | None -> prim
    | Some prim -> prim
  in
  let lam = lambda_of_prim p.prim_name prim loc args (Some arg_exps) in
  let lam =
    if primitive_needs_event_after prim then begin
      match exp with
      | None -> lam
      | Some exp -> event_after loc exp lam
    end else begin
      lam
    end
  in
  lam

(* Error report *)

open Format

let report_error ppf = function
  | Unknown_builtin_primitive prim_name ->
      fprintf ppf "Unknown builtin primitive \"%s\"" prim_name
  | Wrong_arity_builtin_primitive prim_name ->
      fprintf ppf "Wrong arity for builtin primitive \"%s\"" prim_name

let () =
  Location.register_error_of_exn
    (function
      | Error (loc, err) ->
          Some (Location.error_of_printer ~loc report_error err)
      | _ ->
        None
    )