This repository was archived by the owner on Dec 3, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcodegen.ml
More file actions
1787 lines (1728 loc) · 84 KB
/
Copy pathcodegen.ml
File metadata and controls
1787 lines (1728 loc) · 84 KB
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
(** Code generation module *)
open Common
open Ast
open Types
open Symtable1
open Llvm
exception CodegenError of string
(* Trying not to make a new context per module. OK so far. *)
let context = global_context()
let float_type = double_type context
let int_type = i32_type (* i64_type *) context
let int64_type = i64_type context
let bool_type = i1_type context
let byte_type = i8_type context
let void_type = void_type context
(* LLVM doesn't like pointers to void, use this for polymorphic ptrs *)
let voidptr_type = pointer_type (i8_type context)
let nulltag_type = i8_type context
let varianttag_type = i32_type context
let is_pointer_value llval = classify_type (type_of llval) = TypeKind.Pointer
let is_pointer_type llval = classify_type llval = TypeKind.Pointer
(* Implement comparison for typetag so we can make a map of them. *)
(* Note: haven't done this yet, still using the string pair for basetype *)
module TypeTag = struct
type t = typetag
let compare t1 t2 =
String.compare (typetag_to_string t1) (typetag_to_string t2)
end
module TtagMap = Map.Make(TypeTag)
(** type environment to store lltypes and field/tag offsets for named
classes. Entries built by add_lltype. Used a lot by ttag_to_lltype *)
module Lltenv = struct
(* fieldmap is used for both struct offsets and union tags. *)
(* ...could it skip the typetags and get lltype directly??? Maybe only if
the lltype is fully specified. *)
type fieldmap = (int * typetag) StrMap.t
(* Maps modulename, typename pair to the LLVM type and field map. *)
type lltentry = {
lltype: lltype;
fieldmap: fieldmap;
tvslots: int list list array
}
type t = (* (lltype * fieldmap) *) lltentry PairMap.t
let empty: t = PairMap.empty
let add = PairMap.add
(* Can I have a function to take a typetag and pull in_module and
classname out of that, so I don't have to awkwardly pass pairs?
Yes, could, but REMEMBER: the LLtenv is only used for looking up
*classes*. Array and nullable types are generated at the point
of use. *)
(* Return just the LLVM type for a given type name. *)
let find = PairMap.find
let find_opt = PairMap.find_opt
let find_lltype tkey tmap = (PairMap.find tkey tmap).lltype
(* Look up the base typename for a class. *)
let find_lltype_opt tkey tmap =
Option.map (fun ent -> ent.lltype) (PairMap.find_opt tkey tmap)
let find_class_lltype tclass tmap =
(PairMap.find (tclass.in_module, tclass.classname) tmap).lltype
(* Get the mapping of fields to types for a struct type. *)
let find_class_fieldmap tclass tmap =
(PairMap.find (tclass.in_module, tclass.classname) tmap).fieldmap
(* Get the offset of a record field from a type's field map. *)
let find_field tkey fieldname tmap =
let ent = PairMap.find tkey tmap in
StrMap.find fieldname ent.fieldmap
end
(** Process a classData to generate a new llvm type entry for lltenv *)
let rec add_lltype the_module (* returns (classdata, Lltenv.fieldmap) *)
(types: classData PairMap.t) (lltypes: Lltenv.t) layout (cdata: classData)
=
match Lltenv.find_opt (cdata.in_module, cdata.classname) lltypes with
(* TESTME: don't think this can happen; make it trigger an error and test. *)
| Some ent -> ent
| None -> (
debug_print ("generating lltype for " ^ cdata.classname);
let primtype_entry lltype : Lltenv.lltentry =
{ lltype=lltype; fieldmap=StrMap.empty; tvslots=[||] } in
match (cdata.in_module, cdata.classname) with
(* need special case for primitive types. Should handle this with some
* data structure, so it's consistent among modules *)
(* These are only hit when we recurse, so maybe we shouldn't? *)
| ("", "Void") -> primtype_entry void_type
| ("", "Int") -> primtype_entry int_type
| ("", "Float") -> primtype_entry float_type
| ("", "Byte") -> primtype_entry byte_type
| ("", "Bool") -> primtype_entry bool_type
| ("", "String") -> primtype_entry (pointer_type (i8_type context))
| ("", "NullType") -> primtype_entry nulltag_type (* causes crash? *)
| _ ->
(* Process non-primitive type.
1. create named struct lltype to fill in later *)
let context = module_context the_module in
(* guess we always use the qualified type name in llvm *)
let typename = cdata.in_module ^ "::" ^ cdata.classname in
let llstructtype =
match type_by_name the_module typename with
| None -> named_struct_type context typename
| Some llty -> llty (* how could it already exist? See above also *)
in
(* Create map from type variables to the "slots" they appear in
(will be converted to an array when done) *)
(* TODO: with tuples, each may be a pair. Arrays different too *)
let slotmap: (string * int list list) array = Array.init
(List.length cdata.tparams)
(fun i -> (List.nth cdata.tparams i, [])) in
(* Pull out fields; same for both struct and variant types *)
let fielddata =
match cdata.kindData with
| Struct fields ->
(* mutability info about struct fields not needed for codegen, so we
filter it out. *)
List.map (fun fi -> (fi.fieldname, [fi.fieldtype])) fields
| Variant vts -> vts (* variants are already a list for each variant *)
| _ -> []
in
(* 2. generate list of (name, lltype, offset, type) for fields *)
let ftypeinfo = List.mapi (fun i (fieldname, ftys) ->
(* Inner loop because variants can have a tuple of types *)
let flltys = List.map (fun fttag ->
match fttag with
| Typevar tv ->
(* Append a new entry to the slots list for this tv *)
let tvix, (_, prevmap) = array_find_index_ex slotmap
(fun (v, _) -> v = tv) in
Array.set slotmap tvix (tv, prevmap @ [[i]]); (* needs j too for tuples *)
(voidptr_type, Typevar tv)
| Namedtype tinfo ->
let mname, tname =
tinfo.tclass.in_module, tinfo.tclass.classname in
let fllty = match Lltenv.find_opt (mname, tname) lltypes with
| Some fent ->
debug_print ("#CG add_lltype: existing type for field "
^ string_of_lltype fent.lltype);
(* If this field takes type args, for each one take its slots
list and add the entries to the parent slot list. *)
List.iteri (fun fi fttag -> match fttag with
(* Look up field typearg (which should be a variable)
in this's slotmap *)
| Namedtype _ ->
failwith "type arg in class supposed to be a typevar"
| Typevar ftv ->
(* get index and slots of typevar in parent type. *)
let tvix, (_, prevmap) = array_find_index_ex slotmap
(fun (v, _) -> v = ftv) in
(* get the field's slotmap for that type param's position *)
let fslots = Array.get fent.tvslots fi in
(* append slot address to parent field number for each *)
let pfslots = List.map (fun faddr -> i::faddr) fslots in
Array.set slotmap tvix (ftv, prevmap @ pfslots)
) tinfo.typeargs;
fent.lltype
(* If the field's lltype isn't generated yet, either recurse
or, if it's a recursive type, fetch the named type *)
| None ->
if tinfo.tclass.rectype then
let ftypename = mname ^ "::" ^ tname in
match type_by_name the_module ftypename with
| Some llfieldtype -> pointer_type llfieldtype
| None ->
(* if it's an external rectype, add it as normal *)
if mname = cdata.in_module then (
debug_print ("#CG: recursive field type " ^ ftypename
^ " not found, adding named struct lltype.");
pointer_type (named_struct_type context ftypename)
)
else
(* mname not included because local types have no
module prefix in the tenv. Think is is the right way *)
(add_lltype the_module types lltypes layout
(PairMap.find ("", tname) types)).lltype
else
(add_lltype the_module types lltypes layout
(PairMap.find ("", tname) types)).lltype
in
(* check for non-base types and add them if needed. *)
(* currently allows array of nullable but no nullable array *)
let fllty =
if is_option_type fttag then
struct_type context [| nulltag_type; fllty |]
else fllty in
(fllty, fttag)
) ftys
in
match flltys with
(* Field has no typetag only for variant types *)
| [] -> (fieldname, void_type, i, void_ttag)
| [(llty, ttag)] -> (fieldname, llty, i, ttag)
| flltypes -> (* make struct type for variant tuple *)
let llvtup = struct_type context
(Array.of_list (List.map fst flltypes)) in
(* wishful thinking: that I don't need the ttags for
each type in the variant tuple. *)
(fieldname, llvtup, i, void_ttag) (* TESTME: doing without tag. *)
(* Create array type for the field if needed. *)
(* TESTME: not reached because Array is now a class.
Other array types created in ttag_to_lltype.
test if we need this. *)
(*let fieldlltype =
if is_array_type fty then
struct_type context
[| int_type; pointer_type (array_type ty1 0) |]
else ty1 in
(fieldname, fieldlltype, i, fty)) *)
) fielddata
in
(* 3. Create the mapping from field names to offset and type. *)
(* do we still need the high-level type? maybe for lookup info. *)
let fieldmap =
List.fold_left (fun fomap (fname, _, i, ftype) ->
StrMap.add fname (i, ftype) fomap
) StrMap.empty ftypeinfo in
match cdata.kindData with
(* 4. generate the llvm named struct type *)
(* Fields have already been generated, but may want to split it out
into a more sensible separate function for each kind. *)
| Struct _ ->
struct_set_body llstructtype
(List.map (fun (_, lty, _, _) -> lty) ftypeinfo
|> Array.of_list) false; (* false: don't use packed structs. *)
debug_print ("generated struct type body: "
^ string_of_lltype llstructtype);
if cdata.rectype then
(* recursive types are reference types *)
{ lltype=(pointer_type llstructtype); fieldmap=fieldmap;
tvslots=Array.map snd slotmap }
else
{ lltype=llstructtype; fieldmap=fieldmap;
tvslots=Array.map snd slotmap }
(* Variant case: struct of tag + optional byte array for the union *)
| Variant _ ->
(* Compute max size of any of the variant subtypes. *)
let maxsize =
List.fold_left (fun max llvarty ->
let typesize =
if llvarty = void_type then Int64.of_int 0
else Llvm_target.DataLayout.abi_size llvarty layout
in if typesize > max then typesize else max
)
(Int64.of_int 0)
(List.map (fun (_, llvarty, _, _) -> llvarty) ftypeinfo)
in
debug_print (cdata.classname ^ " max variant size: "
^ Int64.to_string maxsize);
(* compute two-field struct type (tag and data value) *)
(* TODO: optimize to have just the tag (enum) if max size is zero. *)
(* let vstructtype = named_struct_type context typename in *)
struct_set_body llstructtype
(if maxsize = Int64.zero then
[| varianttag_type |]
else
[| varianttag_type;
(* Voodoo magic: adding 4 bytes fixes my double problem. *)
array_type (i8_type context) (Int64.to_int maxsize + 4) |])
false;
if cdata.rectype then
{ lltype=(pointer_type llstructtype); fieldmap=fieldmap;
tvslots=Array.map snd slotmap }
else
{ lltype=llstructtype; fieldmap=fieldmap;
tvslots=Array.map snd slotmap }
(* FIXME: these cases should go above, I think. *)
| Hidden ->
(* Unknown implementation, must be treated as void pointer *)
primtype_entry voidptr_type
| _ -> (* TODO: opaque type and newtype *)
(* Now it's an opaque type, but I really don't want to assume.
need to put an opaque marker in classData?
Maybe go with a kind variant instead of just lists that can be empty? *)
failwith ("BUG: missing codegen for class type " ^ cdata.classname)
)
(** change all locations of a type variable in an llvm struct type to a
specified (pointer) type. *)
let specify_gtype (llentry: Lltenv.lltentry) tvindex conctype =
(* TODO: arrays will work differently. *)
let tvslots = Array.get llentry.tvslots tvindex in
let newtype = List.fold_left (fun llty tvslot ->
let rec digloop stty slot =
match slot with
| [] -> stty (* only hit if no replacement *)
| ix::[] ->
let starr = struct_element_types llty in
(Array.set starr ix conctype;
struct_set_body stty starr false;
stty)
| ix::next::rest ->
let starr = struct_element_types stty in
(Array.set starr ix (digloop (Array.get starr ix) (next::rest));
struct_set_body stty starr false;
stty)
in digloop llty tvslot
) llentry.lltype tvslots
in newtype (* Don't need an entry for on-the-fly types. Symtable? *)
(** Use a type tag to generate the LLVM type, adding to the base type
AND updating generic pointer types with specific ones if there. *)
let rec ttag_to_lltype lltypes ty = match ty with
| Typevar _ ->
voidptr_type
| Namedtype tinfo -> (
(* Arrays and Options are just classes now, but codegen is specific for them. *)
(* For arrays, create struct of size and storage pointer *)
if is_array_type ty then
let elttype = ttag_to_lltype lltypes (array_element_type ty) in
struct_type context
(* get inner type from class params now! *)
[| int_type; pointer_type (array_type elttype 0) |]
(* for option, use a (possibly smaller) tag with a struct *)
else if is_option_type ty then
let basetype = ttag_to_lltype lltypes (option_base_type ty) in
struct_type context [| nulltag_type; basetype |]
else
match Lltenv.find_opt
(tinfo.tclass.in_module, tinfo.tclass.classname) lltypes with
| None -> failwith ("BUG: no lltype found for "
^ tinfo.tclass.in_module ^ "::"
^ tinfo.tclass.classname)
(* Get the lltype for each typearg and replace the voidptr with it. *)
| Some ent -> List.fold_left (fun llty (i, targ) ->
match targ with
| Typevar _ -> llty
(* entry is the same for every loop? Yeah, it's just a different
type variable that's being replaced in the lltype. *)
| Namedtype _ -> specify_gtype ent i (ttag_to_lltype lltypes targ)
) ent.lltype (List.mapi (fun i a -> (i, a)) tinfo.typeargs)
)
(** Wrap a value in an outer type. Used for assigning, passing or
returning a value for a nullable (Option) type *)
let promote_value the_val outertype builder =
debug_print ("#CG: promote_value " ^ string_of_lltype (type_of the_val)
^ " to type " ^ string_of_lltype outertype);
(* so far, promotion only to nullable. *)
(* should put in a new way to check since it's using the lltype now? *)
(* if not (outertype.nullable) then
failwith "BUG: can only promote value to nullable type for now"
else *)
(* Note that this does allocate the struct type. *)
let alloca = build_alloca outertype "promotedaddr" builder in
let tagval =
(* It seems I can get away with checking just the LLVM type here. *)
if is_null the_val then const_int nulltag_type 0
else const_int nulltag_type 1 in
let tagaddr = build_struct_gep alloca 0 "tagaddr" builder in
ignore (build_store tagval tagaddr builder);
(* Only build a store for the value if it's not null. *)
if not (is_null the_val) then (
let valaddr = build_struct_gep alloca 1 "valaddr" builder in
ignore (build_store the_val valaddr builder));
(* Now reload the whole thing for the result. *)
build_load alloca "promotedval" builder
(** Generate a garbage collected array alloca *)
let build_gc_array_malloc eltType llsize name the_module builder =
match lookup_function "GC_malloc" the_module with
| None -> failwith "BUG: GC_malloc llvm function not found"
| Some llmalloc ->
let datasize = build_mul llsize (size_of eltType) "malloc_size" builder in
let dataptr = build_call llmalloc [|datasize|] "mallocbytes" builder in
build_bitcast dataptr (pointer_type eltType) name builder
let build_gc_malloc eltType name the_module builder =
match lookup_function "GC_malloc" the_module with
| None -> failwith "BUG: GC_malloc llvm function not found"
| Some llmalloc ->
let dataptr = build_call llmalloc [|size_of eltType|] "mallocbytes" builder
in
build_bitcast dataptr (pointer_type eltType) name builder
(** Rewritten equality comparison function for any fixed-size concrete type. *)
let gen_eqcompare val1 val2 valty (* lltypes *) _ builder =
(* make true and false result blocks at the beginning, for short-circuiting. *)
let the_function = block_parent (insertion_block builder) in
(* let true_bb = append_block context "eqcomp_true" the_function in *)
let false_bb = append_block context "eqcomp_false" the_function in
let true_bb = insert_block context "eqcomp_true" false_bb in
(* TESTME: builder's insertion point is still before the new blocks? *)
let rec cmp_walk val1 val2 valty done_bb =
(* First, check indirection and possible pointer equality. *)
(* A fixed-size type will never be pointed more than once, right? *)
let val1, val2 = match classify_type (type_of val1),
classify_type (type_of val2) with
| TypeKind.Pointer, TypeKind.Pointer ->
(* if both pointers and the same, we can stop. *)
let pointereq = build_icmp Icmp.Eq val1 val2 "pointereq" builder in
(* builder won't move to the inserted block, will it? *)
let next_bb = insert_block context "eqcomp_next" done_bb in
(* branch to true or next *)
ignore (build_cond_br pointereq done_bb next_bb builder);
(* move builder to start of next block *)
position_at_end next_bb builder;
(* loads for new val1 and val2 *)
(build_load val1 "val1load" builder,
build_load val2 "val2load" builder)
| TypeKind.Pointer, _ ->
(build_load val1 "val1load" builder, val2)
| _, TypeKind.Pointer ->
(val1, build_load val2 "val2load" builder)
| _, _ -> (val1, val2)
in
(* let cmp_res = *) match classify_type (type_of val1) with
| TypeKind.Integer ->
let res = build_icmp Icmp.Eq val1 val2 "int_eq" builder in
ignore (build_cond_br res done_bb false_bb builder);
| TypeKind.Double ->
let res = build_fcmp Fcmp.Oeq val1 val2 "float_eq" builder in
ignore (build_cond_br res done_bb false_bb builder);
| _ ->
if is_struct_type valty then
let fields = get_struct_fields valty in
let rec fields_loop i =
(* No extractvalue needed if I just load from the gep pointer? *)
(* let field1ref = build_struct_gep val1 i "f1ptr" builder in
let field2ref = build_struct_gep val2 i "f2ptr" builder in *)
(* but will it be double-pointed if a pointer?
Trying just extracting always *)
let fval1 = build_extractvalue val1 i "fval1" builder in
let fval2 = build_extractvalue val2 i "fval2" builder in
let fieldtype = (List.nth fields i).fieldtype in
let next_bb = if i < List.length fields - 1
then insert_block context "fieldcmp" done_bb
else done_bb in
cmp_walk fval1 fval2 fieldtype next_bb;
if i < List.length fields - 1 then (
position_at_end next_bb builder; (* no-op if at end *)
fields_loop (i+1)
)
in fields_loop 0
else if is_variant_type valty then
(* Could check the first field regardless, then if it's a variant type
insert the cast, then compare as usual! *)
(* For tuples in variants, may be two different struct types.
Is it enough just to compare the tag and know other code will
never be reached? Oh, may have to cast it! Not a generics issue! *)
(* The issue is, will we ever be loading tuple types that we don't know
the type of? Wait, even if the tags match, how do I load? *)
failwith "lemme get back to ya"
else failwith ("Unexpected type in comparison: "
^ typetag_to_string valty ^ ", "
^ string_of_lltype (type_of val1) ^ ", "
^ string_of_lltype (type_of val2))
in
cmp_walk val1 val2 valty true_bb;
let phi_block = append_block context "eqcomp_phi" the_function in
position_at_end true_bb builder;
ignore (build_br phi_block builder);
position_at_end false_bb builder;
ignore (build_br phi_block builder);
position_at_end phi_block builder;
let phi = build_phi [(const_int bool_type 1, true_bb);
(const_int bool_type 0, false_bb)] "eq_phi" builder in
debug_print (string_of_llvalue the_function);
phi
(*
(** Generate an equality comparison. This could get complex. *)
let rec gen_eqcomp val1 val2 valty lltypes builder =
match valty with
| Typevar _ ->
failwith "!BUG (codegen): can't compare generic types for equality"
| Namedtype _ ->
if is_struct_type valty then
let fields = get_struct_fields valty in
let rec checkloop i prevcmp =
(* generate next field compare value, generate AND with previous *)
(* later: optimize to not need to generate a const starting value *)
if i = List.length fields then
prevcmp
else
(* get the pointer. assume structs are pointers? *)
let field1val =
(* I'll have a separate comp for any pointer type, but is this more
* efficient for a pointer to a struct? *)
if is_pointer_value val1 then
let field1ptr = build_struct_gep val1 i "f1ptr" builder in
build_load field1ptr "f1val" builder
else
build_extractvalue val1 i "f1val" builder in
let field2val =
if is_pointer_value val2 then
let field1ptr = build_struct_gep val2 i "f2ptr" builder in
build_load field1ptr "f2val" builder
else
build_extractvalue val2 i "f2val" builder in
(* might be good to make "fields" an array *)
let fieldtype = (List.nth fields i).fieldtype in
let cmpval = gen_eqcomp field1val field2val fieldtype lltypes builder in
(* Could using branches so we can short-circuit be faster? *)
let andval = build_and prevcmp cmpval "cmpand" builder in
checkloop (i+1) andval
in
checkloop 0 (const_int bool_type 1) (* starter true value *)
else if is_variant_type valty then (
let variants = get_type_variants valty in
(* check tag, then load and cast the value tuple if it exists and
generate comparison for that. *)
let var1tag =
if is_pointer_value val1 then
let tagPtr = build_struct_gep val1 0 "tag1ptr" builder in
build_load tagPtr "tag1val" builder
else
build_extractvalue val1 0 "tag1val" builder in
let var2tag =
if is_pointer_value val2 then
let tagPtr = build_struct_gep val2 0 "tag2ptr" builder in
build_load tagPtr "tag2val" builder
else
build_extractvalue val2 0 "tag2val" builder in
if Array.length (struct_element_types (type_of val1)) == 1 then
build_icmp Icmp.Eq var1tag var2tag "tagcomp" builder
else
(debug_print "variant has values in it, building out compare";
let start_bb = insertion_block builder in
let parent_function = block_parent start_bb in
(* first "then", if the tag doesn't match *)
(* let first_then = append_block context "then" parent_function in *)
let ncases = List.length variants in
let rec genblocks i =
if i == 0 then []
else
let condblock = append_block context "tagcond" parent_function in
position_at_end condblock builder; (* needed? *)
let thenblock =
append_block context ("tagthen_" ^ Int.to_string i) parent_function in
position_at_end thenblock builder;
condblock :: thenblock :: genblocks (i-1)
in
let caseblocks = genblocks ncases in
let cont_block = append_block context "caseeq_cont" parent_function in
debug_print ("Generated blocks for " ^ Int.to_string ncases ^ " cases.");
(* avoid nesting: "then" case is if they are unequal, second if it's 0, *)
position_at_end start_bb builder;
let tag_eq = build_icmp Icmp.Eq var1tag var2tag "tag_eq" builder in
ignore (build_cond_br tag_eq (List.hd caseblocks) cont_block builder);
(* check which variant it is and branch to comparison for that type. *)
let rec gen_caseblocks caseval blocks =
match blocks with
| [] -> []
| cond_bb :: then_bb :: rest -> (
position_at_end cond_bb builder;
(* Oh, it tests in each block whether to go on or match the next *)
let condval =
build_icmp Icmp.Eq (const_int varianttag_type caseval) var1tag
("tagcmp_" ^ Int.to_string caseval) builder in
let next_bb = match rest with
| [] -> cont_block
| next_bb :: _ -> next_bb in
ignore (build_cond_br condval then_bb next_bb builder);
position_at_end then_bb builder;
(* cast, then compare *)
let variant = List.nth variants caseval in
let compval, then_end_bb =
match variant with
| (_, []) ->
debug_print ("#CG-eqcomp: no value attached to this case");
(const_int bool_type 1, then_bb)
| (_, vttags) ->
debug_print "#CG-eqcomp: Variant has value(s), generating compare";
List.iter (fun varty -> ) vttags;
let llvarty = ttag_to_lltype lltypes varty in
(* generate the pointer to the variant's value with cast *)
let gen_varval_ptr theval =
let varptr =
if is_pointer_value theval then
build_struct_gep theval 1 "varptr" builder
else
(* Easiest way is just to store so we can cast a pointer *)
let valalloca =
build_alloca (ttag_to_lltype lltypes valty)
"varstruct" builder in
ignore (build_store theval valalloca builder);
build_struct_gep valalloca 1 "varptr" builder
in
build_bitcast varptr (pointer_type llvarty)
"typedvarp" builder
in
let varval1 = gen_varval_ptr val1 in
let varval2 = gen_varval_ptr val2 in
let compval = gen_eqcomp varval1 varval2 varty lltypes builder in
let then_end_bb = insertion_block builder in
(compval, then_end_bb)
in
position_at_end then_end_bb builder;
ignore (build_br cont_block builder);
debug_print ("Generated then block for case " ^ Int.to_string caseval);
(* The condition may jump to the merge block also; add to the phi list *)
if next_bb == cont_block then
(condval, cond_bb)
:: (compval, then_end_bb) :: gen_caseblocks (caseval+1) rest
else
(compval, then_end_bb) :: gen_caseblocks (caseval+1) rest)
| _ :: [] ->
failwith "BUG: odd number of case blocks"
in (* end gen_caseblocks *)
let phiList =
(tag_eq, start_bb)
:: gen_caseblocks 0 caseblocks in
(* Yay, I get to make a phi! The phi is of all the compare results. *)
position_at_end cont_block builder;
debug_print "#CG: building phi value";
(* I wonder if the error is a bug in if-then? *)
let phi = build_phi phiList "finalcmp" builder in
phi
)) (* end of variant type equality code *)
else (* primitive type comparisons. check for pointer *)
let val1 = if is_pointer_value val1 then
build_load val1 "val1" builder
else val1 in
let val2 = if is_pointer_value val2 then
build_load val2 "val2" builder
else val2 in
if (type_of val1) = int_type || (type_of val1) = byte_type then
build_icmp Icmp.Eq val1 val2 "eqcomp" builder
else if (type_of val2) = float_type then
build_fcmp Fcmp.Oeq val1 val2 "eqcomp" builder
else
(* for records, could I just dereference if needed and compare the
* array directly? Don't think so in LLVM, that's a vector op. *)
failwith ("Equality for type " ^ typetag_to_string valty
^ ": " ^ string_of_lltype (type_of val1) ^ " not supported yet")
*)
(** Generate value of a constant expression. Currenly used for global var
initializer and case branches *)
let rec gen_constexpr_value lltypes (ex: typetag expr) =
(* How many types will this support? Might need a tenv later *)
if ex.decor = int_ttag then
match ex.e with
| ExpLiteral (IntVal n) -> const_of_int64 int_type n true
| ExpUnop (OpNeg, e) -> const_neg (gen_constexpr_value lltypes e)
| ExpBinop (e1, op, e2) -> (
let c1 = gen_constexpr_value lltypes e1 in
let c2 = gen_constexpr_value lltypes e2 in
match op with (* TODO: make a map for these so don't need the long switch *)
| OpPlus -> const_add c1 c2
| OpMinus -> const_sub c1 c2
| OpTimes -> const_mul c1 c2
| OpDiv -> const_sdiv c1 c2
| _ -> failwith "Unimplemented Int const binop"
)
| _ -> failwith "Unimplemented Int const expression"
else if ex.decor = float_ttag then
match ex.e with
| ExpLiteral (FloatVal x) -> const_float float_type x
| ExpUnop (OpNeg, e) -> const_fneg (gen_constexpr_value lltypes e)
| _ -> failwith "Unimplemented Float const expression"
else if ex.decor = byte_ttag then
match ex.e with
| ExpLiteral (ByteVal c) -> const_int byte_type (int_of_char c)
| _ -> failwith "Unsupported Byte const expression"
else if ex.decor = bool_ttag then
match ex.e with
| ExpLiteral (BoolVal b) -> const_int bool_type (if b then 1 else 0)
| _ -> failwith "Unsupported Bool const expression"
else
(* struct type *)
match ex.e with
| ExpRecord fieldlist ->
(* Iterate over the fields and write the value in an llvalue array *)
let lltype = Lltenv.find_class_lltype (get_type_class ex.decor)
lltypes in
let fieldmap = Lltenv.find_class_fieldmap (get_type_class ex.decor)
lltypes in
let valarray = Array.make (List.length fieldlist)
(const_int int_type 0) in
List.iter (fun (fname, fexp) ->
let (offset, _) = StrMap.find fname fieldmap in
let fieldval = gen_constexpr_value lltypes fexp in
Array.set valarray offset fieldval
) fieldlist;
const_named_struct lltype valarray
| _ -> failwith "Unimplemented constexpr type"
(** Emit array bounds-checking instructions *)
let gen_bounds_check ixval arraysize the_module builder =
(* Do the compares to zero and the array size *)
let zerocheck = build_icmp Icmp.Slt
ixval (const_int int_type 0) "zerobound" builder in
let sizecheck = build_icmp Icmp.Sge
ixval arraysize "sizebound" builder in
let checkres = build_or zerocheck sizecheck "boundcmp" builder in
(* add all jump targets at once (seems cleaner that way) *)
let cond_spot = insertion_block builder in
let this_function = block_parent cond_spot in
let failblock = append_block context "boundsfail" this_function in
let okblock = append_block context "boundsok" this_function in
let contblock = append_block context "boundscont" this_function in
(* move back and insert the conditional jump *)
position_at_end cond_spot builder;
build_cond_br checkres failblock okblock builder |> ignore;
(* build the fail block *)
position_at_end failblock builder;
match lookup_function "exit" the_module with
| None -> failwith "BUG: could not find exit function"
| Some exitfunc -> (
(* Made up failure exit code for array OOB *)
build_call exitfunc [|const_int int_type 111|] "" builder
|> ignore;
build_br contblock builder |> ignore;
(* build the OK block with just a jump to the continuation *)
position_at_end okblock builder;
build_br contblock builder |> ignore;
position_at_end contblock builder
)
(** Find the target address of a varexp from symtable entry and
field type information *)
let rec get_varexp_alloca the_module builder varexp syms lltypes =
let ((varname, ixs), fields) = varexp in
debug_print ("#CG: get_varexp_alloca for varexp under var " ^ varname);
let (entry, _) = Symtable.findvar varname syms in
match entry.addr with
| None -> failwith ("BUG: get_varexp_alloca: alloca address not present for "
^ entry.symname)
| Some alloca ->
(* traverse indices and record fields to generate the final alloca. *)
let rec field_allocas flds ixs (parentty: typetag) alloca =
debug_print ("#CG: field_allocas with parent type: "
^ typetag_to_string parentty);
(* 1. if there are array index expressions [], then load and
strip off array type *)
let rec ix_allocas prevalloca parentty ixs =
match ixs with
| [] -> (prevalloca, parentty)
| ixexpr :: rest ->
debug_print ("#CG: computing index expression into "
^ string_of_llvalue prevalloca);
let ixval = gen_expr the_module builder syms lltypes ixexpr in
(* get the array at index 1. alloca is the address of the struct. *)
let arraydata = build_struct_gep prevalloca 1 "arraydata" builder in
(* have to load to get the actual pointer to the llvm array *)
let dataptr = build_load arraydata "dataptr" builder in
(* Load the array size to do the bounds check. *)
let arraysize = build_load
(build_struct_gep prevalloca 0 "sizeptr" builder)
"arraysize" builder
in
gen_bounds_check ixval arraysize the_module builder;
(* gep to the 0th element first to "follow the pointer" *)
let newalloca, newty =
(build_gep dataptr [|(const_int int_type 0); ixval|]
"elementtptr" builder,
(array_element_type parentty)) in
debug_print ("#CG: index expr alloca: " ^ string_of_llvalue newalloca
^ "\n type: " ^ typetag_to_string newty);
ix_allocas newalloca newty rest
in
let (alloca, parentty) = ix_allocas alloca parentty ixs in (* newty) = *)
(* 2. get the field offset if there is one. *)
match flds with
| [] -> (alloca, parentty)
| (fld, ixopt)::rest ->
debug_print ("#CG: computing field offset in type: "
^ typetag_to_string parentty);
(* Get just the class of parent type so we can find its field info.
Analysis determined it's not a nullable. *)
(* Would we ever need to get the alloca for a generic (just
the pointer)? *)
let ptypekey = (get_type_modulename parentty,
get_type_classname parentty) in
(* If it's the built-in array.length, it has no further fields,
we're done. *)
(* the test for = "length" should be redundant. *)
if (is_array_type parentty) && fld = "length" then
(build_struct_gep alloca 0 "length" builder, int_ttag)
else (
(* check if pointer load needed first, for recursive type. *)
(* May need to generalize this for generic types too *)
(* it's always a pointer, don't need to load? *)
let alloca = if
(* is_pointer_type (element_type (type_of alloca)) *)
is_recursive_type parentty
then
build_load alloca "pointed-deref" builder
else alloca in
(* Look up field offset in Lltenv, emit gep *)
let offset, fieldtype = Lltenv.find_field ptypekey fld lltypes in
let alloca = build_struct_gep alloca offset "field" builder in
debug_print ("get_varexp_alloca: generated struct gep for '" ^ fld
^ "' for type " ^ string_of_lltype (type_of alloca));
(* Get more specific type for field if generic; it becomes the
parent type for the next iteration *)
let fieldtype, alloca =
match fieldtype with
| Namedtype _ -> (fieldtype, alloca)
| Typevar tv ->
(* Cast generic pointer to specific type pointer *)
let fieldtype = specify_typevar parentty tv in
debug_print ("#CG get_varexp_alloca: Specified field type to "
^ typetag_to_string fieldtype);
(* I guess I'm not adding an extra indirection if it's a pointer
type already, so only load if it's not. *)
(* Don't know if that was the right decision, wherever it
was made. *)
let spec_lltype = ttag_to_lltype lltypes fieldtype in
let alloca =
if not (is_pointer_type spec_lltype) then
build_load alloca "generic-load" builder
else alloca in
let alloca =
build_bitcast alloca (pointer_type spec_lltype)
"generic_cast" builder in
(fieldtype, alloca)
in
debug_print "#CG get_varexp_alloca: recursing";
field_allocas rest ixopt fieldtype alloca )
in
(* top-level call *)
field_allocas fields ixs entry.symtype alloca
(** Generate LLVM code for an expression *)
and gen_expr the_module builder syms lltypes (ex: typetag expr) =
match ex.e with
| ExpLiteral NullVal -> const_int nulltag_type 0 (* maybe used now *)
| ExpLiteral (IntVal i) -> const_of_int64 int_type i true (* signed *)
| ExpLiteral (FloatVal f) -> const_float float_type f
| ExpLiteral (ByteVal c) -> const_int byte_type (int_of_char c)
| ExpLiteral (BoolVal b) -> const_int bool_type (if b then 1 else 0)
| ExpLiteral (StringVal s) ->
(* It will build the instruction /and/ return the ptr value *)
build_global_stringptr s "sconst" builder
| ExpVal (e) ->
(* val(exp) is the nullable wrapper, so promote to the null-tag container. *)
let evalue = gen_expr the_module builder syms lltypes e in
let nullabletype = ttag_to_lltype lltypes ex.decor in
promote_value evalue nullabletype builder
| ExpVar (((varname, _), _) as varexp) -> (
(* gets complicated with arrays and fields; call out to helper function *)
let (alloca, _) =
get_varexp_alloca the_module builder varexp syms lltypes in
debug_print ("#CG: ExpVar: varexp alloca created for type "
^ typetag_to_string ex.decor ^ " with alloca "
^ string_of_llvalue alloca);
let res =
(* should not load if of generic type! generic02.dl *)
if not (is_generic_type ex.decor) then
build_load alloca (varname ^ "-exp-load") builder
else alloca in
(debug_print "#CG: finished generating VarExp"; res)
)
(* prior code to deal with refs was here *)
| ExpRecord fieldlist ->
(* Get the LLVM struct type from the lltenv *)
let typekey = (get_type_modulename ex.decor,
get_type_classname ex.decor) in
let llty = Lltenv.find_lltype typekey lltypes in
let recaddr =
(* recursive record types are heap-allocated. *)
if is_recursive_type ex.decor then
(* llty is already the pointer type *)
build_gc_malloc (element_type llty) "rectype" the_module builder
else
build_alloca llty "recaddr" builder in
List.iter (fun (fname, fexp) ->
(* have to use the map from field names to numbers *)
let fexpval = gen_expr the_module builder syms lltypes fexp in
(* get the pointer to the field from the allocated struct *)
let fieldptr =
build_struct_gep recaddr
(fst (Lltenv.find_field typekey fname lltypes))
"fieldptr" builder in
(* check if null promotion is needed; the field lltype might be an opaque
pointer, so use the typename to fetch the actual type from the lltenv *)
let fieldtype = element_type (type_of fieldptr) in
debug_print ("#CG ExpRecord: field and expr types: "
^ string_of_lltype fieldtype ^ ", "
^ string_of_lltype (type_of fexpval));
let finalval =
if fieldtype = type_of fexpval
then fexpval
else if is_pointer_type fieldtype then (
(* generic (pointer) field and value is pointer - just cast
to the void pointer *)
if is_pointer_type (type_of fexpval) then
build_bitcast fexpval voidptr_type "genfield" builder
else (
(* generic field type and value is value - store *)
debug_print ("#CG ExpRecord: need store for generic field");
let fieldaddr =
build_alloca (type_of fexpval) "fieldaddr" builder in
let _ = build_store fexpval fieldaddr builder in
(* cast to the generic pointer *)
build_bitcast fieldaddr voidptr_type "fieldarg" builder
))
(* need a better way to determine when to promote? *)
else (
debug_print ("#CG ExpRecord: field value promotion needed");
promote_value (*castedval*) fexpval fieldtype builder )
in
debug_print ("ExpRecord: field value store: " ^ string_of_llvalue
(build_store finalval fieldptr builder));
) fieldlist;
(* recursive types return the pointer, otherwise the value *)
(* I think for consistency: all rectype expressions are pointers. *)
if is_recursive_type ex.decor then
recaddr
else
build_load recaddr "recordval" builder
| ExpVariant (variant, etup) ->
let tyname = get_type_classname ex.decor in
let tymod = get_type_modulename ex.decor in
debug_print ("** Generating variant expression code of type " ^ tyname);
(* 1. Look up lltype and allocate struct *)
let tyent = Lltenv.find (tymod, tyname) lltypes in
(* 2. Look up variant type, allocate struct, store tag value *)
debug_print ("#CG: variant lltype: " ^ string_of_lltype tyent.lltype);
let typesize = (* TODO: have one sizeof function for the whole codegen *)
if is_pointer_type tyent.lltype then
Array.length (struct_element_types (element_type tyent.lltype))
else
Array.length (struct_element_types tyent.lltype)
in
debug_print ("#CG: Got variant typesize of " ^ string_of_int typesize);
let (tagval, subty) = StrMap.find variant tyent.fieldmap in
let structsubty =
struct_type context
(if typesize = 1 || subty = void_ttag
then [| varianttag_type |]
else
let llsubty = ttag_to_lltype lltypes subty in
[| varianttag_type; llsubty |]
) in
debug_print (" variant subtype struct: " ^ string_of_lltype structsubty);
let structaddr =
if is_recursive_type ex.decor then
build_gc_malloc structsubty "variantSubAddr" the_module builder
else
build_alloca structsubty "variantSubAddr" builder
in
let tagaddr = build_struct_gep structaddr 0 "tag" builder in
ignore (build_store (const_int varianttag_type tagval) tagaddr builder);
(* 3. generate code for expr (if exists) and store in the value slot *)
(match etup with
| [] -> ()
| e :: _ -> (* FIXME: handle the full tuple *)
(* Think we need to hint this to the variant subtype
* (for instance, so "null" will be promoted *)
let expval =
let eval1 = gen_expr the_module builder syms lltypes e in
debug_print "finished codegen for variant field";
if not (types_equal e.decor subty) then
let nullabletype = ttag_to_lltype lltypes subty in
promote_value eval1 nullabletype builder
else eval1 in
let valaddr = build_struct_gep structaddr 1 "varVal" builder in
ignore (build_store expval valaddr builder)
);
(* 4. cast the pointer to the general struct type and load the whole thing *)
(* It still wants the cast even if no value (because named struct?) *)
let castedaddr =
if is_recursive_type ex.decor then
build_bitcast structaddr tyent.lltype "varstruct" builder
else
build_bitcast structaddr (pointer_type tyent.lltype) "varstruct" builder in
debug_print ("Casted variant struct addr " ^ string_of_llvalue structaddr
^ " ter " ^ string_of_llvalue castedaddr);
if is_recursive_type ex.decor then
castedaddr
else build_load castedaddr "filledVariant" builder
(* it's stored anyway, so why not just use the pointer? *)
(* because semantics, and LLVM can elide load/store anyway. *)
(* castedaddr *)
| ExpSeq elist ->
let eltType = ttag_to_lltype lltypes (List.hd elist).decor in
debug_print ("eltType: " ^ typetag_to_string ((List.hd elist).decor));
debug_print ("eltType: " ^ string_of_lltype eltType);
(* alloca for the raw array data. why is it including the *)
let datalloca = (* build_array_alloca *) (* build_array_malloc *)
build_gc_array_malloc eltType
(const_int int_type (List.length elist)) "arrdata"
the_module builder in
List.iteri (fun i e ->
let v = gen_expr the_module builder syms lltypes e in
let ep = build_gep datalloca [|const_int int_type i|] "i" builder in
debug_print(string_of_llvalue (build_store v ep builder));
) elist;
(* create the struct *)
let structalloca = build_alloca (ttag_to_lltype lltypes ex.decor)