summaryrefslogtreecommitdiff
path: root/cgen/sem-frags.scm
blob: 7e50bd0a817d95c5ec2d7f95b810c8e6b310197a (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
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
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
; Semantic fragments.
; Copyright (C) 2000 Red Hat, Inc.
; This file is part of CGEN.
; See file COPYING.CGEN for details.

; Background info:
; Some improvement in pbb simulator efficiency is obtained in cases like
; the ARM where for example operand2 computation is expensive in terms of
; cpu cost, code size, and subroutine call overhead if the code is put in
; a subroutine.  It could be inlined, but there are numerous occurences
; resulting in poor icache usage.
; If the computation is put in its own fragment then code size is reduced
; [improving icache usage] and subroutine call overhead is removed in a
; computed-goto simulator [arguments are passed in machine generated local
; variables].
;
; The basic procedure here is to:
; - break all insns up into a set of statements
;   This is either one statement in the case of insns that don't begin with a
;   sequence, or a list of statements, one for each element in the sequence.
; - find a profitable set of common leading statements (called the "header")
;   and a profitable set of common trailing statements (called the "trailer")
;   What is "profitable" depends on
;   - how expensive the statement is
;   - how long the statement is
;   - the number of insns using the statement
;   - what fraction of the total insn the statement is
; - rewrite insn semantics in terms of the new header and trailer fragments
;   plus a "middle" part that is whatever is left over
;   - there is always a header, the middle and trailer parts are optional
;   - cti insns require a header and trailer, though they can be the same
;     fragment
;
; TODO:
; - check ARM orr insns which come out as header, tiny middle, trailer
;   - the tiny middle seems like a waste (combine with trailer?)
; - there are 8 trailers consisting of just `nop' for ARM
; - rearranging statements to increase number and length of common sets
; - combine common middle fragments
; - parallel's not handled yet (only have to handle parallel's at the
;   top level)
; - insns can also be split on timing-sensitive boundaries (pipeline, memory,
;   whatever) though that is not implemented yet.  This may involve rtl
;   additions.
;
; Usage:
; - call sim-sfrag-init! first, to initialize
; - call sim-sfrag-analyze-insns! to create the semantic fragments
; - afterwards, call
;   - sim-sfrag-insn-list
;   - sim-sfrag-frag-table
;   - sim-sfrag-usage-table
;   - sim-sfrag-locals-list

; Statement computation.

; Set to #t to collect various statistics.

(define -stmt-stats? #f)

; Collection of computed stats.  Only set if -stmt-stats? = #t.

(define -stmt-stats #f)

; Collection of computed statement data.  Only set if -stmt-stats? = #t.

(define -stmt-stats-data #f)

; Create a structure recording data of all statements.
; A pair of (next-ordinal . table).

(define (-stmt-data-make hash-size)
  (cons 0 (make-vector hash-size nil))
)

; Accessors.

(define (-stmt-data-table data) (cdr data))
(define (-stmt-data-next-num data) (car data))
(define (-stmt-data-set-next-num! data newval) (set-car! data newval))
(define (-stmt-data-hash-size data) (vector-length (cdr data)))

; A single statement.
; INSN semantics either consist of a single statement or a sequence of them.

(define <statement>
  (class-make '<statement> nil
	      '(
		; RTL code
		expr

		; Local variables of the sequence `expr' is in.
		locals

		; Ordinal of the statement.
		num

		; Costs.
		; SPEED-COST is the cost of executing fragment, relative to a
		; simple add.
		; SIZE-COST is the size of the fragment, relative to a simple
		; add.
		; ??? The cost numbers are somewhat arbitrary and subject to
		; review.
		speed-cost
		size-cost

		; Users of this statement.
		; Each element is (owner-number . owner-object),
		; where owner-number is an index into the initial insn table
		; (e.g. insn-list arg of sfrag-create-cse-mapping), and
		; owner-object is the corresponding object.
		users
		)
	      nil)
)

(define-getters <statement> -stmt (expr locals num speed-cost size-cost users))

(define-setters <statement> -stmt (users))

; Make a <statement> object of EXPR.
; LOCALS is a list of local variables of the sequence EXPR is in.
; NUM is the ordinal of EXPR.
; SPEED-COST is the cost of executing the statement, relative to a simple add.
; SIZE-COST is the size of the fragment, relative to a simple add.
; ??? The cost numbers are somewhat arbitrary and subject to review.
;
; The user list is set to nil.

(define (-stmt-make expr locals num speed-cost size-cost)
  (make <statement> expr locals num speed-cost size-cost nil)
)

; Add a user of STMT.

(define (-stmt-add-user! stmt user-num user-obj)
  (-stmt-set-users! stmt (cons (cons user-num user-obj) (-stmt-users stmt)))
  *UNSPECIFIED*
)

; Lookup STMT in DATA.
; CHAIN-NUM is an argument so it need only be computed once.
; The result is the found <statement> object or #f.

(define (-frag-lookup-stmt data chain-num stmt)
  (let ((table (-stmt-data-table data)))
    (let loop ((stmts (vector-ref table chain-num)))
      (cond ((null? stmts)
	     #f)
	    ; ??? equal? should be appropriate rtx-equal?, blah blah blah.
	    ((equal? (-stmt-expr (car stmts)) stmt)
	     (car stmts))
	    (else
	     (loop (cdr stmts))))))
)

; Hash a statement.

; Computed hash value.
; Global 'cus -frag-hash-compute! is defined globally so we can use
; /fastcall (FIXME: Need /fastcall to work on non-global procs).

(define -frag-hash-value-tmp 0)

(define (-frag-hash-string str)
  (let loop ((chars (map char->integer (string->list str))) (result 0))
    (if (null? chars)
	result
	(loop (cdr chars) (modulo (+ (* result 7) (car chars)) #xfffffff))))
)

(define (-frag-hash-compute! rtx-obj expr mode parent-expr op-pos tstate appstuff)
  (let ((h 0))
    (case (rtx-name expr)
      ((operand)
       (set! h (-frag-hash-string (symbol->string (rtx-operand-name expr)))))
      ((local)
       (set! h (-frag-hash-string (symbol->string (rtx-local-name expr)))))
      ((const)
       (set! h (rtx-const-value expr)))
      (else
       (set! h (rtx-num rtx-obj))))
    (set! -frag-hash-value-tmp
	  ; Keep number small.
	  (modulo (+ (* -frag-hash-value-tmp 3) h op-pos)
		  #xfffffff)))

  ; #f -> "continue with normal traversing"
  #f
)

(define (-frag-hash-stmt stmt locals size)
  (set! -frag-hash-value-tmp 0)
  (rtx-traverse-with-locals #f #f stmt -frag-hash-compute! locals #f) ; FIXME: (/fastcall-make -frag-hash-compute!))
  (modulo -frag-hash-value-tmp size)
)

; Compute the speed/size costs of a statement.

; Compute speed/size costs.
; Global 'cus -frag-cost-compute! is defined globally so we can use
; /fastcall (FIXME: Need /fastcall to work on non-global procs).

(define -frag-speed-cost-tmp 0)
(define -frag-size-cost-tmp 0)

(define (-frag-cost-compute! rtx-obj expr mode parent-expr op-pos tstate appstuff)
  ; FIXME: wip
  (let ((speed 0)
	(size 0))
    (case (rtx-class rtx-obj)
      ((ARG)
       #f) ; these don't contribute to costs (at least for now)
      ((SET)
       ; FIXME: speed/size = 0?
       (set! speed 1)
       (set! size 1))
      ((UNARY BINARY TRINARY)
       (set! speed 1)
       (set! size 1))
      ((IF)
       (set! speed 2)
       (set! size 2))
      (else
       (set! speed 4)
       (set! size 4)))
    (set! -frag-speed-cost-tmp (+ -frag-speed-cost-tmp speed))
    (set! -frag-size-cost-tmp (+ -frag-size-cost-tmp size)))

  ; #f -> "continue with normal traversing"
  #f
)

(define (-frag-stmt-cost stmt locals)
  (set! -frag-speed-cost-tmp 0)
  (set! -frag-size-cost-tmp 0)
  (rtx-traverse-with-locals #f #f stmt -frag-cost-compute! locals #f) ; FIXME: (/fastcall-make -frag-cost-compute!))
  (cons -frag-speed-cost-tmp -frag-size-cost-tmp)
)

; Add STMT to statement table DATA.
; CHAIN-NUM is the chain in the hash table to add STMT to.
; {SPEED,SIZE}-COST are passed through to -stmt-make.
; The result is the newly created <statement> object.

(define (-frag-add-stmt! data chain-num stmt locals speed-cost size-cost)
  (let ((stmt (-stmt-make stmt locals (-stmt-data-next-num data) speed-cost size-cost))
	(table (-stmt-data-table data)))
    (vector-set! table chain-num (cons stmt (vector-ref table chain-num)))
    (-stmt-data-set-next-num! data (+ 1 (-stmt-data-next-num data)))
    stmt)
)

; Return the locals in EXPR.
; If a sequence, return locals.
; Otherwise, return nil.
; The result is in assq'able form.

(define (-frag-expr-locals expr)
  (if (rtx-kind? 'sequence expr)
      (rtx-sequence-assq-locals expr)
      nil)
)

; Return the statements in EXPR.
; If a sequence, return the sequence's expressions.
; Otherwise, return (list expr).

(define (-frag-expr-stmts expr)
  (if (rtx-kind? 'sequence expr)
      (rtx-sequence-exprs expr)
      (list expr))
)

; Analyze statement STMT.
; If STMT is already in STMT-DATA increment its frequency count.
; Otherwise add it.
; LOCALS are locals of the sequence STMT is in.
; USAGE-TABLE is a vector of statement index lists for each expression.
; USAGE-INDEX is the index of USAGE-TABLE to use.
; OWNER is the object of the owner of the statement.

(define (-frag-analyze-expr-stmt! locals stmt stmt-data usage-table expr-num owner)
  (logit 3 "Analyzing statement: " (rtx-strdump stmt) "\n")
  (let* ((chain-num
	  (-frag-hash-stmt stmt locals (-stmt-data-hash-size stmt-data)))
	 (stmt-obj (-frag-lookup-stmt stmt-data chain-num stmt)))

    (logit 3 "  chain #" chain-num  "\n")

    (if (not stmt-obj)
	(let* ((costs (-frag-stmt-cost stmt locals))
	       (speed-cost (car costs))
	       (size-cost (cdr costs)))
	  (set! stmt-obj (-frag-add-stmt! stmt-data chain-num stmt locals
					  speed-cost size-cost))
	  (logit 3 "  new statement, #" (-stmt-num stmt-obj) "\n"))
	(logit 3   "  existing statement, #" (-stmt-num stmt-obj) "\n"))

    (-stmt-add-user! stmt-obj expr-num owner)

    ; If first entry, initialize list, otherwise append to existing list.
    (if (null? (vector-ref usage-table expr-num))
	(vector-set! usage-table expr-num (list (-stmt-num stmt-obj)))
	(append! (vector-ref usage-table expr-num)
		 (list (-stmt-num stmt-obj)))))

  *UNSPECIFIED*
)

; Analyze each statement in EXPR and add it to STMT-DATA.
; OWNER is the object of the owner of the expression.
; USAGE-TABLE is a vector of statement index lists for each expression.
; USAGE-INDEX is the index of the USAGE-TABLE entry to use.
; As each statement's ordinal is computed it is added to the usage list.

(define (-frag-analyze-expr! expr owner stmt-data usage-table usage-index)
  (logit 3 "Analyzing " (obj:name owner) ": " (rtx-strdump expr) "\n")
  (let ((locals (-frag-expr-locals expr))
	(stmt-list (-frag-expr-stmts expr)))
    (for-each (lambda (stmt)
		(-frag-analyze-expr-stmt! locals stmt stmt-data
					  usage-table usage-index owner))
	      stmt-list))
  *UNSPECIFIED*
)

; Compute statement data from EXPRS, a list of expressions.
; OWNERS is a vector of objects that "own" each corresponding element in EXPRS.
; The owner is usually an <insn> object.  Actually it'll probably always be
; an <insn> object but for now I want the disassociation.
;
; The result contains:
; - vector of statement lists of each expression
;   - each element is (stmt1-index stmt2-index ...) where each stmtN-index is
;     an index into the statement table
; - vector of statements (the statement table of the previous item)
;   - each element is a <statement> object

(define (-frag-compute-statements exprs owners)
  (logit 2 "Computing statement table ...\n")
  (let* ((num-exprs (length exprs))
	 (hash-size
	  ; FIXME: This is just a quick hack to put something down on paper.
	  ; blah blah blah.  Revisit as necessary.
	  (cond ((> num-exprs 300) 1019)
		((> num-exprs 100) 511)
		(else 127))))

    (let (; Hash table of expressions.
	  (stmt-data (-stmt-data-make hash-size))
	  ; Statement index lists for each expression.
	  (usage-table (make-vector num-exprs nil)))

      ; Scan each expr, filling in stmt-data and usage-table.
      (let loop ((exprs exprs) (exprnum 0))
	(if (not (null? exprs))
	    (let ((expr (car exprs))
		  (owner (vector-ref owners exprnum)))
	      (-frag-analyze-expr! expr owner stmt-data usage-table exprnum)
	      (loop (cdr exprs) (+ exprnum 1)))))

      ; Convert statement hash table to vector.
      (let ((stmt-hash-table (-stmt-data-table stmt-data))
	    (end (vector-length (-stmt-data-table stmt-data)))
	    (stmt-table (make-vector (-stmt-data-next-num stmt-data) #f)))
	(let loop ((i 0))
	  (if (< i end)
	      (begin
		(map (lambda (stmt)
		       (vector-set! stmt-table (-stmt-num stmt) stmt))
		     (vector-ref stmt-hash-table i))
		(loop (+ i 1)))))

	; All done.  Compute stats if asked to.
	(if -stmt-stats?
	    (begin
	      ; See how well the hashing worked.
	      (set! -stmt-stats-data stmt-data)
	      (set! -stmt-stats
		    (make-vector (vector-length stmt-hash-table) #f))
	      (let loop ((i 0))
		(if (< i end)
		    (begin
		      (vector-set! -stmt-stats i
				   (length (vector-ref stmt-hash-table i)))
		      (loop (+ i 1)))))))

	; Result.
	(cons usage-table stmt-table))))
)

; Semantic fragment selection.
;
; "semantic fragment" is the name assigned to each header/middle/trailer
; "fragment" as each may consist of more than one statement, though not
; necessarily all statements of the original sequence.

(define <sfrag>
  (class-make '<sfrag> '(<ident>)
	      '(
		; List of insn's using this frag.
		users

		; Ordinal's of each element of `users'.
		user-nums

		; Semantic format of insns using this fragment.
		sfmt

		; List of statement numbers that make up `semantics'.
		; Each element is an index into the stmt-table arg of
		; -frag-pick-best.
		; This is #f if the sfrag wasn't derived from some set of
		; statements.
		stmt-numbers

		; Raw rtl source of fragment.
		semantics

		; Compiled source.
		compiled-semantics

		; Boolean indicating if this frag is for parallel exec support.
		parallel?

		; Boolean indicating if this is a header frag.
		; This includes all frags that begin a sequence.
		header?

		; Boolean indicating if this is a trailer frag.
		; This includes all frags that end a sequence.
		trailer?
		)
	      nil)
)

(define-getters <sfrag> sfrag
  (users user-nums sfmt stmt-numbers semantics compiled-semantics
	 parallel? header? trailer?)
)

(define-setters <sfrag> sfrag
  (header? trailer?)
)

; Sorter to merge common fragments together.
; A and B are lists of statement numbers.

(define (-frag-sort a b)
  (cond ((null? a)
	 (not (null? b)))
	((null? b)
	 #f)
	((< (car a) (car b))
	 #t)
	((> (car a) (car b))
	 #f)
	(else ; =
	 (-frag-sort (cdr a) (cdr b))))
)

; Return a boolean indicating if L1,L2 match in the first LEN elements.
; Each element is an integer.

(define (-frag-list-match? l1 l2 len)
  (cond ((= len 0)
	 #t)
	((or (null? l1) (null? l2))
	 #f)
	((= (car l1) (car l2))
	 (-frag-list-match? (cdr l1) (cdr l2) (- len 1)))
	(else
	 #f))
)

; Return the number of expressions that match in the first LEN statements.

(define (-frag-find-matching expr-table indices stmt-list len)
  (let loop ((num-exprs 0) (indices indices))
    (cond ((null? indices)
	   num-exprs)
	  ((-frag-list-match? stmt-list
			      (vector-ref expr-table (car indices)) len)
	   (loop (+ num-exprs 1) (cdr indices)))
	  (else
	   num-exprs)))
)

; Return a boolean indicating if making STMT-LIST a common fragment
; among several owners is profitable.
; STMT-LIST is a list of statement numbers, indices into STMT-TABLE.
; NUM-EXPRS is the number of expressions with STMT-LIST in common.

(define (-frag-merge-profitable? stmt-table stmt-list num-exprs)
  ; FIXME: wip
  (and (>= num-exprs 2)
       (or ; No need to include speed costs yet.
	   ;(>= (-frag-list-speed-cost stmt-table stmt-list) 10)
	   (>= (-frag-list-size-cost stmt-table stmt-list) 4)))
)

; Return the cost of executing STMT-LIST.
; STMT-LIST is a list of statment numbers, indices into STMT-TABLE.
;
; FIXME: The yardstick to use is wip.  Currently we measure things relative
; to a simple add insn which is given the value 1.

(define (-frag-list-speed-cost stmt-table stmt-list)
  ; FIXME: wip
  (apply + (map (lambda (stmt-num)
		  (-stmt-speed-cost (vector-ref stmt-table stmt-num)))
		stmt-list))
)

(define (-frag-list-size-cost stmt-table stmt-list)
  ; FIXME: wip
  (apply + (map (lambda (stmt-num)
		  (-stmt-size-cost (vector-ref stmt-table stmt-num)))
		stmt-list))
)

; Compute the longest set of fragments it is desirable/profitable to create.
; The result is (number-of-matching-exprs . stmt-number-list)
; or #f if there isn't one (the longest set is the empty set).
;
; What is desirable depends on a few things:
; - how often is it used?
; - how expensive is it (size-wise and speed-wise)
; - relationship to other frags
;
; STMT-TABLE is a vector of all statements.
; STMT-USAGE-TABLE is a vector of all expressions.  Each element is a list of
; statement numbers (indices into STMT-TABLE).
; INDICES is a sorted list of indices into STMT-USAGE-TABLE.
; STMT-USAGE-TABLE is processed in the order specified by INDICES.
;
; FIXME: Choosing a statement list should depend on whether there are existing
; chosen statement lists only slightly shorter.

(define (-frag-longest-desired stmt-table stmt-usage-table indices)
  ; STMT-LIST is the list of statements in the first expression.
  (let ((stmt-list (vector-ref stmt-usage-table (car indices))))

    (let loop ((len 1) (prev-num-exprs 0))

      ; See how many subsequent expressions match at length LEN.
      (let ((num-exprs (-frag-find-matching stmt-usage-table (cdr indices)
					    stmt-list len)))
	; If there aren't any, we're done.
	; If LEN-1 is usable, return that.
	; Otherwise there is no profitable list of fragments.
	(if (= num-exprs 0)

	    (let ((matching-stmt-list (list-take (- len 1) stmt-list)))
	      (if (-frag-merge-profitable? stmt-table matching-stmt-list
					   prev-num-exprs)
		  (cons prev-num-exprs matching-stmt-list)
		  #f))

	    ; Found at least 1 subsequent matching expression.
	    ; Extend LEN and see if we still find matching expressions.
	    (loop (+ len 1) num-exprs)))))
)

; Return list of lists of objects for each unique <sformat-argbuf> in
; USER-LIST.
; Each element of USER-LIST is (insn-num . <insn> object).
; The result is a list of lists.  Each element in the top level list is
; a list of elements of USER-LIST that have the same <sformat-argbuf>.
; Insns are also distinguished by being a CTI insn vs a non-CTI insn.
; CTI insns require special handling in the semantics.

(define (-frag-split-by-sbuf user-list)
  ; Sanity check.
  (if (not (elm-bound? (cdar user-list) 'sfmt))
      (error "sformats not computed"))
  (if (not (elm-bound? (insn-sfmt (cdar user-list)) 'sbuf))
      (error "sformat argbufs not computed"))

  (let ((result nil)
	; Find INSN in SFMT-LIST.  The result is the list INSN belongs in
	; or #f.
	(find-obj (lambda (sbuf-list insn)
		    (let ((name (obj:name (sfmt-sbuf (insn-sfmt insn)))))
		      (let loop ((sbuf-list sbuf-list))
			(cond ((null? sbuf-list)
			       #f)
			      ((and (eq? name
					 (obj:name (sfmt-sbuf (insn-sfmt (cdaar sbuf-list)))))
				    (eq? (insn-cti? insn)
					 (insn-cti? (cdaar sbuf-list))))
			       (car sbuf-list))
			      (else
			       (loop (cdr sbuf-list))))))))
	)
    (let loop ((users user-list))
      (if (not (null? users))
	  (let ((try (find-obj result (cdar users))))
	    (if try
		(append! try (list (car users)))
		(set! result (cons (list (car users)) result)))
	    (loop (cdr users)))))

    ; Done
    result)
)

; Return a list of desired fragments to create.
; These consist of the longest set of profitable leading statements in EXPRS.
; Each element of the result is an <sfrag> object.
;
; STMT-TABLE is a vector of all statements.
; STMT-USAGE-TABLE is a vector of statement number lists of each expression.
; OWNER-TABLE is a vector of owner objects of each corresponding expression
; in STMT-USAGE-TABLE.
; KIND is one of 'header or 'trailer.
;
; This works for trailing fragments too as we do the computation based on the
; reversed statement lists.

(define (-frag-compute-desired-frags stmt-table stmt-usage-table owner-table kind)
  (logit 2 "Computing desired " kind " frags ...\n")

  (let* (
	 (stmt-usage-list
	  (if (eq? kind 'header)
	      (vector->list stmt-usage-table)
	      (map reverse (vector->list stmt-usage-table))))
	 ; Sort STMT-USAGE-TABLE.  That will bring exprs with common fragments
	 ; together.
	 (sorted-indices (sort-grade stmt-usage-list -frag-sort))
	 ; List of statement lists that together yield the fragment to create,
	 ; plus associated users.
	 (desired-frags nil)
	 )

    ; Update STMT-USAGE-TABLE in case we reversed the contents.
    (set! stmt-usage-table (list->vector stmt-usage-list))

    (let loop ((indices sorted-indices) (iteration 1))
      (logit 3 "Iteration " iteration "\n")
      (if (not (null? indices))
	  (let ((longest (-frag-longest-desired stmt-table stmt-usage-table indices)))

	    (if longest

		; Found an acceptable frag to create.
		(let* ((num-exprs (car longest))
		       ; Reverse statement numbers back if trailer.
		       (stmt-list (if (eq? kind 'header)
				      (cdr longest)
				      (reverse (cdr longest))))
		       (picked-indices (list-take num-exprs indices))
		       ; Need one copy of the frag for each sbuf, as structure
		       ; offsets will be different in generated C/C++ code.
		       (sfmt-users (-frag-split-by-sbuf
				    (map (lambda (expr-num)
					   (cons expr-num
						 (vector-ref owner-table
							     expr-num)))
					 picked-indices))))

		  (logit 3 "Creating frag of length " (length stmt-list) ", " num-exprs " users\n")
		  (logit 3 "Indices: " picked-indices "\n")

		  ; Create an sfrag for each sbuf.
		  (for-each
		   (lambda (users)
		     (let* ((first-owner (cdar users))
			    (sfrag
			     (make <sfrag>
			       (symbol-append (obj:name first-owner)
					      (if (eq? kind 'header)
						  '-hdr
						  '-trlr))
			       ""
			       atlist-empty
			       (map cdr users)
			       (map car users)
			       (insn-sfmt first-owner)
			       stmt-list
			       (apply
				rtx-make
				(cons 'sequence
				      (cons 'VOID
					    (cons nil
						  (map (lambda (stmt-num)
							 (-stmt-expr
							  (vector-ref stmt-table
								      stmt-num)))
						       stmt-list)))))
			       #f ; compiled-semantics
			       #f ; parallel?
			       (eq? kind 'header)
			       (eq? kind 'trailer)
			       )))
		       (set! desired-frags (cons sfrag desired-frags))))
		   sfmt-users)

		  ; Continue, dropping statements we've put into the frag.
		  (loop (list-drop num-exprs indices) (+ iteration 1)))

		; Couldn't find an acceptable statement list.
		; Try again with next one.
		(begin
		  (logit 3 "No acceptable frag found.\n")
		  (loop (cdr indices) (+ iteration 1)))))))

    ; Done.
    desired-frags)
)

; Return the set of desired fragments to create.
; STMT-TABLE is a vector of each statement.
; STMT-USAGE-TABLE is a vector of (stmt1-index stmt2-index ...) elements for
; each expression, where each stmtN-index is an index into STMT-TABLE.
; OWNER-TABLE is a vector of owner objects of each corresponding expression
; in STMT-USAGE-TABLE.
;
; Each expression is split in up to three pieces: header, middle, trailer.
; This computes pseudo-optimal headers and trailers (if they exist).
; The "middle" part is whatever is leftover.
;
; The result is a vector of 4 elements:
; - vector of (header middle trailer) semantic fragments for each expression
;   - each element is an index into the respective table or #f if not present
; - list of header fragments, each element is an <sfrag> object
; - same but for trailer fragments
; - same but for middle fragments
;
; ??? While this is a big function, each piece is simple and straightforward.
; It's kept as one big function so we can compute each expression's sfrag list
; as we go.  Though it's not much extra expense to not do this.

(define (-frag-pick-best stmt-table stmt-usage-table owner-table)
  (let (
	(num-stmts (vector-length stmt-table))
	(num-exprs (vector-length stmt-usage-table))
	; FIXME: Shouldn't have to do vector->list.
	(stmt-usage-list (vector->list stmt-usage-table))
	; Specify result holders here, simplifies code.
	(desired-header-frags #f)
	(desired-trailer-frags #f)
	(middle-frags #f)
	; Also allocate space for expression sfrag usage table.
	; We compute it as we go to save scanning the header and trailer
	; lists twice.
	; copy-tree is needed to avoid shared storage.
	(expr-sfrags (copy-tree (make-vector (vector-length stmt-usage-table)
					     #(#f #f #f))))
	)

    ; Compute desired headers.
    (set! desired-header-frags
	  (-frag-compute-desired-frags stmt-table stmt-usage-table owner-table
				       'header))

    ; Compute the header used by each expression.
    (let ((expr-hdrs-v (make-vector num-exprs #f))
	  (num-hdrs (length desired-header-frags)))
      (let loop ((hdrs desired-header-frags) (hdrnum 0))
	(if (< hdrnum num-hdrs)
	    (let ((hdr (car hdrs)))
	      (for-each (lambda (expr-num)
			  (vector-set! (vector-ref expr-sfrags expr-num) 0
				       hdrnum)
			  (vector-set! expr-hdrs-v expr-num hdr))
			(sfrag-user-nums hdr))
	      (loop (cdr hdrs) (+ hdrnum 1)))))

      ; Truncate each expression by the header it will use and then find
      ; the set of desired trailers.
      (let ((expr-hdrs (vector->list expr-hdrs-v)))

	(set! desired-trailer-frags
	      (-frag-compute-desired-frags
	       stmt-table
	       ; FIXME: Shouldn't have to use list->vector.
	       ; [still pass a vector, but use vector-map here instead of map]
	       (list->vector
		(map (lambda (expr hdr)
		       (if hdr
			   (list-drop (length (sfrag-stmt-numbers hdr)) expr)
			   expr))
		     stmt-usage-list expr-hdrs))
	       owner-table
	       'trailer))

	; Record the trailer used by each expression.
	(let ((expr-trlrs-v (make-vector num-exprs #f))
	      (num-trlrs (length desired-trailer-frags)))
	  (let loop ((trlrs desired-trailer-frags) (trlrnum 0))
	    (if (< trlrnum num-trlrs)
		(let ((trlr (car trlrs)))
		  (for-each (lambda (expr-num)
			      (vector-set! (vector-ref expr-sfrags expr-num) 2
					   trlrnum)
			      (vector-set! expr-trlrs-v expr-num trlr))
			    (sfrag-user-nums trlr))
		  (loop (cdr trlrs) (+ trlrnum 1)))))

	  ; We have the desired headers and trailers, now compute the middle
	  ; part for each expression.  This is just what's left over.
	  ; ??? We don't try to cse the middle part.  Though we can in the
	  ; future should it prove useful enough.
	  (logit 2 "Computing middle frags ...\n")
	  (let* ((expr-trlrs (vector->list expr-trlrs-v))
		 (expr-middle-stmts
		  (map (lambda (expr hdr trlr)
			 (list-tail-drop
			  (if trlr (length (sfrag-stmt-numbers trlr)) 0)
			  (list-drop
			   (if hdr (length (sfrag-stmt-numbers hdr)) 0)
			   expr)))
		       stmt-usage-list expr-hdrs expr-trlrs)))

	    ; Finally, record the middle sfrags used by each expression.
	    (let loop ((tmp-middle-frags nil)
		       (next-middle-frag-num 0)
		       (expr-num 0)
		       (expr-middle-stmts expr-middle-stmts))

	      (if (null? expr-middle-stmts)

		  ; Done!
		  ; [The next statement executed after this is the one at the
		  ; end that builds the result.  Maybe it should be built here
		  ; and this should be the last statement, but I'm trying this
		  ; style out for awhile.]
		  (set! middle-frags (reverse! tmp-middle-frags))

		  ; Does this expr have a middle sfrag?
		  (if (null? (car expr-middle-stmts))
		      ; Nope.
		      (loop tmp-middle-frags
			    next-middle-frag-num
			    (+ expr-num 1)
			    (cdr expr-middle-stmts))
		      ; Yep.
		      (let ((owner (vector-ref owner-table expr-num)))
			(vector-set! (vector-ref expr-sfrags expr-num)
				     1 next-middle-frag-num)
			(loop (cons (make <sfrag>
				      (symbol-append (obj:name owner) '-mid)
				      (string-append (obj:comment owner)
						     ", middle part")
				      (obj-atlist owner)
				      (list owner)
				      (list expr-num)
				      (insn-sfmt owner)
				      (car expr-middle-stmts)
				      (apply
				       rtx-make
				       (cons 'sequence
					     (cons 'VOID
						   (cons nil
							 (map (lambda (stmt-num)
								(-stmt-expr
								 (vector-ref stmt-table stmt-num)))
							      (car expr-middle-stmts))))))
				      #f ; compiled-semantics
				      #f ; parallel?
				      #f ; header?
				      #f ; trailer?
				      )
				    tmp-middle-frags)
			      (+ next-middle-frag-num 1)
			      (+ expr-num 1)
			      (cdr expr-middle-stmts))))))))))

    ; Result.
    (vector expr-sfrags
	    desired-header-frags
	    desired-trailer-frags
	    middle-frags))
)

; Given a list of expressions, return list of locals in top level sequences.
; ??? Collisions will be handled by rewriting rtl (renaming locals).
;
; This has to be done now as the cse pass must (currently) take into account
; the rewritten rtl.
; ??? This can be done later, with an appropriate enhancement to rtx-equal?
; ??? cse can be improved by ignoring local variable name (of course).

(define (-frag-compute-locals! expr-list)
  (logit 2 "Computing common locals ...\n")
  (let ((result nil)
	(lookup-local (lambda (local local-list)
			(assq (car local) local-list)))
	(local-equal? (lambda (l1 l2)
			(and (eq? (car l1) (car l2))
			     (mode:eq? (cadr l1) (cadr l2)))))
	)
    (for-each (lambda (expr)
		(let ((locals (-frag-expr-locals expr)))
		  (for-each (lambda (local)
			      (let ((entry (lookup-local local result)))
				(if (and entry
					 (local-equal? local entry))
				    #f ; already present
				    (set! result (cons local result)))))
			    locals)))
	      expr-list)
    ; Done.
    result)
)

; Common subexpression computation.

; Given a list of rtl expressions and their owners, return a pseudo-optimal
; set of fragments and a usage list for each owner.
; Common fragments are combined and the original expressions become a sequence
; of these fragments.  The result is "pseudo-optimal" in the sense that the
; desired result is somewhat optimal, though no attempt is made at precise
; optimality.
;
; OWNERS is a list of objects that "own" each corresponding element in EXPRS.
; The owner is usually an <insn> object.  Actually it'll probably always be
; an <insn> object but for now I want the disassociation.
;
; The result is a vector of six elements:
; - sfrag usage table for each owner #(header middle trailer)
; - statement table (vector of all statements, made with -stmt-make)
; - list of sequence locals used by header sfrags
;   - these locals are defined at the top level so that all fragments have
;     access to them
;   - ??? Need to handle collisions among incompatible types.
; - header sfrags
; - trailer sfrags
; - middle sfrags

(define (-sem-find-common-frags-1 exprs owners)
  ; Sanity check.
  (if (not (elm-bound? (car owners) 'sfmt))
      (error "sformats not computed"))

  ; A simple procedure that calls, in order:
  ; -frag-compute-locals!
  ; -frag-compute-statements
  ; -frag-pick-best
  ; The rest is shuffling of results.

  ; Internally it's easier if OWNERS is a vector.
  (let ((owners (list->vector owners))
	(locals (-frag-compute-locals! exprs)))

    ; Collect statement usage data.
    (let ((stmt-usage (-frag-compute-statements exprs owners)))
      (let ((stmt-usage-table (car stmt-usage))
	    (stmt-table (cdr stmt-usage)))

	; Compute the frags we want to create.
	; These are in general sequences of statements.
	(let ((desired-frags
	       (-frag-pick-best stmt-table stmt-usage-table owners)))
	  (let (
		(expr-sfrags (vector-ref desired-frags 0))
		(headers (vector-ref desired-frags 1))
		(trailers (vector-ref desired-frags 2))
		(middles (vector-ref desired-frags 3))
		)
	    ; Result.
	    (vector expr-sfrags stmt-table locals
		    headers trailers middles))))))
)

; Cover proc of -sem-find-common-frags-1.
; See its documentation.

(define (sem-find-common-frags insn-list)
  (-sem-find-common-frags-1
   (begin
     (logit 2 "Simplifying/canonicalizing rtl ...\n")
     (map (lambda (insn)
	    ; Must pass canonicalized and macro-expanded rtl.
	    (rtx-simplify #f insn (insn-semantics insn)
			  (-build-known-values insn)))
	  insn-list))
   insn-list)
)

; Subroutine of sfrag-create-cse-mapping to compute INSN's fragment list.
; FRAG-USAGE is a vector of 3 elements: #(header middle trailer).
; Each element is a fragment number or #f if not present.
; Numbers in FRAG-USAGE are indices relative to their respective subtables
; of FRAG-TABLE (which is a vector of all 3 tables concatenated together).
; NUM-HEADERS,NUM-TRAILERS are used to compute absolute indices.
;
; No header may have been created.  This happens when
; it's not profitable (or possible) to merge this insn's
; leading statements with other insns.  Ditto for
; trailer.  However, each cti insn must have a header
; and a trailer (for pc handling setup and change).
; Try to use the middle fragment if present.  Otherwise,
; use the x-header,x-trailer virtual insns.

(define (-sfrag-compute-frag-list! insn frag-usage frag-table num-headers num-trailers x-header-relnum x-trailer-relnum)
  ; `(list #f)' is so append! works.  The #f is deleted before returning.
  (let ((result (list #f))
	(header (vector-ref frag-usage 0))
	(middle (and (vector-ref frag-usage 1)
		     (+ (vector-ref frag-usage 1)
			num-headers num-trailers)))
	(trailer (and (vector-ref frag-usage 2)
		      (+ (vector-ref frag-usage 2)
			 num-headers)))
	(x-header-num x-header-relnum)
	(x-trailer-num (+ x-trailer-relnum num-headers))
	)

    ; cse'd header created?
    (if header
	; Yep.
	(append! result (list header))
	; Nope.  Use the middle frag if present, otherwise use x-header.
	; Can't use the trailer fragment because by definition it is shared
	; among several insns.
	(if middle
	    ; Mark the middle frag as the header frag.
	    (sfrag-set-header?! (vector-ref frag-table middle) #t)
	    ; No middle, use x-header.
	    (append! result (list x-header-num))))

    ; middle fragment present?
    (if middle
	(append! result (list middle)))

    ; cse'd trailer created?
    (if trailer
	; Yep.
	(append! result (list trailer))
	; Nope.  Use the middle frag if present, otherwise use x-trailer.
	; Can't use the header fragment because by definition it is shared
	; among several insns.
	(if middle
	    ; Mark the middle frag as the trailer frag.
	    (sfrag-set-trailer?! (vector-ref frag-table middle) #t)
	    ; No middle, use x-trailer.
	    (append! result (list x-trailer-num))))

    ; Done.
    (cdr result))
)

; Subroutine of sfrag-create-cse-mapping to find the fragment number of the
; x-header/x-trailer virtual frags.

(define (-frag-lookup-virtual frag-list name)
  (let loop ((i 0) (frag-list frag-list))
    (if (null? frag-list)
	(assert (not "expected virtual insn not present"))
	(if (eq? name (obj:name (car frag-list)))
	    i
	    (loop (+ i 1) (cdr frag-list)))))
)

; Handle complex case, find set of common header and trailer fragments.
; The result is a vector of:
; - fragment table (a vector)
; - table mapping used fragments for each insn (a list)
; - locals list

(define (sfrag-create-cse-mapping insn-list)
  (logit 1 "Creating semantic fragments for pbb engine ...\n")

  (let ((cse-data (sem-find-common-frags insn-list)))

    ; Extract the results of sem-find-common-frags.
    (let ((sfrag-usage-table (vector-ref cse-data 0))
	  (stmt-table (vector-ref cse-data 1))
	  (locals-list (vector-ref cse-data 2))
	  (header-list1 (vector-ref cse-data 3))
	  (trailer-list1 (vector-ref cse-data 4))
	  (middle-list (vector-ref cse-data 5)))

      ; Create two special frags: x-header, x-trailer.
      ; These are used by insns that don't have one or the other.
      ; Header/trailer table indices are already computed for each insn
      ; so append x-header/x-trailer to the end.
      (let ((header-list
	     (append header-list1
		     (list
		      (make <sfrag>
			'x-header
			"header fragment for insns without one"
			(atlist-parse '(VIRTUAL) "" "semantic frag computation")
			nil ; users
			nil ; user ordinals
			(insn-sfmt (current-insn-lookup 'x-before))
			#f ; stmt-numbers
			(rtx-make 'nop)
			#f ; compiled-semantics
			#f ; parallel?
			#t ; header?
			#f ; trailer?
			))))
	    (trailer-list
	     (append trailer-list1
		     (list
		      (make <sfrag>
			'x-trailer
			"trailer fragment for insns without one"
			(atlist-parse '(VIRTUAL) "" "semantic frag computation")
			nil ; users
			nil ; user ordinals
			(insn-sfmt (current-insn-lookup 'x-before))
			#f ; stmt-numbers
			(rtx-make 'nop)
			#f ; compiled-semantics
			#f ; parallel?
			#f ; header?
			#t ; trailer?
			)))))

	(let ((num-headers (length header-list))
	      (num-trailers (length trailer-list))
	      (num-middles (length middle-list)))

	  ; Combine the three sfrag tables (headers, trailers, middles) into
	  ; one big one.
	  (let ((frag-table (list->vector (append header-list
						  trailer-list
						  middle-list)))
		(x-header-relnum (-frag-lookup-virtual header-list 'x-header))
		(x-trailer-relnum (-frag-lookup-virtual trailer-list 'x-trailer))
		)
	    ; Convert sfrag-usage-table to one that refers to the one big
	    ; sfrag table.
	    (logit 2 "Computing insn frag usage ...\n")
	    (let ((insn-frags
		   (map (lambda (insn frag-usage)
			  (-sfrag-compute-frag-list! insn frag-usage
						     frag-table
						     num-headers num-trailers
						     x-header-relnum
						     x-trailer-relnum))
			insn-list
		        ; FIXME: vector->list
			(vector->list sfrag-usage-table)))
		  )
	      (logit 1 "Done fragment creation.\n")
	      (vector frag-table insn-frags locals-list)))))))
)

; Data analysis interface.

(define -sim-sfrag-init? #f)
(define (sim-sfrag-init?) -sim-sfrag-init?)

; Keep in globals for now, simplifies debugging.
; evil globals, blah blah blah.
(define -sim-sfrag-insn-list #f)
(define -sim-sfrag-frag-table #f)
(define -sim-sfrag-usage-table #f)
(define -sim-sfrag-locals-list #f)

(define (sim-sfrag-insn-list)
  (assert -sim-sfrag-init?)
  -sim-sfrag-insn-list
)
(define (sim-sfrag-frag-table)
  (assert -sim-sfrag-init?)
  -sim-sfrag-frag-table
)
(define (sim-sfrag-usage-table)
  (assert -sim-sfrag-init?)
  -sim-sfrag-usage-table
)
(define (sim-sfrag-locals-list)
  (assert -sim-sfrag-init?)
  -sim-sfrag-locals-list
)

(define (sim-sfrag-init!)
  (set! -sim-sfrag-init? #f)
  (set! -sim-sfrag-insn-list #f)
  (set! -sim-sfrag-frag-table #f)
  (set! -sim-sfrag-usage-table #f)
  (set! -sim-sfrag-locals-list #f)
)

(define (sim-sfrag-analyze-insns!)
  (if (not -sim-sfrag-init?)
      (begin
	(set! -sim-sfrag-insn-list (non-multi-insns (non-alias-insns (current-insn-list))))
	(let ((frag-data (sfrag-create-cse-mapping -sim-sfrag-insn-list)))
	  (set! -sim-sfrag-frag-table (vector-ref frag-data 0))
	  (set! -sim-sfrag-usage-table (vector-ref frag-data 1))
	  (set! -sim-sfrag-locals-list (vector-ref frag-data 2)))
	(set! -sim-sfrag-init? #t)))

  *UNSPECIFIED*
)

; Testing support.

(define (-frag-small-test-data)
  '(
    (a . (sequence VOID ((SI tmp)) (set DFLT tmp rm) (set DFLT rd rm)))
    (b . (sequence VOID ((SI tmp)) (set DFLT tmp rm) (set DFLT rd rm)))
    (c . (set DFLT rd rm))
    )
)

(define (-frag-test-data)
  (cons
   (map (lambda (insn)
	  ; Must pass canonicalized and macro-expanded rtl.
	  (rtx-simplify #f insn (insn-semantics insn)
			(-build-known-values insn)))
	(non-multi-insns (non-alias-insns (current-insn-list))))
   (non-multi-insns (non-alias-insns (current-insn-list))))
)

(define test-sfrag-table #f)
(define test-stmt-table #f)
(define test-locals-list #f)
(define test-header-list #f)
(define test-trailer-list #f)
(define test-middle-list #f)

(define (frag-test-run)
  (let* ((test-data (-frag-test-data))
	 (frag-data (sem-find-common-frags (car test-data) (cdr test-data))))
    (set! test-sfrag-table (vector-ref frag-data 0))
    (set! test-stmt-table (vector-ref frag-data 1))
    (set! test-locals-list (vector-ref frag-data 2))
    (set! test-header-list (vector-ref frag-data 3))
    (set! test-trailer-list (vector-ref frag-data 4))
    (set! test-middle-list (vector-ref frag-data 5))
    )
  *UNSPECIFIED*
)