-
Notifications
You must be signed in to change notification settings - Fork 0
/
decode.scm
761 lines (688 loc) · 29.4 KB
/
decode.scm
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
; Application independent decoder support.
; Copyright (C) 2000, 2004, 2009 Red Hat, Inc.
; This file is part of CGEN.
;
; This file provides utilities for building instruction set decoders.
; At present its rather limited, and is geared towards the simulator
; where the goal is hyper-efficiency [not that there isn't room for much
; improvement, but rather that that's what the current focus is].
;
; The CPU description file provides the first pass's bit mask with the
; `decode-assist' spec. This gives the decoder a head start on how to
; efficiently decode the instruction set. The rest of the decoder is
; determined algorithmically.
; ??? Need to say more here.
;
; The main entry point is decode-build-table.
;
; Main procedure call tree:
; decode-build-table
; /build-slots
; /build-decode-table-guts
; /build-decode-table-entry
; /build-slots
; /build-decode-table-guts
;
; /build-slots//build-decode-table-guts are recursively called to construct a
; tree of "table-guts" elements, and then the application recurses on the
; result. For example see sim-decode.scm.
;
; The decoder exits when insns are unambiguously determined, even if there are
; more opcode bits to examine, leaving it to the caller to validate any
; remaining bits.
;
; FIXME: Don't create more than 3 shifts (i.e. no more than 3 groups).
; Decoder data structures and accessors.
; The set of instruction is internally recorded as a tree of two data
; structures: "table-guts" and "table-entry".
; [The choice of "table-guts" is historical, a better name will come to mind
; eventually.]
; Decoded tables data structure, termed "dtable-guts".
; A simple data structure of 4 elements:
; bitnums: list of bits that have been used thus far to decode the insn
; startbit: bit offset in instruction of value in C local variable `insn'
; (note that this is independent of LSB0?)
; bitsize: size of value in C local variable `insn'
; entries: list of insns that match the decoding thus far,
; each entry in the list is a `dtable-entry' record
(define (dtable-guts-make bitnums startbit bitsize entries)
(vector bitnums startbit bitsize entries)
)
; Accessors.
(define (dtable-guts-bitnums tg) (vector-ref tg 0))
(define (dtable-guts-startbit tg) (vector-ref tg 1))
(define (dtable-guts-bitsize tg) (vector-ref tg 2))
(define (dtable-guts-entries tg) (vector-ref tg 3))
; A decoded subtable.
; A simple data structure of 3 elements:
; key: name to distinguish this subtable from others, used for lookup
; table: a table-guts element
; name: name of C variable containing the table
;
; The implementation uses a list so the lookup can use assv.
(define (subdtable-make key table name)
(list key table name)
)
; Accessors.
(define (subdtable-key st) (car st))
(define (subdtable-table st) (cadr st))
(define (subdtable-name st) (caddr st))
; List of decode subtables.
(define /decode-subtables nil)
(define (subdtable-lookup key) (assv key /decode-subtables))
; Add SUBTABLE-GUTS to the subtables list if not already present.
; Result is the subtable entry already present, or new entry.
; The key is computed so as to make comparisons possible with assv.
(define (subdtable-add subtable-guts name)
(let* ((key (string->symbol
(string-append
(numbers->string (dtable-guts-bitnums subtable-guts) " ")
" " (number->string (dtable-guts-bitsize subtable-guts))
(string-map
(lambda (elm)
(case (dtable-entry-type elm)
((insn)
(stringsym-append " " (obj:name (dtable-entry-value elm))))
((table)
(stringsym-append " " (subdtable-name (dtable-entry-value elm))))
((expr)
(stringsym-append " " (exprtable-name (dtable-entry-value elm))))
(else (error "bad dtable entry type:"
(dtable-entry-type elm)))))
(dtable-guts-entries subtable-guts)))))
(entry (subdtable-lookup key)))
(if (not entry)
(begin
(set! /decode-subtables (cons (subdtable-make key subtable-guts name)
/decode-subtables))
(car /decode-subtables))
entry))
)
; An instruction and predicate for final matching.
(define (exprtable-entry-make insn expr)
(vector insn expr (rtl-find-ifields expr))
)
; Accessors.
(define (exprtable-entry-insn entry) (vector-ref entry 0))
(define (exprtable-entry-expr entry) (vector-ref entry 1))
(define (exprtable-entry-iflds entry) (vector-ref entry 2))
; Return a pseudo-cost of processing exprentry X.
(define (exprentry-cost x)
(let ((expr (exprtable-entry-expr x)))
(case (rtx-name expr)
((member) (length (rtx-member-set expr)))
(else 4)))
)
; Sort an exprtable, optimum choices first.
; Basically an optimum choice is a cheaper choice.
(define (exprtable-sort expr-list)
(sort expr-list
(lambda (a b)
(let ((costa (exprentry-cost a))
(costb (exprentry-cost b)))
(< costa costb))))
)
; Return the name of the expr table for INSN-EXPRS,
; which is a list of exprtable-entry elements.
(define (/gen-exprtable-name insn-exprs)
(string-map (lambda (x)
(string-append (obj:str-name (exprtable-entry-insn x))
"-"
(rtx-strdump (exprtable-entry-expr x))))
insn-exprs)
)
; A set of instructions that need expressions to distinguish.
; Typically the expressions are ifield-assertion specs.
; INSN-EXPRS is a sorted list of exprtable-entry elements.
; The list is considered sorted in the sense that the first insn to satisfy
; its predicate is chosen.
(define (exprtable-make name insn-exprs)
(vector name insn-exprs)
)
; Accessors.
(define (exprtable-name etable) (vector-ref etable 0))
(define (exprtable-insns etable) (vector-ref etable 1))
; Decoded table entry data structure.
; A simple data structure of 3 elements:
; index: index in the parent table
; entry type indicator: 'insn or 'table or 'expr
; value: the insn or subtable or exprtable
(define (dtable-entry-make index type value)
(assert value)
(vector index type value)
)
; Accessors.
(define (dtable-entry-index te) (vector-ref te 0))
(define (dtable-entry-type te) (vector-ref te 1))
(define (dtable-entry-value te) (vector-ref te 2))
; Return #t if BITNUM is a good bit to use for decoding.
; MASKS is a list of opcode masks.
; MASK-LENS is a list of lengths of each value in MASKS.
; BITNUM is the number of the bit to test. It's value depends on LSB0?.
; It can be no larger than the smallest element in MASKS.
; E.g. If MASK-LENS consists of 16 and 32 and LSB0? is #f, BITNUM must
; be from 0 to 15.
; FIXME: This isn't quite right. What if LSB0? = #t? Need decode-bitsize.
; LSB0? is non-#f if bit number 0 is the least significant bit.
;
; FIXME: This is just a first cut, but the governing intent is to not require
; targets to specify decode tables, hints, or algorithms.
; Certainly as it becomes useful they can supply such information.
; The point is to avoid having to as much as possible.
;
; FIXME: Bit numbers shouldn't be considered in isolation.
; It would be better to compute use counts of all of them and then see
; if there's a cluster of high use counts.
(define (/usable-decode-bit? masks mask-lens bitnum lsb0?)
(let* ((has-bit (map (lambda (msk len)
(bit-set? msk (if lsb0? bitnum (- len bitnum 1))))
masks mask-lens)))
(or (all-true? has-bit)
; If half or more insns use the bit, it's a good one.
; FIXME: An empirical guess at best.
(>= (count-true has-bit) (quotient (length has-bit) 2))
))
)
; Compute population counts for each bit. Return it as a vector indexed by bit
; number. Rather than computing raw popularity, attempt to compute
; "disinguishing value" or inverse-entropy for each bit. The idea is that the
; larger the number for any particular bit slot, the more instructions it can
; be used to distinguish. Raw mask popularity is not enough -- popular masks
; may include useless "reserved" fields whose values don't change, and thus are
; useless in distinguishing.
;
; NOTE: mask-lens are not necessarily all the same value.
; E.g. for the m32r it can consist of both 16 and 32.
; But all masks must exist in the window specified by STARTBIT,DECODE-BITSIZE,
; and all bits in the result must live in that window.
; If no distinguishing bit fits in the window, return an empty vector.
(define (/distinguishing-bit-population masks mask-lens values lsb0?)
(let* ((max-length (apply max mask-lens))
(0-population (make-vector max-length 0))
(1-population (make-vector max-length 0))
(num-insns (length masks)))
; Compute the 1- and 0-population vectors
(for-each (lambda (mask len value)
(logit 5 " population count mask=" (number->hex mask) " len=" len "\n")
(for-each (lambda (bitno)
(let ((lsb-bitno (if lsb0? bitno (- len bitno 1))))
; ignore this bit if it's not set in the mask
(if (bit-set? mask lsb-bitno)
(let ((chosen-pop-vector (if (bit-set? value lsb-bitno)
1-population 0-population)))
(vector-set! chosen-pop-vector bitno
(+ 1 (vector-ref chosen-pop-vector bitno)))))))
(/range len)))
masks mask-lens values)
; Compute an aggregate "distinguishing value" for each bit.
(list->vector
(map (lambda (p0 p1)
(logit 4 p0 "/" p1 " ")
; The most useful bits for decoding are those with counts in both
; p0 and p1. These are the bits which distinguish one insn from
; another. Assign these bits a high value (greater than num-insns).
;
; The next most useful bits are those with counts in either p0
; or p1. These bits represent specializations of other insns.
; Assign these bits a value between 0 and (num-insns - 1). Note that
; p0 + p1 is guaranteed to be <= num-insns. The value 0 is assigned
; to bits for which p0 or p1 is equal to num_insns. These are bits
; which are always 1 or always 0 in the ISA and are useless for
; decoding purposes.
;
; Bits with no count in either p0 or p1 are useless for decoding
; and should never be considered. Assigning these bits a value of
; 0 ensures this.
(cond
((= (+ p0 p1) 0) 0)
((= (* p0 p1) 0) (- num-insns (+ p0 p1)))
(else (+ num-insns (sqrt (* p0 p1))))))
(vector->list 0-population) (vector->list 1-population))))
)
; Return a list (0 ... LIMIT-1).
(define (/range limit)
(let loop ((i 0)
(indices (list)))
(if (= i limit)
(reverse! indices)
(loop (+ i 1) (cons i indices))))
)
; Return a list (BASE ... BASE+SIZE-1).
(define (/range2 base size)
(let loop ((i base)
(indices (list)))
(if (= i (+ base size))
(reverse! indices)
(loop (+ i 1) (cons i indices))))
)
; Return a copy of VECTOR, with all entries with given INDICES set
; to VALUE.
(define (/vector-copy-set-all vector indices value)
(let ((new-vector (make-vector (vector-length vector))))
(for-each (lambda (index)
(vector-set! new-vector index (if (memq index indices)
value
(vector-ref vector index))))
(/range (vector-length vector)))
new-vector)
)
; Return a list of indices whose counts in the given vector exceed the given
; threshold.
; Sort them in decreasing order of popularity.
(define (/population-above-threshold population threshold)
(let* ((unsorted
(find (lambda (index) (if (vector-ref population index)
(>= (vector-ref population index) threshold)
#f))
(/range (vector-length population))))
(sorted
(sort unsorted (lambda (i1 i2) (> (vector-ref population i1)
(vector-ref population i2))))))
sorted)
)
; Return the top few most popular indices in the population vector,
; ignoring any that are already used (marked by #f). Don't exceed
; `size' unless the clustering is just too good to pass up.
(define (/population-top-few population size)
(let loop ((old-picks (list))
(remaining-population population)
(count-threshold (apply max (map (lambda (value) (or value 0))
(vector->list population)))))
(let* ((new-picks (/population-above-threshold remaining-population count-threshold)))
(logit 4 "/population-top-few"
" desired=" size
" picks=(" old-picks ") pop=(" remaining-population ")"
" threshold=" count-threshold " new-picks=(" new-picks ")\n")
(cond
; No point picking bits with population count of zero. This leads to
; the generation of layers of subtables which resolve nothing. Generating
; these tables can slow the build by several orders of magnitude.
((= 0 count-threshold)
(logit 2 "/population-top-few: count-threshold is zero!\n")
old-picks)
; No new matches?
((null? new-picks)
(if (null? old-picks)
(logit 2 "/population-top-few: No bits left to pick from!\n"))
old-picks)
; Way too many matches?
((> (+ (length new-picks) (length old-picks)) (+ size 3))
(list-take (+ 3 size) (append old-picks new-picks))) ; prefer old-picks
; About right number of matches?
((> (+ (length new-picks) (length old-picks)) (- size 1))
(append old-picks new-picks))
; Not enough? Lower the threshold a bit and try to add some more.
(else
(loop (append old-picks new-picks)
(/vector-copy-set-all remaining-population new-picks #f)
; Notice magic clustering decay parameter
; vvvv
(* 0.75 count-threshold))))))
)
; Given list of insns, return list of bit numbers of constant bits in opcode
; that they all share (or mostly share), up to MAX elements.
; ALREADY-USED is a list of bitnums we can't use.
; STARTBIT is the bit offset of the instruction value that C variable `insn'
; holds (note that this is independent of LSB0?).
; DECODE-BITSIZE is the number of bits of the insn that `insn' holds.
; LSB0? is non-#f if bit number 0 is the least significant bit.
;
; Nil is returned if there are none, meaning that there is an ambiguity in
; the specification up to the current word as defined by startbit,
; decode-bitsize, and more bytes need to be fetched.
;
; We assume INSN-LIST matches all opcode bits before STARTBIT (if any).
; FIXME: Revisit, as a more optimal decoder is sometimes achieved by doing
; a cluster of opcode bits that appear later in the insn, and then coming
; back to earlier ones.
;
; All insns are assumed to start at the same address so we handle insns of
; varying lengths - we only analyze the common bits in all of them.
;
; Note that if we get called again to compute further opcode bits, we
; start looking at STARTBIT again (rather than keeping track of how far in
; the insn word we've progressed). We could do this as an optimization, but
; we also have to handle the case where the initial set of decode bits misses
; some and thus we have to go back and look at them. It may also turn out
; that an opcode bit is skipped over because it doesn't contribute much
; information to the decoding process (see /usable-decode-bit?). As the
; possible insn list gets wittled down, the bit will become significant. Thus
; the optimization is left for later.
; Also, see preceding FIXME: We can't proceed past startbit + decode-bitsize
; until we've processed all bits up to startbit + decode-bitsize.
(define (decode-get-best-bits insn-list already-used startbit max decode-bitsize lsb0?)
(let* ((raw-population (/distinguishing-bit-population (map insn-base-mask insn-list)
(map insn-base-mask-length insn-list)
(map insn-value insn-list)
lsb0?))
;; (undecoded (if lsb0?
;; (/range2 startbit (+ startbit decode-bitsize))
;; (/range2 (- startbit decode-bitsize) startbit)))
(used+undecoded already-used) ; (append already-used undecoded))
(filtered-population (/vector-copy-set-all raw-population used+undecoded #f))
(favorite-indices (/population-top-few filtered-population max))
(sorted-indices (sort favorite-indices (lambda (a b)
(if lsb0? (> a b) (< a b))))))
(logit 3
"Best decode bits (prev=" already-used " start=" startbit " decode=" decode-bitsize ")"
"=>"
"(" sorted-indices ")\n")
sorted-indices)
)
(define (OLDdecode-get-best-bits insn-list already-used startbit max decode-bitsize lsb0?)
(let ((masks (map insn-base-mask insn-list))
; ??? We assume mask lengths are repeatedly used for insns longer
; than the base insn size.
(mask-lens (map insn-base-mask-length insn-list))
(endbit (if lsb0?
-1 ; FIXME: for now (gets sparc port going)
(+ startbit decode-bitsize)))
(incr (if lsb0? -1 1)))
(let loop ((result nil)
(bitnum (if lsb0?
(+ startbit (- decode-bitsize 1))
startbit)))
(if (or (= (length result) max) (= bitnum endbit))
(reverse! result)
(if (and (not (memq bitnum already-used))
(/usable-decode-bit? masks mask-lens bitnum lsb0?))
(loop (cons bitnum result) (+ bitnum incr))
(loop result (+ bitnum incr))))
))
)
;; Subroutine of /opcode-slots to simplify it.
;; Compute either the opcode value or mask for the bits in BITNUMS.
;; DEFAULT is 0 when computing the opcode value, 1 for the mask value.
;; DECODE-LEN is (length BITNUMS).
(define (/get-subopcode-value value insn-len decode-len bitnums default lsb0?)
;;(display (list val insn-len decode-len bl)) (newline)
;; Oh My God. This isn't tail recursive.
(letrec ((compute
;; BNS is the remaining elements of BITNUMS to examine.
;; THIS-BN ranges from (length bitnums), ..., 3, 2, 1.
(lambda (bns this-bn)
(if (null? bns)
0
(let ((bn (car bns)))
(+ (if (or (and (>= bn insn-len) (= default 1))
(and (< bn insn-len)
(bit-set? value
(if lsb0?
bn
(- insn-len bn 1)))))
(integer-expt 2 (- this-bn 1))
0)
(compute (cdr bns) (- this-bn 1))))))))
(compute bitnums decode-len))
)
; Return list of decode table entry numbers for INSN's opcode bits BITNUMS.
; This is the indices into the decode table that match the instruction.
; LSB0? is non-#f if bit number 0 is the least significant bit.
;
; Example: If BITNUMS is (0 1 2 3 4 5), and the constant (i.e. opcode) part of
; the those bits of INSN is #b1100xx (where 'x' indicates a non-constant
; part), then the result is (#b110000 #b110001 #b110010 #b110011).
(define (/opcode-slots insn bitnums lsb0?)
(let ((opcode (insn-value insn)) ;; FIXME: unused, overridden below
(insn-len (insn-base-mask-length insn))
(decode-len (length bitnums)))
(let* ((opcode (/get-subopcode-value (insn-value insn) insn-len decode-len bitnums 0 lsb0?))
(opcode-mask (/get-subopcode-value (insn-base-mask insn) insn-len decode-len bitnums 1 lsb0?))
(indices (missing-bit-indices opcode-mask (- (integer-expt 2 decode-len) 1))))
(logit 3 "insn =" (obj:name insn)
" insn-value=" (number->hex (insn-value insn))
" insn-base-mask=" (number->hex (insn-base-mask insn))
" insn-len=" insn-len
" decode-len=" decode-len
" opcode=" (number->hex opcode)
" opcode-mask=" (number->hex opcode-mask)
" indices=" indices "\n")
(map (lambda (index) (+ opcode index)) indices)))
)
; Subroutine of /build-slots.
; Fill slot in INSN-VEC that INSN goes into.
; BITNUMS is the list of opcode bits.
; LSB0? is non-#f if bit number 0 is the least significant bit.
;
; Example: If BITNUMS is (0 1 2 3 4 5) and the constant (i.e. opcode) part of
; the first six bits of INSN is #b1100xx (where 'x' indicates a non-constant
; part), then elements 48 49 50 51 of INSN-VEC are cons'd with INSN.
; Each "slot" is a list of matching instructions.
(define (/fill-slot! insn-vec insn bitnums lsb0?)
(logit 3 "Filling slots for " (obj:str-name insn)
", bitnums " bitnums "\n")
(let ((slot-nums (/opcode-slots insn bitnums lsb0?)))
;(display (list "Filling slot(s)" slot-nums "...")) (newline)
(for-each (lambda (slot-num)
(vector-set! insn-vec slot-num
(cons insn (vector-ref insn-vec slot-num))))
slot-nums)
*UNSPECIFIED*
)
)
; Given a list of constant bitnums (ones that are predominantly, though perhaps
; not always, in the opcode), record each insn in INSN-LIST in the proper slot.
; LSB0? is non-#f if bit number 0 is the least significant bit.
; The result is a vector of insn lists. Each slot is a list of insns
; that go in that slot.
(define (/build-slots insn-list bitnums lsb0?)
(let ((result (make-vector (integer-expt 2 (length bitnums)) nil)))
; Loop over each element, filling RESULT.
(for-each (lambda (insn)
(/fill-slot! result insn bitnums lsb0?))
insn-list)
result)
)
; Compute the name of a decode table, prefixed with PREFIX.
; INDEX-LIST is a list of pairs: list of bitnums, table entry number,
; in reverse order of traversal (since they're built with cons).
; INDEX-LIST may be empty.
(define (/gen-decode-table-name prefix index-list)
(set! index-list (reverse index-list))
(string-append
prefix
"table"
(string-map (lambda (elm) (string-append "_" (number->string elm)))
; CDR of each element is the table index.
(map cdr index-list)))
)
; Generate one decode table entry for INSN-VEC at INDEX.
; INSN-VEC is a vector of slots where each slot is a list of instructions that
; map to that slot (opcode value). If a slot is nil, no insn has that opcode
; value so the decoder marks it as being invalid.
; STARTBIT is the bit offset of the instruction value that C variable `insn'
; holds (note that this is independent of LSB0?).
; DECODE-BITSIZE is the number of bits of the insn that `insn' holds.
; INDEX-LIST is a list of pairs: list of bitnums, table entry number.
; LSB0? is non-#f if bit number 0 is the least significant bit.
; INVALID-INSN is an <insn> object to use for invalid insns.
; The result is a dtable-entry element (or "slot").
; ??? For debugging.
(define /build-decode-table-entry-args #f)
(define (/build-decode-table-entry insn-vec startbit decode-bitsize index index-list lsb0? invalid-insn)
(let ((slot (vector-ref insn-vec index)))
(logit 2 "Processing decode entry "
(number->string index)
" in "
(/gen-decode-table-name "decode_" index-list)
", "
(cond ((null? slot) "invalid")
((= 1 (length slot)) (insn-syntax (car slot)))
(else "subtable"))
" ...\n")
(cond
; If no insns map to this value, mark it as invalid.
((null? slot) (dtable-entry-make index 'insn invalid-insn))
; If only one insn maps to this value, that's it for this insn.
((= 1 (length slot))
; FIXME: Incomplete: need to check further opcode bits.
(dtable-entry-make index 'insn (car slot)))
; Otherwise more than one insn maps to this value and we need to look at
; further opcode bits.
(else
(logit 3 "Building subtable at index " (number->string index)
", decode-bitsize = " (number->string decode-bitsize)
", indices used thus far:"
(string-map (lambda (i) (string-append " " (number->string i)))
(apply append (map car index-list)))
"\n")
(let ((bitnums (decode-get-best-bits slot
(apply append (map car index-list))
startbit 4
decode-bitsize lsb0?)))
; If bitnums is nil, either there is an ambiguity or we need to read
; more of the instruction in order to distinguish insns in SLOT.
(if (and (null? bitnums)
(< startbit (apply min (map insn-length slot))))
(begin
; We might be able to resolve the ambiguity by reading more bits.
; We know from the < test that there are, indeed, more bits to
; be read.
; FIXME: It's technically possible that the next
; startbit+decode-bitsize chunk has no usable bits and we have to
; iterate, but rather unlikely.
; The calculation of the new startbit, decode-bitsize will
; undoubtedly need refinement.
(set! startbit (+ startbit decode-bitsize))
(set! decode-bitsize
(min decode-bitsize
(- (apply min (map insn-length slot))
startbit)))
(set! bitnums (decode-get-best-bits slot
;nil ; FIXME: what to put here?
(apply append (map car index-list))
startbit 4
decode-bitsize lsb0?))))
; If bitnums is still nil there is an ambiguity.
(if (null? bitnums)
(begin
; Try filtering out insns which are more general cases of
; other insns in the slot. The filtered insns will appear
; in other slots as appropriate.
(set! slot (filter-non-specialized-ambiguous-insns slot))
(if (= 1 (length slot))
; Only 1 insn left in the slot, so take it.
(dtable-entry-make index 'insn (car slot))
; There is still more than one insn in 'slot',
; so there is still an ambiguity.
(begin
; If all insns are marked as DECODE-SPLIT, don't warn.
(if (not (all-true? (map (lambda (insn)
(obj-has-attr? insn 'DECODE-SPLIT))
slot)))
(message "WARNING: Decoder ambiguity detected: "
(string-drop1 ; drop leading comma
(string-map (lambda (insn)
(string-append ", " (obj:str-name insn)))
slot))
"\n"))
; Things aren't entirely hopeless. We've warned about
; the ambiguity. Now, if there are any identical insns,
; filter them out. If only one remains, then use it.
(set! slot (filter-identical-ambiguous-insns slot))
(if (= 1 (length slot))
; Only 1 insn left in the slot, so take it.
(dtable-entry-make index 'insn (car slot))
; Otherwise, see if any ifield-assertion
; specs are present.
; FIXME: For now we assume that if they all have an
; ifield-assertion spec, then there is no ambiguity (it's left
; to the programmer to get it right). This can be made more
; clever later.
; FIXME: May need to back up startbit if we've tried to read
; more of the instruction. We currently require that
; all bits get used before advancing startbit, so this
; shouldn't be necessary. Verify.
(let ((assertions (map insn-ifield-assertion slot)))
(if (not (all-true? assertions))
(begin
; Save arguments for debugging purposes.
(set! /build-decode-table-entry-args
(list insn-vec startbit decode-bitsize index index-list lsb0? invalid-insn))
(error "Unable to resolve ambiguity (maybe need some ifield-assertion specs?)")))
; FIXME: Punt on even simple cleverness for now.
(let ((exprtable-entries
(exprtable-sort (map exprtable-entry-make
slot
assertions))))
(dtable-entry-make index 'expr
(exprtable-make
(/gen-exprtable-name exprtable-entries)
exprtable-entries))))))))
; There is no ambiguity so generate the subtable.
; Need to build `subtable' separately because we
; may be appending to /decode-subtables recursively.
(let* ((insn-vec (/build-slots slot bitnums lsb0?))
(subtable
(/build-decode-table-guts insn-vec bitnums startbit
decode-bitsize index-list lsb0?
invalid-insn)))
(dtable-entry-make index 'table
(subdtable-add subtable
(/gen-decode-table-name "" index-list)))))))
)
)
)
; Given a vector of insn slots INSN-VEC, generate the guts of the decode table,
; recorded as a "dtable-guts" data structure.
;
; BITNUMS is the list of bit numbers used to build the slot table.
; I.e., (= (vector-length insn-vec) (ash 1 (length bitnums))).
; STARTBIT is the bit offset of the instruction value that C variable `insn'
; holds (note that this is independent of LSB0?).
; For example, it is initially zero. If DECODE-BITSIZE is 16 and after
; scanning the first fetched piece of the instruction, more decoding is
; needed, another piece will be fetched and STARTBIT will then be 16.
; DECODE-BITSIZE is the number of bits of the insn that `insn' holds.
; INDEX-LIST is a list of pairs: list of bitnums, table entry number.
; Decode tables consist of entries of two types: actual insns and
; pointers to other tables.
; LSB0? is non-#f if bit number 0 is the least significant bit.
; INVALID-INSN is an <insn> object representing invalid insns.
;
; BITNUMS is recorded with the guts so that tables whose contents are
; identical but are accessed by different bitnums are treated as separate in
; /decode-subtables. Not sure this will ever happen, but play it safe.
(define (/build-decode-table-guts insn-vec bitnums startbit decode-bitsize index-list lsb0? invalid-insn)
(logit 2 "Processing decoder for bits"
(numbers->string bitnums " ")
", startbit " startbit
", decode-bitsize " decode-bitsize
", index-list " index-list
" ...\n")
(assert (= (vector-length insn-vec) (ash 1 (length bitnums))))
(dtable-guts-make
bitnums startbit decode-bitsize
(map (lambda (index)
(/build-decode-table-entry insn-vec startbit decode-bitsize index
(cons (cons bitnums index)
index-list)
lsb0? invalid-insn))
(iota (vector-length insn-vec))))
)
; Entry point.
; Return a table that efficiently decodes INSN-LIST.
; The table is a "dtable-guts" data structure, see dtable-guts-make.
;
; BITNUMS is the set of bits to initially key off of.
; DECODE-BITSIZE is the number of bits of the instruction that `insn' holds.
; LSB0? is non-#f if bit number 0 is the least significant bit.
; INVALID-INSN is an <insn> object representing the `invalid' insn (for
; instructions values that don't decode to any entry in INSN-LIST).
(define (decode-build-table insn-list bitnums decode-bitsize lsb0? invalid-insn)
; Initialize the list of subtables computed.
(set! /decode-subtables nil)
; ??? Another way to handle simple forms of ifield-assertions (like those
; created by insn specialization) is to record a copy of the insn for each
; possible value of the ifield and modify its ifield list with the ifield's
; value. This would then let the decoder table builder handle it normally.
; I wouldn't create N insns, but would rather create an intermediary record
; that recorded the necessary bits (insn, ifield-list, remaining
; ifield-assertions).
(let ((insn-vec (/build-slots insn-list bitnums lsb0?)))
(let ((table-guts (/build-decode-table-guts insn-vec bitnums
0 decode-bitsize
nil lsb0?
invalid-insn)))
table-guts))
)