-
Notifications
You must be signed in to change notification settings - Fork 313
/
meta-vmdef.lisp
2252 lines (2125 loc) · 98.6 KB
/
meta-vmdef.lisp
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
;;;; This file contains the implementation-independent facilities used
;;;; for defining the compiler's interface to the VM in a given
;;;; implementation that are needed at meta-compile time. They are
;;;; separated out from vmdef.lisp so that they can be compiled and
;;;; loaded without trashing the running compiler.
;;;;
;;;; FIXME: The "trashing the running [CMU CL] compiler" motivation no
;;;; longer makes sense in SBCL, since we can cross-compile cleanly.
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB-C")
;;;; storage class and storage base definition
;;; Define a storage base having the specified NAME. KIND may be :FINITE,
;;; :UNBOUNDED or :NON-PACKED. The following keywords are legal:
;;; :SIZE specifies the number of locations in a :FINITE SB or
;;; the initial size of an :UNBOUNDED SB.
;;;
;;; We enter the basic structure at meta-compile time, and then fill
;;; in the missing slots at load time.
(defmacro !define-storage-bases (&rest definitions &aux (index -1) forms)
(dolist (def definitions)
(destructuring-bind (name kind &key size (size-increment size)
(size-alignment 1))
(cdr def)
(declare (type symbol name))
(declare (type (member :finite :unbounded :non-packed) kind))
;; SIZE is either mandatory or forbidden.
(ecase kind
(:non-packed
(when size
(error "A size specification is meaningless in a ~S SB." kind)))
((:finite :unbounded)
(unless size (error "Size is not specified in a ~S SB." kind))
(aver (<= size sb-vm:finite-sc-offset-limit))
(aver (= 1 (logcount size-alignment)))
(aver (not (logtest size (1- size-alignment))))
(aver (not (logtest size-increment (1- size-alignment))))))
(push (if (eq kind :non-packed)
`(make-storage-base :name ',name :kind ,kind)
`(make-finite-sb-template
:index ,(incf index) :name ',name
:kind ,kind :size ,size
:size-increment ,size-increment
:size-alignment ,size-alignment))
forms)))
;; Do not clobber the global var while running the cross-compiler.
`(eval-when (#-sb-xc :compile-toplevel :load-toplevel :execute)
(setf *backend-sbs* (vector ,@(nreverse forms)))))
;;; Define a storage class NAME that uses the named Storage-Base.
;;; NUMBER is a small, non-negative integer that is used as an alias.
;;; The following keywords are defined:
;;;
;;; :ELEMENT-SIZE Size
;;; The size of objects in this SC in whatever units the SB uses.
;;; This defaults to 1.
;;;
;;; :ALIGNMENT Size
;;; The alignment restrictions for this SC. TNs will only be
;;; allocated at offsets that are an even multiple of this number.
;;; This defaults to 1.
;;;
;;; :LOCATIONS (Location*)
;;; If the SB is :FINITE, then this is a list of the offsets within
;;; the SB that are in this SC.
;;;
;;; :RESERVE-LOCATIONS (Location*)
;;; A subset of the Locations that the register allocator should try to
;;; reserve for operand loading (instead of to hold variable values.)
;;;
;;; :SAVE-P {T | NIL}
;;; If T, then values stored in this SC must be saved in one of the
;;; non-save-p :ALTERNATE-SCs across calls.
;;;
;;; :ALTERNATE-SCS (SC*)
;;; Indicates other SCs that can be used to hold values from this SC across
;;; calls or when storage in this SC is exhausted. The SCs should be
;;; specified in order of decreasing \"goodness\". There must be at least
;;; one SC in an unbounded SB, unless this SC is only used for restricted or
;;; wired TNs.
;;;
;;; :CONSTANT-SCS (SC*)
;;; A list of the names of all the constant SCs that can be loaded into this
;;; SC by a move function.
(defmacro !define-storage-class (name number sb-name &key (element-size '1)
(alignment '1) locations reserve-locations
save-p alternate-scs constant-scs
operand-size)
(declare (type symbol name))
(declare (type sc-number number))
(declare (type symbol sb-name))
(declare (type list locations reserve-locations alternate-scs constant-scs))
(declare (type boolean save-p))
(unless (= (logcount alignment) 1)
(error "alignment not a power of two: ~W" alignment))
(let ((sb (sb-or-lose sb-name)))
(if (eq (sb-kind sb) :finite)
(let ((size (sb-size sb))
(element-size (eval element-size)))
(declare (type unsigned-byte element-size))
(dolist (el locations)
(declare (type unsigned-byte el))
(unless (<= 1 (+ el element-size) size)
(error "SC element ~W out of bounds for ~S" el sb))))
(when locations
(error ":LOCATIONS is meaningless in a ~S SB." (sb-kind sb))))
(unless (subsetp reserve-locations locations)
(error "RESERVE-LOCATIONS not a subset of LOCATIONS."))
(when (and (or alternate-scs constant-scs)
(eq (sb-kind sb) :non-packed))
(error
"It's meaningless to specify alternate or constant SCs in a ~S SB."
(sb-kind sb))))
(let ((nstack-p
(if (or (eq sb-name 'non-descriptor-stack)
(find 'non-descriptor-stack
(mapcar #'sc-or-lose alternate-scs)
:key (lambda (x)
(sb-name (sc-sb x)))))
t nil)))
`(progn
(eval-when (#-sb-xc :compile-toplevel :load-toplevel :execute)
(let ((res (make-storage-class
:name ',name :number ',number
:sb (sb-or-lose ',sb-name)
:element-size ,element-size
:operand-size ,operand-size
:alignment ,alignment
:locations (make-sc-locations ',locations)
:reserve-locations (make-sc-locations ',reserve-locations)
:save-p ',save-p
:number-stack-p ,nstack-p
:alternate-scs (mapcar #'sc-or-lose
',alternate-scs)
:constant-scs (mapcar #'sc-or-lose
',constant-scs))))
(setf (gethash ',name *backend-sc-names*) res)
(setf (svref (sc-load-costs res) ',number) 0)))
(let ((old (svref *backend-sc-numbers* ',number)))
(when (and old (not (eq (sc-name old) ',name)))
(warn "redefining SC number ~W from ~S to ~S" ',number
(sc-name old) ',name)))
(setf (svref *backend-sc-numbers* ',number) (sc-or-lose ',name))
(setf (gethash ',name *backend-sc-names*) (sc-or-lose ',name))
(setf (sc-sb (sc-or-lose ',name)) (sb-or-lose ',sb-name))
',name)))
;;;; move/coerce definition
;;; Given a list of pairs of lists of SCs (as given to DEFINE-MOVE-VOP,
;;; etc.), bind TO-SC and FROM-SC to all the combinations.
(defmacro do-sc-pairs ((from-sc-var to-sc-var scs) &body body)
`(do ((froms ,scs (cddr froms))
(tos (cdr ,scs) (cddr tos)))
((null froms))
(dolist (from (car froms))
(let ((,from-sc-var (sc-or-lose from)))
(dolist (to (car tos))
(let ((,to-sc-var (sc-or-lose to)))
,@body))))))
;;; Define the function NAME and note it as the function used for
;;; moving operands from the From-SCs to the To-SCs. Cost is the cost
;;; of this move operation. The function is called with three
;;; arguments: the VOP (for context), and the source and destination
;;; TNs. An ASSEMBLE form is wrapped around the body. All uses of
;;; DEFINE-MOVE-FUN should be compiled before any uses of
;;; DEFINE-VOP.
(defmacro define-move-fun ((name cost) lambda-list scs &body body)
(declare (type index cost))
(when (or (oddp (length scs)) (null scs))
(error "malformed SCs spec: ~S" scs))
`(progn
(eval-when (:compile-toplevel :load-toplevel :execute)
(do-sc-pairs (from-sc to-sc ',scs)
(unless (eq from-sc to-sc)
(let ((num (sc-number from-sc)))
(setf (svref (sc-move-funs to-sc) num) ',name)
(setf (svref (sc-load-costs to-sc) num) ',cost)))))
(defun ,name ,lambda-list
(declare (ignorable ,(car lambda-list)))
(sb-assem:assemble ()
,@body))))
(defglobal *sc-vop-slots*
'((:move . sc-move-vops)
(:move-arg . sc-move-arg-vops)))
;;;; primitive type definition
;;; Define a primitive type NAME. Each SCS entry specifies a storage
;;; class that values of this type may be allocated in. TYPE is the
;;; type descriptor for the Lisp type that is equivalent to this type.
(defmacro !def-primitive-type (name scs &key (type name))
(declare (type symbol name) (type list scs))
(let ((scns (mapcar #'sc-number-or-lose scs)))
`(progn
(/show "doing !DEF-PRIMITIVE-TYPE" ,(string name))
(assert (not (gethash ',name *backend-primitive-type-names*)))
(setf (gethash ',name *backend-primitive-type-names*)
(make-primitive-type :name ',name
:scs ',scns
:specifier ',type))
(/show0 "done with !DEF-PRIMITIVE-TYPE")
',name)))
;;; Define NAME to be an alias for RESULT in VOP operand type restrictions.
(defmacro !def-primitive-type-alias (name result)
;; Just record the translation.
`(progn
(assert (not (assoc ',name *backend-primitive-type-aliases*)))
(push (cons ',name ,result) *backend-primitive-type-aliases*)
',name))
;;;; VOP definition structures
;;;;
;;;; DEFINE-VOP uses some fairly complex data structures at
;;;; meta-compile time, both to hold the results of parsing the
;;;; elaborate syntax and to retain the information so that it can be
;;;; inherited by other VOPs.
;;; FIXME: all VOP-PARSE slots should be readonly.
;;; Unfortunately it acts as both mutable working storage for the DEFINE-VOP
;;; expander, and the immutable object finally produced.
;;; An OPERAND-PARSE object contains stuff we need to know about an
;;; operand or temporary at meta-compile time. Besides the obvious
;;; stuff, we also store the names of per-operand temporaries here.
(defstruct (operand-parse
(:copier nil)
#-sb-xc-host (:pure t))
;; name of the operand (which we bind to the TN)
(name nil :type symbol :read-only t)
;; the way this operand is used:
(kind (missing-arg) :read-only t
:type (member :argument :result :temporary
:more-argument :more-result))
;; If true, the name of an operand that this operand is targeted to.
;; This is only meaningful in :ARGUMENT and :TEMPORARY operands.
(target nil :type (or symbol null) :read-only t)
;; TEMP is a temporary that holds the TN-REF for this operand.
(temp (make-operand-parse-temp) :type symbol)
;; the time that this operand is first live and the time at which it
;; becomes dead again. These are TIME-SPECs, as returned by
;; PARSE-TIME-SPEC.
(born nil :read-only t)
(dies nil :read-only t)
;; Variable that is bound to the load TN allocated for this operand, or to
;; NIL if no load-TN was allocated.
(load-tn (make-operand-parse-load-tn) :type symbol :read-only t)
;; an expression that tests whether to do automatic operand loading
(load t :read-only t)
;; In a wired or restricted temporary this is the SC the TN is to be
;; packed in. Otherwise, if a non-nil list, the names of the SCs that
;; this operand is allowed into. If NIL, there is no restriction.
(scs nil :type (or symbol list) :read-only t)
;; If non-null, we are a temp wired to this offset in SC.
(offset nil :type (or unsigned-byte null) :read-only t)
(unused-if nil))
(declaim (freeze-type operand-parse))
(defun operand-parse-sc (parse) ; Enforce a single symbol
(the (and symbol (not null)) (operand-parse-scs parse)))
;;; A VOP-PARSE object holds everything we need to know about a VOP at
;;; meta-compile time.
(defstruct (vop-parse #-sb-xc-host (:pure t))
(source-location)
;; the name of this VOP
(name nil :type symbol)
;; If true, then the name of the VOP we inherit from.
(inherits nil :type (or symbol null))
;; lists of OPERAND-PARSE structures describing the arguments,
;; results and temporaries of the VOP
(args nil :type list)
(results nil :type list)
(temps nil :type list)
;; OPERAND-PARSE structures containing information about more args
;; and results. If null, then there there are no more operands of
;; that kind
(more-args nil :type (or operand-parse null))
(more-results nil :type (or operand-parse null))
;; a list of all the above together
(operands nil :type list)
;; Which results can accept :unused TNs
(optional-results nil :type list)
;; names of variables that should be declared IGNORE
(ignores () :type list)
;; true if this is a :CONDITIONAL VOP. T if a branchful VOP,
;; a list of condition descriptor otherwise. See $ARCH/pred.lisp
;; for more information.
(conditional-p nil)
;; argument and result primitive types. These are pulled out of the
;; operands, since we often want to change them without respecifying
;; the operands.
(arg-types :unspecified :type (or (member :unspecified) list))
(result-types :unspecified :type (or (member :unspecified) list))
;; the guard expression specified, or NIL if none
(guard nil)
;; the cost of and body code for the generator
(cost 0 :type unsigned-byte)
(body :unspecified :type (or (member :unspecified) list))
;; info for VOP variants. The list of forms to be evaluated to get
;; the variant args for this VOP, and the list of variables to be
;; bound to the variant args.
(variant () :type list)
(variant-vars () :type list)
;; variables bound to the VOP and Vop-Node when in the generator body
(vop-var '.vop. :type symbol)
(node-var nil :type (or symbol null))
;; a list of the names of the codegen-info arguments to this VOP
(info-args () :type list)
;; an efficiency note associated with this VOP
(note nil :type (or string null))
;; a list of the names of functions this VOP is a translation of and
;; the policy that allows this translation to be done. :FAST is a
;; safe default, since it isn't a safe policy.
(translate () :type list)
(ltn-policy :fast :type ltn-policy)
;; stuff used by life analysis
(save-p nil :type (member t nil :compute-only :force-to-stack))
;; info about how to emit MOVE-ARG VOPs for the &MORE operand in
;; call/return VOPs
(move-args nil :type (member nil :local-call :full-call :known-return :fixed))
(before-load :unspecified :type (or (member :unspecified) list)))
(declaim (freeze-type vop-parse))
(defprinter (vop-parse)
name
(inherits :test inherits)
args
results
temps
(more-args :test more-args)
(more-results :test more-results)
(conditional-p :test conditional-p)
ignores
arg-types
result-types
cost
body
(variant :test variant)
(variant-vars :test variant-vars)
(info-args :test info-args)
(note :test note)
translate
ltn-policy
(save-p :test save-p)
(move-args :test move-args))
;;; The list of slots in the structure, not including the OPERANDS slot.
;;; Order here is insignificant; it happens to be alphabetical.
(defglobal vop-parse-slot-names
'(arg-types args before-load body conditional-p cost guard ignores info-args inherits
ltn-policy more-args more-results move-args name node-var note optional-results result-types
results save-p source-location temps translate variant variant-vars vop-var))
;; A sanity-check. Of course if this fails, the likelihood is that you can't even
;; get this far in cross-compilaion. So it's probably not worth much.
(eval-when (#+sb-xc :compile-toplevel)
(assert (equal (length (dd-slots (find-defstruct-description 'vop-parse)))
(1+ (length vop-parse-slot-names)))))
(defprinter (operand-parse)
name
kind
(target :test target)
born
dies
(scs :test scs)
(load :test load)
(offset :test offset))
;;; Make NAME be the VOP used to move values in the specified FROM-SCs
;;; to the representation of the TO-SCs of each SC pair in SCS.
;;;
;;; If KIND is :MOVE-ARG, then the VOP takes an extra argument,
;;; which is the frame pointer of the frame to move into.
;;;
;;; We record the VOP and costs for all SCs that we can move between
;;; (including implicit loading).
(defmacro define-move-vop (name kind &rest scs)
(when (or (oddp (length scs)) (null scs))
(error "malformed SCs spec: ~S" scs))
(let ((accessor (or (cdr (assoc kind *sc-vop-slots*))
(error "unknown kind ~S" kind))))
`(progn
,@(when (eq kind :move)
`((eval-when (:compile-toplevel :load-toplevel :execute)
(do-sc-pairs (from-sc to-sc ',scs)
(compute-move-costs from-sc to-sc
,(vop-parse-cost
(vop-parse-or-lose name)))))))
(let ((vop (template-or-lose ',name)))
(setf (vop-info-move-vop-p vop) t)
(do-sc-pairs (from-sc to-sc ',scs)
(dolist (dest-sc (cons to-sc (sc-alternate-scs to-sc)))
(let ((vec (,accessor dest-sc)))
(let ((scn (sc-number from-sc)))
(setf (svref vec scn)
(adjoin-template vop (svref vec scn))))
(dolist (sc (append (sc-alternate-scs from-sc)
(sc-constant-scs from-sc)))
(let ((scn (sc-number sc)))
(setf (svref vec scn)
(adjoin-template vop (svref vec scn))))))))))))
;;;; miscellaneous utilities
;;; Find the operand or temporary with the specifed Name in the VOP
;;; Parse. If there is no such operand, signal an error. Also error if
;;; the operand kind isn't one of the specified Kinds. If Error-P is
;;; NIL, just return NIL if there is no such operand.
(defun find-operand (name parse &optional
(kinds '(:argument :result :temporary))
(error-p t))
(declare (symbol name) (type vop-parse parse) (list kinds))
(let ((found (find name (vop-parse-operands parse)
:key #'operand-parse-name)))
(if found
(unless (member (operand-parse-kind found) kinds)
(error "Operand ~S isn't one of these kinds: ~S." name kinds))
(when error-p
(error "~S is not an operand to ~S." name (vop-parse-name parse))))
found))
;;; Get the VOP-PARSE structure for NAME or die trying. For all
;;; meta-compile time uses, the VOP-PARSE should be used instead of
;;; the VOP-INFO.
(defun vop-parse-or-lose (name)
(the vop-parse
(or (gethash name *backend-parsed-vops*)
(error "~S is not the name of a defined VOP." name))))
;;; Return a list of LET-forms to parse a TN-REF list into the temps
;;; specified by the operand-parse structures. MORE-OPERAND is the
;;; OPERAND-PARSE describing any more operand, or NIL if none. REFS is
;;; an expression that evaluates into the first TN-REF.
(defun access-operands (operands more-operand refs)
(declare (list operands))
(collect ((res))
(let ((prev refs))
(dolist (op operands)
(let ((n-ref (operand-parse-temp op)))
(res `(,n-ref ,prev))
(setq prev `(tn-ref-across ,n-ref))))
(when more-operand
(res `(,(operand-parse-name more-operand) ,prev))))
(res)))
;;; This is used with ACCESS-OPERANDS to prevent warnings for TN-REF
;;; temps not used by some particular function. It returns the name of
;;; the last operand, or NIL if OPERANDS is NIL.
(defun ignore-unreferenced-temps (operands)
(when operands
(operand-parse-temp (car (last operands)))))
;;; Grab an arg out of a VOP spec, checking the type and syntax and stuff.
(defun vop-spec-arg (spec type &optional (n 1) (last t))
(let ((len (length spec)))
(when (<= len n)
(error "~:R argument missing: ~S" n spec))
(when (and last (> len (1+ n)))
(error "extra junk at end of ~S" spec))
(let ((thing (elt spec n)))
(unless (typep thing type)
(error "~:R argument is not a ~S: ~S" n type spec))
thing)))
;;;; time specs
;;; Return a time spec describing a time during the evaluation of a
;;; VOP, used to delimit operand and temporary lifetimes. The
;;; representation is a fixnum [phase][16-bit sub-phase].
;;; The sub-phase is 0 in the :LOAD and :SAVE phases.
(defun parse-time-spec (spec)
(let ((dspec (if (atom spec) (list spec 0) spec)))
(unless (and (= (length dspec) 2)
(typep (second dspec) 'unsigned-byte))
(error "malformed time specifier: ~S" spec))
(let ((phase (case (first dspec)
(:load 0)
(:argument 1)
(:eval 2)
(:result 3)
(:save 4)
(t
(error "unknown phase in time specifier: ~S" spec))) )
(sub-phase (second dspec)))
(+ (ash phase 16)
sub-phase))))
;;;; generation of emit functions
(defun compute-temporaries-description (parse)
(let ((temps (vop-parse-temps parse))
(element-type '(unsigned-byte 16)))
(when temps
(let ((results (sb-xc:make-array (length temps) :element-type element-type))
(index 0))
(dolist (temp temps)
(declare (type operand-parse temp))
(let ((sc (operand-parse-sc temp))
(offset (operand-parse-offset temp)))
(aver sc)
(setf (aref results index)
(if offset
(+ (ash offset (1+ sb-vm:sc-number-bits))
(ash (sc-number-or-lose sc) 1)
1)
(ash (sc-number-or-lose sc) 1))))
(incf index))
results))))
(defun compute-ref-ordering (parse)
(let* ((num-args (+ (length (vop-parse-args parse))
(if (vop-parse-more-args parse) 1 0)))
(num-results (+ (length (vop-parse-results parse))
(if (vop-parse-more-results parse) 1 0)))
(index 0))
(collect ((refs) (targets))
(dolist (op (vop-parse-operands parse))
(when (operand-parse-target op)
(unless (member (operand-parse-kind op) '(:argument :temporary))
(error "cannot target a ~S operand: ~S" (operand-parse-kind op)
(operand-parse-name op)))
(let ((target (find-operand (operand-parse-target op) parse
'(:temporary :result))))
;; KLUDGE: These formulas must be consistent with those in
;; EMIT-VOP, and this is currently maintained by
;; hand. -- WHN 2002-01-30, paraphrasing APD
(targets (+ (* index max-vop-tn-refs)
(ecase (operand-parse-kind target)
(:result
(+ (position-or-lose target
(vop-parse-results parse))
num-args))
(:temporary
(+ (* (position-or-lose target
(vop-parse-temps parse))
2)
1
num-args
num-results)))))))
(let ((born (operand-parse-born op))
(dies (operand-parse-dies op)))
(ecase (operand-parse-kind op)
(:argument
(refs (cons (cons dies nil) index)))
(:more-argument
(refs (cons (cons dies nil) index)))
(:result
(refs (cons (cons born t) index)))
(:more-result
(refs (cons (cons born t) index)))
(:temporary
(refs (cons (cons dies nil) index))
(incf index)
(refs (cons (cons born t) index))))
(incf index)))
(let* ((sorted (stable-sort (refs)
(lambda (x y)
(let ((x-time (car x))
(y-time (car y)))
(if (>= x-time y-time)
(if (>= y-time x-time)
(and (not (cdr x)) (cdr y))
nil)
t)))
:key #'car))
;; :REF-ORDERING element type
;;
;; KLUDGE: was (MOD #.MAX-VOP-TN-REFS), which is still right
(oe-type '(unsigned-byte 8))
;; :TARGETS element-type
;;
;; KLUDGE: was (MOD #.(* MAX-VOP-TN-REFS 2)), which does
;; not correspond to the definition in
;; src/compiler/vop.lisp.
(te-type '(unsigned-byte 16))
(ordering (sb-xc:make-array (length sorted) :element-type oe-type)))
(let ((index 0))
(dolist (ref sorted)
(setf (aref ordering index) (cdr ref))
(incf index)))
`(:num-args ,num-args
:num-results ,num-results
:ref-ordering ,ordering
,@(when (targets)
`(:targets ,(coerce (targets) `(vector ,te-type)))))))))
(defun make-emit-function-and-friends (parse)
`(:temps ,(compute-temporaries-description parse)
,@(compute-ref-ordering parse)))
;;;; generator functions
;;; Return an alist that translates from lists of SCs we can load OP
;;; from to the move function used for loading those SCs. We quietly
;;; ignore restrictions to :non-packed (constant) and :unbounded SCs,
;;; since we don't load into those SCs.
(defun find-move-funs (op load-p)
(collect ((funs))
(dolist (sc-name (operand-parse-scs op))
(unless (or (consp sc-name)
(getf *backend-cond-scs* sc-name))
(let* ((sc (sc-or-lose sc-name))
(scn (sc-number sc))
(load-scs (append (when load-p
(sc-constant-scs sc))
(sc-alternate-scs sc))))
(cond
(load-scs
(dolist (alt load-scs)
(unless (member (sc-name alt) (operand-parse-scs op) :test #'eq)
(let* ((altn (sc-number alt))
(name (if load-p
(svref (sc-move-funs sc) altn)
(svref (sc-move-funs alt) scn)))
(found (or (assoc alt (funs) :test #'member)
(rassoc name (funs)))))
(unless name
(error "no move function defined to ~:[save~;load~] SC ~S ~
~:[to~;from~] from SC ~S"
load-p sc-name load-p (sc-name alt)))
(cond (found
(pushnew alt (car found)))
(t
(funs (cons (list alt) name))))))))
((member (sb-kind (sc-sb sc)) '(:non-packed :unbounded)))
(t
(error "SC ~S has no alternate~:[~; or constant~] SCs, yet it is~@
mentioned in the restriction for operand ~S"
sc-name load-p (operand-parse-name op)))))))
(funs)))
;;; Return a form to load/save the specified operand when it has a
;;; load TN. For any given SC that we can load from, there must be a
;;; unique load function. If all SCs we can load from have the same
;;; move function, then we just call that when there is a load TN. If
;;; there are multiple possible move functions, then we dispatch off
;;; of the operand TN's type to see which move function to use.
(defun call-move-fun (parse op load-p)
(let ((funs (find-move-funs op load-p))
(load-tn (operand-parse-load-tn op)))
(if funs
(let* ((tn `(tn-ref-tn ,(operand-parse-temp op)))
(n-vop (vop-parse-vop-var parse))
(form (if (rest funs)
`(sc-case ,tn
,@(mapcar (lambda (x)
`(,(mapcar #'sc-name (car x))
,(if load-p
`(,(cdr x) ,n-vop ,tn
,load-tn)
`(,(cdr x) ,n-vop ,load-tn
,tn))))
funs))
(if load-p
`(,(cdr (first funs)) ,n-vop ,tn ,load-tn)
`(,(cdr (first funs)) ,n-vop ,load-tn ,tn)))))
(cond (load-p
form)
((eq (operand-parse-load op) t)
`(when ,load-tn ,form))
(t
`(when (eq ,load-tn ,(operand-parse-name op))
,form))))
`(when ,load-tn
(error "load TN allocated, but no move function?~@
VM definition is inconsistent, recompile and try again.")))))
;;; Return the TN that we should bind to the operand's var in the
;;; generator body. In general, this involves evaluating the :LOAD-IF
;;; test expression.
(defun decide-to-load (parse op)
(let ((load (operand-parse-load op))
(load-tn (operand-parse-load-tn op))
(temp (operand-parse-temp op))
(loads (and (eq (operand-parse-kind op) :argument)
(call-move-fun parse op t))))
(if (eq load t)
`(cond (,load-tn
,loads
,load-tn)
(t
(tn-ref-tn ,temp)))
(collect ((binds)
(ignores))
(dolist (x (vop-parse-operands parse))
(when (member (operand-parse-kind x) '(:argument :result))
(let ((name (operand-parse-name x)))
(binds `(,name (tn-ref-tn ,(operand-parse-temp x))))
(ignores name))))
`(cond ((and ,load-tn
(let ,(binds)
(declare (ignorable ,@(ignores)))
,load))
,loads
,load-tn)
(t
(tn-ref-tn ,temp)))))))
;;; Make a lambda that parses the VOP TN-REFS, does automatic operand
;;; loading, and runs the appropriate code generator.
(defun make-generator-function (parse)
(declare (type vop-parse parse))
(let ((n-vop (vop-parse-vop-var parse))
(operands (vop-parse-operands parse))
(n-info (gensym)) (n-variant (gensym))
(dummy (gensym)))
(collect ((binds)
(loads)
(saves))
(dolist (op operands)
(ecase (operand-parse-kind op)
((:argument :result)
(let ((temp (operand-parse-temp op))
(name (operand-parse-name op)))
(cond ((and (operand-parse-load op) (operand-parse-scs op))
(binds `(,(operand-parse-load-tn op)
(tn-ref-load-tn ,temp)))
(binds `(,name ,(decide-to-load parse op)))
(when (eq (operand-parse-kind op) :result)
(saves (call-move-fun parse op nil))))
(t
(binds `(,name (tn-ref-tn ,temp)))))))
(:temporary
(binds `(,(operand-parse-name op)
(tn-ref-tn ,(operand-parse-temp op)))))
((:more-argument :more-result))))
`(named-lambda (vop ,(vop-parse-name parse)) (,n-vop)
(declare (ignorable ,n-vop))
(let* (,@(access-operands (vop-parse-args parse)
(vop-parse-more-args parse)
`(vop-args ,n-vop))
,@(access-operands (vop-parse-results parse)
(vop-parse-more-results parse)
`(vop-results ,n-vop))
,@(access-operands (vop-parse-temps parse) nil
`(vop-temps ,n-vop))
,@(when (vop-parse-info-args parse)
`((,n-info (vop-codegen-info ,n-vop))
,@(mapcar (lambda (x) `(,x (pop ,n-info)))
(vop-parse-info-args parse))))
,@(when (vop-parse-variant-vars parse)
`((,n-variant (vop-info-variant (vop-info ,n-vop)))
,@(mapcar (lambda (x) `(,x (pop ,n-variant)))
(vop-parse-variant-vars parse))))
,@(when (vop-parse-node-var parse)
`((,(vop-parse-node-var parse) (vop-node ,n-vop))))
,@(and (neq (vop-parse-before-load parse) :unspecified)
`((,dummy (progn
,@(vop-parse-before-load parse)))))
,@(binds))
(declare (ignore ,@(vop-parse-ignores parse)
,@(and (neq (vop-parse-before-load parse) :unspecified)
`(,dummy))))
,@(loads)
;; RETURN-FROM can exit the ASSEMBLE while continuing on with saves.
(block ,(vop-parse-name parse)
(assemble ()
,@(vop-parse-body parse)))
,@(saves))))))
(defun make-after-sc-function (parse)
(let ((unused-temps
(remove-if-not #'operand-parse-unused-if
(vop-parse-temps parse))))
(when unused-temps
(let* ((n-vop (vop-parse-vop-var parse))
(n-info (gensym))
(n-variant (gensym))
(bindings
`(,@(access-operands (vop-parse-args parse)
(vop-parse-more-args parse)
`(vop-args ,n-vop))
,@(access-operands (vop-parse-results parse)
(vop-parse-more-results parse)
`(vop-results ,n-vop))
,@(and unused-temps
(access-operands (vop-parse-temps parse) nil
`(vop-temps ,n-vop)))
,@(when (vop-parse-info-args parse)
`((,n-info (vop-codegen-info ,n-vop))
,@(mapcar (lambda (x) `(,x (pop ,n-info)))
(vop-parse-info-args parse))))
,@(when (vop-parse-variant-vars parse)
`((,n-variant (vop-info-variant (vop-info ,n-vop)))
,@(mapcar (lambda (x) `(,x (pop ,n-variant)))
(vop-parse-variant-vars parse))))
,@(loop for op in (vop-parse-operands parse)
when
(ecase (operand-parse-kind op)
((:argument :result)
`(,(operand-parse-name op)
(tn-ref-tn ,(operand-parse-temp op))))
(:temporary
(and (operand-parse-unused-if op)
`(,(operand-parse-name op)
(tn-ref-tn ,(operand-parse-temp op)))))
((:more-argument :more-result)))
collect it))))
`(lambda (,n-vop)
(let* ,bindings
(declare (ignorable ,@(mapcar #'car bindings)))
,@(loop for op in unused-temps
collect `(when ,(operand-parse-unused-if op)
(setf (tn-kind ,(operand-parse-name op)) :unused)))))))))
(defvar *parse-vop-operand-count*)
(defun make-operand-parse-temp ()
(symbolicate! #.(find-package "SB-C") "OPERAND-PARSE-TEMP-"
*parse-vop-operand-count*))
(defun make-operand-parse-load-tn ()
(symbolicate! #.(find-package "SB-C")
"OPERAND-PARSE-LOAD-TN-" *parse-vop-operand-count*))
;;; Given a list of operand specifications as given to DEFINE-VOP,
;;; return a list of OPERAND-PARSE structures describing the fixed
;;; operands, and a single OPERAND-PARSE describing any more operand.
;;; If we are inheriting a VOP, we default attributes to the inherited
;;; operand of the same name.
(defun parse-vop-operands (parse specs kind)
(declare (list specs)
(type (member :argument :result) kind))
(let ((num -1)
(more nil))
(collect ((operands))
(dolist (spec specs)
(unless (and (consp spec) (symbolp (first spec)) (oddp (length spec)))
(error "malformed operand specifier: ~S" spec))
(when more
(error "The MORE operand isn't the last operand: ~S" specs))
(incf *parse-vop-operand-count*)
(incf num)
(let* ((name (first spec))
(old (if (vop-parse-inherits parse)
(find-operand name
(vop-parse-or-lose
(vop-parse-inherits parse))
(list* kind
(if (eq kind :argument)
'(:more-argument)
'(:more-result)))
nil)
nil))
(res
(nconc (list :kind kind)
(if old
(list
:target (operand-parse-target old)
:born (operand-parse-born old)
:dies (operand-parse-dies old)
:scs (operand-parse-scs old)
:load-tn (operand-parse-load-tn old)
:load (operand-parse-load old))
(ecase kind
(:argument
(list :born (parse-time-spec :load)
:dies (parse-time-spec `(:argument ,num))))
(:result
(list :born (parse-time-spec `(:result ,num))
:dies (parse-time-spec :save))))))))
(do ((tail (rest spec) (cddr tail)))
((null tail))
(let ((key (first tail))
(value (second tail)))
(case key
(:scs
(aver (typep value 'list))
(aver (= (length value) (length (remove-duplicates value))))
(setq value (copy-list value)))
(:load-tn
(aver (typep value 'symbol)))
(:load-if
(setq key :load))
(:more
(aver (typep value 'boolean))
(setq key :kind
value (if (eq kind :argument) :more-argument :more-result))
(setf (getf res :load) nil)
(setq more t))
(:target
(aver (typep value 'symbol)))
(:from
(unless (eq kind :result)
(error "can only specify :FROM in a result: ~S" spec))
(setq key :born value (parse-time-spec value)))
(:to
(unless (eq kind :argument)
(error "can only specify :TO in an argument: ~S" spec))
(setq key :dies value (parse-time-spec value)))
(t
(error "unknown keyword in operand specifier: ~S" spec)))
(setf (getf res key) value)))
(setq res (apply #'make-operand-parse :name name res)
more (if more res nil))
(cond ((not more)
(operands res))
((operand-parse-target more)
(error "cannot specify :TARGET in a :MORE operand"))
((operand-parse-load more)
(error "cannot specify :LOAD-IF in a :MORE operand")))))
(values (the list (operands)) more))))
;;; Parse a temporary specification, putting the OPERAND-PARSE
;;; structures in the PARSE structure.
(defun parse-temporary (spec parse)
(declare (list spec)
(type vop-parse parse))
(let ((len (length spec)))
(unless (>= len 2)
(error "malformed temporary spec: ~S" spec))
(unless (listp (second spec))
(error "malformed options list: ~S" (second spec)))
(unless (evenp (length (second spec)))
(error "odd number of arguments in keyword options: ~S" spec))
(unless (consp (cddr spec))
(warn "temporary spec allocates no temps:~% ~S" spec))
(dolist (name (cddr spec))
(unless (symbolp name)
(error "bad temporary name: ~S" name))
;; It's almost always a mistake to have overlaps in the operand names.
;; But I guess that some users think it's fine?
#+sb-xc-host
(when (member name (vop-parse-temps parse) :key #'operand-parse-name)
(warn "temp ~s already exists in ~s" name (vop-parse-name parse)))
(incf *parse-vop-operand-count*)
(let ((res (list :born (parse-time-spec :load)
:dies (parse-time-spec :save))))
(do ((opt (second spec) (cddr opt)))
((null opt))
(let ((key (first opt))
(value (second opt)))
(case (first opt)
(:target
(setf value (vop-spec-arg opt 'symbol 1 nil)))
(:sc
(setf key :scs value (vop-spec-arg opt 'symbol 1 nil)))
(:offset
(aver (typep (setq value (eval value)) 'unsigned-byte)))
(:from
(setf key :born value (parse-time-spec value)))
(:to
(setf key :dies value (parse-time-spec value)))
;; backward compatibility...
(:scs
(let ((scs (vop-spec-arg opt 'list 1 nil)))
(unless (= (length scs) 1)
(error "must specify exactly one SC for a temporary"))
(setf value (first scs))))
(:unused-if)
(t
(error "unknown temporary option: ~S" opt)))
(setf (getf res key) value)))
(setq res (apply #'make-operand-parse :name name :kind :temporary res))
(unless (and (>= (operand-parse-dies res)
(operand-parse-born res))
(< (operand-parse-born res)
(operand-parse-dies res)))
(error "Temporary lifetime doesn't begin before it ends: ~S" spec))
(unless (operand-parse-scs res)
(error "must specify :SC for all temporaries: ~S" spec))
(setf (vop-parse-temps parse)
(cons res
(remove name (vop-parse-temps parse)
:key #'operand-parse-name))))))
(values))
(defun compute-parse-vop-operand-count (parse)
(declare (type vop-parse parse))
(labels ((compute-count-aux (parse)
(declare (type vop-parse parse))