-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathzcprim.lisp
2917 lines (2655 loc) · 118 KB
/
zcprim.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
; -*- Mode: Lisp; Package: Zeta-C; Base: 10 -*-
; File: ZCPRIM.LISP
;
; This code has been placed in the public domain.
;
; This file contains the definitions of the Zeta-C primitives.
(defvar zcprim>*expanding-defunc+* nil
"T when a defunc+ expansion is in process.")
(defvar zcprim>*defun-specials* :unbound
"A list of variables to be declared special, that's being accumulated for the
top-level defun.")
(defvar zcprim>*defun-static-inits* :unbound
"A list of initialization clauses for statics, that's being accumulated for the
top-level defun.")
(defvar zcprim>*defun-cleanup-forms* :unbound
"A list of forms to do at the end of the function, with an unwind-protect even.")
(defvar zcprim>*defun-toplevel-forms* :unbound
"A list of miscellaneous things to do at top level of a defun.")
(defvar zcprim>*defun-function-call-info* :unbound
"A list of lists of the form
(caller callee expected-return-type . arg-types-passed)
telling how the function has been called from this function.")
(defprim c:+
"Arithmetic addition. Also, one arg may be a pointer. Or, the second arg may be
omitted (the ANSI spec defines unary + to constrain the order of evaluation of
arithmetic expressions; we don't rearrange expressions anyway, so just ignore
it)."
(((arg1 type1 :unary))
(values arg1 (zcprim>unary-arith-result "+ (unary)" type1)))
(((arg1 type1 :binary) (arg2 type2 :binary))
(cond ((and (zctype>number-p type1) (zctype>number-p type2))
(values (zcprim>+ arg1 arg2)
(zcprim>binary-arith-result "+" type1 type2)))
((and (zctype>arith-pointer-p type1) (zctype>integer-p type2))
(zcprim>pointer-plus-int arg1 arg2 type1 #'zcprim>+ **env))
((and (zctype>integer-p type1) (zctype>arith-pointer-p type2))
(zcprim>pointer-plus-int arg2 arg1 type2 #'zcprim>+ **env))
(t (zcerror "Wrong argument type to +: ~A or ~A" type1 type2)))))
(defprim c:-
"Arithmetic subtraction. The first arg may be a pointer. Alternatively, both
args may be pointers, but they must point into the same array (this constraint
is enforced at runtime). Or, the second arg may be omitted, for unary
negation."
(((arg1 type1 :unary))
(values (zcprim>- arg1) (zcprim>unary-arith-result "- (unary)" type1)))
(((arg1 type1 :binary) (arg2 type2 :binary))
(cond ((and (zctype>number-p type1) (zctype>number-p type2))
(values (zcprim>- arg1 arg2)
(zcprim>binary-arith-result "- (binary)" type1 type2)))
((and (zctype>arith-pointer-p type1) (zctype>integer-p type2))
(zcprim>pointer-plus-int arg1 arg2 type1 #'zcprim>- **env))
((and (zctype>arith-pointer-p type1) (zctype>arith-pointer-p type2))
(zcprim>pointer-subtract arg1 type1 arg2 type2 **env))
(t (zcerror "Wrong argument type to -: ~A or ~A" type1 type2)))))
(defun zcprim>pointer-plus-int (arg1 arg2 type1 operation env)
"Handles addition and subtraction of a pointer and an integer. OPERATION should
be #'ZCPRIM>+ or #'ZCPRIM>-."
(nlet ((let-clauses ptr-array ptr-index
(zcprim>analyze-pointer-exp arg1 type1)))
(values (zcprim>let-form let-clauses
`(zcptr>cons ,ptr-array
,(funcall operation ptr-index
(zcprim>scale-ptr-offset arg2 type1 env))))
(zctype>canonicalize-pointer type1))))
(defun zcprim>pointer-subtract (arg1 type1 arg2 type2 env)
"Handles the subtraction of two pointers."
(nlet ((let-clauses-1 array-1 index-1
(zcprim>analyze-pointer-exp arg1 type1))
(let-clauses-2 array-2 index-2
(zcprim>analyze-pointer-exp arg2 type2)))
(if (not (and (zctype>arith-pointer-p type1) (zctype>arith-pointer-p type2)
(zctype>match type1 type2 env)))
(zcerror "Wrong argument type in pointer subtraction: ~A or ~A"
type1 type2)
(values (zcprim>let-form (append let-clauses-1 let-clauses-2)
`(if (zcptr>subtract-check ,array-1 ,array-2)
,(zcprim>unscale-ptr-difference (zcprim>- index-1 index-2)
type1 env)))
(zctype>int)))))
(defprim c:*
"Arithmetic multiplication. Or, with one argument, pointer dereferencing."
(((arg1 type1 :unary))
(cond ((not (zctype>pointer-p type1))
(zcerror "Wrong argument type to * (pointer dereference): ~A" type1))
; Special case for pointer-to-function deref.
((zctype>function-pointer-p type1)
(values arg1 (zctype>pointer-deref-type type1)))
(t
(zcprim>deref-pointer arg1 type1 **env))))
(((arg1 type1 :binary) (arg2 type2 :binary))
(values (zcprim>* arg1 arg2)
(zcprim>binary-arith-result "* (multiply)" type1 type2))))
(defun zcprim>deref-pointer (ptr type env &optional unpacked-struct-reference)
"Creates an expression to dereference a pointer expression PTR, of type TYPE.
Returns that as its first value, and the type of that as its second."
(nlet ((dtype (zctype>pointer-deref-type type))
((let-clauses array index (zcprim>analyze-pointer-exp ptr type))))
(values
(cond ((and (listp array) (eq (car array) 'zcprim>address-var) (eql index 0)
;; The following is to make sure a cast hasn't intervened.
(zctype>equal type (zcenv>type (cadr array) env)))
;; Special case for "*&variable".
(cadr array))
((or (zctype>struct-p dtype) (zctype>array-p dtype))
;; Special case for pointer-to-aggregate deref in flat mode.
(zcprim>let-form let-clauses
`(zcptr>flat-deref (zcptr>cons ,array ,index))))
;; Keep track of references to cells in unpacked structs, as they
;; require shorten-on-store just like variables.
(unpacked-struct-reference
(zcprim>let-form let-clauses `(aref ,array ,index)))
(t (zcprim>let-form let-clauses (zcprim>aref array index dtype))))
dtype)))
(defun zcprim>aref (array index type)
(cond ((zctype>signed-char-p type)
`(zcptr>aref-s8b ,array ,index))
((zctype>signed-short-p type)
`(zcptr>aref-s16b ,array ,index))
(t `(zcptr>aref ,array ,index))))
(defun zcprim>aref-to-aset (aref-sym)
(or (cdr (assq aref-sym '((aref . aset)
(zcptr>aref . zcptr>aset)
(zcptr>aref-s8b . zcptr>aset-s8b)
(zcptr>aref-s16b . zcptr>aset-s16b))))
(zcerror "Internal error: Flavor of AREF, ~A, not accounted for!?"
aref-sym)))
(defprim c://
"Arithmetic division. Negative results of integer division truncate toward 0."
(((arg1 type1 :binary) (arg2 type2 :binary))
(values (zcprim>// arg1 arg2)
(zcprim>binary-arith-result "//" type1 type2))))
(defprim c:%
"Arithmetic remainder. Result has same sign as the dividend."
(((arg1 type1 :binary) (arg2 type2 :binary))
(values (zcprim>\ arg1 arg2)
(zcprim>binary-int-arith-result "%" type1 type2))))
(defprim c:<<
"Logical left shift."
(((arg1 type1 :unary) (arg2 type2 :unary))
(values (zcprim>ash arg1 arg2)
(if (not (and (zctype>integer-p type1) (zctype>integer-p type2)))
(zcerror "Wrong argument type to <<: ~A or ~A" type1 type2)
type1))))
(defprim c:>>
"Arithmetic right shift."
(((arg1 type1 :unary) (arg2 type2 :unary))
(values (zcprim>ash arg1 (zcprim>- arg2))
(if (not (and (zctype>integer-p type1) (zctype>integer-p type2)))
(zcerror "Wrong argument type to <<: ~A or ~A" type1 type2)
type1))))
(defprim c:&
"Unary: pointer creation (address-of); binary: bitwise AND."
(((arg type))
(zcprim>pointer-to arg type **env **form))
(((arg1 type1 :binary) (arg2 type2 :binary))
(values (or (zcprim>&-ldb-optimization arg1 arg2)
(zcprim>&-ldb-optimization arg2 arg1)
(zcprim>logand arg1 arg2))
(zcprim>binary-int-arith-result "& (bitwise AND)" type1 type2))))
(defun zcprim>pointer-to (exp type env form)
"Makes a pointer to expression EXP, of type TYPE."
(bcond ((zctype>array-p type)
(zcwarn "Redundant & operator applied to array ~A" exp)
(values (zcprim>canonicalize-if-needed exp type)
(zctype>canonicalize-pointer type)))
((and (zctype>function-pointer-p type) (listp exp)
(eq (car exp) 'zcprim>function))
(zcwarn "Redundant & operator applied to function name ~A" (cadr exp))
(values exp type))
((zctype>struct-p type)
;; Special case for structures in flat mode.
(values (zcprim>canonicalize-if-needed exp
(zctype>array-of (zctype>int)))
(zctype>pointer-to type)))
(((let-clauses array index arefs-p
(and (zcprim>lvalue-ok-check exp type (cadr form))
(zcprim>analyze-aref-exp exp type)))
arefs-p) ; special case for &foo[i], &*foo
(values (zcprim>let-form let-clauses `(zcptr>cons ,array ,index))
(zctype>pointer-to type)))
((symbolp exp)
(values `(zcptr>cons ,(zcprim>variable-address exp env) 0)
(zctype>pointer-to type)))
((and (listp exp) (memq (car exp) '(ldb ldb-signed)))
(zcerror "Cannot make a pointer to a bit field"))
(t (zcerror "Internal error: don't know how to make a pointer to ~A"
exp))))
(defun zcprim>variable-address (sym env)
(nlet ((ignore depth (zcenv>type sym env)))
(when (> depth 0)
(zcenv>annotate sym 'address-taken t env)))
`(zcprim>address-var ,sym))
; We put off translating this into sym.ADDRESS so the expression won't look like
; an lvalue.
(defmacro zcprim>address-var (sym)
(zcprim>variable-address-var sym))
(defun zcprim>&-ldb-optimization (arg1 arg2)
"If ARG1 is a fixnum of the form 2^n-1, and ARG2 is a form `(ash ,foo (- ,bar)),
then instead of taking LOGAND of the two we can do an LDB out of FOO."
(and (fixnump arg1) (< (haulong arg1) 24.) (= arg1 (1- (^ 2 (haulong arg1))))
(listp arg2) (eq (car arg2) 'ash)
(let ((ash-arg2 (caddr arg2)))
(and (listp ash-arg2) (eq (car ash-arg2) '-) (null (cddr ash-arg2))
(cond ((and (fixp (cadr ash-arg2)) (fixp (cadr arg2)))
(load-byte (cadr arg2) (cadr ash-arg2) (haulong arg1)))
(t `(ldb (byte ,(haulong arg1) ,(cadr ash-arg2))
,(cadr arg2))))))))
(defprim c:/|
"Bitwise OR."
(((arg1 type1 :binary) (arg2 type2 :binary))
(values (zcprim>logior arg1 arg2)
(zcprim>binary-int-arith-result "/| (bitwise OR)" type1 type2))))
(defprim c:^
"Bitwise XOR."
(((arg1 type1 :binary) (arg2 type2 :binary))
(values (zcprim>logxor arg1 arg2)
(zcprim>binary-int-arith-result "^ (bitwise XOR)" type1 type2))))
(defprim c:~
"Bitwise NOT, a.k.a. ones complement."
(((arg1 type1 :unary))
(values (zcprim>lognot arg1)
(zcprim>unary-int-arith-result "~ (bitwise NOT)" type1))))
; ================================================================
; Comparison and logical operators.
(defprim c:==
"Equality comparison."
(((arg1 type1 :binary) (arg2 type2 :binary))
(zcprim>compare arg1 type1 arg2 type2 **env 'eql t "==")))
(defprim c:!=
"Inequality comparison."
(((arg1 type1 :binary) (arg2 type2 :binary))
(zcprim>compare arg1 type1 arg2 type2 **env 'eql nil "!=")))
(defprim c:<
"Less-than comparison."
(((arg1 type1 :binary) (arg2 type2 :binary))
(zcprim>compare arg1 type1 arg2 type2 **env '< t "<")))
(defprim c:>
"Greater-than comparison."
(((arg1 type1 :binary) (arg2 type2 :binary))
(zcprim>compare arg2 type2 arg1 type1 **env '< t ">")))
(defprim c:<=
"Less-than-or-equal-to comparison."
(((arg1 type1 :binary) (arg2 type2 :binary))
(zcprim>compare arg2 type2 arg1 type1 **env '< nil "<=")))
(defprim c:>=
"Greater-than-or-equal-to comparison."
(((arg1 type1 :binary) (arg2 type2 :binary))
(zcprim>compare arg1 type1 arg2 type2 **env '< nil ">=")))
(defun zcprim>compare (arg1 type1 arg2 type2 env pred polarity pred-name)
(when (not (memq pred '(eql <)))
(zcerror "Internal error: pred ~A no longer allowed" pred))
(cond ((and (zctype>number-p type1) (zctype>number-p type2))
(values (zcprim>apply-polarity
polarity
(cond ((and (zctype>unsigned-p type1) (eq pred '<))
(zcprim>unsigned-< arg1 arg2))
((and (numberp arg1) (numberp arg2))
(funcall pred arg1 arg2))
(t `(,pred ,arg1 ,arg2))))
(zctype>boolean)))
((or (and (zctype>function-pointer-p type1)
(or (zctype>function-pointer-p type2) (zctype>zero-p type2)))
(and (zctype>zero-p type1) (zctype>function-pointer-p type2)))
(if (neq pred 'eql)
(zcerror "Pointers to functions cannot be compared with ~A" pred-name)
(let ((arg1 (if (zctype>zero-p type1) (zcprim>null-function-pointer)
arg1))
(arg2 (if (zctype>zero-p type2) (zcprim>null-function-pointer)
arg2)))
(values (zcprim>apply-polarity polarity (if (eq arg1 arg2) t
`(eq ,arg1 ,arg2)))
(zctype>boolean)))))
((and (or (zctype>pointer-p type1) (zctype>pointer-p type2))
(zctype>match type1 type2 env))
(values (zcprim>pointer-compare arg1 type1 arg2 type2 pred polarity)
(zctype>boolean)))
((and (zctype>lispval-p type1) (zctype>lispval-p type2))
(when (neq pred 'eql)
(zcerror "lispvals cannot be compared with ~A" pred-name))
(values (zcprim>apply-polarity polarity `(eql ,arg1 ,arg2))
(zctype>boolean)))
(t (zcerror "Argument types are incorrect or don't match to ~A: ~A and ~A"
pred-name type1 type2))))
; I'm not going to try to do constant folding in here. Maybe later.
(defun zcprim>pointer-compare (arg1 type1 arg2 type2 pred polarity)
(nlet ((let-clauses-1 array-1 index-1
(zcprim>analyze-pointer-exp arg1 type1))
(let-clauses-2 array-2 index-2
(zcprim>analyze-pointer-exp arg2 type2))
((test-form
(cond ((neq pred 'eql)
`(progn ,(and (not *compare-incomparable-pointers*)
`(zcptr>compare-check ,array-1 ,array-2))
(,pred ,index-1 ,index-2)))
((and (null array-1) (null array-2) (= index-1 index-2))
t)
((null array-1) `(and (null ,array-2) (zerop ,index-2)))
((null array-2) `(and (null ,array-1) (zerop ,index-1)))
(t `(and (eq ,array-1 ,array-2) (= ,index-1 ,index-2)))))))
;; These let clauses can't depend on each other, so we use APPEND.
(zcprim>let-form (append let-clauses-1 let-clauses-2)
(zcprim>apply-polarity polarity test-form))))
(defun zcprim>apply-polarity (polarity exp)
"From boolean expression EXP, creates another expression which is T when EXP
equals POLARITY."
(cond (polarity exp)
((memq exp '(T NIL)) (not exp))
(t `(not ,exp))))
; These three can't get type errors! Types checked in zcprim>standard-coercions.
(defprim c:!
"Logical NOT."
(((arg type :boolean))
(values (if (memq arg '(T NIL)) (not arg) `(not ,arg))
type))) ; might be lispval
(defprim c:&&
"Logical AND. As you would expect, this only evaluates as many arguments
as it needs to for a result (/"short-circuiting/")."
((&rest (args types :boolean))
(values (if (every args #'(lambda (arg) (memq arg '(T NIL))))
(gmap (:and) :id (:list args))
`(and . ,args))
(if (gmap (:and) #'zctype>lispval-p (:list types)) (zctype>lispval)
(zctype>boolean)))))
(defprim c:/|/|
"Logical OR. As you would expect, this only evaluates as many arguments
as it needs to for a result (/"short-circuiting/")."
((&rest (args types :boolean))
(ignore types)
(values (if (every args #'(lambda (arg) (memq arg '(T NIL))))
(gmap (:or) :id (:list args))
`(or . ,args))
(if (gmap (:and) #'zctype>lispval-p (:list types)) (zctype>lispval)
(zctype>boolean)))))
; ================================================================
; Assignment and modify-in-place operators. C has a gaggle of these.
(defprim c:=
"Plain old assignment."
(((arg1 type1 :lvalue) (arg2 type2))
(values (zcprim>store-value arg1 type1 arg2 type2 **env **context)
type1)))
(defun zcprim>store-value (dest dest-type src src-type env context)
(let ((src (zcprim>coerce-numbers src src-type dest-type)))
(cond ((not (zctype>match dest-type src-type env))
(zcwarn "Mismatched argument types to = (assignment): (~A)~A and (~A)~A; attempting cast"
(zclstn>type-string dest-type env) dest
(zclstn>type-string src-type env) src)
(zcprim>store-value dest dest-type
(zcprim>cast src src-type dest-type env) dest-type
env context))
;; For C value printer only (arrays are not usually lvalues!)
((zctype>array-p dest-type)
(zcprim>structure-assign src dest dest-type env))
((zctype>arith-pointer-p dest-type)
(zcprim>store-pointer-value dest dest-type src src-type context))
((zctype>struct-p dest-type)
(when (not *firstclass-structures*)
(zcerror "Assignments to structures are not allowed. If you want to
permit them (for Unix compatibility), set ZETA-C:*FIRSTCLASS-STRUCTURES* to T."))
(zcprim>structure-assign src dest dest-type env))
;; If storing into a char or short variable that's not an array or
;; packed-struct element, shorten explicitly.
((zctype>shorter-than-int-p dest-type)
(nlet ((ignore ignore ignore arefs-p
(zcprim>analyze-aref-exp dest dest-type)))
`(zcprim>setf ,dest
,(zcprim>shorten-if-necessary
src dest-type (and arefs-p (neq arefs-p 'aref))))))
;; Ugly special case: assignment of NULL to ptr-to-func. How generalize?
((and (zctype>function-pointer-p dest-type) (zctype>zero-p src-type))
;; We keep knowledge about the conversion in ZCPRIM>CAST.
;; ZCPRIM>STANDARD-COERCIONS knows too.
`(zcprim>setf ,dest ,(zcprim>cast (zcprim>null-pointer)
(zctype>pointer-to (zctype>int))
dest-type env)))
(t `(zcprim>setf ,dest ,src)))))
(defun zcprim>store-pointer-value (dest dest-type src src-type context)
(nlet ((src-lets array index
(zcprim>analyze-pointer-exp src src-type))
(dest-lets dest-array dest-index
(zcprim>analyze-pointer-exp dest dest-type))
((array-temp array-val (zcprim>select-trivial array dest-array context)))
((index-temp index-val (zcprim>select-trivial index dest-index context)))
;; We have to use VALUES here to make sure both parts of the
;; source are computed before either of the stores occur.
(((setf-lets `(ignore
(zcprim>setf (values ,dest-array ,dest-index)
(values ,(or array-temp array)
,(or index-temp index))))))))
(zcprim>let-form (zcprim>subordinate-nlet-clauses
(append src-lets dest-lets)
`(,@(and array-temp `((,array-temp ,array)))
,@(and index-temp `((,index-temp ,index)))
,(if (or array-temp index-temp) `(,setf-lets) setf-lets)))
`(zcptr>cons ,array-val ,index-val))))
(defun zcprim>select-trivial (src dest context)
(if (zcprim>trivial-p src) (values nil src)
(if (zcprim>trivial-p dest) (values nil dest)
(let ((temp (and (not (memq ':statement (car context)))
(zcprim>gen-var))))
(values temp temp)))))
(defmacro zcprim>setf (dest value)
"This is needed to handle a couple of cases that aren't handled naturally by
Symbolics SETF."
(cond ((nlistp dest) `(setq ,dest ,value))
((eq (car dest) 'nlet)
`(nlet ,(cadr dest)
,@(butlast (cddr dest))
(zcprim>setf ,(car (last (cddr dest))) ,value)))
;; I can't believe this doesn't work already.
((eq (car dest) 'progn)
`(progn ,@(butlast (cdr dest))
(zcprim>setf ,(car (last (cdr dest))) ,value)))
;; The one case where we don't want to use the result; it would generate
;; excessively poor code anyway.
((eq (car dest) 'values)
(if (gmap (:and) #'symbolp (:list (cdr dest)))
`(multiple-value ,(cdr dest) ,value)
;; This will not be the best code, because very-short-lifetime locals
;; are poorly optimized.
(let ((temps (mapcar #'(lambda (ignore) (zcprim>gen-var))
(cdr dest))))
`(nlet ((,@temps ,value))
. ,(mapcar #'(lambda (place temp) `(zcprim>setf ,place ,temp))
(cdr dest) temps)))))
(t `(#+Symbolics cl:setf #-Symbolics setf ,dest ,value))))
(defun zcprim>shorten-if-necessary (exp type override)
"Generates an expression to shorten EXP to type TYPE, if that is one of the
four short-integer types. OVERRIDE, which should be true if the store is taking
place into a byte array, prevents any shortening."
(cond ((or override (not (zctype>shorter-than-int-p type))) exp)
((zctype>char-p type) (zcprim>ldb '(byte 8 0) exp))
((zctype>signed-short-p type) (zcprim>16-bit-sign-extend exp))
((zctype>signed-char-p type) (zcprim>8-bit-sign-extend exp))
((zctype>unsigned-short-p type) (zcprim>ldb '(byte 16 0) exp))
(t (zcerror "Internal error: type ~A not accounted for!?" type))))
(defun zcprim>coerce-numbers (exp from-type to-type)
"If necessary, wraps code around EXP to do numeric-type coercion."
(cond ((and (not (zctype>double-p from-type))
(zctype>double-p to-type))
(zcprim>dfloat exp))
((and (or (zctype>integer-p from-type) (zctype>double-p from-type))
(zctype>float-p to-type) (not (zctype>double-p to-type)))
(zcprim>float exp))
((and (zctype>integer-p from-type) (zctype>float-p to-type))
(zcprim>float exp))
((and (zctype>float-p from-type) (zctype>integer-p to-type))
;; Note: C doesn't specify direction of truncation of negative numbers.
(zcprim>fix exp))
(t exp)))
(defun zcprim>structure-assign (src dest type env)
;; I know this looks strange but it works iff zcprim>struct-reference does.
(nlet ((int-array (zctype>array-of (zctype>int)))
((src-lets src-array src-index (zcprim>analyze-pointer-exp src int-array))
(dst-lets dst-array dst-index
(zcprim>analyze-pointer-exp dest int-array))))
(zcprim>let-form (zcprim>subordinate-nlet-clauses
(append src-lets dst-lets)
`((ignore (zcprim>copy-structure
,src-array ,src-index ,dst-array ,dst-index
,(zctype>sizeof-in-scale type env)))))
`(zcptr>cons ,dst-array ,dst-index))))
#+Symbolics
(defun zcprim>copy-structure (src-array src-index dst-array dst-index length)
(let ((src-array src-array) (dst-array dst-array))
(declare (sys:array-register src-array dst-array))
(dotimes (i length)
(aset (aref src-array (+ i src-index)) dst-array (+ i dst-index)))))
#-Symbolics
(defun zcprim>copy-structure (src-array src-index dst-array dst-index length)
;; This assumes word alignment, but does not assume a particular scale.
(nlet ((src-array (zcprim>array-as-q src-array))
(dst-array (zcprim>array-as-q dst-array))
(ascale (zcprim>array-scale src-array))
((src-index src-byte
(floor (zcprim>rescale-index ascale :8B src-index) 4)))
((dst-index dst-byte
(floor (zcprim>rescale-index ascale :8B dst-index) 4)))
((length (ceiling (zcprim>rescale-index ascale :8B length) 4))))
(when (or (not (zerop src-byte)) (not (zerop dst-byte)))
(ferror "ZETA-C Internal error: src and dst must be word-aligned"))
;; First we copy the boxed data...
(%blt-typed (aloc src-array src-index) (aloc dst-array dst-index) length 1)
;; ... and then the unboxed
(let ((src-16b (zcprim>array-as-16b src-array))
(dst-16b (zcprim>array-as-16b dst-array))
(src-start (* 2 src-index))
(dst-start (* 2 dst-index))
(len (* 2 length)))
(copy-array-portion src-16b src-start (+ src-start len)
dst-16b dst-start (+ dst-start len)))))
(defprim c:++x
(((arg type :lvalue))
(values (zcprim>pre-increment arg type "++ (pre)" #'zcprim>+
(zcprim>scale-ptr-offset 1 type **env)
(zctype>int))
(zctype>canonicalize-pointer type))))
(defprim c:x++
(((arg type :lvalue))
(values
(if (memq ':statement (car **context))
;; To avoid CONS D-IGNORE instructions.
(zcprim>pre-increment arg type "++ (post)" #'zcprim>+
(zcprim>scale-ptr-offset 1 type **env) (zctype>int))
(zcprim>post-increment arg type "++ (post)" #'zcprim>+
(zcprim>scale-ptr-offset 1 type **env) (zctype>int)))
(zctype>canonicalize-pointer type))))
(defprim c:--x
(((arg type :lvalue))
(values (zcprim>pre-increment arg type "-- (pre)" #'zcprim>+
(zcprim>scale-ptr-offset -1 type **env)
(zctype>int))
(zctype>canonicalize-pointer type))))
(defprim c:x--
(((arg type :lvalue))
(values
(if (memq ':statement (car **context))
;; To avoid CONS D-IGNORE instructions.
(zcprim>pre-increment arg type "-- (post)" #'zcprim>+
(zcprim>scale-ptr-offset -1 type **env)
(zctype>int))
(zcprim>post-increment arg type "-- (post)" #'zcprim>+
(zcprim>scale-ptr-offset -1 type **env)
(zctype>int)))
(zctype>canonicalize-pointer type))))
(defun zcprim>increment (arg type name symbol-fun aref-exp-fun bit-field-exp-fun)
(cond ((zctype>arith-pointer-p type)
(nlet ((let-clauses array index (zcprim>analyze-pointer-exp arg type))
((index++ (zcprim>increment index (zctype>int) name
symbol-fun aref-exp-fun nil))))
(zcprim>let-form let-clauses `(zcptr>cons ,array ,index++))))
((zctype>number-p type)
(bcond ((symbolp arg)
(funcall symbol-fun arg))
(((let-clauses array index arefs-p
(if (memq (car arg) '(ldb ldb-signed))
(zcprim>analyze-aref-exp (caddr arg) type)
(zcprim>analyze-aref-exp arg type)))
arefs-p)
;; We assume that the array and index are both referenced twice.
(nlet ((array ar-lets (zcprim>make-temp-if-needed array))
(index idx-lets (zcprim>make-temp-if-needed index)))
(zcprim>let-form (zcprim>subordinate-nlet-clauses
let-clauses (append ar-lets idx-lets))
(if (memq (car arg) '(ldb ldb-signed))
(funcall bit-field-exp-fun array index
(car arg) (cadr arg))
(funcall aref-exp-fun arefs-p array index)))))
(t (zcerror "Internal error: Can't apply /"~A/" to ~A" name arg))))
(t (zcerror "Wrong argument type to ~A: ~A" name type))))
(defun zcprim>pre-increment (arg type name op arg2 type2)
(zcprim>increment
arg type name
#'(lambda (sym)
(nlet ((arg1 ignore arg2 type2
(zcprim>standard-binary-coercions sym type arg2 type2)))
`(setq ,sym ,(zcprim>shorten-if-necessary
(zcprim>coerce-numbers (funcall op arg1 arg2) type2 type)
type nil))))
#'(lambda (aref-sym array index)
(nlet ((arg1 ignore arg2 type2
(zcprim>standard-binary-coercions `(,aref-sym ,array ,index)
type arg2 type2)))
`(,(zcprim>aref-to-aset aref-sym)
,(zcprim>shorten-if-necessary
(zcprim>coerce-numbers (funcall op arg1 arg2) type2 type)
type (neq aref-sym 'aref))
,array ,index)))
;; Yeesh. Bit fields. What a mess. Oh well.
#'(lambda (array index ldb-sym byte)
(nlet ((word-var (zcprim>gen-var))
(byte-var (zcprim>gen-var))
(arg1 ignore arg2 type2
(zcprim>standard-binary-coercions `(aref ,array ,index)
type arg2 type2)))
`(nlet ((,word-var ,arg1)
((,byte-var ,(zcprim>coerce-numbers
(funcall op `(,ldb-sym ,byte ,word-var) arg2)
type2 type))))
(aset (dpb ,byte-var ,byte ,word-var) ,array ,index)
,byte-var)))))
(defun zcprim>post-increment (arg type name op arg2 type2)
(zcprim>increment
arg type name
#'(lambda (sym)
(nlet ((arg1 ignore arg2 type2
(zcprim>standard-binary-coercions sym type arg2 type2)))
`(prog1 ,sym (setq ,sym ,(zcprim>shorten-if-necessary
(zcprim>coerce-numbers (funcall op arg1 arg2)
type2 type)
type nil)))))
#'(lambda (aref-sym array index)
(nlet ((temp-var (zcprim>gen-var))
(arg1 ignore arg2 type2
(zcprim>standard-binary-coercions `(,aref-sym ,array ,index)
type arg2 type2)))
`(nlet ((,temp-var ,arg1))
(progn (,(zcprim>aref-to-aset aref-sym)
,(zcprim>shorten-if-necessary
(zcprim>coerce-numbers (funcall op temp-var arg2)
type2 type)
type (neq aref-sym 'aref))
,array ,index)
,temp-var))))
#'(lambda (array index ldb-sym byte)
(nlet ((word-var (zcprim>gen-var))
(byte-var (zcprim>gen-var))
(arg1 ignore arg2 type2
(zcprim>standard-binary-coercions `(aref ,array ,index) type
arg2 type2)))
`(nlet ((,word-var ,arg1)
((,byte-var (,ldb-sym ,byte ,word-var))))
(aset (dpb ,(zcprim>coerce-numbers (funcall op byte-var arg2)
type2 type)
,byte ,word-var)
,array ,index)
,byte-var)))))
(defprim c:+=
(((arg1 type1 :lvalue) (arg2 type2))
(zcprim>op-assign arg1 type1 arg2 type2 **env "+=" t t 'zcprim>+)))
(defprim c:-=
(((arg1 type1 :lvalue) (arg2 type2))
(zcprim>op-assign arg1 type1 arg2 type2 **env "-=" t t 'zcprim>-)))
(defprim c:*=
(((arg1 type1 :lvalue) (arg2 type2))
(zcprim>op-assign arg1 type1 arg2 type2 **env "*=" nil t 'zcprim>*)))
(defprim c://=
(((arg1 type1 :lvalue) (arg2 type2))
(zcprim>op-assign arg1 type1 arg2 type2 **env "//=" nil t 'zcprim>//)))
(defprim c:%=
(((arg1 type1 :lvalue) (arg2 type2))
(zcprim>op-assign arg1 type1 arg2 type2 **env "%=" nil t 'zcprim>\)))
(defprim c:<<=
(((arg1 type1 :lvalue) (arg2 type2))
(zcprim>op-assign arg1 type1 arg2 type2 **env "<<=" nil nil 'zcprim>ash)))
(defprim c:>>=
(((arg1 type1 :lvalue) (arg2 type2))
(zcprim>op-assign arg1 type1 (zcprim>- arg2) type2 **env ">>=" nil nil
'zcprim>ash)))
(defprim c:&=
(((arg1 type1 :lvalue) (arg2 type2))
(zcprim>op-assign arg1 type1 arg2 type2 **env "&=" nil nil 'zcprim>logand)))
(defprim c:/|=
(((arg1 type1 :lvalue) (arg2 type2))
(zcprim>op-assign arg1 type1 arg2 type2 **env "|=" nil nil 'zcprim>logior)))
(defprim c:^=
(((arg1 type1 :lvalue) (arg2 type2))
(zcprim>op-assign arg1 type1 arg2 type2 **env "^=" nil nil 'zcprim>logxor)))
(defun zcprim>op-assign (arg1 type1 arg2 type2 env name pointer-allowed-p
float-allowed-p operation)
(if (or (and (or (and float-allowed-p (zctype>number-p type1))
(zctype>integer-p type1))
(or (and float-allowed-p (zctype>number-p type2))
(zctype>integer-p type2)))
(and pointer-allowed-p
(zctype>arith-pointer-p type1) (zctype>integer-p type2)))
(values (zcprim>pre-increment arg1 type1 name operation
(zcprim>scale-ptr-offset arg2 type1 env) type2)
(zctype>canonicalize-pointer type1))
(zcerror "Wrong argument type to ~A: ~A and//or ~A" name type1 type2)))
; ================================================================
; Function calling.
(defun zcprim>trans-misc-fun-call (exp env context)
"Translates a call to a user function from C into Lisp, returning two values: the
translated expression and its type."
(nlet ((fun-exp fun-type
(zcmac>translate-exp (car exp) env (cons '(:funcall) context))))
(zcprim>trans-misc-fun-call-1 fun-exp fun-type exp env context)))
(defun zcprim>trans-misc-fun-call-1 (fun-exp fun-type exp env context)
(nlet ((trans-exp ret-type (zcprim>trans-misc-fun-call-2 fun-exp fun-type
exp env context)))
(if (or (zctype>arith-pointer-p ret-type)
;; Structures look like pointers thereto.
(zctype>struct-p ret-type))
(let ((array-temp (zcprim>gen-var))
(index-temp (zcprim>gen-var)))
(values (zcprim>let-form `((,array-temp ,index-temp ,trans-exp))
`(zcptr>cons ,array-temp ,index-temp))
ret-type))
(values trans-exp ret-type))))
(defun zcprim>trans-misc-fun-call-2 (fun-exp fun-type exp env context)
(nlet ((arg-exps arg-types let-clauses
(zcprim>trans-function-arg-list (cdr exp) env context)))
(cond ((zctype>function-pointer-p fun-type)
;; Be mellow about calling function pointers, per ANSI draft.
(values (zcprim>let-form let-clauses `(funcall ,fun-exp . ,arg-exps))
(zctype>function-return-type
(zctype>pointer-deref-type fun-type))))
((not (zctype>function-p fun-type))
(zcerror "Attempt to call ~A of type ~A as a function"
(car exp) fun-type))
((listp (car exp)) ; Explicit func ptr deref (e.g., "(*fp)()")
(values (zcprim>let-form let-clauses `(funcall ,fun-exp . ,arg-exps))
(zctype>function-return-type fun-type)))
(t
(zcprim>caller-check-arg-types fun-exp fun-type arg-types
(zcprim>function-name context))
(values (zcprim>let-form let-clauses `(,fun-exp . ,arg-exps))
(zctype>function-return-type fun-type))))))
(defun zcprim>trans-function-arg-list (raw-args env context)
"Translates the argument expressions RAW-ARGS into the argument forms that will
actually be passed to the function. Returns three values: a list of argument
forms, a list of types, and a list of let-clauses for the function-call form.
Note that pointer arguments turn into two forms, so there can be more forms than
types."
(nlet ((arg-exps arg-types
(zcmac>translate-rest-arg raw-args t env (cons nil context))))
(zcprim>trans-function-arg-list-1 arg-exps arg-types)))
(defun zcprim>trans-function-arg-list-1 (arg-exps arg-types)
(if (null arg-exps)
(values nil nil nil)
(nlet ((arg type (zcprim>standard-unary-coercions (car arg-exps)
(car arg-types)))
(cdr-args cdr-types cdr-let-clauses
(zcprim>trans-function-arg-list-1 (cdr arg-exps) (cdr arg-types))))
(if (not (zctype>arith-pointer-p type))
(values (cons arg cdr-args) (cons type cdr-types) cdr-let-clauses)
(nlet ((let-clauses array index (zcprim>analyze-pointer-exp arg type)))
(values (cons array (cons index cdr-args))
(cons (zctype>canonicalize-pointer type) cdr-types)
(append let-clauses cdr-let-clauses)))))))
; This one is used when a function is called...
(defun zcprim>caller-check-arg-types (fun fun-type arg-types caller)
"Checks the function's type and argument types against the declared types, if
known. Also accumulates them for future checking."
(nlet ((zcenv>*substitute-quietly* t) ; for ZCENV>DEFINITION call below.
((fun-type (if (zctype>function-param-types-p fun-type)
fun-type ; The locally visible type may not
(and (symbolp fun) ; have param types; look around.
(zcenv>definition fun (zcenv>global-env)))))))
(when (and fun-type (zctype>function-param-types-p fun-type))
(zctype>arglist-type-check arg-types (zctype>function-param-types fun-type)
fun caller)))
(when (and caller (symbolp fun))
(zcprim>accum-call-info `(,caller ,fun ,(zctype>function-return-type fun-type)
. ,arg-types))))
(defun zcprim>accum-call-info (call)
"Adds the information about this call to the list for the function being
compiled; but only if it's nonredundant."
(when (not (gmap (:or) #'(lambda (ocall)
(and (eq (second call) (second ocall))
(= (length call) (length ocall))
(gmap (:and) #'(lambda (type otype)
(and (zctype>equal type otype)))
(:list (secondcdr call))
(:list (secondcdr ocall)))))
(:list zcprim>*defun-function-call-info*)))
(push call zcprim>*defun-function-call-info*)))
; ... and this one when it's defined (actually, at load time).
(defun zcprim>load-check-arg-types (fun *source-location*)
"Checks the recorded call info for this function (who calls it with which types,
and what type was it declared to return) against the correct types."
(nlet ((fun-type (zcenv>function-type fun (zcenv>global-env)))
((return-type (zctype>function-return-type fun-type))
(param-types (zctype>function-param-types fun-type)))
(zcprim>*expanding-defunc+* fun))
(dolist (call-info-entry (get fun 'call-info-alist))
(let ((caller (car call-info-entry))
(calls (cdr call-info-entry)))
(dolist (call calls)
(zctype>arglist-type-check (thirdcdr call) param-types fun caller)
(when (and (not (zctype>equal return-type (third call)))
(not (zctype>void-p (third call))))
(zcwarn "Function ~A assumed by ~A to return type /"~A/"; actual type /"~A/""
fun caller (zclstn>type-string (third call))
(zclstn>type-string return-type))))))))
; Finally, this is used at load time to tell the world about our callees.
(defun zcprim>record-call-info (call-info)
"Records the accumulated call info for this function's callees. I.e., for each
callee, maintains an alist (on its CALL-INFO-ALIST property) of the form
((<caller> <call-info> <call-info> ...) ...). Each <call-info> is of the form
(<caller> <callee> <return-type> <param-type> <param-type> ...)."
;; We sort prior to grouping. Sort predicate hardly matters.
(nlet ((sorted (sort call-info
#'(lambda (i1 i2) (alphalessp (second i1) (second i2)))))
((grouped (group sorted #'(lambda (ci1 ci2)
(eq (second ci1) (second ci2)))))))
(dolist (group grouped)
(nlet ((callee (second (car group)))
((alist (get callee 'call-info-alist))
((frame (assq (first (car group)) alist)))))
(if (null frame)
(push (cons (first (car group)) group) (get callee 'call-info-alist))
(rplacd frame group))))))
; ================================================================
; Array and structure referencing.
(defprim c:[]
(((arg1 type1) (arg2 type2))
(cond ((and (zctype>arith-pointer-p type1) (zctype>integer-p type2))
(nlet ((offset-exp type
(zcprim>pointer-plus-int arg1 arg2 type1 #'zcprim>+ **env)))
(zcprim>deref-pointer offset-exp type **env)))
((and (zctype>arith-pointer-p type2) (zctype>integer-p type1))
(nlet ((offset-exp type
(zcprim>pointer-plus-int arg2 arg1 type2 #'zcprim>+ **env)))
(zcprim>deref-pointer offset-exp type **env)))
(t
(zcerror "Wrong argument type to [] (array ref): ~A or ~A"
type1 type2)))))
(defprim c:/.
(((struct stype) "e elt)
(zcprim>struct-reference "." struct stype stype elt **env **context)))
(defprim c:->
(((struct-p stype) "e elt)
(if (not (zctype>pointer-p stype))
(zcerror "Wrong first argument type to -> (struct//union reference): ~A"
stype)
(zcprim>struct-reference "->" (zcprim>deref-pointer struct-p stype **env)
(zctype>pointer-deref-type stype)
stype elt **env **context))))
(defun zcprim>struct-reference (name struct stype origtype elt env context)
"Process a structure (or union) reference."
(ignore context)
(if (not (zctype>struct-p stype))
;; Extension to allow arbitrary kinds of "struct" references.
(nlet ((hook (and (zctype>lispval-p stype) (zctype>get-lispval-hook stype))))
(if hook (funcall hook :struct-reference stype struct elt env)
(zcerror "Wrong first argument type to ~A (struct//union reference): ~A"
name origtype)))
(nlet ((elts (zctype>struct-elements stype env))
((eltpr (assq elt elts))
((elt-type (zctype>eltpr-type eltpr)))))
(if (not eltpr)
(zcerror "Element ~A not found in struct//union type ~A" elt stype)
(nlet ((access-exp result-type
(zcprim>deref-pointer
(zcprim>pointer-plus-int
(zcprim>coerce-struct-pointer struct elt-type
(neq (zctype>struct-class stype)
:packed-struct))
(selectq (zctype>struct-class stype)
((:struct :packed-struct)
(zctype>struct-elt-offset elt stype env))
(:union 0))
(zctype>array-of (zctype>int)) #'zcprim>+ env)
(zctype>pointer-to elt-type) env
(neq (zctype>struct-class stype) :packed-struct))))
(zcprim>struct-hack-bit-field access-exp elt-type result-type))))))
(defun zcprim>struct-hack-bit-field (access-exp elt-type result-type)
"Given an expression to access a struct element, if the element is a bit field
within that word, wraps an appropriate LDB around the expression."
(if (not (zctype>bits-p elt-type)) (values access-exp result-type)
(let ((ldb-form (if (zctype>unsigned-p (cadr elt-type)) 'ldb 'ldb-signed)))
(values `(,ldb-form (byte ,(caddr elt-type) ,(cadddr elt-type)) ,access-exp)
(if (zctype>unsigned-p (cadr elt-type))
(zctype>unsigned) (zctype>int))))))
(defmacro ldb-signed (bytespec word)
(unless (and (listp bytespec) (and (eq (car bytespec) 'byte)))
(ferror "ZETA-C internal error: (BYTE ...) form expected, not ~S" bytespec))
(nlet ((temp (zcprim>gen-var))
(size (cadr bytespec)))
`(nlet ((,temp (ldb ,bytespec ,word)))
(dpb ,temp (byte ,(1- size) 0) (- (ldb (byte 1 ,(1- size)) ,temp))))))
#+Symbolics (putprop 'ldb-signed (get 'ldb 'lt::setf-method) 'lt::setf-method)
#-Symbolics (putprop 'ldb-signed (get 'ldb 'si:setf-method) 'si:setf-method)
(defun zcprim>coerce-struct-pointer (ptr elt-type unpacked)
"Given a pointer to some kind of struct, coerces it to point to ELT-TYPE."
(nlet ((let-clauses array index
(zcprim>analyze-pointer-exp ptr (zctype>array-of (zctype>int)))))
(selectq (zctype>type-scale elt-type unpacked)
(:Q ptr)
(:16B (zcprim>let-form let-clauses
`(zcptr>cons (zcprim>art-16b-slot ,array) ,(zcprim>* index 2))))
(:8B (zcprim>let-form let-clauses
`(zcptr>cons (zcprim>art-8b-slot ,array) ,(zcprim>* index 4)))))))
(defun zcprim>array-leader-init (name type env)
`((,name ,type ,env) nil nil nil nil nil #+Chars nil))
(defun zcprim>cast-array-leader-init (parent)
`(,parent nil nil nil nil nil #+Chars nil))
(defsubst zcprim>array-leader-length ()
#-Chars 6 #+Chars 7)
(defsubst zcprim>array-desc (frob)
(array-leader frob 0))
;;; In general, use NAMED-STRUCTURE-P instead.
(defsubst zcprim>array-named-structure-symbol (frob)
(array-leader frob 1))
(defsubst zcprim>array-freelist-link (frob)
(array-leader frob 2))
(defsubst zcprim>art-q-slot (frob)
(array-leader frob 3))
(defsubst zcprim>art-16b-slot (frob)
(array-leader frob 4))
(defsubst zcprim>art-8b-slot (frob)
(array-leader frob 5))
#+Chars
(defsubst zcprim>art-string-slot (frob)
(array-leader frob 6))
(defun zcprim>array-consed-type (frob)
"Returns the type with which FROB was originally consed."
(let ((desc (zcprim>array-desc frob)))
(if (arrayp desc)
(cadr (zcprim>array-desc desc))
(cadr desc))))
(defun zcprim>array-consed-env (frob)
"Returns the type with which FROB was originally consed."
(let ((desc (zcprim>array-desc frob)))
(if (arrayp desc)
(caddr (zcprim>array-desc desc))
(caddr desc))))
(defun zcprim>scale-slot (scale frob)