forked from acl2/acl2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
acl2-fns.lisp
2265 lines (1915 loc) · 91.1 KB
/
acl2-fns.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
; ACL2 Version 8.4 -- A Computational Logic for Applicative Common Lisp
; Copyright (C) 2022, Regents of the University of Texas
; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright
; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0.
; This program is free software; you can redistribute it and/or modify
; it under the terms of the LICENSE file distributed with ACL2.
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; LICENSE for more details.
; Written by: Matt Kaufmann and J Strother Moore
; email: [email protected] and [email protected]
; Department of Computer Science
; University of Texas at Austin
; Austin, TX 78712 U.S.A.
(in-package "ACL2")
(proclaim-optimize)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; PRELIMINARIES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmacro qfuncall (fn &rest args)
; Avoid noise in CCL about undefined functions, not avoided by funcall alone.
; But this doesn't help in ANSI GCL or CMU CL 19e on Linux, and even has broken
; on CMU CL 18d on Solaris, so we just punt on this trick for those Lisps.
(if (not (symbolp fn))
(error "~s requires a symbol, not ~s" 'qfuncall fn)
#+(and cltl2 (not cmu) (not gcl))
`(let () (declare (ftype function ,fn)) (,fn ,@args))
#-(and cltl2 (not cmu) (not gcl))
`(funcall ',fn ,@args)))
(defmacro defun-one-output (&rest args)
; Use this for raw Lisp functions that are known to return a single value in
; raw Lisp, since make-defun-declare-form uses that assumption to make an
; appropriate declaration.
(cons 'defun args))
; The following alist associates package names with Common Lisp packages, and
; is used in function find-package-fast, which is used by princ$ in place of
; find-package in order to save perhaps 15% of the print time.
(defparameter *package-alist* nil)
(defparameter *find-package-cache* nil)
(defun-one-output find-package-fast (string)
(if (equal string (car *find-package-cache*))
(cdr *find-package-cache*)
(let ((pair (assoc string *package-alist* :test 'equal)))
(cond (pair (setq *find-package-cache* pair) (cdr pair))
(t (let ((pkg (find-package string)))
(push (cons string pkg) *package-alist*)
pkg))))))
(defvar *global-symbol-key* (make-symbol "*GLOBAL-SYMBOL-KEY*"))
(defun global-symbol (x)
(or (get x *global-symbol-key*)
(setf (get x *global-symbol-key*)
(intern (symbol-name x)
(find-package-fast
(concatenate 'string
*global-package-prefix*
(symbol-package-name x)))))))
(defmacro live-state-p (x)
(list 'eq x '*the-live-state*))
#-acl2-loop-only
(defun get-global (x state-state)
; Keep this in sync with the #+acl2-loop-only definition of get-global (which
; doesn't use qfuncall).
(cond ((live-state-p state-state)
(return-from get-global
(symbol-value (the symbol (global-symbol x))))))
(cdr (assoc x (qfuncall global-table state-state))))
(defmacro f-get-global (x st)
(cond ((and (consp x)
(eq 'quote (car x))
(symbolp (cadr x))
(null (cddr x)))
; The cmulisp compiler complains about unreachable code every (perhaps) time
; that f-get-global is called in which st is *the-live-state*. The following
; optimization is included primarily in order to eliminate those warnings;
; the extra efficiency is pretty minor, though a nice side effect.
(if (eq st '*the-live-state*)
`(let ()
(declare (special ,(global-symbol (cadr x))))
,(global-symbol (cadr x)))
(let ((s (gensym)))
`(let ((,s ,st))
(declare (special ,(global-symbol (cadr x))))
(cond ((live-state-p ,s)
,(global-symbol (cadr x)))
(t (get-global ,x ,s)))))))
(t `(get-global ,x ,st))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; SUPPORT FOR NON-STANDARD ANALYSIS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Amazingly, some lisps do not have a definition for realp. In those
; implementations (apparently including at least one early version of GCL), we
; will use rationalp as a poor substitute which however suffices for ACL2
; objects.
#+:non-standard-analysis
(defun acl2-realp (x)
(rationalp x))
#+(and :non-standard-analysis CLTL2)
(if (not (fboundp 'common-lisp::realp))
(setf (symbol-function 'common-lisp::realp) (symbol-function 'acl2-realp)))
#+(and :non-standard-analysis (not CLTL2))
(if (not (fboundp 'lisp::realp))
(setf (symbol-function 'lisp::realp) (symbol-function 'acl2-realp)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; GCL VERSION QUERIES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
#+gcl
(defun gcl-version-> (major minor extra &optional weak)
; When true, this guarantees that the current GCL version is greater than
; major.minor.extra (or if weak is non-nil, than greater than or equal to).
; The converse holds for versions of GCL past perhaps 2.0.
(and (boundp 'si::*gcl-major-version*)
(integerp si::*gcl-major-version*)
(if (= si::*gcl-major-version* major)
(and (boundp 'si::*gcl-minor-version*)
(integerp si::*gcl-minor-version*)
(if (= si::*gcl-minor-version* minor)
(and (boundp 'si::*gcl-extra-version*)
(integerp si::*gcl-extra-version*)
(if weak
(>= si::*gcl-extra-version* extra)
(> si::*gcl-extra-version* extra)))
(if weak
(>= si::*gcl-minor-version* minor)
(> si::*gcl-minor-version* minor))))
(if weak
(>= si::*gcl-major-version* major)
(> si::*gcl-major-version* major)))))
#+gcl
(defun gcl-version->= (major minor extra)
(gcl-version-> major minor extra t))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; PROCLAIMING
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Essay on Proclaiming
; The variable acl2::*do-proclaims* determines whether or not we proclaim ACL2
; function types before compiling them. The intent of proclaiming is to
; improve performance, though we once observed proclaiming to have had the
; opposite effect for some combination of ACL2 and Allegro CL; see a comment in
; proclaim-file. It might be worthwhile to revisit, from time to time,
; proclaiming in more Lisps, especially those used heavily in the community.
; During the boot-strap we avoid any activity related to proclaiming types
; except, perhaps, by generating or loading file acl2-proclaims.lisp. At one
; time we did some proclaiming on a file-by-file basis, but this approach
; requires a lot of care to be done right. For example with *do-proclaims*
; true, compile-acl2 had by default used a file-by-file load/proclaim/compile
; process, while load-acl2 had proclaimed all files after loading the compiled
; files. We believe that this process allowed load-acl2 to come up with more
; specific function types during load-acl2 than had been used by compile-acl2
; We may have this change in types lead to buggy behavior.
; So in order to proclaim during the boot-strap, we use steps as shown below.
; First, here is the general process, which is currently unused. But we could
; try it for any Lisp other than GCL; see below for what we do in GCL. We note
; that GNUmakefile orchestrates both versions of these steps for "make".
; --- COMPILE: ---
; (1) In a fresh Lisp, call compile-acl2 without any proclaiming.
; --- OPTIONALLY PROCLAIM: ---
; (2) In a fresh Lisp, call generate-acl2-proclaims, which does these steps if
; *do-proclaims* is true (currently, GCL only), and otherwise is a no-op.
; (a) load-acl2;
; (b) initialize-acl2;
; (c) proclaim-files, to write out file "acl2-proclaims.lisp".
; --- OPTIONALLY RECOMPILE: ---
; (3) In a fresh Lisp, if *do-proclaims* is true (also see (2) above), load the
; generated file "acl2-proclaims.lisp" and then recompile.
; --- SAVE EXECUTABLE: ---
; (4) In a fresh Lisp, call save-acl2, which does these steps:
; (a) load "acl2-proclaims.lisp" if *do-proclaims* is true;
; (b) load-acl2;
; (c) initialize-acl2;
; (d) save an executable.
; In GCL, we modify this process to utilize GCL's own ability to proclaim
; function types. Note that this process doesn't proclaim types for defconst
; forms; from a Lisp perspective, that wouldn't make sense, since defconst
; symbols are variables, not constants.
; --- COMPILE: ---
; (1) In a fresh Lisp, call compile-acl2 without any proclaiming, as follows.
; (compiler::emit-fn t)
; (acl2::compile-acl2)
; (compiler::make-all-proclaims "*.fn")
; --- OPTIONALLY PROCLAIM: ---
; (2) In a fresh Lisp, call generate-acl2-proclaims, which does only the following
; if *do-proclaims* is true, and otherwise is a no-op.
; Rename sys-proclaim.lisp to acl2-proclaims.lisp.
; --- OPTIONALLY RECOMPILE: ---
; (3) In a fresh Lisp, if *do-proclaims* is true (also see (2) above), load the
; generated file "acl2-proclaims.lisp" and then recompile.
; --- SAVE EXECUTABLE: ---
; (4) In a fresh Lisp, call save-acl2, which does these steps:
; (a) load "acl2-proclaims.lisp" if *do-proclaims* is true;
; (b) load-acl2;
; (c) initialize-acl2;
; (d) save an executable.
; At one time we proclaimed for CCL, but one profiling run of a
; compute-intensive include-book form showed that this was costing us some 10%
; of the time. After checking with Gary Byers we decided that there was little
; if any benefit in CCL for proclaiming functions, so we no longer do it.
; Perhaps we should reconsider some time. See also comments below in
; *do-proclaims*.
; We considered adding &OPTIONAL to the end of every VALUES form (see comments
; below), based on output (since forgotten) from SBCL. But GCL has issued
; several dozen warnings during the build when this happened, so for now, since
; we are only proclaiming functions for GCL, we omit the &optional.
(defvar *do-proclaims*
; We may want to experiment for proclaiming with other Lisps besides GCL. But
; this might not be a good idea, in particular for Allegro CL and CCL (see
; above).
; In fact we experimented with CCL and ACL2(h) on 8/9/2013, by temporarily
; setting this variable to T. We got these results from "time" for make -j 8
; with target regression-fresh on dunnottar.cs.utexas.edu. The results are
; inconclusive, so we keep things simple (and avoid stepping on the possibility
; of future CCL improvements) by the skipping of function proclaiming in CCL.
; Built as follows (not showing here the setting PREFIX=):
; make ACL2_HONS=h LISP=ccl-trunk ACL2_SIZE=3000000
; 27815.314u 1395.775s 1:09:03.35 705.0% 0+0k 2008+1736952io 34pf+0w
; Built as follows (not showing here the setting PREFIX=):
; make ACL2_HONS=h LISP=ccl-trunk ACL2_SIZE=3000000 \
; ACL2_PROCLAIMS_ACTION=generate_and_reuse
; 27272.420u 1401.555s 1:09:11.18 690.7% 0+0k 333088+1750384io 303pf+0w
#+gcl
; The special value of :gcl says to use GCL's automatic mechanism during the
; boot-strap (but use ACL2's during normal execution). But it also should work
; to use t instead. Experiments may lead us to prefer one over the other.
:gcl
#-gcl nil)
(defun macroexpand-till (form sym)
; In order to find the THEs that we want to find to do automatic proclaiming of
; the output types of functions, we need to do macroexpansion at proclaim-time.
; It is possible that a given implementation of Common Lisp will macroexpand
; THE forms further. Hence we gently do the macroexpansion we need, one
; expansion at a time, looking for the THE we want to find.
(loop (cond ((and (consp form) (eq (car form) sym))
(return form))
(t (multiple-value-bind
(new-form flg)
(macroexpand-1 form)
(cond ((null flg) (return form))
(t (setq form new-form))))))))
(defun get-type-from-dcls (var dcls)
(cond ((endp dcls) t)
((and (consp (car dcls))
(eq 'type (caar dcls))
(member var (cddr (car dcls))))
(cadr (car dcls)))
(t (get-type-from-dcls var (cdr dcls)))))
(defun arg-declarations (formals dcls)
(cond ((endp formals) nil)
(t (cons (get-type-from-dcls (car formals) dcls)
(arg-declarations (cdr formals) dcls)))))
(defun collect-types (l)
(cond ((null (cdr l)) nil)
((stringp (car l))
(collect-types (cdr l)))
((consp (car l))
(let ((exp (car l)))
(cond ((and (consp exp) (eq (car exp) 'declare))
(append (cdr exp) (collect-types (cdr l))))
(t nil))))
(t nil)))
(defun convert-type-to-integer-pair (typ)
; Typ is (integer i j), (signed-byte i), or (unsigned-byte i). We return an
; equivalent type (integer i' j').
(case (car typ)
(integer (cdr typ))
(signed-byte (let ((n (expt 2 (1- (cadr typ)))))
(list (- n) (1- n))))
(unsigned-byte (list 0 (1- (expt 2 (cadr typ)))))
(t (error
"Unexpected type for convert-to-integer-type ~s"
typ))))
(defvar *acl2-output-type-abort* nil)
(defun min-integer-* (x y)
(cond ((and (integerp x) (integerp y))
(min x y))
(t '*)))
(defun max-integer-* (x y)
(cond ((and (integerp x) (integerp y))
(max x y))
(t '*)))
(defun max-output-type-for-declare-form (type1 type2)
; We return a supertype of type1 and type2, preferably as small as possible,
; else nil. We assume that each typei that is not null is (values ...) or is
; some sort of integer type.
(cond
((equal type1 type2)
type1)
((or (eq type1 '*)
(eq type2 '*))
'*)
((not (equal (and (consp type1)
(eq (car type1) 'values))
(and (consp type2)
(eq (car type2) 'values))))
'*)
((and (or (eq type1 'integer)
(and (consp type1)
(eq (car type1) 'integer)
(or (null (cddr type1))
(member '* (cdr type1) :test 'eq))))
(or (eq type2 'integer)
(and (consp type2)
(eq (car type2) 'integer)
(or (null (cddr type2))
(member '* (cdr type2) :test 'eq)))))
'integer)
((or (atom type1) (atom type2)) ; so, type is t since neither is *
t)
((cdr (last type1)) ; (not (true-listp type1))
(error
"Non-atom, non-true-listp type for max-output-type-for-declare-form: ~s"
type1))
((cdr (last type2)) ; (not (true-listp type2))
(error
"Non-atom, non-true-listp type for max-output-type-for-declare-form: ~s"
type2))
(t (let ((sym1 (car type1))
(sym2 (car type2)))
(cond
((eq sym1 sym2)
(case sym1
((signed-byte unsigned-byte)
(if (< (cadr type1) (cadr type2))
type2
type1))
(integer
(list 'integer
(min-integer-* (cadr type1) (cadr type2))
(max-integer-* (caddr type1) (caddr type2))))
(values
(cons 'values (max-output-type-for-declare-form-lst (cdr type1)
(cdr type2))))
(otherwise
(error
"Unexpected type for max-output-type-for-declare-form: ~s"
type1))))
((or (eq sym1 'values) (eq sym2 'values)) ; but not both
'*)
(t (let* ((pair1 (convert-type-to-integer-pair type1))
(pair2 (convert-type-to-integer-pair type2))
(lower1 (car pair1))
(upper1 (cadr pair1))
(lower2 (car pair2))
(upper2 (cadr pair2))
(lower-min (min-integer-* lower1 lower2))
(upper-max (max-integer-* upper1 upper2)))
(cond
((and (eql lower1 lower-min) (eql upper1 upper-max))
type1)
((and (eql lower2 lower-min) (eql upper2 upper-max))
type2)
(t
(list 'integer lower-min upper-max))))))))))
(defun max-output-type-for-declare-form-lst (type-list1 type-list2)
; Type-list1 and type-list2 are known to be true lists (nil-terminated
; lists).
(cond ((or (null type-list1) (null type-list2))
(cond
((and (null type-list1) (null type-list2))
nil)
((and *acl2-output-type-abort*
(or (atom type-list1) (atom type-list2)))
(cons '*
(max-output-type-for-declare-form-lst
(cdr type-list1) (cdr type-list2))))
(t
(error "Implementation error:~%~
max-output-type-for-declare-form-lst called on lists of~%~
different length:~%~
~s~% ~s~%~
Please contact the ACL2 implementors."
type-list1 type-list2))))
(t (cons (max-output-type-for-declare-form
(car type-list1) (car type-list2))
(max-output-type-for-declare-form-lst
(cdr type-list1) (cdr type-list2))))))
(declaim (ftype (function (t t)
(values t) ; see comment above about &optional
)
output-type-for-declare-form-rec))
(declaim (ftype (function (t t)
(values t) ; see comment above about &optional
)
output-type-for-declare-form-rec-list))
(defun output-type-for-declare-form-rec (form flet-alist)
; We return either nil or *, or else a type for form that ideally is as small
; as possible.
; Note that this isn't complete. In particular, ACL2's proclaiming mechanism
; for GCL produces a return type of * for RETRACT-WORLD, because it can return
; what RETRACT-WORLD1 returns, and below we don't account for defined raw Lisp
; functions like RETRACT-WORLD1. This could presumably be remedied by doing a
; sort of iterative computation of return types till we reach a fixed point,
; but that's for another day.
(cond
((integerp form)
`(integer ,form ,form))
((characterp form)
'character)
((atom form)
t)
((and (eq (car form) 'quote)
(consp (cdr form)))
(cond ((integerp (cadr form))
`(integerp ,(cadr form) ,(cadr form)))
((rationalp (cadr form))
`rational)
((numberp (cadr form))
'number)
((characterp (cadr form))
'character)
((null (cadr form))
'null)
((symbolp (cadr form))
'symbol)
((stringp (cadr form))
'string)
((consp (cadr form))
'cons)
(t ; impossible?
t)))
((and (eq (car form) 'setq) ; always returns a single value
(equal (length form) 3) ; avoid considering aborts from other values
)
(let ((x (output-type-for-declare-form-rec (car (last form))
flet-alist)))
(cond (*acl2-output-type-abort* '*)
((and (consp x)
(eq (car x) 'values))
(cadr x))
((eq x '*) t)
(t x))))
((and (eq (car form) 'setf)
(equal (length form) 3) ; avoid considering aborts from other values
)
(output-type-for-declare-form-rec (car (last form))
flet-alist))
((eq (car form) 'return-last)
(cond ((equal (cadr form) ''mbe1-raw)
(output-type-for-declare-form-rec (caddr form) flet-alist))
((and (equal (cadr form) ''progn)
(consp (caddr form))
(eq (car (caddr form)) 'throw-nonexec-error))
(setq *acl2-output-type-abort* '*))
(t (output-type-for-declare-form-rec (car (last form)) flet-alist))))
((eq (car form) 'return-from)
(output-type-for-declare-form-rec (car (last form)) flet-alist))
((eq (car form) 'when)
(let ((tmp (output-type-for-declare-form-rec (car (last form))
flet-alist)))
(cond (*acl2-output-type-abort* '*)
((or (atom tmp)
(not (eq (car tmp) 'values)))
t)
(t '*))))
((assoc (car form) flet-alist :test 'eq)
(cdr (assoc (car form) flet-alist :test 'eq)))
((member (car form) '(values
mv
; Note: for swap-stobjs, cadr and caddr have the same type.
swap-stobjs)
:test 'eq)
(cond ((null (cdr form))
(setq *acl2-output-type-abort* '*))
((null (cddr form)) ; (values &), or (mv &) if mv is values
(let* ((*acl2-output-type-abort* nil)
(tmp (output-type-for-declare-form-rec (cadr form)
flet-alist)))
(cond ((and (symbolp tmp)
(not (eq tmp '*))
(not *acl2-output-type-abort*))
tmp)
(t t))))
(t
(cons 'values
(loop for x in (cdr form)
collect
(let* ((*acl2-output-type-abort* nil)
(tmp
(output-type-for-declare-form-rec x
flet-alist)))
(cond ((and (symbolp tmp)
(not (eq tmp '*))
(not *acl2-output-type-abort*))
tmp)
(t t))))))))
((member (car form) '(flet labels)
:test 'eq) ; (flet bindings val)
(let ((temp flet-alist))
(dolist (binding (cadr form))
(let ((fn (car binding))
(body (car (last binding)))
*acl2-output-type-abort*)
(let ((typ (output-type-for-declare-form-rec body flet-alist)))
(push (cons fn
(if *acl2-output-type-abort*
'*
typ))
temp))))
(output-type-for-declare-form-rec (car (last form)) temp)))
((eq (car form) 'the)
(let ((typ (cadr form)))
(cond ((member typ '(integer fixnum character) :test 'eq)
typ)
((and (consp typ)
(member (car typ)
'(integer signed-byte unsigned-byte values)
:test 'eq))
typ)
(t t))))
((eq (car form) 'if)
(cond
((eq (cadr form) t) ; as generated for final cond branch in CCL
(output-type-for-declare-form-rec (caddr form) flet-alist))
((eq (cadr form) nil) ; perhaps not necessary
(output-type-for-declare-form-rec (cadddr form) flet-alist))
(t (let ((type1 (output-type-for-declare-form-rec (caddr form) flet-alist)))
(if (eq type1 '*) ; optimization
'*
(max-output-type-for-declare-form
type1
(output-type-for-declare-form-rec (cadddr form) flet-alist)))))))
((member (car form) '(let let*) :test 'eq)
(cond ((cddr form)
(output-type-for-declare-form-rec (car (last form)) flet-alist))
(t t)))
((eq (car form) 'multiple-value-bind)
(cond ((cdddr form)
(output-type-for-declare-form-rec (car (last form)) flet-alist))
(t t)))
((eq (car form) 'unwind-protect)
(output-type-for-declare-form-rec (cadr form) flet-alist))
((member (car form) '(time progn ec-call) :test 'eq)
(output-type-for-declare-form-rec (car (last form)) flet-alist))
((member (car form)
'(tagbody ; e.g., ld-fn
throw-raw-ev-fncall ; e.g., from defchoose
throw-nonexec-error
eval
error
)
:test 'eq)
(setq *acl2-output-type-abort* '*))
((member (car form)
; Feel free to add any symbol to the following list that, when called, is
; guaranteed to yield a single value.
'(* + - / 1+ 1-
= /= < <= > >=
append assoc
concatenate format import list list* make-hash-table member
mv-list nreverse position rassoc remove subsetp
; (strip-cars *ca<d^n>r-alist*)
CADR CDAR CAAR CDDR
CAADR CDADR CADAR CDDAR CAAAR CDAAR CADDR CDDDR
CAAADR CADADR CAADAR CADDAR CDAADR CDDADR CDADAR CDDDAR
CAAAAR CADAAR CAADDR CADDDR CDAAAR CDDAAR CDADDR CDDDDR)
:test 'eq)
t)
(t (multiple-value-bind
(new-form flg)
(macroexpand-1 form)
(cond ((null flg)
(cond ((atom form) t)
((eq (car form) 'multiple-value-prog1)
(and (consp (cdr form))
(output-type-for-declare-form-rec (cadr form) flet-alist)))
; Note: We don't expect multiple-value-setq to show up in ACL2.
((and (consp (car form))
(eq (caar form) 'lambda))
(output-type-for-declare-form-rec (caddr (car form)) flet-alist))
((not (symbolp (car form))) ; should always be false
'*)
(t (let ((x (and ; check that (w *the-live-state*) is bound
(boundp 'ACL2_GLOBAL_ACL2::CURRENT-ACL2-WORLD)
(fboundp 'get-stobjs-out-for-declare-form)
(qfuncall get-stobjs-out-for-declare-form
(car form)))))
(cond ((consp (cdr x))
(cons 'values
(make-list (length x)
:initial-element
t)))
(x t)
(t (setq *acl2-output-type-abort* '*)))))))
(t (output-type-for-declare-form-rec new-form flet-alist)))))))
(defun output-type-for-declare-form-rec-list (forms flet-alist)
(cond ((atom forms)
nil)
(t (cons (let ((tp (output-type-for-declare-form-rec (car forms)
flet-alist)))
(if (member tp '(nil *) :test 'eq)
t
tp))
(output-type-for-declare-form-rec-list (cdr forms)
flet-alist)))))
(defun output-type-for-declare-form (fn form)
; We return a list of output types, one per value.
(let* ((*acl2-output-type-abort* nil) ; protect for call on next line
(result (output-type-for-declare-form-rec form nil))
(stobjs-out (and
(not (member fn (symbol-value '*stobjs-out-invalid*)
:test #'eq))
; check that (w *the-live-state*) is bound
(boundp 'ACL2_GLOBAL_ACL2::CURRENT-ACL2-WORLD)
(fboundp 'get-stobjs-out-for-declare-form)
(qfuncall get-stobjs-out-for-declare-form fn))))
(when (and stobjs-out
(not (eq (and (consp result)
(eq (car result) 'values))
(consp (cdr stobjs-out))))
(not *acl2-output-type-abort*))
(error "Implementation error in ~s:~%~
stobjs-out and result don't mesh.~%~
Stobjs-out = ~s~%~
Result = ~s~%~
Please contact the ACL2 implementors."
(list 'output-type-for-declare-form fn '|defun...|)
stobjs-out result))
(cond
((and (consp result)
(eq (car result) 'values))
result ; see comment above about &optional
)
((or *acl2-output-type-abort*
(eq result '*))
'*)
(t
(list 'values result) ; see comment above about &optional
))))
(defun make-defun-declare-form (fn form
&optional
(output-type nil output-type-p))
; See the comment in proclaim-file for why we don't proclaim in more lisps.
(when *do-proclaims*
(let* ((output-type
(if output-type-p
output-type
(output-type-for-declare-form fn (car (last form))))))
(let ((formals (caddr form)))
(cond
((null (intersection formals lambda-list-keywords
:test 'eq))
`(declaim (ftype (function
,(arg-declarations
formals
(collect-types (cdddr form)))
,output-type)
; WARNING: Do not replace (cadr form) by fn below. These can differ! Fn is
; passed to output-type-for-declare-form in order to get its 'stobjs-out, but
; (cadr form) can be the *1* function for fn. The mistaken placement of fn
; below caused a factor of 4 slowdown in GCL in the first lemma5 in community
; book books/unicode/utf8-decode.lisp, because the proclaim for function
; utf8-combine4-guard was overwritten by a subsequent weaker proclaimed type
; that was supposed to be generated for the *1* function, but instead was
; generated for utf8-combine4-guard.
,(cadr form))))
(t nil))))))
(defun make-defconst-declare-form (form)
; We assume that the form has already been evaluated.
(when *do-proclaims*
(let* ((output (macroexpand-till (caddr form) 'the))
(output-type (cond ((and (consp output)
(eq 'the (car output)))
(cadr output))
(t nil))))
(cond
(output-type
`(declaim (type ,output-type ,(cadr form))))
(t (let ((val (symbol-value (cadr form))))
(if (integerp val)
`(declaim (type (integer ,val ,val) ,(cadr form)))
nil)))))))
(defun make-defstobj-declare-form (form)
(when *do-proclaims*
(let* ((name (cadr form))
(args (cddr form))
; The loss of efficiency caused by using symbol-value below should be more than
; compensated for by the lack of a warning here when building the system.
(template (qfuncall defstobj-template name args nil))
(raw-defs (qfuncall defstobj-raw-defs name template
; We do not want to rely on having the world available here, so we pass in nil
; for the final argument of defstobj-raw-defs. The only effect of using nil
; instead of a world in such a context is that additional checking by
; translate-declaration-to-guard is missing. We also pass in nil for
; congruent-to, since we don't think it makes any difference in the resulting
; declare form.
nil nil)))
(cons 'progn
(mapcar (function
(lambda (def) (if (member (symbol-value
'*stobj-inline-declare*)
def
:test (function equal))
nil
(make-defun-declare-form
(car def)
(cons 'defun def)))))
raw-defs)))))
(defmacro eval-or-print (form stream)
`(let ((form ,form)
(stream ,stream))
(when form
(if stream
(format stream "~s~%" form)
(eval form)))))
(defun proclaim-form (form &optional stream)
; We assume that this function is called under proclaim-file, which binds
; *package*. See the comment below for the in-package case.
(when *do-proclaims*
(cond ((consp form)
(case (car form)
((in-package)
(eval-or-print form stream)
(when stream
; We make sure that when we're merely printing, nevertheless we are in the
; correct package as we read the rest of the file.
(eval form))
nil)
((defmacro defvar defparameter) nil)
((defconst)
(eval-or-print (make-defconst-declare-form form) stream)
nil)
((defstobj)
(eval-or-print (make-defstobj-declare-form form) stream))
((eval-when)
(dolist (x (cddr form))
(proclaim-form x stream))
nil)
((progn mutual-recursion)
(dolist (x (cdr form))
(proclaim-form x stream))
nil)
((defun defund)
; debugging code:
; (let ((decl-form (make-defun-declare-form (cadr form) form)))
; (when (null decl-form)
; (format t "@@ Failed: ~s~%" (cadr form)))
; (eval-or-print decl-form stream))
; non-debugging code:
(eval-or-print (make-defun-declare-form (cadr form) form)
stream)
nil)
(defun-one-output
(eval-or-print (make-defun-declare-form
(cadr form)
form
'(values t) ; see comment above about &optional
)
stream)
nil)
(otherwise nil)))
(t nil))))
(defun proclaim-file (name &optional stream)
; Proclaims the functions in the file name that are either at the top-level, or
; within a progn, mutual-recursion, or eval-when. IMPORTANT: This function
; assumes that the defconst forms in the given file have already been
; evaluated. One way to achieve this state of affairs, of course, is to load
; the file first.
; Just before creating Version_2.5 we decided to consider proclaiming for
; Lisps other than GCL. However, our tests in Allegro suggested that this may
; not help. The comment below gives some details. Perhaps we will proclaim
; for MCL in the future. At any rate, CCL (OpenMCL) is supported starting with
; Version_2.8. We tried proclaiming for that Lisp, but no longer do so; see
; Section "PROCLAIMING" above.
; Here is a summary of three comparable user times from certifying all the ACL2
; books in June 2000, just before Version_2.5 is complete. The first column,
; labeled "Comp", is the one to be looked at for comparison purposes. These are
; all done on the same Sun workstation, using Allegro 5.0.1. The meanings of
; these numbers are explained below.
;
; Comp Actual Comments
; Recent ACL2 without proclaim: 5:41 5:36:42 no meta
; Recent ACL2 *with* proclaim: 5:54 5:53:58
; April ACL2 (before non-std.): 5:48 5:35:58 missing some pipeline and ~40
; sec. user time on powerlists
;
; The "Comp" column estimates how long the run would have taken if all books had
; certified, except that no run gets past book batcher-sort in the powerlists/
; directory. (The April run bogs down even slightly earlier.) The first row is
; adjusted by about 4 minutes because the run started with book meta-plus-lessp.
; The April run broke on book basic-def from case-studies/pipeline and hence
; missed the rest of that directory's books. The above points account for the
; addition of time from "Actual" to the appropriate comparison time in the first
; column.
(when *do-proclaims*
(format t "Note: Proclaiming file ~s.~%" name)
(with-open-file
(file name :direction :input)
(let ((eof (cons nil nil))
(*package* *package*))
(loop
(let ((form (read file nil eof)))
(cond ((eq eof form) (return nil))
(t (proclaim-form form stream)))))
nil))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ACL2's Implementation of the Backquote Readmacro
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defparameter *comma* (make-symbol "COMMA")
"*comma* is used by the backquote reader. When the reader
encounters <,foo>, it returns (list *comma* read:<foo>>).")
(defparameter *comma-atsign* (make-symbol "COMMA-ATSIGN")
"*comma-atsign* is used by the backquote reader. When the reader
encounters <,@foo>, it returns (list *comma-atsign* read:<foo>).")
(defparameter *backquote-counter* 0
"READ cannot handle a comma or comma-atsign unless there is a
pending backquote that will eliminate the *comma* or *comma-atsign*.
In the SPECIAL variable *backquote-counter* we keep track of the number of
backquotes that are currently pending. It is crucial that this variable
be SPECIAL.")
(defun backquote (x)
"The two functions BACKQUOTE and BACKQUOTE-LST implement backquote
as described on pp. 349-350 of CLTL1 except that (a) use of vector
notation causes an error and (b) the use of ,. is not permitted."
; It must be emphasized that the ACL2 implementation of backquote is
; only one of many implementations that are consistent with the Common
; Lisp specification of backquote. That spec requires only that
; backquote produce a form that when evaluated will produce a
; designated object. We impose the requirement that *acl2-readtable*
; be used both when checking files with ACL2 and when later compiling
; or using those files. This requirement guarantees that we obtain
; the same behavior of backquote across all Common Lisps. For
; example, it is an ACL2 theorem, across all Common Lisps that
; (equal (car '`(,a)) 'cons)
; This theorem is definitely not true about the implementation of
; backquote provided by the implementors of each Common Lisp. In
; fact, the lefthand side of this theorem represents an informal
; misuse of the backquote notation because one is intended to consider
; the effects of evaluating backquoted forms, not the forms
; themselves. (In some Common Lisps, the lefthand side might even
; evaluate to a symbol in a nonstandard package.) Nevertheless,
; because we inflict our definition of backquote on the ACL2 user at
; all times, the above equation is a theorem throughout, so no harm is
; done. On the other hand, if we used the local implementation of
; backquote of each particular Common Lisp, we would get different
; ACL2 theorems in different Common Lisps, which would be bad.
; Unlike most implementors of backquote, we do no serious
; optimization. We feel this inattention to efficiency is justified
; at the moment because in the usage we expect, the only serious costs
; will be small ones, during compilation.
; Our backquote always returns a cons-expression on a cons. In
; particular, '`(a) returns (CONS 'A 'NIL) and not '(a), which would
; be legal in any backquote that produces a constant, e.g., one
; containing no commas. We rely on the fact that the backquote of a
; cons is a cons-expression in our documentation of methods of
; bypassing the restrictions translate puts on LAMBDA objects in :FN
; slots. For example, see :DOC gratuitous-lambda-object-restrictions.
(cond ((and (vectorp x) (not (stringp x)))
(error "ACL2 does not handle vectors in backquote."))
((atom x) (list 'quote x))
((eq (car x) *comma*) (cadr x))
((eq (car x) *comma-atsign*) (error "`,@ is an error"))
(t (backquote-lst x))))
(defun backquote-lst (l)
(cond ((atom l) (list 'quote l))
((eq (car l) *comma*)
(cadr l))
((eq (car l) *comma-atsign*)
(error ". ,@ is illegal."))
((and (consp (car l))
(eq (caar l) *comma*))
(list 'cons
(cadr (car l))
(backquote-lst (cdr l))))
((and (consp (car l))
(eq (caar l) *comma-atsign*))
(cond ((null (cdr l))
(cadr (car l)))
(t (list 'append (cadr (car l)) (backquote-lst (cdr l))))))
(t
(list 'cons
(backquote (car l))
(backquote-lst (cdr l))))))
(defvar *read-object-comma-count* nil)
(defun acl2-comma-reader (stream)
(when *read-object-comma-count*
(incf *read-object-comma-count*))
(when (< *backquote-counter* 0)
(let* ((pathname (and (typep stream 'file-stream)
(pathname stream)))
(posn (and pathname
(file-position stream))))
(clear-input stream)