-
Notifications
You must be signed in to change notification settings - Fork 84
/
lispc.6
executable file
·2641 lines (2121 loc) · 113 KB
/
lispc.6
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
-*-Text-*-
This file describes the LISP compiler, COMPLR.
It could use more work; many nodes are too long, many others refer
only to the Multics implementation, and some are completely
incomprehensible.
This file put into INFO format by David Eppstein <KRONJ at MC>.
File: LISPC, Node: Top, Up: (DIR)
LISP programs can be compiled into machine code. This
representation of a program is more compact than the interpreted
list-structure representation, and it can be executed much more
quickly. However, a price must be paid for these benefits. It is not
as easy to intervene in the execution of compiled programs as it is
with interpreted programs. Thus most LISP programs should not be
compiled until after they have been debugged.
In addition, not all LISP programs can be compiled. There are
certain things which can be done with the interpreter that cannot be
effectively compiled. These include indiscriminate use of the
functions eval and apply, especially with pdl-pointer arguments;
"nonlocal" use of the go and return functions; and functions which
modify their own code. Also, there are a number of functions which
detect illegal arguments when they are called interpretively but not
when a call to them is compiled; erroneous compiled programs can
therefore damage the LISP environment and can cause strange errors to
occur -- be forewarned. However, most "normal" programs are
compilable.
* Menu:
* Peculiarities::
* Declare::
* Running Functions::
* Running the Compiler::
* The Lisp Assembly Program: LAP
* Internal Implementation Details: Details
* Other Languages::
Node: Peculiarities, Up: Top, Next: Declare
Some operations are compiled in such a way that they will behave
somewhat differently from the way they did when they were interpreted.
It is sometimes necessary to make a "declaration" in order to obtain
the desired behavior. This is explained on .
* Menu:
* Vars::
* InLine Coding::
* Function Calling::
* Input::
* Output::
* Functions::
Node: Vars, Up: Peculiarities, Next: InLine Coding
In the interpreter "variables" are implemented as atomic symbols
which possess "shallow-bound" value cells. The continual manipulation
of value cells would decrease the efficiency of compiled code, so the
compiler defines two types of variables: "special variables" and
"local variables." Special variables are identical to variables in
the interpreter.
Local variables are more like the variables in commonly-used
algebraic programming languages such as Algol or PL/I. A local
variable no longer has an associated atomic symbol; thus it can only
be referred to from the text of the same function that bound it. The
compiler creates local variables for prog- variables, do-variables,
and lambda-variables, unless directed otherwise. The compiled code
stores local variables in machine registers or in locations within a
stack.
The principal difference between local variables and special
variables is in the way a binding of a variable is compiled. (A
binding has to be compiled when a prog-, do-, or lambda-expression is
compiled, and for the entry to a function which has lambda-variables
to be bound to its arguments.) If the variable to be bound has been
declared to be special, the binding is compiled as code to imitate the
way the interpreter binds variables: the value of the atomic symbol is
saved and a new value is stored into its value cell. If the variable
to be bound has not been declared special, the binding is compiled as
the declaration of a new local variable. Code is generated to store
the value to which the variable is to be bound into the register or
stack-location assigned to the new local variable. This runs
considerably faster than a special binding.
Although a special variable is associated with an atomic symbol
which has the name of the variable, the name of a local variable
appears only in the input file - in compiled code there is no
connection between local variables and atomic symbols. Because this
is so, a local variable in one function may not be used as a "free
variable" in another function since there is no way for the location
of the variable to be communicated between the two functions.
When the usage of a variable in a program to be compiled does not
conform to these rules, i.e. it is somewhere used as a "free
variable," the variable must be declared special. There are two
common cases in which this occurs. One is where a "global" variable
is being used, i.e. a variable which is setq'ed by many functions but
is never bound. The other is where two functions cooperate, one
binding a variable and then calling the other one which uses that
variable as a free variable.
Certain variables built into the LISP system, such as IBASE and
ERRSET, are automatically assumed by the compiler to be special unless
declared otherwise. This exception to the general rule is predicated
on the assumption that the when the user sets, for example, the value
of IBASE, he intends to affect the operation of the READ function.
Node: InLine Coding, Up: Peculiarities, Next: Function Calling, Previous: Vars
Another difference between the compiler and the interpreter is
"in-line coding," also called "open coding." When a form such as (and
(foo x) (bar)) is evaluated by the interpreter, the built-in function
and is called and it performs the desired operation. But to compile
this form as a call to the function and with list-structured arguments
derived from (foo x) and (bar) would negate much of the advantage of
compiling. Instead the compiler recognizes and as part of the LISP
language and compiles machine code to carry out the intent of (and
(foo x) (bar)) without actually calling the and function. This code
might look like:
pick up value of variable x
call function foo
is the result nil?
if yes, the value of the and is nil
if no, call the function bar
the result of the and is what bar returned.
This "in-line coding" is done for all special forms (cond, prog, and,
errset, setq, etc.); thus compiled code will usually not call any of
the built-in fsubrs.
Another difference between the compiler and the interpreter has to
do with arithmetic operations. Most computers on which MACLISP is
implemented have special instructions for performing all the common
arithmetic operations. The MACLISP compiler contains a "number
compiler" feature which allows the LISP arithmetic functions to be
"in-line coded" using these instructions.
A problem arises here because of the generality of the MACLISP
arithmetic functions, such as plus, which are equally at home with
fixnums, flonums, and bignums. Most present-day computers are not
this versatile in their arithmetic instructions, which would preclude
open-coding of plus. There are several ways out of this problem. One
is to use the special purpose functions which only work with one kind
of number. For example, if you are using plus but actually you are
only working with fixnums, use + instead. The compiler can compile (+
a b c) to use the machine's fixnum-addition instruction. A second
solution is to write (plus a b (foo c)) but tell the compiler that the
values of the variables a and b, and the result of the function foo
can never be anything but fixnums. This is done by means of the
"number declarations" which are described on . A third way is to use
the FIXSW and FLOSW declarations. Note that when interpreted (plus a
b (foo c)) can legitimately produce a bignum even though all three
numbers added are fixnums, but the open-compiled code will not check
for overflow and will simply lose high-order bits in such cases. This
is true no matter how you cause the expression to be open-coded.
Another problem that can arise in connection with the in-line
coding of arithmetic operations is that the LISP representation of
numbers and the machine representation of numbers may not be the same.
Of course, this depends on the particular implementation. If these
two representations are different, the compiler would store variables
which were local and declared to be numeric-only in the machine form
rather than the LISP form. This could result in compilation of poor
code which frequently converts number representations and in various
other problems. Compilers which have this problem provide a (closed
t) declaration which inhibits open coding of arithmetic operations.
Node: Function Calling, Up: Peculiarities, Next: Input, Previous: InLine Coding
Another property of compiled code that should be understood is the
way functions are called. In the interpreter function calling
consists of searching the property list of the called function for a
functional property (if it is an atomic symbol) and then recursively
evaluating the body of the function if it is an expr, or transferring
control to the function if it is a subr. In compiled code function
calling is designed according to the belief that most of the functions
called by compiled code will be machine executable, i.e. "subrs":
other compiled functions, or built-in functions, and only infrequently
will compiled code call an interpreted function. Therefore a calling
mechanism is used which provides for efficient transfer between
machine-executable functions without constant searching of property
lists. This mechanism is called the "uuo link" mechanism for
historical reasons.
When a compiled function is first loaded into the environment, it
has a uuo link for each function it will call. This uuo link contains
information indicating that it is "unsnapped" and giving the name of
the function to be called, which is an atomic symbol. The first time
a call is made through such a uuo link, the fact that it is
"unsnapped" is recognized and a special linking routine is entered.
This routine searches the property list of the function to be called,
looking for a functional property (or an autoload property) in just
the same way as the interpreter would. If the function turns out to
be an expr, or is undefined, the interpreter is used to apply the
function and the result is given back to the compiled code. The link
is left "unsnapped" so that every time this function is called the
interpreter will be invoked to interpret its definition. If, however,
the function being called is machine executable (a subr), the link is
"snapped." Exactly what this means is implementation dependent but
the effect is that from then on whenever a call is made through this
uuo link, control will be transferred directly to the called function
using the subroutine-calling instruction of the machine, and neither
the linking routine nor the interpreter will be called.
There is a flag which can be set so that links will not be snapped
even if they go to a function which is machine executable. This flag
is the value of the atomic symbol nouuo. (See .) There is also a
function, (sstatus uuolinks), which unsnaps all the links in the
environment. These facilities are used in circumstances such as when
a compiled function is redefined, or compiled code is being traced or
otherwise debugged.
In the pdp-10 implementation a uuo link is implemented as an
instruction which is executed when a call is to be made through the
link. An "unsnapped" link consists of a special instruction, "UUO",
which causes the LISP linking routine in the interpreter to be called.
The address field of the uuo points to the atomic symbol which names
the function to be called. The operation code and accumulator fields
indicate the type of call and number of arguments. When the link is
snapped the UUO instruction is replaced with a "PUSHJ" instruction,
which is the machine instruction for calling subroutines.
In the Multics implementation, a uuo link is implemented as a
pointer. To call through this link a "tspbp" instruction indirect
through the pointer is used. An unsnapped link points at the linking
subroutine and various fields in the pointer, left unused by the
machine, indicate the type of call, number of arguments, and the
atomic symbol which names the function. When the link is snapped the
pointer is changed to point at the first instruction of the called
function.
Before a function can be used it must be made known in the LISP
environment. Interpreted functions are made known simply by putting a
functional property on the property list of the atomic symbol which
names the function. This is usually done using the built-in function
defun. Compiled functions must be made known by a more complex
mechanism known as "loading," because of the complexity of the support
mechanisms needed to make compiled functions execute efficiently. In
some dialects of LISP the compiler automatically makes the compiled
functions known, but in MACLISP the compiler creates a file in the
file system of the host operating system, and this file has to be
loaded before the compiled function can be called. In the pdp-10
implementation this file is called a "fasl file." In the Multics
implementation it is called an "object segment." Loading is described
in detail on .
Node: Input, Up: Peculiarities, Next: Output, Previous: Function Calling
The input to the compiler consists of an ascii file containing a
number of S-expressions. The format of this file is such that it
could be read into a LISP environment using a function such as load or
uread, and then the functions defined in this file would be executed
interpretively.
When a file is compiled, the compiler reads successive
S-expressions from the file and processes them. Each is classified as
a function definition, a "declare-form", a "macro-form", or a "random
form" according to what type of object it is and according to its car
if it is a list.
A function definition is a form whose car is one of the atoms
defun, defprop. When the compiler encounters a function definition if
it defines a macro the macro is defined for use at compile time. If
it defines an expr or a fexpr, the compiler translates the definition
from LISP to machine code and outputs it into the "fasl file" or
"object segment" which is the output from the compiler. If it defines
some other property, it is treated as a random form.
A macro form is any form whose car has previously been defined to
be a macro. When a macro form is read from the input file, the
compiler will apply the macro and then process the result as if it had
been read from the input file. Thus if foo is a macro which expands
(foo a b c) into (defun a ...), the resulting function definition will
be compiled.
A declare-form is a form whose car is the atom declare. It is
ignored by the interpreter because there is an fsubr called declare in
MACLISP which does nothing.
A progn form is any form which starts out as (progn 'compile...
The compiler processes each of the remaining elements of the progn
form as if it had been encountered at top level in the file. Progn
forms are useful for macros (and read macro characters) which are to
expand into a function definition and a declare form. For example,
one might define a macro defloat such that (defloat f (a b) ...)
expanded into I don't know pub well enough. Note that the (progn
'compile ...) will be processed correctly by the interpreter also.
Forms which are calls to the functions include or %include are
simply evaluated when they are encountered. This causes the contents
of the specified file to be included in the compilation at that point,
just as when interpreting such a form would cause the contents of the
file to be loaded at that point. See the description of %include,
below.
A random form is anything read from the input file that is not one
of the special types of forms described above. It is simply copied
into the output file of the compiler in such a way that when that file
is loaded it will be evaluated.
Node: Output, Up: Peculiarities, Next: Functions, Previous: Input
The output of the compiler normally consists of error and warning
messages on the terminal, and a file of machine code which can be
loaded into a lisp environment with load or fasload. In the pdp-10
implementation it is also possible to get a "lap file." This is a file
which contains machine code in symbolic form.
In the Multics implementation the compiler produces a standard
object segment with a translator name of "lisp" and a symbol section
which contains the information used by load to define functions, set
up constants needed by the compiled code, set up "uuo links", etc.
When the object segment is "loaded", it is not copied into the lisp
environment. Instead a "linkage block" is set up in the environment
and initialized according to directives in the segment's symbol
section. This block includes the reference name of the object segment
and a pointer to it. Thus compiled code is automatically shared
between multiple users in the Multics implementation. However, list
structure constants used by the compiled code can never be shared.
In the pdp-10 implementation the output of the compiler is a "fasl
file." This file begins with a header identifying it as a fasl file
and indicating what version of lisp it was produced for. (This is
used to detect incompatibilities.) The rest of the file consists of a
series of directives to load and relocate code words, set up
list-structure constants, reference value cells of symbols, evaluate
random S-expressions, etc.
fasload operates by reading through the file, storing code in
lisp's binary program space, and generating the necessary LISP objects
for constants used by the compiled code. Normally none of this is
shared between users, but see for information on how to make it pure
and shared.
There is a function defined in the compiler, coutput, which can be
used to put a random S-expression into the output file. When the file
is loaded, this S-expression will be evaluated. This can be used to
print the version number of the program, initialize its data base,
etc. It cannot be used to fool around with obarrays because of the
way the loader handles atomic symbols. For efficiency, it creates a
table of all the atoms needed by the file being loaded, and creates
and interns them all just once. This makes loading much faster, but
means that everything in a file has to go on the same obarray.
The coutput function usually does not have to be used, since the
compiler coutputs any "random form" it reads from the input file. It
is provided for the benefit of certain classes of hairy macros.
Node: Functions, Up: Peculiarities, Previous: Output
Functions Connected with the Compiler:
declare FSUBR
In the interpreter, declare is like comment. In the compiler,
the arguments are evaluated at compile time. This is used to
make declarations, to gobble up input needed only in the
interpreter, or to print messages at compile time.
Examples:
(declare (special x y) (*fexpr f00))
(declare (read)) ;in compiler, gobble next S-expression.
(something-needed-only-in-the-interpreter)
(declare (princ '|Now compiling foobar|))
%include FSUBR
(%include name) is used to cause an "include file" to be
included in the input to the compiler. It works in the
interpreter also, causing the specified file to be inpush'ed.
name may be a string or an atomic symbol. The translator search
rules are used.
In the PDP-10 implementation, this function is called include
(no "%"). name may also be a namelist.
(defun include fexpr (x)
(prog (file)
(setq file (open (car x)))
(eoffn file '+internal-include-eoffn)
(inpush file)))
(defun +internal-include-eoffn (f v) nil)
Node: Declare, Up: Top, Next: Running Functions, Previous: Peculiarities
It is often necessary to supply information to the compiler beyond
the definition of a function with defun, in order to compile the
function, although the definition is all that the interpreter needs in
order to interpret the function. This information can be supplied
through declarations.
A declare form is a list whose first element is the atom declare
and whose remaining elements are forms called "declarations." The
compiler processes a declare form by evaluating each of the
declarations, at compile time. Usually the declarations call on one
of the declaration functions which the compiler provides. These are
described below. However, it is permissible for a declaration to be
any evaluable form, and it is permissible for a declaration to read
from the input file by using the read function. This may be used to
prevent the compiler from seeing certain portions of the input which
are only needed when a program is run interpretively. Prefixing a
form in the input file with (declare (eval (read))) would cause it to
be evaluated at compile time if the file was compiled, but evaluated
at read-in time if the file was interpreted. Arbitrarily complex
compile-time processing may be achieved by the combination of
declarations and macros.
A few declarations may be used locally. If a declare form occurs
as the first form of a lambda-, prog- or do- body, it is processed by
the compiler before the compilation of the rest of the body, but is
effective only during the compilation of that body. The declarations
which may be used locally are special, fixnum, flonum, and notype, and
they may apply only to variables bound in that lambda, prog or do, or
to global variables.
Example:
(defun factorial (n)
(declare (fixnum n))
(do ((m n (- m 1))
(a 1 (* m a)))
((= m 0) a)
(declare (fixnum m a))))
The remainder of this section describes the declaration functions
provided by the compiler. Note that if a declaration function
described below is of the form (foo t), its effect can be reversed by
using the form (foo nil).
(special var1 var2 ... )
Declares var1, var2, etc. to be special variables.
(unspecial var1 var2 ... )
Declares var1, var2, etc. to be local variables.
(*expr fcn1 fcn2 ... )
Declares that fcn1, fcn2, etc. are expr- or subr-type functions
that will be called. This declaration is generally supplied by
default by the compiler, but in some peculiar circumstances it is
required to tell the compiler what is going on when the same
symbol is used as both a function and a variable. It is good
practice to put *expr, *lexpr, and *fexpr declarations for all
the functions defined in a file near the beginning of that file.
(*lexpr fcn1 fcn2 ... )
Declares fcn1, fcn2, etc. to be lexpr- or lsubr-type functions
that will be called. This declaration is required for
non-builtin functions unless the functions are defined in the
file being compiled and are not referenced by any functions that
are defined before they are.
(*fexpr fcn1 fcn2 ... )
Declares fcn1, fcn2, etc. to be fexpr- or fsubr-type functions
that will be called. This declaration is required for
non-builtin functions unless the functions are defined in the
file being compiled and are not referenced by any functions that
are defined before they are.
(**array arr1 arr2 ... )
Declares arr1, arr2, etc. to be arrays that will be referred to.
This declaration is obsolete and is being phased out. Use array*
instead. See the note under *expr.
(array* (type arr1 n1 arr2 n2 ... ) ... )
Is used to declare arrays arr1, arr2, etc. type may be fixnum,
flonum, or notype; it indicates what type of objects will be
contained in the arrays. n1, n2, etc. are the number of
dimensions in arr1, arr2, etc. respectively. The extended form
(array* (type (arr1 dim1.1 dim1.2 ... dim1.n) ...)) is
preferred if the dimensions are known at compile-time. The
dimensions declared must be either fixnums, or nil or ?, which
indicate a dimension not known at compile time. If dimensions
are declared, the compiler can generate faster code.
The array* declaration causes the compiler to generate in-line
code for accesses of and stores into the arrays declared. This
code is somewhat faster than the usual subroutine-call array
accessing. The compiler will also generate in-line code if the
arraycall function is used; in this case the array must be named
by an array-pointer value rather than by an atomic symbol. Note
that arrays declared by array* are arrays of fixed name, not
variables whose values are array pointers.
(fixnum var1 var2 ... )
Declares var1, var2, etc. to be variables whose values will
always be fixnums.
(fixnum (fcn type1 type2 ... ) ... )
Declares fcn to be a function which always returns a fixnum
result. Also the types of the arguments may be declared as
type1, type2, etc. An argument type may be fixnum, meaning the
argument must be a fixnum; flonum, meaning the argument must be a
flonum; or notype, meaning the argument may be of any type.
The two types of fixnum declarations may be intermixed, for
example (fixnum x (foo fixnum) y). Note, however, that
functions, unlike variables, may not be declared locally.
(flonum var1 var2 ... (fcn type1 ... ) ... )
Is the same as the fixnum declaration except the variables or
function- results are declared to always be flonums.
(notype var1 var2 ... (fcn type1 ... ) ... )
Is the same as the fixnum declaration except the variables or
function- results are declared not to be of any specific type.
(fixsw t)
Causes the compiler to assume that all arithmetic is to be done
with fixnums exclusively, except that obviously functions such as
+$ and cos will still use flonums.
(fixsw nil)
Turns off the above.
(flosw t)
Causes the compiler to assume that all arithmetic is to be done
with flonums exclusively, except that obviously functions such as
+ and rot will still use fixnums.
(flosw nil)
Turns off the above.
fixsw and flosw are variables; hence, (setq fixsw t) is
equivalent as a declaration to (fixsw t).
(setq special t)
Causes all variables to be special. Note: (special t) does not
do the same thing!.
[Annotation by REM, 1979 Sept 13 -- That is nonsense. The correct declaration
is (setq specials t). Note the plural form! With two differences it should
be obvious that this declaration makes everything special whereas (special t)
merely makes T special.]
(setq nfunvars t)
Causes the compiler to disallow functional variables. All
symbols in function position in a form are assumed to have a
functional property at run time. The case of a symbol whose
value is a functional form is disallowed.
(macros t)
Causes macro definitions to be retained at run time. Normally,
macro definitions in files being compiled are used for
compilation purposes only. This declaration causes them to be
written into the output file as well, so that loading it will
define the macros at run time.
(macros nil)
Causes macros to be used only at compile time. This is the
default choice.
(genprefix foo)
Causes auxiliary functions generated by the compiler (for
instance when function is used) to be named foon, where n is a
number incremented by 1 each time such a function is generated.
The genprefix declaration is used when several separately
compiled files are to be loaded together, in order to avoid name
clashes. In the PDP-10 implementation, the genprefix is
initialized as a function of the directory and first name of the
main input file. On ITS, generated names are thus of the form
"USER;FOO-43", while on TOPS-10 they are of the form
"[10,7]FOO-43".
The following declarations are useful only in the pdp-10
implementation; however, the Multics implementation will accept them
and ignore those which are irrelevant.
(mapex t)
In the pdp-10 implementation, causes all map-type functions to be
open- coded as do loops. (This is always done in the Multics
implementation.) The resulting code is somewhat larger than
otherwise, but also somewhat faster.
(mapex nil)
Causes map-type functions to actually be called. This is the default.
(messioc chars)
Causes an (ioc chars) to be done just before printing out each
error message. In this way one may direct error messages to the
LAP file instead of to the terminal on the pdp-10.
The default messioc is vr which puts the messages in both places.
(muzzled t)
Prevents the pdp-10 fast-arithmetic compiler from printing out a
message every time closed compilation of arithmetic is forced.
(muzzled nil)
Causes the compiler to print a message when closed-compilation is
forced. This is the default.
(symbols t)
Causes the compiler to output LAP directives so that the LAP
assembler will attempt to pass assembly symbols to DDT for
debugging purposes.
(symbols nil)
Does not generate debugging symbols. This is the default.
(closed t)
Causes arithmetic operations to be close-compiled, that is, the
function + will generate in-line code but the function plus will
not in any circumstances. This declaration is necessary if you
apply plus to two fixnums and want a bignum result if the
operation overflows.
(closed nil)
Causes the compiler to produce code that assumes overflow will
not occur, which may give incorrect results in the above case.
When the compiler can determine, by declaration or implication,
that all of the operands to an arithmetic function are fixnums
(or flonums), it will generate code to use the hardware fixnum
(or flonum) instructions. This is the default state.
This declaration only exists in the Multics implementation.
(defpl1 ...)
Defines an interfacing function which may be used to call
programs written in other languages, such as PL/I. See for
details.
Node: Running Functions, Up: Top, Next: Running the Compiler, Previous: Declare
After a file of functions has been compiled, those functions can be
loaded into an environment and then used. They can be loaded either
by using the load or fasload functions described below, or by using
the autoload feature described in section 12.4.4.
The following function is at present available only in the Multics
implementation.
load SUBR 1 arg
(load x), where x is a file specification acceptable by openi,
i.e. a namestring or a namelist, causes the specified file to be
loaded into the environment. The file may be either a source
file or a compiled file (called a "fasl" file in the ITS
implementation and an object segment in the Multics
implementation.) load determines which type of file it is and
acts accordingly. A source file is loaded by openi'ing and
inpush'ing it. A read-eval loop is then executed until the end
of the file is reached. An object file is loaded by reading it,
defining functions as directed by specifications inserted in the
file by the compiler.
fasload FSUBR
fasload takes the same arguments as uread. It causes a file
of compiled functions, called a "fasl" file in some
implementations, to be loaded in. Example:
(fasload foo fasl dsk macsym)
The following function only exists in the Multics implementation.
defsubr LSUBR 3 to 7 args
defsubr is the function used to define new machine code
functions. It defines various types of functions, depending on
its arguments. The way to define a subr written in PL/I is
(defsubr "segname" "entryname" nargs)
which defines segname$entryname as a subr expecting nargs
arguments. The value returned is a pointer which can be
putprop'ed under the subr property or the fsubr property. The
way to define an lsubr written in PL/I is
(defsubr "segname" "entryname" nargs2*1000+nargs1 -2)
which defines segname$entryname as an lsubr allowing from nargs1
to nargs2 arguments. The 1000 is octal. The value returned
should be putprop'ed under the lsubr property.
Examples:
(putprop 'mysubr (defsubr "myfuns" "mysubr" 1) 'subr)
(putprop 'myfsubr (defsubr "myfuns" "myfsubr" 0) 'fsubr)
(putprop 'mylsubr
(defsubr "myfuns" "mylsubr" 2001 -2) 'lsubr)
A function defined in this way receives its arguments and
returns its value on the marked pdl, which may be accessed
through the external static pointer
lisp_static_vars_$stack_ptr
See for details on how to access the arguments, and on the
internal format of LISP data. lisp_static_vars_$nil and
lisp_static_vars_$t_atom are fixed bin(71) external static; they
contain nil and t.
Node: Running the Compiler, Up: Top, Next: LAP, Previous: Running Functions
* Menu:
* The Multics Compiler::
* The ITS Compiler::
Node: The Multics Compiler, Up: Running the Compiler
The compiler is invoked by the lisp_compiler command to Multics.
This command can be abbreviated lcp. The arguments to the command are
the pathname of the input file and options. The compiler appends
".lisp" to the given pathname unless it is preceded by the -pathname
or -pn option. The output object segment is created in the working
directory with a name which is the first component of the name of the
input file. For example, the command
lcp dir>foo.bar
reads the file "dir>foo.bar.lisp" and produces an object segment named
"foo" in the working directory.
Usually no options need be supplied, since there are defaults. The
options available are:
-pathname -pn -p
Causes the following argument to be taken as the exact pathname
of the input file, even if it begins with a minus sign. ".lisp"
will not be appended.
-eval
Causes the following argument to be evaluated by LISP. For example,
lisp_compiler foo -eval "(special x y z)"
-time -times -tm
As each function is compiled, its name and the time taken to
compile it will be typed out.
-total_time -total -tt
At the end of the compilation, metering information will be typed
out.
-nowarn -nw
Suppresses the typing of warning messages. Error messages of a
severity greater than "warning" will still be typed.
-macros -mc
Equivalent to the (macros t) declaration: Causes macro
definitions to be retained at run time.
-all_special
Causes all variables to be made special. Equivalent to the (setq
special t) declaration.
-genprefix -gnp -gp
Takes the following argument as the prefix for names of auxiliary
functions automatically generated by the compiler. Equivalent to
the genprefix declaration.
-check -ck
Causes only the first pass of the compiler to be run. The input
file is checked for errors but no code is generated and no object
segment is produced.
-ioc
If the following argument is x, (ioc x) is evaluated. The main
use for this "-ioc d" which turns on garbage-collection messages
during compilation.
-list -ls
Causes a listing file to be created in the working directory,
containing a copy of the source file and a table of functions
defined and referenced. If the object segment is named "name",
the listing file will be named "name.list".
-long -lg
Causes the listing file to also contain an assembly language
listing, with commentary, of the generated code.
-no_compile -ncp
Causes the compiler not to attempt to compile the file. Instead
the input file is simply treated as being composed entirely of
random forms. It is digested into a form which can be processed
quickly by the load function.
Node: The ITS Compiler, Up: Running the Compiler
The ITS compiler is presently in an anomalous state. There are two
versions, COMPLR and NCOMPLR. NCOMPLR contains the fast-arithmetic
facilities described here. COMPLR is an older version which will soon
go away. At that time, NCOMPLR will be renamed to COMPLR. This
documentation uses the name COMPLR to refer to what is now NCOMPLR, so
it is presently inaccurate but will become accurate in the future.
Invoke the compiler with the :COMPLR command. The compiler will
announce itself, print an underscore or backarrow, and accept a
command line, which should be of the standard form
<output file> _ <input file> (switches)
The file specifications should be standard ITS file names, e.g.
DEV:DIRNAM;FNAME1 FNAME2. If it is necessary to get a "funny"
character such as _ into the file name, it may be quoted with a slash.
The compiler normally processes a file of LISP functions and
produces a so- called "LAP file", containing S-expressions denoting
pdp-10 machine-language instructions, suitable for use with LAP (the
Lisp Assembly Program). However, one may direct the compiler instead
to produce a binary object file, called a "FASL FILE", suitable for
use with the fasload function or the autoload feature. A third option
is to process a previously generated file of LAP code to produce a
FASL file. This is especially useful in the case where
special-purpose functions have been hand-coded in LAP.
If one specifies only an input file name, say FOO BAR, then by
default the name of a generated LAP file will be FOO LAP, and of a
FASL file, FOO FASL.
COMPLR will accept a "Job Command Line" if desired; simply type
:COMPLR <command line><cr>
In this mode COMPLR will automatically proceed itself and run without
the TTY, and kill itself when done.
* Menu:
* Exiting to LISP::
* Switches::
Node: Exiting to LISP, Up: The ITS Compiler, Next: Switches
It may be desirable to execute some LISP functions in the compiler
before actually compiling a file. Typing ctrl/G will cause the
compiler to announce itself and then type an asterisk; you will then
be at lisp's top level. To make the compiler accept a command line,
say (maklap) or type ctrl/^. One useful function for debugging and
snooping around is cl; (cl foo) will compile the function foo, which
should be defined in the compiler's lisp environment, and print LAP
code onto whatever device(s) are open for output.
Node: Switches, Up: The ITS Compiler, Previous: Exiting to LISP
The various modes of operation of the compiler may be controlled by
specifying various switches, which are single letters, inside
parentheses at the end of the command line. A switch may be turned
off by preceding the switch letter with a minus sign. Extraneous or
invalid switches are ignored. Initially all switches are off (the use
of minus sign described above is provided in case the compiler is used
for several files in succession).
The most commonly-used switch setting is "(FK)", which causes a
FASL file to be produced.
Most of the switches correspond to values of atomic symbols within
the compiler. These are noted in parentheses.
The switches are:
A (assemble) The specified input file contains LAP code which is
to be made into a binary FASL file.
D Disown. Causes the compiler to disown itself after it has started
running. This is the safest way to disown a COMPLR, because the
compiler will know that it can't try to get any information from
DDT.
F (fasl) Accept a file of LISP functions, produce a LAP file,
and then assemble the LAP file into a FASL file. This is
probably the most useful mode. With the K switch the LAP file
is not actually produced at all; the lap code is sent directly
to faslap as the compiler generates it.
K (nolap) Kill LAP file. Delete the LAP file after assembly.
Usually used in conjunction with the F switch.
Mo(in)(macros) Equivalent to (declare (macros t)). Causes macro
definitions to be defined at run time as well as at compile
time.
N (noargs) No args properties. Equivalent to (declare (noargs t)).
Normally the compiler outputs information in the LAP code
as to how many arguments each function requires, so that args
properties may be created on the appropriate atomic symbols at
load time. In some implementations these properties occupy a
significant amount of list space; thus it may be desirable to
eliminate these properties.
S (special) Equivalent to (declare (setq special t)). Causes
all variables to be considered special.
T (ttynotes) Causes the compiler to print a note on the user's
terminal as each function is compiled or assembled. This switch
is normally off so that a COMPLR may be proceeded and allowed to
run without the TTY. In any case error messages will be printed
out on the terminal.
Uo(in)(unfaslcomments) Useful only in conjunction with the F or
A switch. Causes the assembler to output comment messages into
a file whose second file name is UNFASL. (Actually, this file
is always created, and error comments will be directed into this
file also if messioc so specifies; but the file is immediately
deleted if it contains nothing significant.) These comment
messages describe the size of each function assembled, and give
other random information also.
V (nfunvarsf1) Equivalent to (declare (nfunvars t)); disallows
functional variables.
W (muzzled) (i.e. Whisper). Equivalent to (declare (muzzled t)).
Prevents the fast-arithmetic compiler from printing out a
message when closed compilation of arithmetic is forced.
X (mapex) Equivalent to (declare (mapex t)). Causes all
map-type functions to be open-coded as do loops. The resulting
code is somewhat larger, but also somewhat faster.
Z (symbols) Equivalent to (declare (symbols t)). Causes the
compiler to output a special directive in the LAP code so that
the LAP assembler will attempt to pass assembly symbols to DDT
for debugging purposes. Primarily of use to machine language
hackers.
Node: LAP, Up: Top, Next: Details, Previous: Running the Compiler
MACLISP includes a facility by which machine-language programs can
be defined as LISP functions. This can be used to gain direct access
to the hardware or the operating system, and may also be used by the
compiler.
The Lisp Assembly Program translates S-expressions which resemble
the native assembly language of the host machine into machine
language, and sets things up so that machine language coding can be
called by LISP programs in the same way that built-in "subrs" are
called.
* Menu:
* LAP on the pdp-10::
* LAP on Multics::
* LAP Words::
* LAP Instructions::
* LAP Operands::
* LAP Expressions::
* Using LAP::
Node: LAP on the pdp-10, Up: LAP, Next: LAP Words
;;;tty hacking fcns ;;;uncertain about syscall 'scml effects
(declare (special tv-main tv-echo tv-size tv-ycor) (genprefix tty))
(defun tv-screen num
(do ((mode (or (= num 0) (arg 1)))
(echoareasize (cond ((< num 2) 10)
(t (arg 2)))))
nil (setq tv-ycor (- 443. (* 12. echoareasize)))
(or (boundp 'tv-size)
(setq tv-main (open '((tty)) '(tty out ascii))
tv-echo (open '((tty)) '(tty out ascii echo))
tv-size (car (status ttysize t))))
(cond ((equal mode 'splitscreen)
(open t '(tty out ascii))
(sstatus ttycons t tv-echo)
(pagel t (- tv-size echoareasize))
(endpagefn t 'tty-endpagefn)
(syscall 0 'scml tv-echo echoareasize))
((equal mode 'smallscreen)
(open t '(tty out echo ascii))
(sstatus ttycons t t)
(pagel t echoareasize)
(syscall 0 'scml tv-echo echoareasize))
(t
(setq echoareasize 0)
(open t '(tty out ascii))
(sstatus ttycons t t)
(pagel t tv-size)
(syscall 0 'scml tv-echo 0))))
(cursorpos 'c tv-main)
(cursorpos 't t)
'done)
(declare (special **more** more-flush))
(setq **more** '##more## more-flush nil)