%!PS-Adobe-3.0
%%Title: (NNTInference.word)
%%Creator: (Microsoft Word: LaserWriter 8 8.2)
%%CreationDate: (4:57 PM Tuesday, December 6, 1994)
%%For: (Henry Baker)
%%Pages: 26
%%DocumentFonts: Times-Roman Symbol Times-Italic Times-Bold Courier Courier-Bold Helvetica-Bold
%%DocumentNeededFonts: Times-Roman Symbol Times-Italic Times-Bold Courier Courier-Bold Helvetica-Bold
%%DocumentSuppliedFonts:
%%DocumentData: Clean7Bit
%%PageOrder: Ascend
%%Orientation: Portrait
%%DocumentMedia: Default 612 792 0 () ()
%ADO_ImageableArea: 31 31 583 761
%%EndComments
userdict begin/dscInfo 5 dict dup begin
/Title(NNTInference.word)def
/Creator(Microsoft Word: LaserWriter 8 8.2)def
/CreationDate(4:57 PM Tuesday, December 6, 1994)def
/For(Henry Baker)def
/Pages 1 def
end def end
save
/version23-manualfeedpatch where { pop false } { true }ifelse
% we don't do an explicit 'get' since product and version MAY
% be in systemdict or statusdict - this technique gets the lookup
% without failure
statusdict begin
product (LaserWriter) eq % true if LaserWriter
version cvr 23.0 eq % true if version 23
end
and % only install this patch if both are true
and % true only if patch is not installed and is for this printer
% save object and boolean on stack
dup { exch restore }if
% either true OR saveobject false
dup
{
/version23-manualfeedpatch true def
/oldversion23-showpage /showpage load def
/showpage % this showpage will wait extra time if manualfeed is true
{%
statusdict /manualfeed known
{% manualfeed known in statusdict
statusdict /manualfeed get
{% if true then we loop for 5 seconds
usertime 5000 add % target usertime
{ % loop
dup usertime sub 0 lt
{ exit }if
}loop
pop % pop the usertime off the stac
}if
}if
oldversion23-showpage
}bind def
}if
not{ restore }if
/md 188 dict def md begin/currentpacking where {pop /sc_oldpacking currentpacking def true setpacking}if
%%BeginFile: adobe_psp_basic
%%Copyright: Copyright 1990-1993 Adobe Systems Incorporated. All Rights Reserved.
/bd{bind def}bind def
/xdf{exch def}bd
/xs{exch store}bd
/ld{load def}bd
/Z{0 def}bd
/T/true
/F/false
/:L/lineto
/lw/setlinewidth
/:M/moveto
/rl/rlineto
/rm/rmoveto
/:C/curveto
/:T/translate
/:K/closepath
/:mf/makefont
/gS/gsave
/gR/grestore
/np/newpath
14{ld}repeat
/$m matrix def
/av 81 def
/por true def
/normland false def
/psb-nosave{}bd
/pse-nosave{}bd
/us Z
/psb{/us save store}bd
/pse{us restore}bd
/level2
/languagelevel where
{
pop languagelevel 2 ge
}{
false
}ifelse
def
/featurecleanup
{
stopped
cleartomark
countdictstack exch sub dup 0 gt
{
{end}repeat
}{
pop
}ifelse
}bd
/noload Z
/startnoload
{
{/noload save store}if
}bd
/endnoload
{
{noload restore}if
}bd
level2 startnoload
/setjob
{
statusdict/jobname 3 -1 roll put
}bd
/setcopies
{
userdict/#copies 3 -1 roll put
}bd
level2 endnoload level2 not startnoload
/setjob
{
1 dict begin/JobName xdf currentdict end setuserparams
}bd
/setcopies
{
1 dict begin/NumCopies xdf currentdict end setpagedevice
}bd
level2 not endnoload
/pm Z
/mT Z
/sD Z
/realshowpage Z
/initializepage
{
/pm save store mT concat
}bd
/endp
{
pm restore showpage
}def
/$c/DeviceRGB def
/rectclip where
{
pop/rC/rectclip ld
}{
/rC
{
np 4 2 roll
:M
1 index 0 rl
0 exch rl
neg 0 rl
:K
clip np
}bd
}ifelse
/rectfill where
{
pop/rF/rectfill ld
}{
/rF
{
gS
np
4 2 roll
:M
1 index 0 rl
0 exch rl
neg 0 rl
fill
gR
}bd
}ifelse
/rectstroke where
{
pop/rS/rectstroke ld
}{
/rS
{
gS
np
4 2 roll
:M
1 index 0 rl
0 exch rl
neg 0 rl
:K
stroke
gR
}bd
}ifelse
%%EndFile
%%BeginFile: adobe_psp_colorspace_level1
%%Copyright: Copyright 1991-1993 Adobe Systems Incorporated. All Rights Reserved.
/G/setgray ld
/:F/setrgbcolor ld
%%EndFile
%%BeginFile: adobe_psp_uniform_graphics
%%Copyright: Copyright 1990-1993 Adobe Systems Incorporated. All Rights Reserved.
/@a
{
np :M 0 rl :L 0 exch rl 0 rl :L fill
}bd
/@b
{
np :M 0 rl 0 exch rl :L 0 rl 0 exch rl fill
}bd
/arct where
{
pop
}{
/arct
{
arcto pop pop pop pop
}bd
}ifelse
/x1 Z
/x2 Z
/y1 Z
/y2 Z
/rad Z
/@q
{
/rad xs
/y2 xs
/x2 xs
/y1 xs
/x1 xs
np
x2 x1 add 2 div y1 :M
x2 y1 x2 y2 rad arct
x2 y2 x1 y2 rad arct
x1 y2 x1 y1 rad arct
x1 y1 x2 y1 rad arct
fill
}bd
/@s
{
/rad xs
/y2 xs
/x2 xs
/y1 xs
/x1 xs
np
x2 x1 add 2 div y1 :M
x2 y1 x2 y2 rad arct
x2 y2 x1 y2 rad arct
x1 y2 x1 y1 rad arct
x1 y1 x2 y1 rad arct
:K
stroke
}bd
/@i
{
np 0 360 arc fill
}bd
/@j
{
gS
np
:T
scale
0 0 .5 0 360 arc
fill
gR
}bd
/@e
{
np
0 360 arc
:K
stroke
}bd
/@f
{
np
$m currentmatrix
pop
:T
scale
0 0 .5 0 360 arc
:K
$m setmatrix
stroke
}bd
/@k
{
gS
np
:T
0 0 :M
0 0 5 2 roll
arc fill
gR
}bd
/@l
{
gS
np
:T
0 0 :M
scale
0 0 .5 5 -2 roll arc
fill
gR
}bd
/@m
{
np
arc
stroke
}bd
/@n
{
np
$m currentmatrix
pop
:T
scale
0 0 .5 5 -2 roll arc
$m setmatrix
stroke
}bd
%%EndFile
%%BeginFile: adobe_psp_basic_text
%%Copyright: Copyright 1990-1993 Adobe Systems Incorporated. All Rights Reserved.
/S/show ld
/A{
0.0 exch ashow
}bd
/R{
0.0 exch 32 exch widthshow
}bd
/W{
0.0 3 1 roll widthshow
}bd
/J{
0.0 32 4 2 roll 0.0 exch awidthshow
}bd
/V{
0.0 4 1 roll 0.0 exch awidthshow
}bd
/fcflg true def
/fc{
fcflg{
vmstatus exch sub 50000 lt{
(%%[ Warning: Running out of memory ]%%\r)print flush/fcflg false store
}if pop
}if
}bd
/$f[1 0 0 -1 0 0]def
/:ff{$f :mf}bd
/MacEncoding StandardEncoding 256 array copy def
MacEncoding 39/quotesingle put
MacEncoding 96/grave put
/Adieresis/Aring/Ccedilla/Eacute/Ntilde/Odieresis/Udieresis/aacute
/agrave/acircumflex/adieresis/atilde/aring/ccedilla/eacute/egrave
/ecircumflex/edieresis/iacute/igrave/icircumflex/idieresis/ntilde/oacute
/ograve/ocircumflex/odieresis/otilde/uacute/ugrave/ucircumflex/udieresis
/dagger/degree/cent/sterling/section/bullet/paragraph/germandbls
/registered/copyright/trademark/acute/dieresis/notequal/AE/Oslash
/infinity/plusminus/lessequal/greaterequal/yen/mu/partialdiff/summation
/product/pi/integral/ordfeminine/ordmasculine/Omega/ae/oslash
/questiondown/exclamdown/logicalnot/radical/florin/approxequal/Delta/guillemotleft
/guillemotright/ellipsis/space/Agrave/Atilde/Otilde/OE/oe
/endash/emdash/quotedblleft/quotedblright/quoteleft/quoteright/divide/lozenge
/ydieresis/Ydieresis/fraction/currency/guilsinglleft/guilsinglright/fi/fl
/daggerdbl/periodcentered/quotesinglbase/quotedblbase/perthousand
/Acircumflex/Ecircumflex/Aacute/Edieresis/Egrave/Iacute/Icircumflex/Idieresis/Igrave
/Oacute/Ocircumflex/apple/Ograve/Uacute/Ucircumflex/Ugrave/dotlessi/circumflex/tilde
/macron/breve/dotaccent/ring/cedilla/hungarumlaut/ogonek/caron
MacEncoding 128 128 getinterval astore pop
level2 startnoload
/copyfontdict
{
findfont dup length dict
begin
{
1 index/FID ne{def}{pop pop}ifelse
}forall
}bd
level2 endnoload level2 not startnoload
/copyfontdict
{
findfont dup length dict
copy
begin
}bd
level2 not endnoload
md/fontname known not{
/fontname/customfont def
}if
/Encoding Z
/:mre
{
copyfontdict
/Encoding MacEncoding def
fontname currentdict
end
definefont :ff def
}bd
/:bsr
{
copyfontdict
/Encoding Encoding 256 array copy def
Encoding dup
}bd
/pd{put dup}bd
/:esr
{
pop pop
fontname currentdict
end
definefont :ff def
}bd
/scf
{
scalefont def
}bd
/scf-non
{
$m scale :mf setfont
}bd
/ps Z
/fz{/ps xs}bd
/sf/setfont ld
/cF/currentfont ld
/mbf
{
/makeblendedfont where
{
pop
makeblendedfont
/ABlend exch definefont
}{
pop
}ifelse
def
}def
%%EndFile
%%BeginFile: adobe_psp_derived_styles
%%Copyright: Copyright 1990-1993 Adobe Systems Incorporated. All Rights Reserved.
/wi
version(23.0)eq
{
{
gS 0 0 0 0 rC stringwidth gR
}bind
}{
/stringwidth load
}ifelse
def
/$o 1. def
/gl{$o G}bd
/ms{:M S}bd
/condensedmtx[.82 0 0 1 0 0]def
/:mc
{
condensedmtx :mf def
}bd
/extendedmtx[1.18 0 0 1 0 0]def
/:me
{
extendedmtx :mf def
}bd
/basefont Z
/basefonto Z
/dxa Z
/dxb Z
/dxc Z
/dxd Z
/dsdx2 Z
/bfproc Z
/:fbase
{
dup/FontType get 0 eq{
dup length dict begin
dup{1 index/FID ne 2 index/UniqueID ne and{def}{pop pop}ifelse}forall
/FDepVector exch/FDepVector get[exch/:fbase load forall]def
}/bfproc load ifelse
/customfont currentdict end definefont
}bd
/:mo
{
/bfproc{
dup dup length 2 add dict
begin
{
1 index/FID ne 2 index/UniqueID ne and{def}{pop pop}ifelse
}forall
/PaintType 2 def
/StrokeWidth .012 0 FontMatrix idtransform pop def
/customfont currentdict
end
definefont
8 dict begin
/basefonto xdf
/basefont xdf
/FontType 3 def
/FontMatrix[1 0 0 1 0 0]def
/FontBBox[0 0 1 1]def
/Encoding StandardEncoding def
/BuildChar
{
exch begin
basefont setfont
( )dup 0 4 -1 roll put
dup wi
setcharwidth
0 0 :M
gS
gl
dup show
gR
basefonto setfont
show
end
}def
}store :fbase
}bd
/:mso
{
/bfproc{
7 dict begin
/basefont xdf
/FontType 3 def
/FontMatrix[1 0 0 1 0 0]def
/FontBBox[0 0 1 1]def
/Encoding StandardEncoding def
/BuildChar
{
exch begin
sD begin
/dxa 1 ps div def
basefont setfont
( )dup 0 4 -1 roll put
dup wi
1 index 0 ne
{
exch dxa add exch
}if
setcharwidth
dup 0 0 ms
dup dxa 0 ms
dup dxa dxa ms
dup 0 dxa ms
gl
dxa 2. div dup ms
end
end
}def
}store :fbase
}bd
/:ms
{
/bfproc{
dup dup length 2 add dict
begin
{
1 index/FID ne 2 index/UniqueID ne and{def}{pop pop}ifelse
}forall
/PaintType 2 def
/StrokeWidth .012 0 FontMatrix idtransform pop def
/customfont currentdict
end
definefont
8 dict begin
/basefonto xdf
/basefont xdf
/FontType 3 def
/FontMatrix[1 0 0 1 0 0]def
/FontBBox[0 0 1 1]def
/Encoding StandardEncoding def
/BuildChar
{
exch begin
sD begin
/dxb .05 def
basefont setfont
( )dup 0 4 -1 roll put
dup wi
exch dup 0 ne
{
dxb add
}if
exch setcharwidth
dup dxb .01 add 0 ms
0 dxb :T
gS
gl
dup 0 0 ms
gR
basefonto setfont
0 0 ms
end
end
}def
}store :fbase
}bd
/:mss
{
/bfproc{
7 dict begin
/basefont xdf
/FontType 3 def
/FontMatrix[1 0 0 1 0 0]def
/FontBBox[0 0 1 1]def
/Encoding StandardEncoding def
/BuildChar
{
exch begin
sD begin
/dxc 1 ps div def
/dsdx2 .05 dxc 2 div add def
basefont setfont
( )dup 0 4 -1 roll put
dup wi
exch dup 0 ne
{
dsdx2 add
}if
exch setcharwidth
dup dsdx2 .01 add 0 ms
0 .05 dxc 2 div sub :T
dup 0 0 ms
dup dxc 0 ms
dup dxc dxc ms
dup 0 dxc ms
gl
dxc 2 div dup ms
end
end
}def
}store :fbase
}bd
/:msb
{
/bfproc{
7 dict begin
/basefont xdf
/FontType 3 def
/FontMatrix[1 0 0 1 0 0]def
/FontBBox[0 0 1 1]def
/Encoding StandardEncoding def
/BuildChar
{
exch begin
sD begin
/dxd .03 def
basefont setfont
( )dup 0 4 -1 roll put
dup wi
1 index 0 ne
{
exch dxd add exch
}if
setcharwidth
dup 0 0 ms
dup dxd 0 ms
dup dxd dxd ms
0 dxd ms
end
end
}def
}store :fbase
}bd
/italicmtx[1 0 -.212557 1 0 0]def
/:mi
{
italicmtx :mf def
}bd
/:v
{
[exch dup/FontMatrix get exch
dup/FontInfo known
{
/FontInfo get
dup/UnderlinePosition known
{
dup/UnderlinePosition get
2 index 0
3 1 roll
transform
exch pop
}{
.1
}ifelse
3 1 roll
dup/UnderlineThickness known
{
/UnderlineThickness get
exch 0 3 1 roll
transform
exch pop
abs
}{
pop pop .067
}ifelse
}{
pop pop .1 .067
}ifelse
]
}bd
/$t Z
/$p Z
/$s Z
/:p
{
aload pop
2 index mul/$t xs
1 index mul/$p xs
.012 mul/$s xs
}bd
/:m
{gS
0 $p rm
$t lw
0 rl stroke
gR
}bd
/:n
{
gS
0 $p rm
$t lw
0 rl
gS
gl
stroke
gR
strokepath
$s lw
/setstrokeadjust where{pop
currentstrokeadjust true setstrokeadjust stroke setstrokeadjust
}{
stroke
}ifelse
gR
}bd
/:o
{gS
0 $p rm
$t 2 div dup rm
$t lw
dup 0 rl
stroke
gR
:n
}bd
%%EndFile
/currentpacking where {pop sc_oldpacking setpacking}if end
%%EndProlog
%%BeginSetup
md begin
countdictstack[{
%%BeginFeature: *ManualFeed False
statusdict /manualfeed false put
%%EndFeature
}featurecleanup
countdictstack[{
%%BeginFeature: *InputSlot Cassette
%%EndFeature
}featurecleanup
countdictstack[{
%%BeginFeature: *PageRegion LetterSmall
lettersmall
%%EndFeature
}featurecleanup
(Henry Baker)setjob
/mT[1 0 0 -1 31 761]def
/sD 16 dict def
300 level2{1 dict dup/WaitTimeout 4 -1 roll put setuserparams}{statusdict/waittimeout 3 -1 roll put}ifelse
%%IncludeFont: Times-Roman
%%IncludeFont: Symbol
%%IncludeFont: Times-Italic
%%IncludeFont: Times-Bold
%%IncludeFont: Courier
%%IncludeFont: Courier-Bold
%%IncludeFont: Helvetica-Bold
/f0_1/Times-Roman
:mre
/f0_18 f0_1 18 scf
/f0_12 f0_1 12 scf
/f0_10 f0_1 10 scf
/f0_9 f0_1 9 scf
/f1_1/Symbol
:bsr
240/apple pd
:esr
/f1_12 f1_1 12 scf
/f1_10 f1_1 10 scf
/f1_9 f1_1 9 scf
/f2_1/Times-Italic
:mre
/f2_10 f2_1 10 scf
/f3_1 f1_1
:mi
/f3_10 f3_1 10 scf
/f4_1/Times-Bold
:mre
/f4_10 f4_1 10 scf
/f5_1 f0_1
:v def
/f6_1 f2_1
:v def
/f7_1/Courier
:mre
/f7_10 f7_1 10 scf
/f8_1/Courier-Bold
:mre
/f8_10 f8_1 10 scf
/f9_1/Helvetica-Bold
:mre
/f9_10 f9_1 10 scf
/Courier findfont[10 0 0 -10 0 0]:mf setfont
%%EndSetup
%%Page: 1 1
%%BeginPageSetup
initializepage
(Henry Baker; page: 1 of 26)setjob
%%EndPageSetup
gS 0 0 552 730 rC
41 14 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
41 718 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
472 718 :M
(1)S
41 43 :M
f0_18 sf
.303 .03(The Nimble Type Inferencer for Common Lisp-84)J
41 66 :M
f0_10 sf
(HENRY G. BAKER)S
41 92 :M
f2_10 sf
.295 .029(Nimble Computer Corporation, 16231 Meadow Ridge Way, Encino, CA 91436)J
41 103 :M
.854 .085(\(818\) 501-4956\312\312\(818\) 986-1360 \(FAX\))J
41 123 :M
f0_9 sf
.55 .055(This work was supported in part by the U.S. Department of Energy Contract No. DE-AC03-88ER80663)J
39 135 -1 1 511 134 1 39 134 @a
41 148 :M
f0_10 sf
.394 .039(We describe a framework and an algorithm for doing )J
f2_10 sf
.607 .061(type inference)J
f0_10 sf
.422 .042( analysis on programs written in full Common)J
41 159 :M
1.37 .137(Lisp-84 \(Common Lisp without the CLOS object-oriented extensions\). The objective of type inference is to)J
41 170 :M
.663 .066(determine tight lattice upper bounds on the range of runtime data types for Common Lisp program variables and)J
41 181 :M
-.035(temporaries. Depending upon the lattice used, type inference can also provide range analysis information for numeric)A
41 192 :M
.396 .04(variables. This lattice upper bound information can be used by an optimizing compiler to choose more restrictive,)J
41 203 :M
-.029(and hence more efficient, representations for these program variables. Our analysis also produces tighter )A
f2_10 sf
-.032(control flow)A
41 214 :M
f0_10 sf
.714 .071(information, which can be used to eliminate redundant tests which result in )J
f2_10 sf
.992 .099(dead code)J
f0_10 sf
.556 .056(. The overall goal of type)J
41 225 :M
-.004(inference is to mechanically extract from Common Lisp programs the same degree of representation information that)A
41 236 :M
.497 .05(is usually provided by the programmer in traditional strongly-typed languages. In this way, we can provide some)J
41 247 :M
1.245 .124(classes of Common Lisp programs execution time efficiency expected only for more strongly-typed compiled)J
41 258 :M
-.054(languages.)A
41 274 :M
.455 .046(The Nimble type inference system follows the traditional lattice/algebraic )J
f2_10 sf
.498 .05(data flow)J
f0_10 sf
.52 .052( techniques [Kaplan80], rather)J
41 285 :M
.055 .005(than the logical/theorem-proving )J
f2_10 sf
.01(unification)A
f0_10 sf
.035 .003( techniques of ML [Milner78]. It can handle )J
f2_10 sf
.063 .006(polymorphic variables and)J
41 296 :M
.195(functions)A
f0_10 sf
.686 .069( in a natural way, and provides for "case-based" analysis that is quite similar to that used intuitively by)J
41 307 :M
.226 .023(programmers. Additionally, this inference system can deduce the termination of some simple loops, thus providing)J
41 318 :M
.11 .011(surprisingly tight upper lattice bounds for many loop variables.)J
41 334 :M
1.451 .145(By using a higher resolution lattice, more precise typing of primitive functions, polymorphic types and case)J
41 345 :M
-.028(analysis, the Nimble type inference algorithm can often produce sharper bounds than unification-based type inference)A
41 356 :M
.983 .098(techniques. At the present time, however, our treatment of higher-order data structures and functions is not as)J
41 367 :M
-.007(elegant as that of the unification techniques.)A
41 383 :M
-.046(Categories and Subject Descriptors:)A
41 399 :M
.013 .001(General Terms: compilers, data types, lattices, Boolean algebras, dataflow, static analysis, polymorphism.)J
41 415 :M
-.037(Additional Key Words and Phrases: Common Lisp, ML, type inference, interpreted language, compiled language.)A
39 424 -1 1 511 423 1 39 423 @a
41 438 :M
f4_10 sf
3.288 .329(1. INTRODUCTION)J
41 454 :M
f0_10 sf
.037 .004(High-level programming languages can be grouped into two camps\321the )J
f2_10 sf
.009(compiled)A
f0_10 sf
.033 .003( languages such as Fortran, Algol,)J
41 465 :M
.314 .031(Pascal, Ada and C\321and the )J
f2_10 sf
.084(interpreted)A
f0_10 sf
.325 .033( languages such as Lisp, APL, and Smalltalk. The compiled languages put)J
41 476 :M
.42 .042(great emphasis on high levels of compile-time type checking and execution speed, while the interpreted languages)J
41 487 :M
.381 .038(put great emphasis on run-time type flexibility and speed of program development. As might have been expected,)J
41 498 :M
.665 .066(each camp has worked hard to add the advantages of the other camp to its own inherent advantages. Interpreted)J
41 509 :M
.201 .02(languages have developed sophisticated compilers for higher execution speed and safety, while compiled languages)J
41 520 :M
-.028(have developed interpreters and incremental compilers for faster program development and more sophisticated typing)A
41 531 :M
-.039(systems for more flexible programs and data structures.)A
41 547 :M
f2_10 sf
-.026(Type inferencing)A
f0_10 sf
-.025( is a technique by which the interpreted languages move towards the goal of achieving the safety and)A
41 558 :M
.725 .072(execution speed of traditional compiled languages, while preserving the flexibility of runtime data typing. Type)J
41 569 :M
-.021(inferencing is the mechanical extraction of "type declarations" from a program that has not provided this information,)A
41 580 :M
.369 .037(which is required of the programmer by traditional compiled languages. An optimizing compiler can then use this)J
41 591 :M
-.052(more precise type information to generate more efficient code.)A
41 607 :M
.223 .022(This paper describes a type inferencer we have developed to test some of the algorithmic limits of the whole notion)J
41 618 :M
.159 .016(of type inferencing. We have chosen to test our approach using Common Lisp, as it is a standardized, dynamically-)J
41 629 :M
2.255 .225(typed language which already contains the infrastructure\321a type description language and optional type)J
41 640 :M
-.041(declarations\321that is needed to support type inference. These type declarations are then used by the type inferencer to)A
41 651 :M
.643 .064(demonstrate its typing of a program. To date, we have focussed on extracting the best type information possible)J
41 662 :M
.122 .012(using algorithmic methods, while giving less emphasis to computational efficiency of these methods.)J
endp
%%Page: 2 2
%%BeginPageSetup
initializepage
(Henry Baker; page: 2 of 26)setjob
%%EndPageSetup
-31 -31 :T
gS 31 31 552 730 rC
72 45 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
72 749 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
503 749 :M
(2)S
72 69 :M
-.058(This paper is structured to provide both the background and approach of the type inference system being developed:)A
72 80 :M
.141 .014(\312\245\312Section 2 describes the nature of "type inference")J
72 91 :M
.256 .026(\312\245\312Section 3 describes the goals of the Nimble type inference system)J
72 102 :M
.352 .035(\312\245\312Section 4 shows the capabilities of our algorithm through a number of examples)J
72 113 :M
.094 .009(\312\245\312Section 5 describes the Kaplan-Ullman type inference algorithm, from which ours evolved)J
72 124 :M
.261 .026(\312\245\312Section 6 describes the Nimble type inference algorithm)J
72 135 :M
.292 .029(\312\245\312Section 7 shows an example of the Nimble type inference algorithm in operation)J
72 146 :M
.402 .04(\312\245\312Section 8 briefly analyzes the complexity of the Nimble algorithm)J
72 157 :M
.171 .017(\312\245\312Section 9 discusses previous work on type inference, both for other languages and Lisp)J
72 168 :M
.936 .094(\312\245\312Section 10 concludes.)J
72 184 :M
f4_10 sf
3.074 .307(2. TYPE INFERENCE)J
72 200 :M
f0_10 sf
-.012(To begin, we need to construct a theoretical framework and background for performing "type inference" on Common)A
72 211 :M
(Lisp programs. We call the process of automatically determining the datatype of objects "type )S
f2_10 sf
(inference)S
f0_10 sf
(" rather than)S
72 222 :M
1.306 .131("type )J
f2_10 sf
.39(checking)A
f0_10 sf
1.434 .143(", since Common Lisp already has a well-developed dynamic type system in which types are)J
72 233 :M
-.009(computed and checked at run-time. However, to improve the efficiency of Lisp programs which have been compiled)A
72 244 :M
-.048(for execution on modern RISC architectures without type-checking hardware, more information is required at compile)A
72 255 :M
.469 .047(time. We need to identify during compilation those variables which will not utilize the full range of Lisp runtime)J
72 266 :M
.044 .004(datatypes, and give them specialized representations which can be dealt with more efficiently. This process is called)J
72 277 :M
f2_10 sf
-.037(representation analysis)A
f0_10 sf
-.036(. Representation analysis has quite different goals from the problem of checking type safety at)A
72 288 :M
.282 .028(compile time, which we will call )J
f2_10 sf
.381 .038(compile time type checking)J
f0_10 sf
.321 .032(. However, these two problems are intimately related,)J
72 299 :M
-.032(and maximal run-time speed is achieved when the results of both kinds of analyses are available.)A
72 315 :M
.095 .009(There has been some confusion about the difference between )J
f2_10 sf
.136 .014(type checking)J
f0_10 sf
.065 .006( for the purposes of )J
f2_10 sf
.025(compiling)A
f0_10 sf
.119 .012( traditional)J
72 326 :M
.642 .064(languages, and )J
f2_10 sf
.863 .086(type checking)J
f0_10 sf
.494 .049( for the purposes of ensuring a program's )J
f2_10 sf
.152(correctness)A
f0_10 sf
.571 .057(. While the redundancy which)J
72 337 :M
.552 .055(results from incorporating type declarations enhances the possibility of detecting semantic errors at compile time,)J
72 348 :M
.267 .027(this redundancy is not the best kind for that purpose. The goal of strong typing declarations in compiled languages)J
72 359 :M
.455 .046(is the efficient mapping of the program onto hardware datatypes, yet hardware datatypes may carry little semantic)J
72 370 :M
.044 .004(meaning for the programmer. For detecting semantic errors, the most powerful kinds of redundancy are provided by)J
72 381 :M
f2_10 sf
.286 .029(abstract data types)J
f0_10 sf
.187 .019( and constructs such as )J
f2_10 sf
.058(assert)A
f0_10 sf
.224 .022(. Abstract data types model the semantic intent of the programmer)J
72 392 :M
.895 .089(with respect to individual variable values, so that global properties of these individual values \(e.g., )J
f2_10 sf
.255(evenness)A
f0_10 sf
.517 .052( or)J
72 403 :M
f2_10 sf
.397(primeness)A
f0_10 sf
1.111 .111( of an integer value\) are maintained. The )J
f2_10 sf
.351(assert)A
f0_10 sf
1.466 .147( construct allows for the specification of complex)J
72 414 :M
f2_10 sf
.26 .026(relationships among )J
f0_10 sf
.199 .02( several variables. However, since we are interested in improving run-time efficiency, we will)J
72 425 :M
1.557 .156(assume that the program is already semantically correct, and will therefore concern ourselves only with the)J
72 436 :M
.02 .002(determination of tight lattice bounds on the values of variables.)J
72 452 :M
.813 .081(Performing type inference requires proving many small theorems about programs, and therefore runs the risk of)J
72 463 :M
.178 .018(being confused with the more difficult task of theorem-proving for the purpose of proving programs correct relative)J
72 474 :M
.911 .091(to some external criteria. While some of the techniques may be similar to both tasks, the goals are completely)J
72 485 :M
.064 .006(different. For example, it is considered acceptable and routine for correctness provers to interact with a programmer)J
72 496 :M
.023 .002(when reasoning about the correctness of his code, but these interactions are not appropriate in a type inferencer. The)J
72 507 :M
.36 .036(whole point of type inference is to prove these small theorems and insert type declarations )J
f2_10 sf
.107(mechanically)A
f0_10 sf
.322 .032(, since the)J
72 518 :M
f2_10 sf
.783 .078(raison d'etre)J
f0_10 sf
.73 .073( of typeless languages is to eliminate unnecessary redundancies\321e.g., declarations\321from programs)J
72 529 :M
1.216 .122(which clutter up the code and reduce productivity in program development and maintenance. Thus, if a type)J
72 540 :M
1.892 .189(inferencer cannot routinely prove a certain class of theorems without human interaction, then its use as a)J
72 551 :M
-.009(productivity-enhancing tool will be severely limited.)A
72 567 :M
.091 .009(We have chosen to perform static type inference on programs in the dynamically-typed Common Lisp programming)J
72 578 :M
.9 .09(language [CLtL84]. Common Lisp-84 has a reasonably complex type system. This system involves 42 simple)J
72 589 :M
.386 .039(type specifiers, 4 standard type operators, and 21 type specialization forms. Common Lisp has the usual primitive)J
72 600 :M
.192 .019(integer and floating point types, characters, the traditional )J
f2_10 sf
.056(atoms)A
f0_10 sf
.091 .009( and )J
f2_10 sf
.052(conses)A
f0_10 sf
.152 .015( of Lisp, vectors, arrays, and strings. In)J
72 611 :M
.557 .056(addition, Common Lisp has a host of non-traditional datatypes, such as )J
f2_10 sf
.758 .076(hash tables)J
f0_10 sf
.146 .015(, )J
f2_10 sf
.148(readtables)A
f0_10 sf
.533 .053(, and other special)J
72 622 :M
.522 .052(purpose types. The datatype system can also be extended through user-defined )J
f2_10 sf
.123(structures)A
f0_10 sf
.477 .048(, which are analogous to)J
72 633 :M
f2_10 sf
-.094(structures)A
f0_10 sf
-.089( in C and to )A
f2_10 sf
-.102(records)A
f0_10 sf
-.1( in Pascal and Ada.)A
72 649 :M
1.57 .157(Common Lisp functions, unlike functions in traditional compiled languages, are inherently )J
f2_10 sf
.395(polymorphic)A
f0_10 sf
.549 .055(. A)J
72 660 :M
.75 .075(function may accept arguments of )J
f2_10 sf
.216(any)A
f0_10 sf
.724 .072( datatype, and perform operations on them without restriction. Of course,)J
72 671 :M
.363 .036(many built-in functions will generate runtime errors if not given arguments of the proper types. The Lisp function)J
72 682 :M
.171 .017(+, for example, will complain if it is not given numbers to add. However, the programmer is under no obligation to)J
72 693 :M
.442 .044(restrict his own functions in such a simple manner. For example, he is free to define his own + function in which)J
72 704 :M
.941 .094(numeric arguments are summed in the normal fashion, but non-numeric arguments are coerced into their "print)J
72 715 :M
.159 .016(length" before summing.)J
endp
%%Page: 3 3
%%BeginPageSetup
initializepage
(Henry Baker; page: 3 of 26)setjob
%%EndPageSetup
-31 -31 :T
gS 31 31 552 730 rC
72 45 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
72 749 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
503 749 :M
(3)S
72 69 :M
.248 .025(Traditional compiled languages such as Algol, Pascal, C and Ada cannot offer this level of flexibility, because they)J
72 80 :M
f2_10 sf
.116(must)A
f0_10 sf
.371 .037( determine a unique datatype for every value at compile time. This is because their built-in operations cannot)J
72 91 :M
.229 .023(accept more than one kind of datatype representation. While this level of specificity regarding datatypes allows for)J
72 102 :M
-.028(great efficiency in the datatypes and operations that they )A
f2_10 sf
-.037(do)A
f0_10 sf
-.031( support, these languages often cripple the programmer by)A
72 113 :M
.289 .029(forcing him to program in styles which may not be the most natural for the problem. These styles of programming)J
72 124 :M
.021 .002(may sometimes be )J
f2_10 sf
.026 .003(less efficient)J
f0_10 sf
.018 .002( than a style based on dynamic data types.)J
72 140 :M
.367 .037(There has been some significant progress in incorporating more complex type specification and type checking into)J
72 151 :M
1.46 .146(compiled languages. The most developed \(in the sense of actual implementation\) of these languages is )J
f2_10 sf
1.282(ML)A
72 162 :M
f0_10 sf
.267 .027([Milner78, Harper86]. ML allows for parameterized datatype specifications by means of )J
f2_10 sf
.362 .036(type variables)J
f0_10 sf
.235 .024(, which can)J
72 173 :M
-.023(stand for "any actual type". These variables are automatically instantiated during the type checking process, allowing)A
72 184 :M
.93 .093(for a much more elegant solution to the problem of polymorphic functions such as )J
f2_10 sf
.258(length)A
f0_10 sf
.301 .03( \()J
f2_10 sf
.258(length)A
f0_10 sf
1.032 .103( produces the)J
72 195 :M
-.031(length of any list, regardless of the type of its elements\). In Pascal, a different )A
f2_10 sf
-.035(length)A
f0_10 sf
-.036( procedure would be required for)A
72 206 :M
.342 .034(each type of list element. In Ada, it is possible to state a generic )J
f2_10 sf
.12(length)A
f0_10 sf
.396 .04( function, but this generic function is not a)J
72 217 :M
-.002(true function, but only a template from which a true function must be instantiated before it can be called [AdaLRM].)A
72 233 :M
2.292 .229(The ML compiler can handle the typing problem of )J
f2_10 sf
.636(length)A
f0_10 sf
2.352 .235( by introducing a type variable \(denoted by)J
72 244 :M
f7_10 sf
.54(')A
f0_10 sf
1.842 .184(\) which stand for )J
f2_10 sf
.317(all)A
f0_10 sf
1.175 .118( actual types, so that arguments to )J
f2_10 sf
.375(length)A
f0_10 sf
1.328 .133( can be specified more)J
72 255 :M
.585 .059(generically as )J
f7_10 sf
.213(list\('A\))A
f0_10 sf
.373 .037( instead of )J
f7_10 sf
.213(list\(integer\))A
f0_10 sf
.197 .02( or )J
f7_10 sf
.213(list\(character\))A
f0_10 sf
.482 .048(. The ML compiler itself then)J
72 266 :M
.83 .083(determines the appropriate actual type to substitute for the type variable in every instance where )J
f2_10 sf
.223(length)A
f0_10 sf
.62 .062( is used.)J
72 277 :M
1.111 .111(However, while ML allows for a parameterized type specification, )J
10 f5_1 :p
182.305 :m
.972 .097(ML still requires that the datatype of every)J
72 289 :M
f2_10 sf
10 f6_1 :p
34.798 :m
.184(instance)A
f0_10 sf
10 f5_1 :p
262.682 :m
.602 .06( of the variable or procedure be resolved to a single actual type)J
.544 .054(. This is because ML is still a traditional)J
72 301 :M
.785 .079(strongly-typed language, without the dynamic run-time datatypes necessary to handle full polymorphism. Thus,)J
72 312 :M
.056 .006(through parameterized type specification, ML extends the power of traditional "compiled" languages in the direction)J
72 323 :M
-.035(of type polymorphism without giving up the level of efficiency on standard hardware expected of these languages.)A
72 339 :M
.081 .008(The Nimble type inferencer, on the other hand, approaches the same goal of high efficiency on "standard" hardware,)J
72 350 :M
1.059 .106(but from the opposite direction: instead of restricting the programming language itself to consist of only those)J
72 361 :M
.645 .064(constructs which can be efficiently compiled, the Nimble type inferencer identifies those )J
f2_10 sf
.172(usages)A
f0_10 sf
.546 .055( of variables and)J
72 372 :M
.544 .054(functions within Common Lisp that can be efficiently compiled, and utilizes the traditional Lisp runtime datatype)J
72 383 :M
.301 .03(system as a fall-back strategy. Thus, the ML and Nimble type inferencing algorithms can be considered analogous)J
72 394 :M
.022 .002(in the sense that they are both looking for constructs that can be efficiently compiled. They differ, however, on what)J
72 405 :M
-.016(is done when no such constructs are found: ML considers this case an error, while the Nimble type inferencer accepts)A
72 416 :M
.084 .008(the program, but its subsequent execution will involve a higher level of run-time type checking than otherwise.)J
72 432 :M
f4_10 sf
2.763 .276(3. GOALS OF THE NIMBLE TYPE INFERENCE SYSTEM)J
72 448 :M
f0_10 sf
.054 .005(The goals of the Nimble type inferencing algorithm are as follows:)J
72 459 :M
1.385 .139(\312\245\312produce the most highly constrained set of datatypes possible for each variable )J
f2_10 sf
.331(definition)A
f0_10 sf
1.004 .1(, to allow for the)J
72 470 :M
-.069(efficient representation of the variable)A
72 481 :M
.499 .05(\312\245\312produce the most highly constrained set of datatypes possible for each variable )J
f2_10 sf
.138(use)A
f0_10 sf
.45 .045(, to allow for the elimination)J
72 492 :M
-.126(of )A
f2_10 sf
-.15(redundant datatype checking)A
f0_10 sf
-.153( during execution)A
72 508 :M
.145 .015(More succinctly, Nimble would like to:)J
72 519 :M
-.002(\312\245\312utilize hardware datatypes whenever possible)A
72 530 :M
.156 .016(\312\245\312eliminate as much redundant run-time type-checking as possible)J
72 546 :M
.232 .023(These two goals are intimately related, and extremely important when compiling Common Lisp for a modern RISC)J
72 557 :M
-.14(architecture.)A
72 573 :M
-.013(Utilizing hardware datatypes is important because some hardware datatypes are extremely efficient. For example, on)A
72 584 :M
1.36 .136(the Intel i860 [Intel89], 32-bit 2's complement integers, 32-bit IEEE floating point numbers and 64-bit IEEE)J
72 595 :M
.09 .009(floating point numbers are all extremely efficient and extremely fast. However, in order to compile a Common Lisp)J
72 606 :M
.207 .021(program using hardware instructions for these datatypes, we must know that the variables and temporaries accessed)J
72 617 :M
.435 .044(by these instructions can take on )J
f2_10 sf
.133(only)A
f0_10 sf
.391 .039( the appropriate types at run-time. In other words, we must be able to )J
f2_10 sf
.176(prove)A
72 628 :M
f0_10 sf
.084 .008(that any run-time type checks for those variables will )J
f2_10 sf
.026(always)A
f0_10 sf
.076 .008( show the variables and values to be of the correct type;)J
72 639 :M
.067 .007(i.e., )J
236.087 :m
.1 .01(we must prove that the run-time type checks are redundant)J
.1 .01(. Thus, the analysis necessary to eliminate redundant)J
72 651 :M
-.036(type checks is a prerequisite for the utilization of specialized representations.)A
72 667 :M
.203 .02(What do we mean by redundant runtime type-checks? Consider the following Common Lisp recursive program for)J
72 678 :M
-.028(the factorial function:)A
72 694 :M
f7_10 sf
(\(defun fact \(n\))S
72 705 :M
( \(if \(zerop n\) 1)S
72 716 :M
( \(* n \(fact \(1- n\)\)\)\)\))S
endp
%%Page: 4 4
%%BeginPageSetup
initializepage
(Henry Baker; page: 4 of 26)setjob
%%EndPageSetup
-31 -31 :T
gS 31 31 552 730 rC
72 45 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
72 749 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
503 749 :M
(4)S
72 69 :M
.097 .01(During the execution of )J
f7_10 sf
.038(\(fact\3125\))A
f0_10 sf
.079 .008(, we first must execute )J
f7_10 sf
.038(\(zerop\3125\))A
f0_10 sf
.092 .009(, which first checks whether )J
f7_10 sf
(5)S
f0_10 sf
.078 .008( is a number,)J
72 80 :M
.251 .025(and if so, whether it is zero. If )J
f7_10 sf
.15(5)A
f0_10 sf
.326 .033( is not a number, then an error is signalled. Having determined that )J
f7_10 sf
.15(5)A
f0_10 sf
.279 .028( is not zero,)J
72 91 :M
.708 .071(we then compute )J
f7_10 sf
.276(\(1-\3125\))A
f0_10 sf
.642 .064(, which in turn checks again whether )J
f7_10 sf
.276(5)A
f0_10 sf
.546 .055( is a number, and what type of number it is, so)J
72 102 :M
.453 .045(that it can perform the appropriate decrement operation, whether for integers, rationals, floating-point numbers, or)J
72 113 :M
.15 .015(complex numbers. However, since we have already checked )J
f7_10 sf
.054(5)A
f0_10 sf
.136 .014( for numberhood, we have shown that the type check)J
72 124 :M
.352 .035(during the first part of the function )J
f7_10 sf
.161(1-)A
f0_10 sf
.319 .032( is redundant. We then pass the result )J
f7_10 sf
.161(5-1)A
f0_10 sf
.152(=)A
f7_10 sf
.161(4)A
f0_10 sf
.236 .024( back to )J
f7_10 sf
.161(fact)A
f0_10 sf
.36 .036( for a recursive)J
72 135 :M
.351 .035(computation. However, we must once again check )J
f7_10 sf
.13(4)A
f0_10 sf
.255 .025( for numberhood as part of the call to )J
f7_10 sf
.13(zerop)A
f0_10 sf
.303 .03( in the recursive)J
72 146 :M
-.005(call, so there is a redundant type check within every recursive call at the entry to )A
f7_10 sf
(zerop)S
f0_10 sf
(.)S
72 162 :M
.925 .093(A similar kind of redundant type checking occurs during the unwinding of the recursion. At the bottom of the)J
72 173 :M
.326 .033(recursion, )J
f7_10 sf
.097(fact)A
f0_10 sf
.183 .018( returns )J
f7_10 sf
.097(1)A
f0_10 sf
.181 .018(, which is a number, as well as an integer. Yet )J
f7_10 sf
.097(*)A
f0_10 sf
.215 .021( will check again whether this result is a)J
72 184 :M
.303 .03(number, and what kind of number it is, before performing its computation. By induction, we know that )J
f7_10 sf
.123(fact)A
f0_10 sf
.264 .026( will)J
72 195 :M
.079 .008(always return a number, if it returns at all, since )J
f7_10 sf
(1)S
f0_10 sf
.065 .006( is a number, and )J
f7_10 sf
(*)S
f0_10 sf
.087 .009( always returns a number. Hence the check for)J
72 206 :M
-.021(numberhood on entry to )A
f7_10 sf
(*)S
f0_10 sf
-.017( inside )A
f7_10 sf
-.029(fact)A
f0_10 sf
-.02( is always redundant.)A
72 222 :M
1.089 .109(In modern RISC architectures type checks are expensive, because they involve conditional branching which is)J
72 233 :M
.059 .006(extremely expensive on a pipelined architecture due to the "pipeline turbulence" it causes. Pipeline turbulence is the)J
72 244 :M
.02 .002(inefficiency that results from idle time slots within the pipeline as a result of the conditional branch. )J
f2_10 sf
.034 .003(Error checks)J
f0_10 sf
( of)S
72 255 :M
.734 .073(the sort described above can be implemented on pipelined architectures as conditional branches whose branch is)J
72 266 :M
(almost never taken. The )S
f2_10 sf
(type dispatch)S
f0_10 sf
.004 0( operation, however, used to find the appropriate type of instruction to execute)J
72 277 :M
.725 .072(\(integer, rational, floating-point, complex\) almost always branches, but to different places at different times, and)J
72 288 :M
-.04(therefore almost always causes pipeline turbulence.)A
72 304 :M
.534 .053(While there exist architectures and compilers whereby this sort of pipeline turbulence can be minimized \(e.g., the)J
72 315 :M
f2_10 sf
.123(Trace)A
f0_10 sf
.423 .042( technology used in the Multiflow machine [Ellis86]\), it still extracts a significant performance penalty. For)J
72 326 :M
.429 .043(example, on many RISC architectures, 32-bit integer operations are 4-6 times faster than conditional branches and)J
72 337 :M
-.005(hence to a first order approximation, the type checks take up )A
f2_10 sf
(all)S
f0_10 sf
-.005( of the execution time!)A
72 353 :M
f4_10 sf
2.757 .276(4. THE CAPABILITIES OF THE NIMBLE TYPE INFERENCE SYSTEM)J
72 369 :M
f0_10 sf
.455 .046(In this section, we show some of the capabilities of the Nimble algorithm to automatically infer tight lattice upper)J
72 380 :M
-.036(bounds on the datatypes of variables.)A
72 396 :M
.253 .025(The most trivial of type inference algorithms should be capable of inferring the types of )J
f2_10 sf
.072(constants)A
f0_10 sf
.257 .026(, whether they be)J
72 407 :M
.725 .073(constant objects, or constant built-in functions. Thus, even in the absence of additional information, the Nimble)J
72 418 :M
-.025(type inferencer \(henceforth called "NTI"\) can )A
f2_10 sf
-.027(automatically)A
f0_10 sf
-.025( infer the following types in our simple recursive factorial)A
72 429 :M
.222 .022(program \(automatically inferred information is shown in )J
f4_10 sf
.055(boldface)A
f0_10 sf
.197 .02( type\):)J
72 445 :M
f7_10 sf
(\(defun fact \(n\))S
72 456 :M
.843 .084( )J
f8_10 sf
4.293 .429(\(declare \(type number n\)\))J
72 467 :M
f7_10 sf
.589 .059( \(if \(zerop n\) \(the )J
f8_10 sf
.227(integer)A
f7_10 sf
.568 .057( 1\))J
72 478 :M
.481 .048( \(the )J
f8_10 sf
.425(number)A
72 489 :M
f7_10 sf
.292 .029( \(* n \(the )J
f8_10 sf
.268(number)A
72 500 :M
f7_10 sf
.195 .019( \(fact \(the )J
f8_10 sf
.191(number)A
72 511 :M
f7_10 sf
.002 0( \(1- n\)\)\)\)\)\)\)\))J
72 527 :M
f0_10 sf
1.793 .179(Given the additional information that fact is only called from elsewhere with )J
f2_10 sf
.464(integer)A
f0_10 sf
1.965 .196( arguments, NTI can)J
72 538 :M
f2_10 sf
-.007(automatically)A
f0_10 sf
-.006( infer the following more restrictive types:)A
72 554 :M
f7_10 sf
(\(defun fact \(n\))S
72 565 :M
.845 .085( )J
f8_10 sf
4.397 .44(\(declare \(type integer n\)\))J
72 576 :M
f7_10 sf
.589 .059( \(if \(zerop n\) \(the )J
f8_10 sf
.227(integer)A
f7_10 sf
.568 .057( 1\))J
72 587 :M
.53 .053( \(the )J
f8_10 sf
.455(integer)A
72 598 :M
f7_10 sf
.328 .033( \(* n \(the )J
f8_10 sf
.293(integer)A
72 609 :M
f7_10 sf
.221 .022( \(fact \(the )J
f8_10 sf
.211(integer)A
72 620 :M
f7_10 sf
.002 0( \(1- n\)\)\)\)\)\)\)\))J
72 636 :M
f0_10 sf
-.015(If fact is only called from elsewhere with )A
f2_10 sf
-.016(non-negative)A
f0_10 sf
-.016( integer arguments, NTI can )A
f2_10 sf
-.016(automatically)A
f0_10 sf
-.016( infer the even more)A
72 647 :M
-.021(restrictive types:)A
72 663 :M
f7_10 sf
(\(defun fact \(n\))S
72 674 :M
.865 .086( )J
f8_10 sf
5.396 .54(\(declare \(type nonnegative-integer n\)\))J
72 685 :M
f7_10 sf
1.041 .104( \(if \(zerop n\) \(the )J
f8_10 sf
.401(positive-integer)A
f7_10 sf
1.004 .1( 1\))J
72 696 :M
.807 .081( \(the )J
f8_10 sf
.634(positive-integer)A
72 707 :M
f7_10 sf
.566 .057( \(* n \(the )J
f8_10 sf
.463(positive-integer)A
72 718 :M
f7_10 sf
.471 .047( \(fact \(the )J
f8_10 sf
.406(non-negative-integer)A
72 729 :M
f7_10 sf
.002 0( \(1- n\)\)\)\)\)\)\)\))J
endp
%%Page: 5 5
%%BeginPageSetup
initializepage
(Henry Baker; page: 5 of 26)setjob
%%EndPageSetup
-31 -31 :T
gS 31 31 552 730 rC
72 45 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
72 749 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
503 749 :M
(5)S
72 69 :M
.304 .03(This last example deserves some comment. The ability of NTI to predict that )J
f7_10 sf
.127(n)A
f0_10 sf
.356 .036( will never become negative, given)J
72 80 :M
.126 .013(that )J
f7_10 sf
.065(n)A
f0_10 sf
.157 .016( started as a positive integer, is a reasonably deep inference. Since this inference then allows NTI to conclude)J
72 91 :M
.182 .018(that the result of fact will always be )J
f2_10 sf
.058(positive)A
f0_10 sf
.226 .023( shows a significant level of subtlety.)J
72 107 :M
.183 .018(If )J
f7_10 sf
.156(fact)A
f0_10 sf
.427 .043( is only called from elsewhere with "small" non-negative integers \(i.e., non-negative )J
f2_10 sf
.115(fixnums)A
f0_10 sf
.454 .045(, in Common)J
72 118 :M
-.013(Lisp parlance\), NTI can )A
f2_10 sf
-.014(automatically)A
f0_10 sf
-.013( infer the following types:)A
72 134 :M
f7_10 sf
(\(defun fact \(n\))S
72 145 :M
.864 .086( )J
f8_10 sf
5.327 .533(\(declare \(type nonnegative-fixnum n\)\))J
72 156 :M
f7_10 sf
1.001 .1( \(if \(zerop n\) \(the )J
f8_10 sf
.386(positive-fixnum)A
f7_10 sf
.965 .097( 1\))J
72 167 :M
.807 .081( \(the )J
f8_10 sf
.634(positive-integer)A
72 178 :M
f7_10 sf
.566 .057( \(* n \(the )J
f8_10 sf
.463(positive-integer)A
72 189 :M
f7_10 sf
.457 .046( \(fact \(the )J
f8_10 sf
.395(non-negative-fixnum)A
72 200 :M
f7_10 sf
.002 0( \(1- n\)\)\)\)\)\)\)\))J
72 216 :M
f0_10 sf
.047 .005(This ability of NTI to automatically prove "fixnum-hood" for all recursive calls to fact is quite significant, because it)J
72 227 :M
.101 .01(allows a compiler to represent )J
f7_10 sf
(n)S
f0_10 sf
.104 .01( as a 32-bit 2's complement integer, and utilize a hardware "decrement" operation to)J
72 238 :M
.335 .033(implement )J
f7_10 sf
.089(\(1-\312n\))A
f0_10 sf
(.)S
72 260 :M
f9_10 sf
4.574 .457(4.1 INCORPORATING PROGRAMMER-SUPPLIED INFORMATION)J
72 276 :M
f0_10 sf
.996 .1(In the factorial example above, NTI could determine that the )J
f2_10 sf
.313(argument)A
f0_10 sf
.952 .095( to fact would always be a nonnegative)J
72 287 :M
.217 .022(fixnum, and could show that the )J
f2_10 sf
.058(result)A
f0_10 sf
.197 .02( would be a positive integer, but no bound could be placed on the size of this)J
72 298 :M
.188 .019(integer. )J
f2_10 sf
.102(We)A
f0_10 sf
.241 .024( know, of course, that the factorial function grows very rapidly, and that arguments greater than 12 will)J
72 310 :M
.999 .1(produce results greater than 2)J
f0_9 sf
0 -3 rm
.266(32)A
0 3 rm
f0_10 sf
.899 .09(. However, the NTI algorithm will usually be incapable of inferring such deep)J
72 321 :M
.248 .025(mathematical results, and in these cases it will be necessary for the programmer to supply additional information to)J
72 332 :M
.131 .013(aid NTI in its analysis if he wants to obtain the most efficient code. The programmer can supply this information in)J
72 343 :M
-.126(several different ways.)A
72 359 :M
-.013(The NTI algorithm relies on all data provided by the programmer, both from declarations and from actual use, so that)A
72 370 :M
-.053(the following programs all produce the same code \(except for the wording of any error message\):)A
72 386 :M
f7_10 sf
(\(defun test1 \(x y\))S
72 397 :M
.005 0( \(declare \(integer x\) \(type \(integer 0 *\) y\)\))J
399 397 :M
(; Info as declaration)S
72 408 :M
( \(expt x y\)\))S
72 424 :M
(\(defun test2 \(x y\))S
72 435 :M
( \(assert \(integerp x\)\))S
371 435 :M
(; Info as assertions)S
72 446 :M
.005 0( \(assert \(typep y '\(integer 0 *\)\)\))J
72 457 :M
( \(expt x y\)\))S
72 473 :M
(\(defun test3 \(x y\))S
72 484 :M
( \(if \(and \(integerp x\))S
371 484 :M
(; Info as conditional)S
72 495 :M
( \(integerp y\))S
72 506 :M
( \(not \(minusp y\)\)\))S
72 517 :M
( \(expt x y\))S
72 528 :M
.004 0( \(error "Bad types for ~S, ~S in test3" x y\)\)\))J
72 544 :M
(\(defun test4 \(x y\))S
72 555 :M
( \(etypecase x)S
371 555 :M
(; Info as typecase)S
72 566 :M
( \(integer)S
72 577 :M
( \(etypecase y)S
72 588 :M
.003 0( \(\(integer 0 *\) \(expt y x\)\)\)\)\))J
72 604 :M
(\(defun test5 \(x y\))S
72 615 :M
.005 0( \(assert \(or \(zerop y\) \(plusp y\)\)\))J
371 615 :M
(; Info as assertion)S
72 626 :M
( \(the integer \(expt x y\)\)\))S
371 626 :M
(; and declaration)S
72 642 :M
f0_10 sf
.777 .078(\(The last example is not necessarily true in all Common Lisps, but true in most, as it assumes that only rational)J
72 653 :M
-.029(arguments can produce rational results from the two-argument exponential function.\))A
72 675 :M
f9_10 sf
3.368 .337(4.2 GENERATING DECLARATIONS WITH THE NIMBLE TYPE INFERENCER)J
72 691 :M
f0_10 sf
.688 .069(Common Lisp already has a syntactically well-specified mechanism for the programmer to declare his intentions)J
72 702 :M
.773 .077(regarding the range of values \(and hopefully the representations\) of variables and temporaries\321the )J
f2_10 sf
.186(declaration)A
f0_10 sf
(.)S
72 713 :M
.069 .007(Declarations allow the programmer to specify to the compiler that more specialized and efficient representations can)J
endp
%%Page: 6 6
%%BeginPageSetup
initializepage
(Henry Baker; page: 6 of 26)setjob
%%EndPageSetup
-31 -31 :T
gS 31 31 552 730 rC
72 45 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
72 749 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
503 749 :M
(6)S
72 69 :M
.796 .08(be used to speed up his program. In fact, the output of the Nimble type inferencer after its analysis of an input)J
72 80 :M
-.028(program is a copy of the input program with additional declarations inserted.)A
72 96 :M
.972 .097(Unfortunately, the )J
f2_10 sf
.246(meaning)A
f0_10 sf
.802 .08( of declarations is not well-specified in Common Lisp-84. While the Common Lisp)J
72 107 :M
.301 .03(standard [CLtL84,p.153] states that "declarations are completely optional and )J
f2_10 sf
.067(correct)A
f0_10 sf
.24 .024( declarations do not affect the)J
72 118 :M
.26 .026(meaning of a correct program" \(emphasis supplied\), the meaning of "correct declaration" is not made clear, and the)J
72 129 :M
.028 .003(standard allows that "an implementation is not required to detect such errors" \(where "such error" presumably means)J
72 140 :M
-.022(the violation of a declaration, i.e., "error" )A
f1_10 sf
S
f0_10 sf
-.024( "incorrect declaration"\).)A
72 156 :M
1.231 .123(The Nimble type inferencer takes a slightly different point of view. NTI considers any programmer-supplied)J
72 167 :M
.209 .021(declarations to be a material part of the program. These declarations are treated as )J
f2_10 sf
.062(constraining)A
f0_10 sf
.236 .024( the possible values)J
72 178 :M
.686 .069(which a variable or a temporary can assume, in much the same way that the "type" of an array in Common Lisp)J
72 189 :M
1.112 .111(constrains the possible values which can be stored into the array. NTI enforces this "constraint" semantics of)J
72 200 :M
.681 .068(declarations by inserting dynamic type checks of its own when it cannot prove that the constraint will always be)J
72 211 :M
-.091(satisfied.)A
72 227 :M
1.151 .115(Since our declarations )J
f2_10 sf
.334(do)A
f0_10 sf
1.012 .101( constrain the values of variables and temporaries, they )J
f2_10 sf
.322(can)A
f0_10 sf
.917 .092( change the meaning of a)J
72 238 :M
.142 .014(program, in the sense that a program with declarations can exhibit a smaller range of correct behavior than the same)J
72 249 :M
.146 .015(program with the declarations elided. Therefore, declarations can become a source of bugs, as well as a way to find)J
72 260 :M
1.586 .159(bugs \(assuming that a compiler or run-time system complains about declarations which actually constrain a)J
72 271 :M
.095 .01(program's meaning\). The fact that declarations may actually introduce bugs into previously correct programs means)J
72 282 :M
-.086(that the traditional advice to "only add declarations to well-debugged programs" becomes circular and nonsensical!)A
72 298 :M
1.429 .143(However, if we advocate that programmers always decorate their programs with declarations, we risk losing)J
72 309 :M
.066 .007(something far more important than the correctness of previously debugged programs. We risk losing the advantages)J
72 320 :M
.798 .08(of polymorphism entirely. Lisp has traditionally been considered a )J
f2_10 sf
.227(polymorphic)A
f0_10 sf
.889 .089( language, where functions can)J
72 331 :M
-.013(accept arguments of different types at different times, and produce different types as results. For example, consider a)A
72 342 :M
-.051(straight-forward implementation of the )A
f2_10 sf
-.054(arcsinh)A
f0_10 sf
-.054( function:)A
72 358 :M
f7_10 sf
(\(defun asinh \(z\))S
72 369 :M
( \(log \(+ z \(sqrt \(1+ \(* z z\)\)\)\)\)\))S
72 385 :M
f0_10 sf
.798 .08(This function should work properly regardless of the argument type. )J
f7_10 sf
.308(asinh)A
f0_10 sf
.25 .025( is )J
f2_10 sf
.236(polymorphic)A
f0_10 sf
.662 .066(, and must return a)J
72 396 :M
.309 .031(floating-point result of the same format when given a floating-point argument, and must return a complex floating-)J
72 407 :M
1.53 .153(point result of the same format when given a complex floating-point argument. This polymorphism can be)J
72 418 :M
.77 .077(implemented in one of two ways: )J
f2_10 sf
1.461 .146(true polymorphism)J
f0_10 sf
.762 .076(, where the function can actually handle all of the possible)J
72 429 :M
.552 .055(types, and )J
f2_10 sf
.175(overloading)A
f0_10 sf
.57 .057(, where any instance of the function works on only one type, and the particular instance is)J
72 440 :M
.024 .002(chosen at compile time. Overloading is more analogous to macro-expansion, except that the choice of which macro-)J
72 451 :M
.621 .062(expansion to use is dependent upon the type of the argument \(and possibly upon the type of the expected result\).)J
72 462 :M
-.033(Overloading is usually more efficient than true polymorphism, because no type dispatching need be performed at run-)A
72 473 :M
.182(time.)A
72 489 :M
.642 .064(By leaving the declarations )J
f2_10 sf
.167(out)A
f0_10 sf
.604 .06( of a function definition, and instead inferring them when the argument and result)J
72 500 :M
-.008(types are already known, we can have reasonably generic function definitions. These generic functions can be stored)A
72 511 :M
.196 .02(in a library and compiled with the properly inferred types on demand. The alternative is precompiling a whole host)J
72 522 :M
-.018(of versions of the same function with differently declared types, and choosing the proper one at compile time through)A
72 533 :M
-.04(overloading techniques. The overloading technique is strongly advocated in the Ada language, but is only moderately)A
72 544 :M
.686 .069(successful due to the restrictions of that language, and due to the poor interaction of overloading with other Ada)J
72 555 :M
.185 .018(language features, such as separate compilation [AdaLRM]. Due to these problems, the default alternative supplied)J
72 566 :M
1.357 .136(by most Common Lisp implementations is to endure the systematic inefficiency of truly polymorphic library)J
72 577 :M
.036(routines.)A
72 593 :M
.946 .095(The Nimble approach, on the other hand, allows for the above definition of )J
f7_10 sf
.384(asinh)A
f0_10 sf
.713 .071( to act as more like an Ada)J
72 604 :M
f2_10 sf
.354 .035(generic function)J
f0_10 sf
.263 .026( [AdaLRM], where each instance\321whether in-line or out-of-line\321will be compiled using only the)J
72 615 :M
.714 .071(functionality required by that instance. Thus, if a programmer only calls )J
f7_10 sf
.285(asinh)A
f0_10 sf
.962 .096( with single-precision floating-)J
72 626 :M
.29 .029(point arguments in a particular program, then the )J
f7_10 sf
.114(asinh)A
f0_10 sf
.298 .03( function compiled for that program will be specialized to)J
72 637 :M
.638 .064(work with only single-precision floating-point arguments and results. This ability to automatically specialize the)J
72 648 :M
.003 0(standard Common Lisp libraries to the actual usage of a program is unique to the Nimble approach.)J
endp
%%Page: 7 7
%%BeginPageSetup
initializepage
(Henry Baker; page: 7 of 26)setjob
%%EndPageSetup
-31 -31 :T
gS 31 31 552 730 rC
72 45 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
72 749 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
503 749 :M
(7)S
72 69 :M
.412 .041(If one actually )J
f2_10 sf
.147(wants)A
f0_10 sf
.555 .055( a truly polymorphic function which is also efficient\321e.g., for a high-performance compiled)J
72 80 :M
1.511 .151(library function\321then one should utilize the following programming technique when using the Nimble type)J
72 91 :M
-.229(inferencer:)A
72 107 :M
f7_10 sf
(\(defun asinh \(z\))S
72 118 :M
( \(typecase z)S
72 129 :M
( \(single-float)S
303 129 :M
(\(log \(+ z \(sqrt \(1+ \(* z z\)\)\)\)\)\))S
72 140 :M
( \(short-float)S
303 140 :M
(\(log \(+ z \(sqrt \(1+ \(* z z\)\)\)\)\)\))S
72 151 :M
( \(double-float)S
303 151 :M
(\(log \(+ z \(sqrt \(1+ \(* z z\)\)\)\)\)\))S
72 162 :M
( \(long-float)S
303 162 :M
(\(log \(+ z \(sqrt \(1+ \(* z z\)\)\)\)\)\))S
72 173 :M
( \(\(complex single-float\))S
303 173 :M
(\(log \(+ z \(sqrt \(1+ \(* z z\)\)\)\)\)\))S
72 184 :M
( \(\(complex short-float\))S
303 184 :M
(\(log \(+ z \(sqrt \(1+ \(* z z\)\)\)\)\)\))S
72 195 :M
( \(\(complex double-float\))S
303 195 :M
(\(log \(+ z \(sqrt \(1+ \(* z z\)\)\)\)\)\))S
72 206 :M
( \(\(complex long-float\))S
303 206 :M
(\(log \(+ z \(sqrt \(1+ \(* z z\)\)\)\)\)\))S
72 217 :M
( \(fixnum)S
303 217 :M
(\(log \(+ z \(sqrt \(1+ \(* z z\)\)\)\)\)\))S
72 228 :M
( \(integer)S
303 228 :M
(\(log \(+ z \(sqrt \(1+ \(* z z\)\)\)\)\)\))S
72 239 :M
( \(rational)S
303 239 :M
(\(log \(+ z \(sqrt \(1+ \(* z z\)\)\)\)\)\))S
72 250 :M
( \(\(complex rational\))S
303 250 :M
(\(log \(+ z \(sqrt \(1+ \(* z z\)\)\)\)\)\))S
72 261 :M
( \(t)S
108 261 :M
(\(log \(+ z \(sqrt \(1+ \(* z z\)\)\)\)\)\)\)\))S
72 277 :M
f0_10 sf
.235 .024(This version of )J
f7_10 sf
.103(asinh)A
f0_10 sf
.263 .026( is more efficient than the previous one when compiled for all numeric types, because while)J
72 288 :M
-.019(the expressions within each arm are identical, they are compiled differently. Within each arm, )A
f7_10 sf
(z)S
f0_10 sf
-.019( is inferred as having)A
72 299 :M
.266 .027(a different type, and hence different versions of )J
f7_10 sf
.108(log)A
f0_10 sf
.075 .007(, )J
f7_10 sf
.108(+)A
f0_10 sf
.075 .007(, )J
f7_10 sf
.108(sqrt)A
f0_10 sf
.228 .023(, etc., are compiled within that arm. In the first arm,)J
72 310 :M
.324 .032(for example, )J
f7_10 sf
.121(+)A
f0_10 sf
.084 .008(, )J
f7_10 sf
.121(1+)A
f0_10 sf
.17 .017(, and )J
f7_10 sf
.121(*)A
f0_10 sf
.324 .032( can be open-coded with single hardware instructions, and for some processors, )J
f7_10 sf
.121(log)A
f0_10 sf
.262 .026( and)J
72 321 :M
f7_10 sf
.12(sqrt)A
f0_10 sf
.299 .03( have single hardware instructions, as well. \(If the programmer also specifies in his calling program that this)J
72 332 :M
.227 .023(version of )J
f7_10 sf
.099(asinh)A
f0_10 sf
.24 .024( should be expanded inline, then the Nimble algorithm will in most cases be able to eliminate the)J
72 343 :M
-.012(type check on the entry to the body of )A
f7_10 sf
-.018(asinh)A
f0_10 sf
-.012(, as well as the dead code from all the unused arms.\))A
72 359 :M
.116 .012(The Nimble type inferencing algorithm allows the programmer to use standard )J
f2_10 sf
.216 .022(case-based programming)J
f0_10 sf
.1 .01( in addition)J
72 370 :M
.095 .009(to declarations to tell the compiler what to do. Consider, for example, the )J
f7_10 sf
.041(abs)A
f0_10 sf
.14 .014( function:)J
72 386 :M
f7_10 sf
(\(defun abs \(z\))S
72 397 :M
( \(cond \(\(complexp z\))S
72 408 :M
.004 0( \(sqrt \(+ \(sqr \(realpart z\)\) \(sqr \(imagpart z\)\)\)\)\))J
72 419 :M
( \(\(minusp z\) \(- z\)\))S
72 430 :M
( \(t z\)\)\))S
72 446 :M
f0_10 sf
1.252 .125(NTI is able to infer that within the first )J
f7_10 sf
.632(cond)A
f0_10 sf
1.128 .113( clause )J
f7_10 sf
.632(z)A
f0_10 sf
1.575 .157( is complex, hence numeric, hence )J
f7_10 sf
.632(realpart)A
f0_10 sf
1.373 .137( and)J
72 457 :M
f7_10 sf
.172(imagpart)A
f0_10 sf
.419 .042( are defined and cannot fail. Furthermore, those functions produce real results, so )J
f7_10 sf
.172(sqr)A
f0_10 sf
.515 .052( produces non-)J
72 468 :M
.142 .014(negative real results, hence )J
f7_10 sf
.053(sqrt)A
f0_10 sf
.118 .012( always produces a real result. Within the second )J
f7_10 sf
.053(cond)A
f0_10 sf
.122 .012( clause, NTI can infer that)J
72 479 :M
f7_10 sf
.143(z)A
f0_10 sf
.293 .029( is a negative real, because )J
f7_10 sf
.143(minusp)A
f0_10 sf
.32 .032( cannot be applied to other than real numbers, and it is true only for negative)J
72 490 :M
.233 .023(reals. NTI therefore concludes that the second clause produces a positive real result. It may be surprising, but NTI)J
72 501 :M
.402 .04(also infers that )J
f7_10 sf
.185(z)A
f0_10 sf
.361 .036( is a non-negative real in the third )J
f7_10 sf
.185(cond)A
f0_10 sf
.448 .045( clause even though it apparently could be any Lisp type)J
72 512 :M
.384 .038(other than complex and negative numbers. However, since )J
f7_10 sf
.141(minusp)A
f0_10 sf
.372 .037( is not defined for non-numeric types, control)J
72 523 :M
.011 .001(will never reach the final clause unless )J
f7_10 sf
(z)S
f0_10 sf
.008 .001( is a number, and since )J
f7_10 sf
(minusp)S
f0_10 sf
.012 .001( cannot be applied to complex numbers and)J
72 534 :M
.706 .071(will return true for negative numbers, )J
f7_10 sf
.275(z)A
f0_10 sf
.616 .062( must therefore be a non-negative real in the third clause. Putting all the)J
72 545 :M
-.004(clauses together, NTI concludes that the result from )A
f7_10 sf
(abs)S
f0_10 sf
-.005( must always be a non-negative real number.)A
72 561 :M
(It is also instructive to see whether NTI can infer that the square function )S
f7_10 sf
(sqr)S
f0_10 sf
( always produces a non-negative result)S
72 572 :M
-.023(for non-complex arguments.)A
72 588 :M
f7_10 sf
(\(defun sqr \(z\))S
72 599 :M
( \(* z z\)\))S
72 615 :M
f0_10 sf
.434 .043(Given this simple definition for )J
f7_10 sf
.168(sqr)A
f0_10 sf
.368 .037(, NTI can only conclude that )J
f7_10 sf
.168(z)A
f0_10 sf
.416 .042( is numeric, and cannot say anything about the)J
72 626 :M
.263 .026(sign of )J
f7_10 sf
.147(z)A
f0_10 sf
.252 .025( when )J
f7_10 sf
.147(z)A
f0_10 sf
.34 .034( is real. However, consider the following definition for the )J
f7_10 sf
.147(sqr)A
f0_10 sf
.394 .039( function, which is actually more)J
72 637 :M
-.043(efficient for complex arguments:)A
72 653 :M
f7_10 sf
(\(defun sqr \(z\))S
72 664 :M
( \(cond \(\(complexp z\))S
72 675 :M
.004 0( \(let \(\(r \(realpart z\)\) \(i \(imagpart z\)\)\))J
72 686 :M
.003 0( \(complex \(- \(sqr r\) \(sqr i\)\) \(* 2 r i\)\)\)\))J
72 697 :M
( \(\(minusp z\) \(* z z\)\))S
72 708 :M
( \(t \(* z z\)\)\)\))S
endp
%%Page: 8 8
%%BeginPageSetup
initializepage
(Henry Baker; page: 8 of 26)setjob
%%EndPageSetup
-31 -31 :T
gS 31 31 552 730 rC
72 45 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
72 749 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
503 749 :M
(8)S
72 69 :M
.589 .059(While the second and third clauses in )J
f7_10 sf
.251(sqr)A
f0_10 sf
.539 .054( do not help in improving the efficiency of )J
f7_10 sf
.251(sqr)A
f0_10 sf
.524 .052(, they do allow NTI to)J
72 80 :M
.374 .037(infer that )J
f7_10 sf
.179(sqr)A
f0_10 sf
.439 .044( applied to real arguments produces a non-negative real result. This is because NTI can easily infer)J
72 91 :M
.026 .003(that negative*negative is positive and non-negative*non-negative is non-negative, and can put these facts together to)J
72 102 :M
.645 .064(infer that the result of )J
f7_10 sf
.325(sqr)A
f0_10 sf
.823 .082( must always be non-negative for real arguments. \(Note that while NTI required the)J
72 113 :M
.592 .059(programmer to split out the negative real and non-negative real cases in order to extract the most information, an)J
72 124 :M
-.014(optimizing "back-end" could later merge the two clauses back together, so that no efficiency is necessarily lost.\))A
72 140 :M
.813 .081(The Nimble type inference algorithm is not only "case-based", but also "state-based". This means that NTI can)J
72 151 :M
.021 .002(properly type the following program:)J
72 167 :M
f7_10 sf
(\(defun test \(\))S
72 178 :M
( \(let \(\(x 0\)\))S
72 189 :M
( \(let \(\(y x\)\))S
72 200 :M
( \(setq x 0.0\))S
72 211 :M
( \(values x y\)\)\)\))S
72 227 :M
f0_10 sf
.016 .002(For this program, NTI will provide declarations along the lines of the following code:)J
72 243 :M
f7_10 sf
(\(defun test \(\))S
72 254 :M
1.714 .171( \(let \(\(x 0\)\) )J
f8_10 sf
3.472 .347(\(declare \(type \(or fixnum single-float\) x\)\))J
72 265 :M
f7_10 sf
1.059 .106( \(let \(\(y x\)\) )J
f8_10 sf
2.78 .278(\(declare \(fixnum y\)\))J
72 276 :M
f7_10 sf
.568 .057( \(setq x \(the )J
f8_10 sf
.326(single-float)A
f7_10 sf
1.303 .13( 0.0\)\))J
72 287 :M
.654 .065( \(values \(the )J
f8_10 sf
.341(single-float)A
f7_10 sf
.787 .079( x\) \(the )J
f8_10 sf
.341(fixnum)A
f7_10 sf
1.492 .149( y\)\)\)\)\))J
72 303 :M
f0_10 sf
-.039(The Nimble type inferencer can also handle functions with side-effects, without open-coding them:)A
72 319 :M
f7_10 sf
(\(defun test-swap \(\))S
72 330 :M
( \(let \(\(x 0\) \(y 0.0\)\))S
72 341 :M
( \(flet)S
72 352 :M
.003 0( \(\(swap \(&aux z\) \(setq z x x y y z\)\)\))J
72 363 :M
( \(print \(list x y\)\))S
72 374 :M
( \(swap\))S
72 385 :M
( \(values x y\)\)\)\))S
72 401 :M
f0_10 sf
.109 .011(NTI would annotate this example in a manner similar to that shown below:)J
72 417 :M
f7_10 sf
(\(defun test-swap \(\))S
72 428 :M
( \(let \(\(x 0\) \(y 0.0\)\))S
72 439 :M
.788 .079( )J
f8_10 sf
3.751 .375(\(declare \(type \(or fixnum single-float\) x y\)\))J
72 450 :M
f7_10 sf
( \(flet)S
72 461 :M
.938 .094( \(\(swap \(&aux z\) )J
f8_10 sf
2.45 .245(\(declare \(fixnum z\)\))J
72 472 :M
f7_10 sf
( \(setq z x x y y z\)\)\))S
72 483 :M
.68 .068( \(print \(list \(the )J
f8_10 sf
.323(fixnum)A
f7_10 sf
.745 .075( x\) \(the )J
f8_10 sf
.323(single-float)A
f7_10 sf
1.153 .115( y\)\)\))J
72 494 :M
( \(swap\))S
72 505 :M
.654 .065( \(values \(the )J
f8_10 sf
.341(single-float)A
f7_10 sf
.787 .079( x\) \(the )J
f8_10 sf
.341(fixnum)A
f7_10 sf
1.492 .149( y\)\)\)\)\))J
72 521 :M
f0_10 sf
.134 .013(As this example shows, the Nimble type inferencer is able to keep track of the different states of the variables )J
f7_10 sf
.057(x)A
f0_10 sf
.124 .012( and)J
72 532 :M
f7_10 sf
(y)S
f0_10 sf
-.027( at different places in the program.)A
72 548 :M
f4_10 sf
3.376 .338(5. KAPLAN-ULLMAN TYPE INFERENCE)J
72 564 :M
f0_10 sf
.139 .014(Since the Nimble type inference algorithm is derived from the Kaplan-Ullman type inference algorithm [Kaplan80],)J
72 575 :M
1.277 .128(we will discuss the Kaplan-Ullman approach in detail. It should first be pointed out that the Kaplan-Ullman)J
72 586 :M
-.006(approach to type inference uses the same type of lattice techniques as does traditional )A
f2_10 sf
-.006(data flow)A
f0_10 sf
-.006( analysis in compilers)A
72 597 :M
-.054([Aho86].)A
72 613 :M
.606 .061(The Kaplan-Ullman algorithm utilizes a simple state-based model of computation. Within this model, a program)J
72 624 :M
.646 .065(consists of a finite number of variables, and a finite directed graph with nodes, each of which describes a simple)J
72 635 :M
.555 .055(computational step. The simple steps consist of assignment statements of the form z:=f\(a,b,c,...\), where a,b,c,...,z)J
72 646 :M
.073 .007(are program variables, and f\(,,,...\) is a predefined function. Constants are introduced as functions of zero arguments,)J
72 657 :M
1.535 .153(and the flow of control is non-deterministic. The Kaplan-Ullman type inference algorithm seeks to find an)J
72 668 :M
.242 .024(assignment of variable names to datatypes in the datatype lattice which is )J
f2_10 sf
.064(consistent)A
f0_10 sf
.22 .022( with the needs of the program,)J
72 679 :M
.155 .015(and is also )J
f2_10 sf
.068(minimal)A
f0_10 sf
.221 .022(, in that any smaller assignment of datatypes is no longer consistent.)J
72 695 :M
.092 .009(In the classical non-deterministic manner, the flow of control is terminated only by branches of the computation that)J
72 706 :M
f2_10 sf
.084(fail)A
f0_10 sf
.271 .027( in the sense that there are )J
f2_10 sf
.126(no)A
f0_10 sf
.338 .034( legitimate values for variables. In this setting, )J
f2_10 sf
.105(predicates)A
f0_10 sf
.38 .038( are modelled by partial)J
72 717 :M
.525 .053(functions whose results are ignored. One particularly valuable partial function of this kind is "assert\(p\)" which is)J
72 728 :M
-.058(defined only for the argument "true".)A
endp
%%Page: 9 9
%%BeginPageSetup
initializepage
(Henry Baker; page: 9 of 26)setjob
%%EndPageSetup
-31 -31 :T
gS 31 31 552 730 rC
72 45 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
72 749 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
503 749 :M
(9)S
72 69 :M
-.016(Even though the control flow of the Kaplan-Ullman model is non-deterministic, it can accurately model deterministic)A
72 80 :M
.009 .001(programs through the following mapping. An "if-then-else" statement can be modelled as follows:)J
72 96 :M
f7_10 sf
(\(if \(p x y\) \(setq z \(f x y\)\))S
72 107 :M
( \(setq z \(g x y\)\)\))S
72 123 :M
f0_10 sf
-.119(can be modelled as:)A
72 139 :M
f7_10 sf
(\(alt \(progn \(assert \(p x y\)\))S
371 139 :M
(\(setq z \(f x y\)\)\))S
72 150 :M
( \(progn \(assert \(not \(p x y\)\)\))S
371 150 :M
(\(setq z \(g x y\)\)\)\))S
72 166 :M
f0_10 sf
.156 .016(In this case, the construct )J
f7_10 sf
.07(\(alt\312A\312B\))A
f0_10 sf
.176 .018( means "perform either )J
f7_10 sf
.07(A)A
f0_10 sf
.065 .006( or )J
f7_10 sf
.07(B)A
f0_10 sf
.228 .023(", with the choice made non-deterministically,)J
72 177 :M
.223 .022(and following either arm describes a legitimate computation. In the specific usage above, only one \(or none\) of the)J
72 188 :M
.275 .027(arms will actually succeed, since it cannot happen that both )J
f2_10 sf
.092(p)A
f0_10 sf
.143 .014( and )J
f2_10 sf
.249 .025(not p)J
f0_10 sf
.24 .024( are true at the same time. Therefore, while)J
72 199 :M
.22 .022(the )J
f7_10 sf
.126(alt)A
f0_10 sf
.336 .034( construct is powerful enough to introduce true non-determinism, we will only need it under the controlled)J
72 210 :M
-.012(conditions given above.)A
72 226 :M
.387 .039(The Kaplan-Ullman model introduces a )J
f2_10 sf
.079(state)A
f0_10 sf
.309 .031( for each node in the graph which describes the best approximation to)J
72 237 :M
.812 .081(the actual datatypes of all of the program variables at that point in the program. A )J
f2_10 sf
.237(state)A
f0_10 sf
.688 .069( can be thought of as a)J
72 248 :M
.672 .067(simple vector of components\321one for each program variable\321whose values are elements of the datatype lattice)J
72 259 :M
.152 .015(needed by the algorithm. We will actually use )J
f2_10 sf
.053(two)A
f0_10 sf
.155 .016( states for each program point, since we will want to describe the)J
72 270 :M
-.007(state just )A
f2_10 sf
-.009(before)A
f0_10 sf
-.007( as well as the state just )A
f2_10 sf
(after)S
f0_10 sf
-.008( the execution of a particular node.)A
72 286 :M
.38 .038(The Kaplan-Ullman model then proceeds to model the program statements z:=f\(a,b,c,...\) in terms of their effect on)J
72 297 :M
.953 .095(these program states. Since we are utilizing the datatype lattice, rather than actual run-time values, we will be)J
72 308 :M
.167 .017(inferring the )J
f2_10 sf
.047(datatype)A
f0_10 sf
.132 .013( z from knowledge of the )J
f2_10 sf
.046(datatypes)A
f0_10 sf
.159 .016( a,b,c,... The Kaplan-Ullman model utilizes a )J
f2_10 sf
.043("t-function")A
f0_10 sf
(,)S
72 319 :M
.57 .057(which answers such questions in the most conservative way. Thus, if we have an approximation to the datatypes)J
72 330 :M
1.342 .134(a,b,c,..., the t-function of f\(,,,...\) will allow us to produce an approximation to the datatype of z. Perhaps as)J
72 341 :M
.333 .033(importantly, the t-function of f\(,,,...\) will also allow us to approximate a,b,c,... given the datatype of z.)J
72 357 :M
.335 .034(There is nothing paradoxical about these "t-functions". For example, if we know that the result of )J
f7_10 sf
.138<282BCA78CA7929>A
f0_10 sf
.194 .019( is an)J
72 368 :M
.285 .028(integer, then it must be the case that )J
f7_10 sf
.139(x)A
f0_10 sf
.058(,)A
f7_10 sf
.139(y)A
f0_10 sf
.358 .036( were both rationals \(given the standard policy of floating-point )J
f2_10 sf
.116(contagion)A
72 379 :M
f0_10 sf
.671 .067(in Common Lisp\). While this information doesn't allow us to pin down the types of )J
f7_10 sf
.289(x)A
f0_10 sf
.121(,)A
f7_10 sf
.289(y)A
f0_10 sf
.718 .072( more specifically to be)J
72 390 :M
.375 .037(integers \(e.g., 3/4 + 1/4 = 1\), we can usually get additional information regarding one or both of )J
f7_10 sf
.163(x)A
f0_10 sf
.211 .021( and )J
f7_10 sf
.163(y)A
f0_10 sf
.361 .036(, and thus)J
72 401 :M
.056 .006(pin down the types of all three quantities )J
f7_10 sf
(x)S
f0_10 sf
(,)S
f7_10 sf
(y)S
f0_10 sf
.035 .003(, and )J
f7_10 sf
.025(x+y)A
f0_10 sf
.072 .007( more exactly.)J
72 417 :M
1.147 .115(As an aside, it should be noted that this class of inferences is analogous to the )J
f2_10 sf
2.363 .236(Bayesian analysis)J
f0_10 sf
1.376 .138( of Markov)J
72 428 :M
.454 .045(processes [???], where the states of the program are identified with Markov states, and the transition functions are)J
72 439 :M
.327 .033(associated with transition probabilities. However, the Kaplan-Ullman model produces information about the range)J
72 450 :M
.041 .004(of )J
f2_10 sf
.018(possibilities)A
f0_10 sf
.057 .006(, rather than )J
f2_10 sf
.02(probabilities)A
f0_10 sf
.073 .007(. It may be possible to extend type inference analysis to perform probability)J
72 461 :M
.47 .047(analysis, given some method of producing )J
f2_10 sf
.473 .047(a priori)J
f0_10 sf
.456 .046( probabilities about frequency of execution. Such a probability)J
72 472 :M
-.035(analysis would enable a compiler to generate better code for the most probable cases.)A
72 488 :M
.126 .013(Kaplan and Ullman call the propagation of information in the normal direction of computation \(i.e., from arguments)J
72 499 :M
.285 .028(to function values\) )J
f2_10 sf
.508 .051(forward inferencing)J
f0_10 sf
.296 .03(, while the propagation of information in the retrograde direction \(i.e., from)J
72 510 :M
.523 .052(function values to argument values\) )J
f2_10 sf
.9 .09(backward inferencing)J
f0_10 sf
.445 .045(. Thus, for every statement, we can compute a forward)J
72 521 :M
.576 .058(inference function which carries the "before-state" into the "after-state", as well as a backward inference function)J
72 532 :M
.398 .04(which carries the "after-state" into the "before-state". If we assign an index to every statement in the program, we)J
72 543 :M
.224 .022(can then construct a vector of states for the entire program, which then becomes a representation of the )J
f2_10 sf
.062(flowstate)A
f0_10 sf
.139 .014( of)J
72 554 :M
-.008(the program.)A
72 570 :M
.318 .032(This flowstate has little in common with the state of a program running on actual data. In the case of an executing)J
72 581 :M
1.093 .109(program, the "state" of the program would be a program counter, holding the index of the currently executing)J
72 592 :M
.213 .021(program statement, and an environment vector, holding the current values of all of the program variables. Our data)J
72 603 :M
.396 .04(type "flowstate", on the other hand, consists of a vector of vectors whose elements are in the datatype lattice. The)J
72 614 :M
.309 .031(outer vector is indexed by program statements; the inner vectors are indexed by program variables. The reason for)J
72 625 :M
.071 .007(requiring a different state for each program statement is that the approximation to the datatype of a program variable)J
72 636 :M
-.02(at one particular program statement could be different from the datatype assumed at another program statement. This)A
72 647 :M
-.031(is especially true when )A
f2_10 sf
-.034(variable assignment)A
f0_10 sf
-.031( is involved, since the datatype of a program variable can then be different)A
72 658 :M
.02 .002(from one program statement to another.)J
72 674 :M
.108 .011(Once we have represented the flowstate of an entire program in the above manner, we can now describe the forward)J
72 685 :M
1.196 .12(and backward inferencing operations in terms of matrix multiplication operations. To do this, we construct a)J
72 696 :M
f2_10 sf
.145(diagonal)A
f0_10 sf
.387 .039( square matrix F of order )J
f7_10 sf
.196(n)A
f0_10 sf
.372 .037(, where )J
f7_10 sf
.196(n)A
f0_10 sf
.459 .046( is the number of program statements. The element F)J
0 3 rm
.091(ii)A
0 -3 rm
.394 .039( is a function)J
72 709 :M
.756 .076(from states to states which describes the transition of the program statement i in going from a before-state to an)J
endp
%%Page: 10 10
%%BeginPageSetup
initializepage
(Henry Baker; page: 10 of 26)setjob
%%EndPageSetup
-31 -31 :T
gS 31 31 552 730 rC
72 45 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
72 749 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
503 749 :M
(10)S
72 69 :M
.256 .026(after-state. The off-diagonal elements are constant functions yielding the bottom state, which assign to all program)J
72 80 :M
-.018(variables the bottom datatype.)A
72 96 :M
.456 .046(We also construct a square matrix C of order )J
f7_10 sf
.339 .034(n )J
f0_10 sf
.507 .051(in which the element C)J
0 3 rm
.094(ij)A
0 -3 rm
.449 .045( is a function from states to states which)J
72 109 :M
2.153 .215(describes the connectivity of the program statements. C)J
0 3 rm
.362(ij)A
0 -3 rm
1.887 .189( is the identity function if program statement j)J
72 122 :M
.278 .028(immediately follows program statement i, and it is the constant function producing the bottom state otherwise. We)J
72 133 :M
.581 .058(will use a form of matrix multiplication in which the usual role of "+" is taken by the lattice )J
f2_10 sf
.175(join)A
f0_10 sf
.815 .081( operation \(here)J
72 144 :M
.008 .001(extended componentwise to vectors of elements\), and the usual role of "*" is taken by functional composition.)J
72 160 :M
.226 .023(Given a program flowstate S, which is a vector of states indexed by program statements, we can compute the effect)J
72 171 :M
.228 .023(of a single execution of each program statement by means of the matrix-vector product F\245S. This product takes the)J
72 182 :M
.369 .037(before-flowstate S into an after-flowstate F\245S. Similarly, the matrix-vector product C\245S takes the after-flowstate S)J
72 193 :M
.279 .028(into a before-flowstate C\245S. Thus, we can utilize the composition F\245C\245S to take an after-flowstate to another after-)J
72 204 :M
-.016(flowstate.)A
72 220 :M
.064 .006(Kaplan and Ullman show that all of these operations are )J
f2_10 sf
.021(monotonic)A
f0_10 sf
.061 .006( in the datatype lattice, and since this lattice must)J
72 231 :M
.058 .006(obey the finite chain condition, the following sequence has a limit for every S:)J
220 247 :M
.757 .076(S, F\245C\245S, F\245C\245F\245C\245S, F\245C\245F\245C\245F\245C\245S, ...)J
72 263 :M
.511 .051(In particular, the limit exists when S is the \(vector of\) bottom element\(s\) of the datatype lattice. This limit is one)J
72 274 :M
.34 .034(consistent datatype labeling of the program, although it may not be a )J
f2_10 sf
.109(minimal)A
f0_10 sf
.35 .035( such labeling \(in the datatype lattice)J
72 285 :M
-.189(ordering\).)A
72 301 :M
.98 .098(The previous limit can actually be taken with a finite computation. This computation will converge in a finite)J
72 312 :M
.413 .041(amount of time due to the finite chain condition of the original datatype lattice, and the finite numbers of program)J
72 323 :M
.896 .09(variables and program statements. The operation of the program can intuitively be described as assuming each)J
72 334 :M
.247 .025(program variable takes on the value of the bottom lattice element, and then propagating this assumption throughout)J
72 345 :M
.405 .04(the entire program. Whenever a statement of the form z:=C \(wherein z is assigned a constant\), the resulting after-)J
72 356 :M
.029 .003(state is forced to include the possibility of the datatype of the constant, and therefore this state can no longer assign z)J
72 367 :M
-.023(the bottom datatype. If a later assignment of the form x:=f\(z\) occurs, then the forward inferencing function will force)A
72 378 :M
.863 .086(x to include the datatype f\(C\) \(as determined by the "t-function" for f\), and so x will no longer be assigned the)J
72 389 :M
.873 .087(bottom datatype, either, if C is in the domain of f. In this fashion, the datatypes of all program variables at all)J
72 400 :M
.184 .018(program points are approximated from below until an assignment is reached that is consistent for all of the program)J
72 411 :M
.12 .012(statements. Within this computation, the assignment for a particular variable at a particular program point is always)J
72 422 :M
1.339 .134(adjusted monotonically )J
f2_10 sf
.295(upwards)A
f0_10 sf
.896 .09( from the bottom element of the lattice. \(This datatype propagation process is)J
72 433 :M
.507 .051(analogous to the solution of a partial differential equation by relaxation, where the initial boundary conditions are)J
72 444 :M
.117 .012(propagated into the interior of the region until a consistent solution is obtained.\))J
72 460 :M
1.74 .174(It is obvious that this solution is consistent, but upon closer examination, we find that this solution is not)J
72 471 :M
.203 .02(necessarily minimal. The non-minimal solution arises because forward inferencing utilizes information in the same)J
72 482 :M
.38 .038(order that it occurs during normal program flow, and a more stringent constraint on the datatype of a variable may)J
72 493 :M
.886 .089(occur later in the program than the computation producing a value for the variable. Consider, for example, the)J
72 504 :M
-.378(sequence)A
237 520 :M
.379(x:=sqrt\(y\);\312if\312x>0\312then\312A\312else\312B)A
72 536 :M
.36 .036(In this sequence, x is constrained to be a "number" by virtue of being the result of a square root operation, but x is)J
72 547 :M
.295 .029(not necessarily a "non-complex number" until it is compared with 0 in the following step. Since complex numbers)J
72 558 :M
1.239 .124(cannot be compared using the ">" operator, x must have been real, which means that y must have been non-)J
72 569 :M
.155 .015(negative. Thus, in order to compute a minimal datatype assignment for this sequence, a type inferencer must utilize)J
72 580 :M
-.114("backward inferencing" in addition to "forward inferencing".)A
72 596 :M
-.028(The Kaplan-Ullman algorithm therefore extends the matrix inferencing process to include backwards inferencing. To)A
72 607 :M
.547 .055(implement this, we define a backwards inferencing matrix B, which is diagonal and takes after-states into before-)J
72 618 :M
.723 .072(states. We now require the dual of the connection matrix C, where the dual matrix takes before-states into after)J
72 630 :M
1.265 .126(states; this dual connection matrix is simply the )J
f2_10 sf
.369(transpose)A
f0_10 sf
.652 .065( C)J
f0_9 sf
0 -3 rm
.469(T)A
0 3 rm
f0_10 sf
1.178 .118( of the forward connection matrix C. We then)J
72 641 :M
-.063(construct the infinite sequence)A
201 658 :M
-.046(S, C)A
f0_9 sf
0 -3 rm
-.058(T)A
0 3 rm
f0_10 sf
-.047(\245B\245S, C)A
f0_9 sf
0 -3 rm
-.058(T)A
0 3 rm
f0_10 sf
-.048A
f0_9 sf
0 -3 rm
-.058(T)A
0 3 rm
f0_10 sf
-.047(\245B\245S, C)A
f0_9 sf
0 -3 rm
-.058(T)A
0 3 rm
f0_10 sf
-.048A
f0_9 sf
0 -3 rm
-.058(T)A
0 3 rm
f0_10 sf
-.048A
f0_9 sf
0 -3 rm
-.058(T)A
0 3 rm
f0_10 sf
-.042(\245B\245S, ...)A
72 674 :M
1.327 .133(This sequence is monotonic, and must also converge to a limit by the finite chain condition. This limit is a)J
72 685 :M
.54 .054(consistent approximation to the legal datatypes of the program variables, but is usually different from the limit of)J
72 696 :M
.594 .059(the forward inferencing chain. We could perform both of these sequence computations separately, followed by a)J
72 707 :M
(lattice )S
f2_10 sf
(meet)S
f0_10 sf
-.006( operation on the results to get a better approximation than either the forward or the backward inferencing)A
72 718 :M
1.272 .127(computations could produce separately. The result of this complex computation, however, would still not be)J
72 729 :M
.167(minimal.)A
endp
%%Page: 11 11
%%BeginPageSetup
initializepage
(Henry Baker; page: 11 of 26)setjob
%%EndPageSetup
-31 -31 :T
gS 31 31 552 730 rC
72 45 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
72 749 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
503 749 :M
(11)S
72 69 :M
.121 .012(To obtain a better result, Kaplan and Ullman utilize a technique for incorporating an )J
f2_10 sf
.195 .019(upper bound)J
f0_10 sf
.101 .01( for each state into)J
72 80 :M
.767 .077(the approximation process such that no intermediate state is allowed to violate the upper bound. This improved)J
72 91 :M
-.058(process can best be described as a small program:)A
164 107 :M
.478 .048(S := bottom; )J
f4_10 sf
.177(repeat)A
f0_10 sf
.419 .042( {oldS := S; S := \(F\245C\245S\) )J
f2_10 sf
.185(meet)A
f0_10 sf
.277 .028( U} )J
f4_10 sf
.156(until)A
f0_10 sf
.801 .08( S=oldS;)J
72 123 :M
.637 .064(Not allowing the upper bound U to be violated at any point results in a tighter lower bound, but it is still not the)J
72 134 :M
.661 .066(tightest possible.)J
72 150 :M
.45 .045(The final Kaplan-Ullman algorithm is a nested pair of loops. The inner loop is similar to the one above, in which)J
72 161 :M
.484 .048(the upper bound remains constant, while the lower bound runs from bottom up to convergence. The result of this)J
72 172 :M
.429 .043(inner process is a consistent legal datatype assignment, and no actual computation can stray outside its boundaries)J
72 183 :M
.165 .016(without producing an error. Thus, at the completion of a pass through this inner loop, we can take this lower bound)J
72 194 :M
.398 .04(as the new upper bound. Thus, the outer loop runs the inner loop using the lattice )J
f2_10 sf
.131(top)A
f0_10 sf
.454 .045( element as an upper bound,)J
72 205 :M
.908 .091(then takes the resulting lower bound as the new upper bound, and begins the next pass of the inner loop. This)J
72 216 :M
.043 .004(process continues until the upper bound no longer changes, or equivalently, the greatest lower bound equals the least)J
72 227 :M
.96 .096(upper bound. In order to use all of the available information, Kaplan and Ullman suggest alternating forwards)J
72 238 :M
-.054(inferencing and backwards inferencing with each iteration of the outer loop.)A
72 254 :M
-.001(/* Complete Kaplan-Ullman Type Inference Algorithm. */)A
72 265 :M
.211 .021(/* U is upper bound, L is lower bound. */)J
72 276 :M
.288 .029(U := top;)J
72 287 :M
f4_10 sf
.197(repeat)A
f0_10 sf
.288 .029( {)J
122 287 :M
-.046(oldU := U;)A
122 298 :M
.33 .033(L := bottom; )J
f4_10 sf
.121(repeat)A
f0_10 sf
.291 .029( {oldL := L; L := \(F\245C\245S\) )J
f2_10 sf
.126(meet)A
f0_10 sf
.189 .019( U} )J
f4_10 sf
.107(until)A
f0_10 sf
.564 .056( L=oldL;)J
122 309 :M
.183 .018(U := L;)J
122 321 :M
.252 .025(L := bottom; )J
f4_10 sf
.093(repeat)A
f0_10 sf
.205 .021( {oldL := L; L := \(C)J
f0_9 sf
0 -3 rm
.112(T)A
0 3 rm
f0_10 sf
.32 .032J
f2_10 sf
.096(meet)A
f0_10 sf
.145 .014( U} )J
f4_10 sf
.082(until)A
f0_10 sf
.431 .043( L=oldL;)J
122 332 :M
.285 .029(U := L; } )J
f4_10 sf
.145(until)A
f0_10 sf
.815 .081( U=oldU;)J
72 348 :M
.385 .038(Kaplan and Ullman prove that within a certain mathematical framework, their algorithm is optimal. They propose)J
72 359 :M
.448 .045(that their algorithm produces the )J
f2_10 sf
.132(minimal)A
f0_10 sf
.481 .048( datatype assignment within the space of all datatype assignments which)J
72 370 :M
-.019(can be computed via any technique which utilizes a finite procedure in a lattice having the finite chain condition.)A
72 386 :M
.816 .082(However, the Kaplan-Ullman inferencing algorithm does not produce the minimal assignment outside the stated)J
72 397 :M
1.077 .108(framework. Consider the statement z:=x*x in a datatype lattice where negative and non-negative numbers are)J
72 408 :M
.247 .025(different datatypes. Let us further assume that there are alternative paths before this statement that assign to x both)J
72 419 :M
1.223 .122(negative and non-negative numbers. Since the t-function for this statement must allow for all possibilities, it)J
72 430 :M
.428 .043(claims that {+,-}*{+,-}=>{+,-}; in other words, real multiplication can produce both positive and negative results,)J
72 441 :M
.294 .029(given arguments which are both positive and negative. Nevertheless, )J
f2_10 sf
.098(we)A
f0_10 sf
.272 .027( know that x*x is always non-negative, no)J
72 452 :M
.815 .081(matter what the arguments are, so long as they are real. In this instance, the t-function for multiplication is not)J
72 463 :M
.604 .06(producing the sharpest possible information. This is because it does not notice that the two arguments to "*" are)J
72 474 :M
-.025(not only )A
f2_10 sf
-.029(dependent)A
f0_10 sf
-.024(, but identical.)A
72 490 :M
-.016(A smarter t-function which detected duplicated argument variables would give better information in this instance, but)A
72 501 :M
.926 .093(this smarter t-function would be easily defeated by a simple transformation into the computationally equivalent)J
72 512 :M
(sequence [y:=x;\312z:=x*y]. The problem arises from the approximation Kaplan and Ullman make in the representation)S
72 523 :M
.468 .047(of the states of the program variables. The approximation provides that only )J
f2_10 sf
.135(rectangular)A
f0_10 sf
.477 .048( regions in the Cartesian)J
72 534 :M
.191 .019(product of datatypes can be represented as the states of the program variables; in other words, only a variable's own)J
72 545 :M
.358 .036(datatype can be represented, not the relationship between its datatype and that of another variable. As a result, the)J
72 556 :M
-.033(information produced by statements like z:=assert\(x>y\) is ignored, because there is no way to accurately represent the)A
72 567 :M
-.023(non-rectangular region "x>y" in our space of program variable states.)A
72 583 :M
.831 .083(In the Nimble type inference algorithm, we make this same approximation in our representation of states as did)J
72 594 :M
.652 .065(Kaplan and Ullman, and for the same reason: it would take an extraordinarily complex representation and a very)J
72 605 :M
.579 .058(demanding calculation in order to keep track of all such relationships. As an indication of the complexity of this)J
72 616 :M
.328 .033(task, the "first-order theory of real addition with order" can result in a multiply-exponential time complexity for its)J
72 627 :M
-.153(decision procedure [Ferrante75].)A
72 643 :M
.725 .072(Curiously, the statement [if\312x<0\312then\312z:=x*x\312else\312z:=x*x] allows the Kaplan-Ullman algorithm to conclude that z)J
72 654 :M
.517 .052(is always positive! This result obtains because the programmer has forced the algorithm to perform case analysis)J
72 665 :M
-.085(which produces the intended result.)A
72 681 :M
.309 .031(Several rules must be observed in order for a Kaplan-Ullman algorithm to work at all. Since the high-water-marks)J
72 692 :M
.564 .056(\(greatest lower bounds\) achieved during any lower-bound propagation will become the upper bounds for the next)J
72 703 :M
.195 .02(iteration, it is essential that we have found the )J
f2_10 sf
.059(true)A
f0_10 sf
.27 .027( high-water-mark. This means that the lower-bound propagation)J
72 714 :M
.568 .057(must continue until there is )J
f2_10 sf
.202(no)A
f0_10 sf
.483 .048( change, no matter how small. It is also important to see the )J
f2_10 sf
.157(entire)A
f0_10 sf
.734 .073( program, since)J
endp
%%Page: 12 12
%%BeginPageSetup
initializepage
(Henry Baker; page: 12 of 26)setjob
%%EndPageSetup
-31 -31 :T
gS 31 31 552 730 rC
72 45 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
72 749 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
503 749 :M
(12)S
72 69 :M
.274 .027(even a small piece of the program could introduce some new behavior, which would violate some high-water-mark)J
72 80 :M
-.024(achieved on some earlier cycle in the algorithm.)A
72 96 :M
1.042 .104(Since we are performing limit steps, the datatype lattice must obey the finite chain condition \(i.e. it must be a)J
72 107 :M
f2_10 sf
.032(discrete)A
f0_10 sf
.116 .012( lattice [MacLane67]\), else the algorithm may not converge. If the lattice is not discrete, then the limit steps)J
72 118 :M
.113 .011(may run forever. For example, the lattice described in our implementation of Common Lisp's )J
f7_10 sf
.043(subtypep)A
f0_10 sf
.149 .015( predicate)J
72 129 :M
1.404 .14([Baker92] does )J
f2_10 sf
.328(not)A
f0_10 sf
1.246 .125( obey the finite chain condition because its representation allows individual integers to be)J
72 140 :M
-.025(represented, and the lattice of finite ranges of integers does not obey the finite chain condition.)A
72 156 :M
f4_10 sf
2.636 .264(6. THE NIMBLE TYPE INFERENCE ALGORITHM)J
72 172 :M
f0_10 sf
.068 .007(The Nimble type inference \(NTI\) algorithm produces essentially the same information as would a "straight-forward")J
72 183 :M
.677 .068(implementation of the Kaplan-Ullman algorithm, but it does so using a completely different representation and a)J
72 194 :M
.767 .077(completely different organization of its tasks. As a result, it is also much more efficient than a straight-forward)J
72 205 :M
(Kaplan-Ullman implementation.)S
72 221 :M
1.432 .143(The NTI algorithm represents program states in a way which is much more tuned to the requirements of an)J
72 232 :M
.094 .009(expression-oriented language than a statement-oriented language. The NTI algorithm also carries explicit lower and)J
72 243 :M
.162 .016(upper bounds for each state. These lower and upper bound computations are performed simultaneously. While this)J
72 254 :M
.927 .093(technique does not improve the worst-case speed of the Kaplan-Ullman algorithm, it dramatically improves the)J
72 265 :M
-.035(performance in the common cases.)A
72 281 :M
.253 .025(Consider the example: z:=f\(x,y\). The "before-state" for this statement has both a lower bound and an upper bound,)J
72 292 :M
.024 .002(between which any legal datatype assignment must lie. During forward inferencing, we can utilize the lower bounds)J
72 303 :M
.063 .006(of x and y to compute a \(new\) lower bound for z, and we can utilize the upper bounds of x and y to compute a \(new\))J
72 314 :M
.316 .032(upper bound for z. Of course, these new bounds are still subject to previously determined lower and upper bounds)J
72 325 :M
-.012(for z. For this case, we have the following forward propagation rule for z:=f\(x,y\):)A
87 336 :M
-.097(upper\(z\) = t-function\(f,0,upper\(x\),upper\(y\)\) meet upper\(z\))A
88 347 :M
-.097(lower\(z\) = t-function\(f,0,lower\(x\),lower\(y\)\) meet upper\(z\))A
72 363 :M
.511 .051(If we have a program point where two states split, as in the beginning of an )J
f7_10 sf
.239(alt)A
f0_10 sf
.713 .071( \(alternative\) statement, then we)J
72 374 :M
.225 .023(propagate the lower and upper bounds differently from the above case. In this case, we have the following forward)J
72 385 :M
.105 .01(propagation rule for when A splits into B and C:)J
87 396 :M
-.153(upper\(B\) = upper\(A\) meet upper\(B\))A
87 407 :M
-.09(upper\(C\) = upper\(A\) meet upper\(C\))A
87 418 :M
-.153(lower\(B\) = lower\(A\) meet upper\(B\))A
88 429 :M
-.09(lower\(C\) = lower\(A\) meet upper\(C\))A
72 445 :M
.218 .022(If we have a program point where two states join \("merge"\), as in the end of an )J
f7_10 sf
.103(alt)A
f0_10 sf
.307 .031( expression, then we propagate)J
72 456 :M
-.025(the lower and upper bounds using the forward propagation rule for B and C merging into A:)A
87 467 :M
-.099(upper\(A\) = \(upper\(B\) join upper\(C\)\) meet upper\(A\))A
88 478 :M
-.099(lower\(A\) = \(lower\(B\) join lower\(C\)\) meet upper\(A\))A
72 494 :M
-.047(Backward propagation rules for the three cases above follow a similar pattern.)A
72 510 :M
-.118(Backward propagation rule for z:=f\(x,y\):)A
87 521 :M
-.091(upper\(x\) = t-function\(f,1,upper\(x\),upper\(y\),upper\(z\)\) meet upper\(x\))A
87 532 :M
-.091(upper\(y\) = t-function\(f,2,upper\(x\),upper\(y\),upper\(z\)\) meet upper\(y\))A
88 543 :M
-.204(upper\(z\) = upper\(zbefore\))A
72 559 :M
-.026(Backward propagation rule for split of A into B and C:)A
87 570 :M
-.099(upper\(A\) = \(upper\(B\) join upper\(C\)\) meet upper\(A\))A
88 581 :M
-.099(lower\(A\) = \(lower\(B\) join lower\(C\)\) meet upper\(A\))A
72 597 :M
-.016(Backward propagation rule for join of B,C into A:)A
87 608 :M
-.153(upper\(B\) = upper\(A\) meet upper\(B\))A
87 619 :M
-.09(upper\(C\) = upper\(A\) meet upper\(C\))A
87 630 :M
-.153(lower\(B\) = lower\(A\) meet upper\(B\))A
88 641 :M
-.09(lower\(C\) = lower\(A\) meet upper\(C\))A
72 657 :M
.498 .05(The proof of termination for our algorithm is somewhat less obvious than for the Kaplan-Ullman algorithm, since)J
72 668 :M
2.312 .231(we modify both lower and upper bounds simultaneously. However, upper bounds still always decrease)J
72 679 :M
.909 .091(monotonically, as we always "meet" any new value with the old one. But the lower bounds do not necessarily)J
72 690 :M
-.058(monotonically increase, since a new upper bound can decrease a previous lower bound.)A
72 706 :M
1.462 .146(Nevertheless, our algorithm does converge. The proof goes as follows. The upper bound computations are)J
72 717 :M
.334 .033(completely independent of the lower bound computations; they monotonically decrease, and due to the finite chain)J
72 728 :M
.463 .046(condition on the datatype lattice, they still converge to a limit in a finite number of steps. Once the upper bounds)J
endp
%%Page: 13 13
%%BeginPageSetup
initializepage
(Henry Baker; page: 13 of 26)setjob
%%EndPageSetup
-31 -31 :T
gS 31 31 552 730 rC
72 45 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
72 749 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
503 749 :M
(13)S
72 69 :M
.611 .061(stabilize, the lower bounds can, from that point on, only monotonically increase. The situation with stable upper)J
72 80 :M
1.159 .116(bounds is identical to the situation in the original Kaplan-Ullman proof. Since the finite chain condition also)J
72 91 :M
.903 .09(applies to the lower bound computation, that case also converges, in a finite number of steps, to an assignment)J
72 102 :M
.008 .001(which is less than or equal to the stabilized upper bound.)J
72 118 :M
.183 .018(Carrying both lower and upper bounds simultaneously does )J
f2_10 sf
.044(not)A
f0_10 sf
.166 .017( relieve us of the necessity of performing backwards)J
72 129 :M
-.02(inferencing. Our previous example of)A
239 145 :M
.309(x:=sqrt\(y\);if\312x>0\312then\312A\312else\312B)A
72 161 :M
.323 .032(does not allow us to carry back the information about the realness of x into the previous statement, whence we can)J
72 172 :M
.455 .045(conclude that x is actually non-negative, and hence y is non-negative. However, the simultaneous computation of)J
72 183 :M
-.089(upper and lower bounds does improve the speed of convergence.)A
72 199 :M
.551 .055(The question arises as to why an iterative limit step is required at all; i.e., why can't we produce the best solution)J
72 210 :M
.395 .04(with only a single pass through the program? The answer is that certain programs combined with certain datatype)J
72 221 :M
(lattices require iteration. Consider the following simple program:)S
72 237 :M
f7_10 sf
(\(labels)S
72 248 :M
( \(\(foo \(x\))S
72 259 :M
( \(if \(zerop x\) 0)S
72 270 :M
( \(1+ \(foo \(1- x\)\)\)\)\)\))S
72 281 :M
( \(foo 10000\)\))S
72 297 :M
.152(foo)A
f0_10 sf
.395 .04( is an expensive identity function for non-negative integers which goes into an infinite recursion for any other)J
72 308 :M
.201 .02(numbers. Let us assume a datatype lattice having different datatypes for the five ranges: zero, positive integers less)J
72 319 :M
.648 .065(than 100, positive integers 100 or greater, negative integers greater than -100, and negative integers -100 or less.)J
72 330 :M
.375 .037(Using this datatype lattice, the first forward inferencing iteration of our algorithm will assign )J
f7_10 sf
.143(x)A
f0_10 sf
.375 .038( the type {i|i)J
cF
f1_10 sf
.038A
sf
.375 .038(100},)J
72 341 :M
.407 .041(and the union of the types {i|0*A
sf
.407 .041(100} \(i.e., {i|i>0}\) for the result of )J
f7_10 sf
.167(\(1-\312x\))A
f0_10 sf
.401 .04(. The second iteration)J
72 352 :M
.134 .013(will assign )J
f7_10 sf
.057(x)A
f0_10 sf
.128 .013( the type {i|i>0} \(from the union of {i|i)J
cF
f1_10 sf
.013A
sf
.128 .013(100} and {i|i>0}\), and assign to )J
f7_10 sf
.057(\(1-\312x\))A
f0_10 sf
.114 .011( the union of {0} and)J
72 363 :M
.271 .027({i|i>0} \(i.e., {i|i)J
cF
f1_10 sf
.027A
sf
.271 .027(0}\). The third iteration will assign )J
f7_10 sf
.115(x)A
f0_10 sf
.305 .031( the type {i|i)J
cF
f1_10 sf
.031A
sf
.305 .031(0}, but the conditional expression will separate)J
72 374 :M
.076 .008(the cases )J
f7_10 sf
(x)S
f0_10 sf
.066 .007(=0 and )J
f7_10 sf
(x)S
f0_10 sf
.063 .006(>0, so that )J
f7_10 sf
.036(\(1-\312x\))A
f0_10 sf
.085 .009( is again assigned the type {i|i)J
cF
f1_10 sf
.009A
sf
.085 .009(0}.)J
72 390 :M
1.075 .107(Simultaneously with the above iteration to compute the datatype of )J
f7_10 sf
.397(x)A
f0_10 sf
1.072 .107(, the algorithm is also approximating the)J
72 401 :M
.453 .045(result of )J
f7_10 sf
.229(\(foo\312x\))A
f0_10 sf
.479 .048(. This result starts out {}, but then becomes {0} as a result of the first arm of the conditional.)J
72 412 :M
1.579 .158(The second iteration assigns the result the union of {0} and {i|0**A
sf
1.579 .158(i<100}\). The third and)J
72 423 :M
.03 .003(subsequent iterations assign the result {i|i)J
cF
f1_10 sf
.003A
sf
.03 .003(0}.)J
72 439 :M
.467 .047(Thus, the type inference algorithm applied to this program infers the constraints )J
f7_10 sf
.176(x)A
f0_10 sf
cF
f1_10 sf
.033A
sf
.325 .033(0 and )J
f7_10 sf
.176(\(foo\312x\))A
f0_10 sf
cF
f1_10 sf
.037A
sf
.375 .037(0. Note that)J
72 450 :M
.617 .062(the algorithm cannot conclude that )J
f7_10 sf
.223(\(foo\312x\))A
f0_10 sf
.209(=)A
f7_10 sf
.223(x)A
f0_10 sf
.359 .036( or even that )J
f7_10 sf
.223(\(foo\31210000\))A
f0_10 sf
.534 .053(>100, but it is still remarkable that)J
72 461 :M
(the algorithm )S
f2_10 sf
(does)S
f0_10 sf
( conclude that )S
f7_10 sf
(x)S
f0_10 sf
( does )S
f2_10 sf
(not)S
f0_10 sf
-.003( go negative, and that )A
f7_10 sf
(\(foo\312x\))S
f0_10 sf
-.003( also does not go negative!)A
72 477 :M
.344 .034(From the above example, it should be obvious that iterations are necessary in the type inference algorithm in order)J
72 488 :M
.725 .073(to properly handle the limit operation on looping and recursive constructs. These constructs could cause NTI to)J
72 499 :M
.942 .094(work its way up from the bottom of the datatype lattice to the top with only one step per iteration. While this)J
72 510 :M
1.38 .138(looping is quite expensive computationally, it is very important, as it allows sharp bounds information to be)J
72 521 :M
-.116(inferred regarding loop and recursion index variables.)A
72 537 :M
-.021(We thus are led to the simple data type looping/recursion type inference principle:)A
129 548 :M
f2_10 sf
-.041(Loops and recursion in the input program force iteration in the type inference algorithm.)A
72 564 :M
f0_10 sf
.199 .02(On the other hand, with a properly designed integer datatype lattice, this type inference algorithm can conclude that)J
72 575 :M
.711 .071(certain simple loops 1\) terminate; and 2\) stay within the bounds of an implementation-defined short integer type)J
72 587 :M
.716 .072(\(e.g., -128)J
cF
f1_10 sf
.072A
sf
.716 .072(x<128, or -2)J
f0_9 sf
0 -3 rm
.18(30)A
0 3 rm
f0_10 sf
cF
f1_10 sf
.212A
sf
.212(x<2)A
f0_9 sf
0 -3 rm
.18(30)A
0 3 rm
f0_10 sf
.234(\).)A
72 603 :M
.422 .042(We will also show that no fixed number \(i.e., independent of the size of the program text\) of limit steps \(the inner)J
72 614 :M
-.018(loops in the Kaplan-Ullman type inference program given above\) is sufficient to guarantee that we have produced the)A
72 625 :M
-.021(minimal datatype assignment within the Kaplan-Ullman inferencing framework.)A
72 641 :M
-.012(Consider the following program:)A
87 652 :M
-.294(a:=read\(\); b:=read\(\); c:=read\(\); d:=read\(\); e:=read\(\);)A
87 663 :M
-.271(w:=read\(\); x:=read\(\); y:=read\(\); z:=read\(\);)A
87 674 :M
.929 .093(assert a*w>0; v:=0;\312\312assert b*x>0; v:=1;\312\312assert c*y>0; v:=2;\312\312assert d*z>0; v:=3;)J
87 685 :M
.929 .093(assert b*w>0; v:=4;\312\312assert c*x>0; v:=5;\312\312assert d*y>0; v:=6;\312\312assert e*z>0; v:=7;)J
88 696 :M
-.012(assert e>0;)A
endp
%%Page: 14 14
%%BeginPageSetup
initializepage
(Henry Baker; page: 14 of 26)setjob
%%EndPageSetup
-31 -31 :T
gS 31 31 552 730 rC
72 45 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
72 749 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
503 749 :M
(14)S
72 69 :M
1.187 .119(For this example, we will assume that the datatype lattice for numbers simply distinguishes between positive,)J
72 80 :M
.583 .058(negative and zero numbers. The inclusion of the dummy assignments to the variable )J
f7_10 sf
.228(v)A
f0_10 sf
.523 .052( in the program forces the)J
72 91 :M
-.007(creation of multiple states during inferencing, which drastically slows down the propagation of information.)A
72 107 :M
.568 .057(During the first \(forward\) inferencing pass, all variables are set to take on any values, since they are assigned the)J
72 118 :M
-.007(results of "read" operations, which can produce any type of value. However, we quickly learn that all of these values)A
72 129 :M
.876 .088(must be numbers, because they are used in multiplication operations, which can accept only numeric datatypes.)J
72 140 :M
.684 .068(Only at the very end of the forward inferencing pass do we learn that one of the variables\321e\321is a )J
f2_10 sf
.193(positive)A
f0_10 sf
.619 .062( real)J
72 151 :M
(number.)S
72 167 :M
-.001(During the second \(backward\) inferencing pass, the knowledge that e is real and positive allows us to conclude that z)A
72 178 :M
-.017(is also real and positive, hence d is also real and positive. During the third \(forward\) inferencing pass, the knowledge)A
72 189 :M
.079 .008(that d is real and positive allows us to conclude that y is real and positive. During the fourth \(backward\) inferencing)J
72 200 :M
.691 .069(pass, the knowledge that y is real and positive allows us to conclude that c is real and positive. During the fifth)J
72 211 :M
.307 .031(\(forward\) inferencing pass, we conclude that x is a positive real. During the sixth \(backward\) inferencing pass, we)J
72 222 :M
.454 .045(conclude that b is a positive real. During the seventh \(forward\) inferencing pass, we conclude that w is a positive)J
72 233 :M
-.027(real. During the eighth \(backward\) inferencing pass, we conclude that a is a positive real. Thus, we have been forced)A
72 244 :M
.386 .039(into )J
f2_10 sf
.128(eight)A
f0_10 sf
.528 .053( separate limit passes in order to finally converge on the minimal datatype assignment, which concludes)J
72 255 :M
.049 .005(that )J
f2_10 sf
(all)S
f0_10 sf
.064 .006( of the variables must have been positive real numbers.)J
72 271 :M
1.078 .108(While this example was specially contrived to defeat type inferencing algorithms based on the Kaplan-Ullman)J
72 282 :M
.164 .016(algorithm, it can be extended in the obvious way to force the inferencing algorithm to perform )J
f2_10 sf
.053(any)A
f0_10 sf
.165 .017( number of steps.)J
72 293 :M
.261 .026(Thus, through our counter-examples, we have shown that the Kaplan-Ullman inferencing algorithm must consist of)J
72 304 :M
.414 .041(a doubly nested loop, where the inner loop raises the lower bounds and the outer loop lowers the upper bounds by)J
72 315 :M
.437 .044(alternate forward and backward inferencing passes, with no )J
f2_10 sf
.432 .043(a priori)J
f0_10 sf
.317 .032( limits on the number of either the inner or the)J
72 326 :M
.237 .024(outer loop iterations.)J
72 342 :M
.052 .005(An open question is whether we can change the representation of the states in the Nimble type inferencing algorithm)J
72 353 :M
-.027(to more quickly converge even on contrived examples like the one above.)A
72 375 :M
f9_10 sf
3.448 .345(6.1 THE DISCRETE DATATYPE LATTICE OF COMMON LISP "SINGLE-VALUES")J
72 391 :M
f0_10 sf
.642 .064(The Nimble type inferencing algorithm utilizes a Boolean algebra for its datatype lattice, both because it is more)J
72 402 :M
1.218 .122(precise, and because it can be efficiently implemented by means of bit-vectors. These bit-vectors encode the)J
72 413 :M
.566 .057(possibilities of the different datatypes of a value as a union of "atomic" datatypes, where "atomic" here means an)J
72 424 :M
f2_10 sf
.807(atom)A
f0_10 sf
2.106 .211( in a Boolean algebra, and not a Common Lisp )J
f2_10 sf
.807(atom)A
f0_10 sf
2.281 .228(. Thus, a bit-vector could encode the union)J
72 435 :M
-.04({integer,single-float,character}, meaning that the corresponding variable could take on as a value at run-time either an)A
72 446 :M
.345 .035(integer, a single-precision floating point number, or a single character \(a highly improbable situation!\). Unlike the)J
72 457 :M
.243 .024(situation in strongly-typed languages such as C or Ada, there is no )J
f2_10 sf
.076(standard)A
f0_10 sf
.252 .025( lattice required by Common Lisp. We)J
72 468 :M
.033 .003(could utilize a lattice which distinguished between Lisp )J
f2_10 sf
.009(symbols)A
f0_10 sf
.025 .002( which were )J
f2_10 sf
.01(keywords)A
f0_10 sf
.02 .002( and Lisp )J
f2_10 sf
.009(symbols)A
f0_10 sf
.033 .003( which were)J
72 479 :M
.639 .064(not )J
f2_10 sf
.277(keywords)A
f0_10 sf
.75 .075(, or we could lump the two together as simply Lisp )J
f2_10 sf
.27(symbols)A
f0_10 sf
.81 .081(. Similarly, we are free to distinguish)J
72 490 :M
1.063 .106(between negative and non-negative integers\321essentially treating them as having different datatypes. A lattice)J
72 501 :M
.979 .098(allowing finer distinctions, and the resultant greater resolution for distinguishing datatypes, requires longer bit-)J
72 512 :M
.971 .097(vectors to implement. The only requirement is that the lattice satisfy the finite chain condition. This is easily)J
72 523 :M
.4 .04(achieved if the total number of bits in all the bit-vectors is bounded by an )J
f2_10 sf
.526 .053(a priori)J
f0_10 sf
.43 .043( bound. One implication of this)J
72 534 :M
.361 .036(requirement is that we cannot distinguish every single integer, but must group them into a finite set of equivalence)J
72 545 :M
-.037(classes.)A
72 561 :M
.726 .073(We can normally utilize relatively short bit-vectors in the range of 32-64 bits. This is true since the majority of)J
72 572 :M
.663 .066(important Common Lisp distinctions can be made with less than 32 bits [Baker92], and increasing the resolution)J
72 583 :M
.471 .047(further can dramatically increase the space and time required to manipulate the Kaplan-Ullman t-functions \(this is)J
72 594 :M
.005 0(discussed in a later section\). We have found that the datatype resolution required for proper )J
f2_10 sf
(analysis)S
f0_10 sf
( greatly exceeds)S
72 605 :M
.22 .022(the number of datatypes needed for efficient )J
f2_10 sf
.059(representation)A
f0_10 sf
.219 .022(. For example, even though small positive and negative)J
72 616 :M
1.071 .107(integers are both represented by the same hardware datatype, it is useful to propagate finer distinctions during)J
72 627 :M
.135 .013(analysis, since the final lattice upper bounds achieved can be significantly smaller. In the case where it is desired to)J
72 638 :M
-.015(achieve high performance on numeric programs, we have found the following numeric distinctions to be useful:)A
endp
%%Page: 15 15
%%BeginPageSetup
initializepage
(Henry Baker; page: 15 of 26)setjob
%%EndPageSetup
-31 -31 :T
gS 31 31 552 730 rC
72 45 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
72 749 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
503 749 :M
(15)S
72 69 :M
-.09(Integer classes:)A
87 81 :M
.267 .027({i | i < -2)J
f0_9 sf
0 -3 rm
.169(31)A
0 3 rm
f0_10 sf
(})S
87 93 :M
-.151({-2)A
f0_9 sf
0 -3 rm
-.155(31)A
0 3 rm
f0_10 sf
(})S
144 93 :M
.039 .004(; we need this class to handle the asymmetry of 2's complement arithmetic)J
87 105 :M
.357 .036({i | -2)J
f0_9 sf
0 -3 rm
.19(31)A
0 3 rm
f0_10 sf
.32 .032( < i < -1})J
87 116 :M
-.642({-1})A
87 127 :M
-.8({0})A
87 138 :M
-.8({1})A
87 150 :M
.375 .038({i | 1 < i < 2)J
f0_9 sf
0 -3 rm
.257(31)A
0 3 rm
f0_10 sf
(})S
87 162 :M
-.124({2)A
f0_9 sf
0 -3 rm
-.114(31)A
0 3 rm
f0_10 sf
(})S
144 162 :M
.521 .052(; ditto.)J
87 174 :M
.355 .035({i | 2)J
f0_9 sf
0 -3 rm
.212(31)A
0 3 rm
f0_10 sf
.358 .036( < i})J
72 185 :M
.055 .005(Floating point number classes \(type,range\):)J
87 196 :M
(type:)S
144 207 :M
-.065(short-float)A
144 218 :M
-.049(single-float)A
144 229 :M
-.17(double-float)A
144 240 :M
-.048(long-float)A
87 251 :M
-.197(range:)A
144 262 :M
.107 .011({x | -)J
cF
f1_10 sf
.011A
sf
.107 .011( < x < -1.0})J
144 273 :M
-.285({-1.0})A
144 284 :M
.308 .031({x | -1.0 < x < -0.0})J
144 295 :M
-.047({0.0,-0.0})A
239 295 :M
-.072(; use of IEEE standard can force {0.0},{-0.0} distinction.)A
144 306 :M
.4 .04({x | 0.0 < x < 1.0})J
144 317 :M
-.275({1.0})A
144 328 :M
.196 .02({x | 1.0 < x < )J
cF
f1_10 sf
.02A
sf
.196 .02(})J
72 339 :M
.888 .089(\(Note that these distinctions look remarkably similar to those used by those artificial intelligence researchers in)J
72 350 :M
-.026("qualitative reasoning" [IJCAI-89]; perhaps they, too, should investigate Kaplan-Ullman inferencing.\))A
72 366 :M
.477 .048(We do )J
f2_10 sf
.183(not)A
f0_10 sf
.643 .064( currently attempt to track the contents of the higher-order datatypes of Common Lisp, as is routinely)J
72 377 :M
.493 .049(done in ML [Milner78]. In other words, we utilize the single class )J
f7_10 sf
.218(cons)A
f0_10 sf
.472 .047( to stand for all cons cells, regardless of)J
72 388 :M
.331 .033(their contents. While tracking the contents of Lisp lists would be extremely valuable, the resolution required is far)J
72 399 :M
-.009(too great to be handled by our current methods \(however, see [Baker90b], in which we extend ML-style unificational)A
72 410 :M
1.146 .115(type inference to also perform storage use inferencing on these higher-order datatypes\). Similarly, we do )J
f2_10 sf
.462(not)A
72 421 :M
f0_10 sf
1.164 .116(currently attempt to track the types of higher order functions of Common Lisp, but lump them all together as)J
72 432 :M
f7_10 sf
.357(function)A
f0_10 sf
.848 .085(. Once again, this would be extremely valuable, as the experience of ML has shown, but again the)J
72 443 :M
-.007(resolution required is computationally prohibitive.)A
72 459 :M
.815 .082(Note that this lumping of functional objects together as one class does not prevent us from keeping track of the)J
72 470 :M
.61 .061(arguments and results of user functions, but only of functional values \("funargs" or "closures"\). This is possible,)J
72 481 :M
.59 .059(because unlike Scheme [Scheme], Common Lisp keeps functions mostly separate from data objects, and hence is)J
72 492 :M
-.015(amenable to more a classical compiler treatment of typed functions.)A
72 514 :M
f9_10 sf
3.418 .342(6.2 THE LATTICE OF COMMON LISP "MULTIPLE-VALUES")J
72 530 :M
f0_10 sf
.275 .027(Common Lisp, unlike other dialects of Lisp, has a curious notion of )J
f2_10 sf
.46 .046(multiple values)J
f0_10 sf
.26 .026(. These multiple values are not)J
72 541 :M
.241 .024(lists or vectors, and hence are not first-class objects. They can, however, be returned from functions or accepted as)J
72 552 :M
.005 .001(function arguments under certain carefully controlled conditions. These multiple values cause problems for program)J
72 563 :M
-.034(analysis; while they are rarely used, they )A
f2_10 sf
-.039(could)A
f0_10 sf
-.036( be used, and thus they must be everywhere allowed for.)A
72 579 :M
.656 .066(Unlike our rather casual treatment of the other higher order datatypes in Common Lisp, we )J
f2_10 sf
.203(must)A
f0_10 sf
.8 .08( model multiple)J
72 590 :M
.619 .062(values carefully. If we do not, we would not be able to infer anything about the results of any function call, and)J
72 601 :M
.422 .042(Lisp programs consist mainly of function calls. Multiple values can be more easily modeled than the other higher)J
72 612 :M
.028 .003(order data types due to two properties: they are )J
f2_10 sf
.008(functional)A
f0_10 sf
.03 .003( objects, in the sense that their size and components cannot)J
72 623 :M
-.017(be altered via side-effects once they have been constructed; and they are not first class, in the sense that no references)A
72 634 :M
-.057(to them can be created or compared via )A
f7_10 sf
-.083(eq)A
f0_10 sf
(.)S
72 650 :M
.393 .039(Part of the problem of representing multiple values stems from the fact that one can construct functions which can)J
72 661 :M
.737 .074(return a different number of multiple values at different times\321a multiple value "polymorphism". Furthermore,)J
72 672 :M
1.123 .112(some primitive Lisp functions\321such as )J
f7_10 sf
.366(eval)A
f0_10 sf
.339 .034( or )J
f7_10 sf
.366(apply)A
f0_10 sf
.994 .099(\321can return any number of multiple values. Further)J
72 683 :M
.513 .051(complication arises from the fact that multiple values are coerced, in many circumstances, into a single value; the)J
72 694 :M
.365 .037(first one, if it exists, or )J
f7_10 sf
.207(nil)A
f0_10 sf
.443 .044(, if it doesn't. Yet one can also write Lisp code which can tell how many values were)J
72 705 :M
-.043(returned by a function call, and what these values were.)A
72 721 :M
1.619 .162(The Nimble type inferencer represents multiple values using a record structure of 3 components. The first)J
72 732 :M
.354 .035(component is an integer interpreted as a bit-vector which indicates the set of possible numbers of values which are)J
endp
%%Page: 16 16
%%BeginPageSetup
initializepage
(Henry Baker; page: 16 of 26)setjob
%%EndPageSetup
-31 -31 :T
gS 31 31 552 730 rC
72 45 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
72 749 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
503 749 :M
(16)S
72 70 :M
.311 .031(being represented. Thus, a multiple-value which represents a normal single value uses the integer 2 = 2)J
f0_9 sf
0 -3 rm
.092(1)A
0 3 rm
f0_10 sf
.45 .045(, meaning)J
72 82 :M
.446 .045(that the only possible number of values is 1. "Zero values" is represented by the integer 1 = 2)J
f0_9 sf
0 -3 rm
.153(0)A
0 3 rm
f0_10 sf
.52 .052(, and the multiple-)J
72 94 :M
.566 .057(value returned by the Common Lisp )J
f7_10 sf
.217(floor)A
f0_10 sf
.466 .047( function is represented by the integer 4 = 2)J
f0_9 sf
0 -3 rm
.163(2)A
0 3 rm
f0_10 sf
.55 .055(, meaning that exactly 2)J
72 105 :M
-.004(values are returned. If a function sometimes returns one value and sometimes returns two values, then the number of)A
72 117 :M
.055 .005(values is represented by 6 = 2)J
f0_9 sf
0 -3 rm
(1)S
0 3 rm
f0_10 sf
(+2)S
f0_9 sf
0 -3 rm
(2)S
0 3 rm
f0_10 sf
.054 .005(. The number 0 then represents ")J
f2_10 sf
(no)S
f0_10 sf
.101 .01( possible multiple-values"\321)J
f2_10 sf
.018(not)A
f0_10 sf
.075 .007( zero values\321)J
72 128 :M
.308 .031(i.e., the function never returns! Finally, the resulting types for functions like )J
f7_10 sf
.127(eval)A
f0_10 sf
.165 .017( and )J
f7_10 sf
.127(apply)A
f0_10 sf
.335 .034( are represented by)J
72 141 :M
.218 .022(-1 \(= )J
cF
f1_10 sf
.218 .022J
sf
0 -3 rm
cF
f1_10 sf
.143A
sf
0 3 rm
.125 .013( 2)J
f0_9 sf
0 -3 rm
.05(i)A
0 3 rm
f0_10 sf
.293 .029( in 2's complement notation!\). With this encoding for the number of components in a multiple value, it)J
72 152 :M
.684 .068(is easy to perform lattice )J
f2_10 sf
.245(meet)A
f0_10 sf
.404 .04( and )J
f2_10 sf
.202(join)A
f0_10 sf
.82 .082(\321they are simply )J
f7_10 sf
.312(logand)A
f0_10 sf
.404 .04( and )J
f7_10 sf
.312(logior)A
f0_10 sf
.921 .092( of the representation numbers.)J
72 163 :M
1.754 .175(\(The finite chain condition \("fcc"\) holds so long as the number of multiple-values is )J
f2_10 sf
2.009 .201(a priori)J
f0_10 sf
1.891 .189( bounded; in)J
72 174 :M
.368 .037(pathological cases, it is necessary to limit the resolution of the number of values to "0,1,2,...,31,)J
cF
f1_10 sf
.037A
sf
.368 .037(32", for example,)J
72 185 :M
-.098(in order to guarantee fcc.\))A
72 201 :M
.063 .006(The second and third components of the multiple-value record structure are the "finite part" and the "infinite part" of)J
72 212 :M
.444 .044(the multiple-value representation. The finite part is a simple vector whose values are elements of the single-value)J
72 223 :M
.158 .016(datatype lattice\321typically bit-vectors. The infinite part is a single element of the single-value datatype lattice. The)J
72 234 :M
.58 .058(interpretation of these two elements is similar to the digits and sign of a 2's complement number; any component)J
72 245 :M
.549 .055(\(bit\) whose index is less than the length of the finite part can be found in the finite part, and any component \(bit\))J
72 256 :M
1.105 .11(whose index is greater than the length of the finite part has the value of the infinite part. The reason for this)J
72 267 :M
.369 .037(structure is the fact that all multiple-values in Common Lisp have only finite lengths, but since our lattice is also a)J
72 278 :M
.119 .012(Boolean algebra, we must be capable of producing a complement, as well. But complements of finite sequences are)J
72 289 :M
-.029(sequences whose infinite parts are all the same\321hence still representable in our scheme.)A
72 305 :M
-.014(We can now describe )A
f2_10 sf
-.015(meet)A
f0_10 sf
-.013( and )A
f2_10 sf
-.013(join)A
f0_10 sf
-.013( operations on our multiple-value datatype lattice. The number-of-values integers)A
72 316 :M
.303 .03(are combined using logand or logior, respectively. Then the finite part and infinite part of the result are computed.)J
72 327 :M
.201 .02(Before performing either a meet or a join, we must first extend the finite part of the shorter operand to the length of)J
72 338 :M
.501 .05(the longer operand by extending it with copies of its infinite part. We then perform an element-wise meet or join)J
72 349 :M
1.748 .175(utilizing the single-value meet or join operation from the single-value lattice. Finally, we canonicalize by)J
72 360 :M
.422 .042(collapsing the finite part of the result as far as possible; any elements at the end of the finite part vector which are)J
72 371 :M
.294 .029(equal to the infinite part are simply ignored and the vector is shortened. Thus, the implemention of these meet and)J
72 382 :M
.663 .066(join operations is similar to the implementation of addition and subtraction of multiple-precision 2's-complement)J
72 393 :M
-.019(integers.)A
72 409 :M
1.244 .124(Some examples of this encoding are given below. The reason for the )J
f7_10 sf
.529(nil)A
f0_10 sf
1.018 .102( in the infinite part of most of the)J
72 420 :M
.173 .017(multiple-values below is that Common Lisp specifies that access to unsupplied values from a multiple-value returns)J
72 431 :M
f7_10 sf
.148(nil)A
f0_10 sf
(.)S
88 442 :M
f7_10 sf
(\(values\))S
287 442 :M
(<1,<>,{nil}>)S
88 453 :M
(\(values 0\))S
287 453 :M
(<2,<{0}>,{nil}>)S
88 464 :M
(\(floor 3 2\))S
287 464 :M
(<4,<{1},{0}>,{nil}>)S
88 475 :M
(\(if \311)S
287 475 :M
(<6,<{0,1},{1,nil}>,{nil}>)S
88 486 :M
( \(values 0\))S
88 497 :M
( \(values 1 1\)\))S
72 508 :M
(\(eval x\))S
287 508 :M
(<-1,<>,t>)S
72 524 :M
f0_10 sf
.763 .076(\(Note that our representation of multiple-values is ideal as a lattice representation for any datatype consisting of)J
72 535 :M
.066 .007(finite-length sequences\321e.g., character strings. The union of the strings "hi", represented by <4,<{h},{i}>,{}>, and)J
72 546 :M
-.119("there", represented by <32,<{t},{h},{e},{r},{e}>,{}>, is represented by <36,<{h,t},{i,h},{e},{r},{e}>,{}>.\))A
72 568 :M
f9_10 sf
3.089 .309(6.3. THE LATTICE OF COMMON LISP "STATES")J
72 584 :M
f0_10 sf
.383 .038(Once the preliminary transformations described at the end of this section have been performed on a Common Lisp)J
72 595 :M
.107 .011(program, only lexically-scoped variables and global variables remain. Due to their sheer number, we have currently)J
72 606 :M
.587 .059(decided not to independently track the contents of true global variables, which in Common Lisp are the so-called)J
72 617 :M
-.02("value cells" of symbols, and hence components of the "symbol" record structure. These have presently been lumped)A
72 628 :M
.976 .098(together into the single class which tracks the union of all symbol value cells. While this approximation loses)J
72 639 :M
1.59 .159(valuable information about the use of global variables, the approximation must be used in all existing Lisp)J
72 650 :M
.79 .079(implementations, because these variables can be changed by any evaluated function, or by the user himself, and)J
72 661 :M
-.036(therefore must be capable of holding any Lisp datatype.)A
72 677 :M
1.519 .152(More interesting is our treatment of lexically-scoped variables and temporaries. The Nimble type inference)J
72 688 :M
.202 .02(algorithm collapses a Lisp program into a Fortran-like program by ignoring the recursive aspect. In other words, in)J
72 699 :M
.326 .033(our approximation, the various stack frames of a recursive function are modeled by a single stack frame which is a)J
72 710 :M
.907 .091(kind of union of all of the stack frames from the recursion. Thus, each program state within a function can be)J
72 721 :M
.524 .052(represented by its "alist environment"\321i.e., a vector which associates a "single-value" lattice element which each)J
endp
%%Page: 17 17
%%BeginPageSetup
initializepage
(Henry Baker; page: 17 of 26)setjob
%%EndPageSetup
-31 -31 :T
gS 31 31 552 730 rC
72 45 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
72 749 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
503 749 :M
(17)S
72 69 :M
.263 .026(lexically visible variable. Note that lexical variables not directly visible from within the function are not present in)J
72 80 :M
.268 .027(any of the program states within the function, since any changes to any visible variable can only be an effect of the)J
72 91 :M
-.009(function itself. The representation of temporaries is even easier, since a temporary can have only one "producer" and)A
72 102 :M
-.018(\(at most\) one "consumer", and these are tied \(after collapsing the stack\) directly to the program text. Therefore, these)A
72 113 :M
.006 .001(temporaries need not participate in the program states directly, but only as a result of being saved away \(via )J
f7_10 sf
(let)S
f0_10 sf
(\), or)S
72 124 :M
-.025(assigned to a program variable \(via )A
f7_10 sf
-.038(setq)A
f0_10 sf
(\).)S
72 140 :M
.429 .043(Thus, a program )J
f2_10 sf
.109(state)A
f0_10 sf
.42 .042( is an environment which consists of only the named lexical variables visible at that point in)J
72 151 :M
.141 .014(the program; temporaries and global variables do not appear. Furthermore, this environment is a simple linked Lisp)J
72 162 :M
.093 .009(list of "single-value" lattice elements which is indexed by the integer which is the lexical depth of the corresponding)J
72 173 :M
.396 .04(variable in the current lexical environment. This list, and all of its elements, are )J
f2_10 sf
.117(functional)A
f0_10 sf
.368 .037(, in that we perform no)J
72 184 :M
.118 .012(side-effects on them. This means that we are allowed to freely share the )J
f2_10 sf
.03(tails)A
f0_10 sf
.105 .01( of these lists, which we do as much as)J
72 195 :M
.365 .036(possible. Since each program state is almost the same as its predecessors and its successors, and since most of the)J
72 206 :M
.748 .075(changes occur at the top of this list \(the inner-most lexical scope\), only minor modifications need to be made in)J
72 217 :M
-.028(order to produce the next state from a previous state.)A
72 233 :M
.895 .089(This massive sharing of state tails saves space, but more importantly, it saves time. This is because during the)J
72 244 :M
.629 .063(processing of statements to produce the next state, only a small amount of processing need be performed. When)J
72 255 :M
1.076 .108(using tail-sharing, this processing is typically O\()J
f7_10 sf
.369(1)A
f0_10 sf
.793 .079(\) instead of O\()J
f7_10 sf
.369(n)A
f0_10 sf
.748 .075(\), where )J
f7_10 sf
.369(n)A
f0_10 sf
.887 .089( is the number of lexical variables)J
72 266 :M
.831 .083(visible at this point. Similarly, when performing the lattice )J
f2_10 sf
.267(meet)A
f0_10 sf
.44 .044( and )J
f2_10 sf
.22(join)A
f0_10 sf
.873 .087( operations on states, we usually only)J
72 277 :M
.477 .048(process the top few items, because the rest of the tails are identical. Thus, by using a functional representation of)J
72 288 :M
.324 .032(states and massive tail-sharing, we can represent full state information during our inferencing for only a little more)J
72 299 :M
.065 .007(than the storage and time used for a single state which is global to the whole program \(like ML [Milner78]\).)J
72 321 :M
f9_10 sf
3.813 .381(6.4 THE EFFICIENT IMPLEMENTATION OF KAPLAN-ULLMAN "T-FUNCTIONS")J
72 337 :M
f0_10 sf
-.023(The success of the Kaplan-Ullman type inference algorithm depends critically on getting sharp type information from)A
72 348 :M
.371 .037(the primitive functions of the language. In other words, given a function and a set of bounds on its arguments, we)J
72 359 :M
.984 .098(must produce the sharpest possible bounds for the result of the function. In addition to forward and backward)J
72 370 :M
1.171 .117(inferencing information, we would also like to get "side-ways" type inference information; this information is)J
72 381 :M
1.585 .159(extracted from the interaction of constraints on the various arguments of multiple-argument functions. The)J
72 392 :M
.206 .021(symmetry between arguments and results leads to a symmetrical representation for the t-function information. This)J
72 403 :M
.109 .011(representation is in the form of a mathematical )J
f2_10 sf
.029(relation)A
f0_10 sf
.106 .011(, which is simply a subset of a Cartesian product of domains.)J
72 414 :M
.614 .061(In particular, if f:AxB->C, then f can be represented by a relation, i.e., a subset of the Cartesian product AxBxC.)J
72 425 :M
.047 .005(The datatype domain induces an equivalence relation on A, B, and C, in such a way that the t-function for f becomes)J
72 436 :M
.771 .077(the function f':A'xB'->C', where A', B', and C' are the )J
f2_10 sf
1.222 .122(quotient sets)J
f0_10 sf
.486 .049( of A', B', and C' )J
f2_10 sf
.253(induced)A
f0_10 sf
.902 .09( by the equivalence)J
72 447 :M
1.024 .102(relation. While the full relation for f may be infinite in size, if A', B', and C' are all finite, then the Cartesian)J
72 458 :M
-.007(product A'xB'xC' is also finite, and hence f' can be represented by a finite relation on A'xB'xC'.)A
72 474 :M
.225 .022(If A', B', and C' are all small, then f' can be represented by a list of legal triples, or alternatively a 3-dimensional bit)J
72 485 :M
1.062 .106(matrix in which bit ijk is on if and only if k)J
f1_10 sf
.682A
f0_10 sf
1.516 .152(f'\(i,j\). Neither of these representations is particularly small or)J
72 496 :M
1.697 .17(computationally efficient if the number of arguments to \(or returned values from\) a function is very large.)J
72 507 :M
.355 .036(Nevertheless, if the tightest possible information is required, then a smaller representation may not exist unless the)J
72 518 :M
.048 .005(function can somehow be factored into a composition of simpler functions.)J
72 536 :M
.316 .032(Standard binary operations like "+" will require a bit matrix of size )J
f7_10 sf
.133(n)A
f0_10 sf
0 -3 rm
.111(3)A
0 3 rm
.3 .03( to be represented, where )J
f7_10 sf
.133(n)A
f0_10 sf
.269 .027( is the number of)J
72 547 :M
.14 .014("representatives" \(see [Baker92]\) in the datatype lattice representation. We expect )J
f7_10 sf
(n)S
f0_10 sf
.115 .012( to range from 32-64, hence we)J
72 558 :M
.27 .027(will require between 1024 and 8192 32-bit words to simply represent the relation. Binary operations like Common)J
72 571 :M
1.142 .114(Lisp's )J
f7_10 sf
.449(floor)A
f0_10 sf
1.176 .118( operation, which takes two arguments and produces two results, will require )J
f7_10 sf
.449(n)A
f0_10 sf
0 -3 rm
.374(4)A
0 3 rm
1.252 .125( bits, requiring)J
72 582 :M
1.577 .158(between 32,768 and 524,288 32-bit words for its representation. However, should this amount of space be)J
72 593 :M
.786 .079(considered excessive, then )J
f7_10 sf
.247(floor)A
f0_10 sf
.688 .069( can instead be represented by two different functions\321one for each different)J
72 604 :M
.26 .026(result\321for an amount of space between 2048 and 16384 32-bit words. While this is a substantial savings in space,)J
72 615 :M
.972 .097(there is some loss of resolution due to the lack of dependence between the two different results of the )J
f7_10 sf
.526(floor)A
72 626 :M
f0_10 sf
-.006(function in the "two-function" representation.)A
72 642 :M
1.213 .121(The success of the Nimble type inferencer is due, in a large part, to its ability to completely encode the type)J
72 653 :M
.317 .032(complexity of Common Lisp primitive functions without actually interpreting the underlying implementation code.)J
72 664 :M
3.98 .398(The type complexity of some Common Lisp functions is quite high. The exponential function)J
72 675 :M
f7_10 sf
.375(\(expt\312base\312power\))A
f0_10 sf
.818 .082(, for example, is required to return a rational result if base is rational and power is an)J
72 686 :M
.868 .087(integer, and may return a floating-point approximation \(possibly complex\) otherwise. On the other hand, if the)J
72 697 :M
.521 .052(arguments to a library function are rational and the true mathematical result is rational, then an implementation is)J
72 708 :M
.376 .038(free to return either a floating-point number or the actual rational number result [CLtL,p.203]. For example, some)J
72 719 :M
.141 .014(Common Lisp implementations of )J
f7_10 sf
.043(sqrt)A
f0_10 sf
.099 .01( go to the trouble to detect integer perfect squares, and in these cases return)J
72 730 :M
1.523 .152(integer \(rather than floating-point\) results! Given the number of cases to consider, especially when various)J
endp
%%Page: 18 18
%%BeginPageSetup
initializepage
(Henry Baker; page: 18 of 26)setjob
%%EndPageSetup
-31 -31 :T
gS 31 31 552 730 rC
72 45 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
72 749 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
503 749 :M
(18)S
72 69 :M
.97 .097(subranges of numbers are considered, the exact representation for the result type of )J
f7_10 sf
.373(expt)A
f0_10 sf
.346 .035( or )J
f7_10 sf
.373(sqrt)A
f0_10 sf
.954 .095( becomes a)J
72 80 :M
.156 .016(nightmarish expression in traditional Common Lisp type specifier syntax.)J
72 96 :M
1.212 .121(The use in the Nimble type inferencer of bit-arrays to represent the type behavior of Common Lisp primitive)J
72 107 :M
.33 .033(functions is inelegant compared with the clever "type variable" method to handle polymorphism in ML. However,)J
72 118 :M
.203 .02(the bit-array method is capable of handling the type complexity of Common Lisp while the type variable method of)J
72 129 :M
1.202 .12(ML would not extend to this higher level of complexity and polymorphism. Without type variables, ML can)J
72 140 :M
.271 .027(represent only "rectangular approximations" to the true t-functions, while type variables add the ability to represent)J
72 151 :M
.607 .061("diagonal approximations". If a t-function cannot be decomposed into rectangular and diagonal regions, then the)J
72 162 :M
1.035 .104(methods of ML will not achieve the same resolution as our bit-array method. Common Lisp's )J
f7_10 sf
.412(sqrt)A
f0_10 sf
1.368 .137( function)J
72 173 :M
-.032(cannot be easily decomposed into diagonal or rectangular regions, as can be seen by the chart below.)A
280 189 :M
-.06(t-function for sqrt)A
90 201 :M
f0_9 sf
.369 .037(result )J
f1_9 sf
S
180 201 :M
f0_9 sf
.336(integer)A
252 201 :M
.377(ratio)A
324 201 :M
.281(c-rational)A
396 201 :M
.377(float)A
468 201 :M
.253(c-float)A
90 214 :M
f1_9 sf
S
f0_9 sf
.164 .016( argument)J
90 225 :M
.336(integer)A
180 225 :M
(X)S
324 225 :M
(X)S
396 225 :M
(X)S
468 225 :M
(X)S
90 235 :M
.377(ratio)A
252 235 :M
(X)S
324 235 :M
(X)S
396 235 :M
(X)S
468 235 :M
(X)S
90 245 :M
.281(c-rational)A
324 245 :M
(X)S
396 245 :M
(X)S
468 245 :M
(X)S
90 255 :M
.377(float)A
396 255 :M
(X)S
468 255 :M
(X)S
90 265 :M
.253(c-float)A
468 265 :M
(X)S
72 292 :M
f0_10 sf
.503 .05(Given the type complexity of the Common Lisp builtin functions, it becomes difficult to construct the correct bit-)J
72 303 :M
1.052 .105(arrays for the type inferencer. For example: x-y is equivalent to x+\(-y\) in normal algebra and even in normal)J
72 314 :M
-.008(computer arithmetic. However, the t-function for binary "-" cannot be derived from the t-functions for "+" and unary)A
72 325 :M
.283 .028("-" unless the underlying lattice is symmetric about 0. However, given a datatype lattice which is symmetric about)J
72 336 :M
.378 .038(0, we must then derive the t-function of binary "/" from that of "*" and unary "/", which requires a datatype lattice)J
72 347 :M
.554 .055(which is symmetric about 1. Since it is impossible to produce a lattice which is symmetric about both 0 and 1 at)J
72 358 :M
.853 .085(the same time \(unless it is the indiscrete lattice of all rational numbers!\), we cannot, in general, derive accurate)J
72 369 :M
.461 .046(t-functions of functions from the t-functions of their functional factors. Nor can we construct these t-functions by)J
72 380 :M
.198 .02(hand; the number of Common Lisp builtin functions is very large \(on the order of 500\), and each of these bit-arrays)J
72 391 :M
1.073 .107(contains a large number of bits. From these observations, it can be seen that we must somehow automate the)J
72 402 :M
-.034(process of constructing these large bit-arrays.)A
72 418 :M
.159 .016(We can automate the production of the bit arrays for our t-functions by using the machinery of Common Lisp itself.)J
72 429 :M
.331 .033(If we are given a sufficient number of "representative elements", then we can simply execute the function on all of)J
72 440 :M
.025 .003(the representative elements, and note into which class the result falls. With properly chosen representative elements,)J
72 451 :M
.148 .015(we can elicit the complete behavior of the function over the finite set of atomic datatypes in our Boolean lattice. Of)J
72 462 :M
.497 .05(course, the )J
f2_10 sf
.151(choice)A
f0_10 sf
.595 .06( of representative elements cannot be performed automatically, but must be made carefully and)J
72 473 :M
.176 .018(intelligently. The lack of a proper representative may result in the lack of a bit in the t-function array, and hence an)J
72 484 :M
.844 .084(improper inference may be made. Furthermore, the appropriate set of representatives may be different for each)J
72 495 :M
.583 .058(function. Nevertheless, the number of representatives required is still far less than the total number of bits in the)J
72 506 :M
.086 .009(bit-array. Additionally, the ability to get the representatives correct is much less difficult than getting every bit right)J
72 517 :M
-.005(in the t-function bit-arrays by hand.)A
72 533 :M
.308 .031(Certain functions like )J
f7_10 sf
.11(+)A
f0_10 sf
.076 .008(, )J
f7_10 sf
.11(min)A
f0_10 sf
.076 .008(, )J
f7_10 sf
.11(gcd)A
f0_10 sf
.288 .029(, etc., satisfy certain commutativity properties. In these cases, we can eliminate)J
72 544 :M
.908 .091(half of the work for constructing t-functions by considering only non-commutative pairs of arguments and then)J
72 555 :M
.13 .013(or'ing the resulting bit-array with one of its transposes. However, even with such short-cuts, the production of these)J
72 566 :M
.63 .063(bit-arrays is a time-consuming process. The problem is somewhat eased, since the production of these bit-arrays)J
72 577 :M
.735 .073(need be done only once for a particular combination of Lisp implementation and datatype lattice, and so we can)J
72 588 :M
-.072(precompute these arrays.)A
72 604 :M
1.088 .109(An interesting problem occurs when we attempt to precompute large bit-arrays in Common Lisp. There is no)J
72 615 :M
-.013(efficient mechanism to read or write large bit-vector arrays in a compressed form \(i.e., as individual bits\) in Common)A
72 626 :M
.718 .072(Lisp! If the array is output using the )J
f7_10 sf
.376(print)A
f0_10 sf
.822 .082( routine, the array is printed out as integers\321i.e., the digits 0 or 1)J
72 637 :M
.003 0(followed by a single space\321or 16 bits for every one bit in the array! Writing the underlying single-dimensioned bit-)J
72 648 :M
.241 .024(vector results in the vector being printed in #*10110...01 format\321or 8 bits for every one bit in the array. Since the)J
72 659 :M
.366 .037(bit-arrays for the normal binary operations such as )J
f7_10 sf
.142(+)A
f0_10 sf
.362 .036( may require 32K bytes internally, the prospect of reading 1/4)J
72 670 :M
-.032(megabyte for each of these functions seems excessive.)A
72 686 :M
.815 .082(There is no builtin mechanism in Common Lisp to convert bit-vectors into integers. Even if such a mechanism)J
72 697 :M
.11 .011(existed, we could at best print out the bit-vector in hexadecimal format\321or 4 bits for every one bit in the array. For)J
72 708 :M
.211 .021(the Nimble type inferencer, we overcame this problem by tricking Common Lisp into thinking that a bit-vector was)J
72 719 :M
.251 .025(really a character vector. In this way, we achieved a one-to-one correspondence between external and internal bits.)J
72 730 :M
.942 .094(\(Lest advocates of other languages be smug about this point, consider the same problem in Ada. The standard)J
endp
%%Page: 19 19
%%BeginPageSetup
initializepage
(Henry Baker; page: 19 of 26)setjob
%%EndPageSetup
-31 -31 :T
gS 31 31 552 730 rC
72 45 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
72 749 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
503 749 :M
(19)S
72 69 :M
.443 .044(mechanism to read and write boolean arrays in Ada involves the use of character strings )J
f7_10 sf
.178("TRUE")A
f0_10 sf
.231 .023( and )J
f7_10 sf
.178("FALSE")A
f0_10 sf
(,)S
72 80 :M
-.064(which require an average of 5.5 characters \(= 44 bits\) for each internal bit read or written!\))A
72 102 :M
f9_10 sf
3.767 .377(6.5 INCORPORATING USER TYPE DECLARATIONS)J
72 118 :M
f0_10 sf
1.291 .129(User type declarations for program variables are trivially incorporated into the Kaplan-Ullman type inference)J
72 129 :M
1.057 .106(algorithm by using them to initialize the upper bounds instead of initializing these bounds to "top". Since the)J
72 140 :M
2.173 .217(Kaplan-Ullman algorithm uniformly )J
f2_10 sf
.426(meets)A
f0_10 sf
1.405 .14( any new bounds with the upper bounds, the minimal datatype it)J
72 151 :M
.065 .007(determines will always be consistent with the declared datatype. The Nimble type inferencing algorithm follows the)J
72 162 :M
-.18(same procedure.)A
72 184 :M
f9_10 sf
3.264 .326(6.6 INSERTING TYPE CHECKS)J
72 200 :M
f0_10 sf
.263 .026(The Nimble type inferencer puts )J
f7_10 sf
.098(\(the\312)A
f0_10 sf
.077()A
f7_10 sf
.098A
f0_10 sf
.084()A
f7_10 sf
.098<29>A
f0_10 sf
.325 .033( expressions around every expression, including constants)J
72 211 :M
.856 .086(and variables. If the actual value of at run-time is an element of , then this expression acts as an)J
72 222 :M
.065 .007(identity function, while if the actual value is not of the appropriate type, then an error message will be generated and)J
72 233 :M
.717 .072(the program is \(usually\) aborted. If, after type inference, the type of can be proved to be of a subtype of)J
72 244 :M
.403 .04(, then the run-time type check is superfluous. If the compiler used to compile the program uses a complete)J
72 255 :M
.968 .097(decision procedure for )J
f7_10 sf
.333(subtypep)A
f0_10 sf
.775 .077( [Baker92], then it will eliminate all of the type checks that the Nimble type)J
72 266 :M
-.048(inferencer was able to prove superfluous.)A
72 288 :M
f9_10 sf
3.071 .307(6.7 EXTENDING KAPLAN & ULLMAN TO WORK ON USER-DEFINED FUNCTIONS)J
72 304 :M
f0_10 sf
.919 .092(The Kaplan-Ullman type inference algorithm dealt with only primitive functions, and did not treat user-defined)J
72 315 :M
.079 .008(functions. In order to extend Kaplan-Ullman to handle user-defined functions, we must make some approximations.)J
72 326 :M
.84 .084(The most important approximation we make is to identify all instances of the same lexical variable or the same)J
72 337 :M
.385 .038(lexical function. Thus, the argument "X" in a recursive function will always be given the same type, regardless of)J
72 348 :M
.04 .004(where or when it was called\321whether from outside the function or as a recursive call from inside the function. This)J
72 359 :M
.523 .052(approximation is reasonable, since the compiler will be generating only one instance of the lexical variable or the)J
72 370 :M
.014 .001(lexical function, and thus the instance generated must deal with all situations which might occur during run-time.)J
72 386 :M
.617 .062(This approximation collapses the control structure for the program being analyzed into a Fortran-like structure in)J
72 397 :M
.363 .036(which the formal parameters and local variables of a function behave more like Fortran variables \(or Algol "own")J
72 408 :M
-.022(variables\) than like local stack-allocated or heap-allocated variables. Some approximation of this type is necessary in)A
72 419 :M
.336 .034(order to ensure convergence of the type inferencing algorithm. This particular approximation is the most "natural")J
72 430 :M
.242 .024(one to use, as well as one of the easiest to explain to the programmer. If collapsing a higher order control structure)J
72 441 :M
.51 .051(into an iterative control structure loses too much resolution to make the desired distinctions, then the programmer)J
72 452 :M
.677 .068(can always "inline" the function or "unroll" the loop one or more times, to achieve higher resolution. Any other)J
72 463 :M
-.026(policy would be too arbitrary to understand or control.)A
72 479 :M
.6 .06(This collapsing policy also has the advantage of treating Common Lisp lexically scoped free variables \(including)J
72 490 :M
-.006(global variables\) in a uniform and reasonably intuitive manner.)A
72 512 :M
f9_10 sf
3.419 .342(6.8 CONTROL FLOW INFERENCING)J
72 528 :M
f0_10 sf
.032 .003(In addition to infering the types of variables, the Nimble type inferencer also infers whether a portion of the program)J
72 539 :M
.263 .026(is executed or not\321i.e., it performs )J
f2_10 sf
.388 .039(control flow inferencing)J
f0_10 sf
.197 .02(. This is normally used for )J
f2_10 sf
.383 .038(dead code elimination)J
f0_10 sf
.141 .014(. By)J
72 550 :M
.533 .053(utilizing more sophisticated information to determine deadness, we can in more cases infer that a conditional will)J
72 561 :M
.815 .081(only execute one \(or neither\) of its arms. This is important, since dead code can ruin the sharpness of our type)J
72 572 :M
-.032(deductions. Consider, for example, the following code:)A
72 588 :M
f7_10 sf
(\(let \(\(x 3\)\))S
72 599 :M
.004 0( \(if \(integerp x\) x \(setq x 3.0\)\)\))J
72 615 :M
f0_10 sf
.229 .023(Note that the ability to tell that the assignment will )J
f2_10 sf
.074(not)A
f0_10 sf
.243 .024( be executed is essential to determining that )J
f7_10 sf
.104(x)A
f0_10 sf
.23 .023( will hold only)J
72 626 :M
.499 .05(integer values. A sloppier algorithm would conclude that )J
f7_10 sf
.19(x)A
f0_10 sf
.473 .047( could be either an integer or a floating-point number,)J
72 637 :M
.398 .04(thus foregoing a significant optimization. Performing simultaneous control flow and type inferencing is important)J
72 648 :M
.162 .016(when processing the bodies of functions expanded in-line, because such in-lined functions are significant sources of)J
72 659 :M
-.417(dead code.)A
72 675 :M
.423 .042(We can handle dead code elimination within our algorithm by incorporating control information in addition to our)J
72 686 :M
1.283 .128(datatype information in the "flowstate" lattice during the forward inferencing loops. The control information)J
72 697 :M
.159 .016(basically indicates whether a particular node will ever be executed. This information is inferred by induction, using)J
72 708 :M
.043 .004(the fact that the first node in the program is executed as an initial condition. Nodes that have not yet been marked as)J
72 719 :M
.24 .024(executing do not participate in inferencing. If a node is still unmarked when a forward inferencing loop converges,)J
endp
%%Page: 20 20
%%BeginPageSetup
initializepage
(Henry Baker; page: 20 of 26)setjob
%%EndPageSetup
-31 -31 :T
gS 31 31 552 730 rC
72 45 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
72 749 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
503 749 :M
(20)S
72 69 :M
.116 .012(then it must be the case that the node is dead code, and it \(along with the "conditional" branch that goes to it\) can be)J
72 80 :M
-.054(immediately eliminated.)A
72 102 :M
f9_10 sf
3.961 .396(6.9 TWO-PHASE INFERENCING)J
72 118 :M
f0_10 sf
.681 .068(The implemented Nimble type inference algorithm actually involves )J
f2_10 sf
.173(two)A
f0_10 sf
.578 .058( separate type inference processes. This)J
72 129 :M
.538 .054(two-phase implementation arises from the limitations of the Kaplan-Ullman algorithm discussed earlier. The two)J
72 140 :M
-.051(phases are quite similar, except for the following differences:)A
72 151 :M
.252 .025(\312\245\312Phase I uses a higher resolution lattice which does not satisfy the finite chain condition)J
72 162 :M
.121 .012(\312\245\312Phase I does forward inferencing only)J
72 173 :M
.788 .079(\312\245\312Phase I does not loop)J
72 189 :M
-.043(There are several reasons for performing type inferencing with )A
f2_10 sf
-.052(two)A
f0_10 sf
-.043( different lattices. Kaplan-Ullman type inferencing)A
72 200 :M
.277 .028(is an algorithm by which most of the progress in producing sharp lattice upper bounds comes in the early iterations)J
72 211 :M
.392 .039(of the outer loop, while most of the effort in the later stages produces only minor improvements. Thus, the higher)J
72 222 :M
.679 .068(resolution lattice \(involving a correspondingly larger amount of computational effort\) is used in the beginning to)J
72 233 :M
-.012(achieve a better starting point for a more classical Kaplan-Ullman inferencer. We also use the first forward inference)A
72 244 :M
.464 .046(pass to produce very sharp )J
f2_10 sf
.723 .072(control flow)J
f0_10 sf
.501 .05( information; this approach is essential to the removal of dead code which)J
72 255 :M
1.578 .158(would make the subsequent inferencing of sharp bounds impossible. Thus, we utilize the higher resolution)J
72 266 :M
.121 .012(indiscrete lattice where it will be the most productive\321during the first forward inferencing pass\321yet we use it only)J
72 277 :M
-.047(once in order to avoid its non-convergence problem.)A
72 293 :M
.612 .061(The indiscrete lattice used in the first phase of the Nimble type inferencer is the same as that described earlier in)J
72 304 :M
.307 .031([Baker92]. Basically, the lack of discreteness in this lattice is a result of the )J
f2_10 sf
.091(interval)A
f0_10 sf
.401 .04( representation of rational and)J
72 315 :M
.755 .075(floating-point ranges. One end of an interval can grow without bound, or can have an irrational limit, thus, this)J
72 326 :M
1.408 .141(lattice is not discrete. The use of an indiscrete lattice in a Kaplan-Ullman inferencer would prevent it from)J
72 337 :M
-.048(converging.)A
72 353 :M
.26 .026(The fact that the first phase does not loop means that minor modifications must be made to the inferencer to ensure)J
72 364 :M
.663 .066(that the "looping/recursion principle" stated above is not violated. In the classical Kaplan-Ullman algorithm, the)J
72 375 :M
.445 .045(carrying of simultaneous upper/lower bounds enhanced the speed of convergence of the algorithm but not its final)J
72 386 :M
.608 .061(result. The carrying of simultaneous upper/lower bounds, however, is )J
f2_10 sf
.142(essential)A
f0_10 sf
.595 .06( to obtaining a reasonable answer)J
72 397 :M
.157 .016(from the first phase algorithm.)J
72 413 :M
1.072 .107(Forward inferencing using the indiscrete lattice of [Baker92] subsumes a number of traditional static analyses.)J
72 424 :M
.424 .042(Within the limitations of this lattice\321that higher order functions and data types are not modeled, except by )J
f7_10 sf
.219(cons)A
72 435 :M
f0_10 sf
.921 .092(and )J
f7_10 sf
.457(function)A
f0_10 sf
1.409 .141(\321this inferencing can subsume constant propagation for scalars and "interval arithmetic" on)J
72 446 :M
.036 .004(primitive arithmetic functions. Therefore, the bounds for scalar numeric values can be surprisingly tight.)J
72 468 :M
f9_10 sf
3.132 .313(6.10 SUMMARY OF THE NIMBLE TYPE INFERENCE ALGORITHM)J
72 484 :M
f0_10 sf
-.011(The Nimble type inference algorithm conceptually operates in several passes, although they are not implemented that)A
72 495 :M
.992 .099(way. The first pass converts Common Lisp into a somewhat simpler language by eliminating several complex)J
72 506 :M
.722 .072(notions from the language less ruthlessly than [Kranz86]. Macros are expanded; "special" \(dynamically-scoped\))J
72 517 :M
.358 .036(variables are translated into semantically equivalent implementations; the dynamic operations )J
f7_10 sf
.11(catch)A
f0_10 sf
.076 .008(, )J
f7_10 sf
.11(throw)A
f0_10 sf
.239 .024( and)J
72 528 :M
f7_10 sf
.037(unwind-protect)A
f0_10 sf
.108 .011( \(analogous to "exceptions" and "signalling" in Ada\) are translated into semantically equivalent)J
72 539 :M
-.029(operations [Haynes87]; functions declared "inline" are expanded; some constant propagation/dead code elimination is)A
72 550 :M
1.102 .11(performed and many syntactic variations are regularized. The second pass performs forward and control flow)J
72 561 :M
.316 .032(inferencing using the indiscrete lattice of [Baker92]. During the second pass, we eliminate a substantial amount of)J
72 572 :M
-.006(dead code which was accumulated through macro and inline function expansion as well as from the translation of the)A
72 583 :M
1.783 .178(undesired special forms of Common Lisp. The third pass \(which consumes most of the time required for)J
72 594 :M
.017 .002(inferencing\) performs forward, backward and control flow inferencing using the discrete lattice. The results are then)J
72 605 :M
-.015(passed directly to a code generator, or can be output as source code decorated with complete declarations. Due to the)A
72 616 :M
-.023(macro and function expansions and to the issues discussed in [Baker92], this output is not particularly readable.)A
endp
%%Page: 21 21
%%BeginPageSetup
initializepage
(Henry Baker; page: 21 of 26)setjob
%%EndPageSetup
-31 -31 :T
gS 31 31 552 730 rC
72 45 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
72 749 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
503 749 :M
(21)S
72 69 :M
f4_10 sf
2.273 .227(7. THE ANALYSIS OF THE )J
f8_10 sf
.887(TAK)A
f4_10 sf
4.584 .458( FUNCTION)J
72 85 :M
f0_10 sf
(We show below the analysis of the )S
f7_10 sf
(TAK)S
f0_10 sf
( function [Gabriel85] by the Nimble type inferencer:)S
72 101 :M
f7_10 sf
(\(defun do-tak \(\))S
72 112 :M
( \(labels)S
72 123 :M
( \(\(tak \(x y z\))S
72 134 :M
( \(if \(not \(< y x\)\))S
72 145 :M
( z)S
72 156 :M
( \(tak \(tak \(1- x\) y z\))S
72 167 :M
( \(tak \(1- y\) z x\))S
72 178 :M
.002 0( \(tak \(1- z\) x y\)\)\)\)\))J
72 189 :M
( \(tak 18 12 6\)\)\))S
72 205 :M
f0_10 sf
.064 .006(Our algorithm is able to make the following inferences:)J
72 216 :M
(1. )S
f7_10 sf
(x)S
f0_10 sf
(,)S
f7_10 sf
(y)S
f0_10 sf
(,)S
f7_10 sf
(z)S
f0_10 sf
.036 .004( start as integers and are only affected by )J
f7_10 sf
(1-)S
f0_10 sf
.045 .005(, thus are always integers.)J
72 227 :M
(2. )S
f7_10 sf
(x)S
f0_10 sf
(,)S
f7_10 sf
(y)S
f0_10 sf
(,)S
f7_10 sf
(z)S
f0_10 sf
-.006( are always decremented, so they can never grow in the positive direction.)A
72 238 :M
.074 .007(3. The value of the function comes eventually from )J
f7_10 sf
(z)S
f0_10 sf
.07 .007(, and hence is an integer.)J
72 254 :M
.546 .055(However, the algorithm cannot determine whether the function will ever stop, or whether there is any limit to the)J
72 265 :M
.681 .068(size of )J
f7_10 sf
.397(x)A
f0_10 sf
.165(,)A
f7_10 sf
.397(y)A
f0_10 sf
.165(,)A
f7_10 sf
.397(z)A
f0_10 sf
.949 .095( in the negative direction. Thus, although the algorithm can be optimized to assume only )J
f2_10 sf
.312(integer)A
72 276 :M
f0_10 sf
.567 .057(arithmetic, it must still allow for the possibility of negative )J
f2_10 sf
.189(bignums)A
f0_10 sf
.558 .056(, even if they are never used. Unfortunately,)J
72 287 :M
.499 .05(the )J
f2_10 sf
.177(possibility)A
f0_10 sf
.613 .061( of bignums means the )J
f2_10 sf
.177(possibility)A
f0_10 sf
.748 .075( of garbage collection, hence the compiler must be careful about)J
72 298 :M
-.08(keeping clean calling sequences.)A
72 314 :M
.22 .022(Another inference algorithm would have to be )J
f2_10 sf
.073(much)A
f0_10 sf
.211 .021( smarter than the Nimble algorithm in order to determine better)J
72 325 :M
.401 .04(range information. Proving a restricted range is equivalent to proving termination, which cannot be proved within)J
72 336 :M
1.381 .138(the limits of the Kaplan-Ullman framework. Proving termination for )J
f7_10 sf
.5(TAK)A
f0_10 sf
1.546 .155( requires that relationships among)J
72 347 :M
-.008(variables be tracked, which is not done by the Kaplan-Ullman algorithm.)A
72 363 :M
1.109 .111(Due to these considerations, we believe that the Nimble analysis is about the best that can be expected for an)J
72 374 :M
-.03(automatic inferencing process given reasonable time and memory constraints.)A
72 390 :M
f4_10 sf
3.218 .322(8. COMPLEXITY)J
72 406 :M
f0_10 sf
.888 .089(The complexity of the Nimble type inference algorithm is relatively difficult to analyze. As we have shown in)J
72 417 :M
.381 .038(several examples above, it is difficult to determine upper bounds upon the number of iterations in the nested loops)J
72 428 :M
.993 .099(that define the Kaplan-Ullman algorithm. The program text alone is not sufficient to determine the number of)J
72 439 :M
1.288 .129(iterations, since the number will depend upon the resolution of the datatype lattice being used to analyze the)J
72 450 :M
.406 .041(program. It is easy to show examples where the number of iterations of an inner loop is equal to the )J
f2_10 sf
.131(height)A
f0_10 sf
.309 .031( of the)J
72 461 :M
.683 .068(lattice \(i.e., the length of the longest chain\). It should also be possible to demonstrate that the outer loop can be)J
72 472 :M
1.357 .136(forced to descend the entire height of the lattice. If nested loops of this type can be demonstrated, then the)J
72 483 :M
.099 .01(complexity of the Kaplan-Ullman algorithm would be at least exponential in size of the input program.)J
72 499 :M
.098 .01(We have used the Nimble type inference algorithm on a number of small examples, but have not yet used it on large)J
72 510 :M
1.293 .129(programs. As we have pointed out, the first few iterations of the outer loop achieve most of the progress at)J
72 521 :M
.838 .084(producing sharp bounds, with the additional iterations "polishing" the bounds within one of the loops. While it)J
72 532 :M
.258 .026(seems obvious that one should simply cut off the process at some point, the exact point to cut it off is not yet clear.)J
72 543 :M
.596 .06(For example, it is likely that the additional polishing done by the later loops is improving the performance of the)J
72 554 :M
.093 .009(program's )J
f2_10 sf
.019(inner)A
f0_10 sf
.069 .007( loops, which usually occupy most of the program's execution time, and therefore what appears to be)J
72 565 :M
.115 .011(minor progress may actually result in substantial reductions in execution time.)J
72 581 :M
.24 .024(Nevertheless, the Nimble algorithm seems to converge quite rapidly on the examples tried to date. This experience)J
72 592 :M
.167 .017(is consistent with the general experience of dataflow analyzers; that on most programs, they terminate more quickly)J
72 603 :M
-.061(than theory would predict.)A
72 619 :M
.983 .098(The Nimble type inferencer required about 30 seconds to type the )J
f7_10 sf
.389(TAK)A
f0_10 sf
.956 .096( function given above on a 4 Megabyte)J
72 630 :M
.476 .048(Macintosh Plus with a 16MHz 68020 accelerator running Coral Common Lisp. While this is quite slow, it would)J
72 641 :M
.566 .057(have been about 100 times slower without the careful tuning of the bit-vector and bit-array manipulation routines)J
72 652 :M
.442 .044(described in [Baker90c]. While 30 seconds seems excessive to some, the types of computations performed by the)J
72 663 :M
.427 .043(Nimble inferencer could be easily mapped onto parallel architectures: SIMD architectures, such as the Connection)J
72 674 :M
1.826 .183(Machine [Hillis85], for high performance on bit operations; MIMD architectures, for high performance on)J
72 685 :M
-.032(inferencing different parts of the program simultaneously.)A
72 701 :M
f4_10 sf
2.948 .295(9. PREVIOUS WORK)J
72 717 :M
f0_10 sf
.125 .012(Schwartz and Tenenbaum [Schwartz75][Tenenbaum74] were early researchers in type inferencing for the high level)J
72 728 :M
.828 .083(language SETL. They utilized dataflow techniques on a lattice that included higher order data structures which)J
endp
%%Page: 22 22
%%BeginPageSetup
initializepage
(Henry Baker; page: 22 of 26)setjob
%%EndPageSetup
-31 -31 :T
gS 31 31 552 730 rC
72 45 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
72 749 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
503 749 :M
(22)S
72 69 :M
-.043(included both "forward" and "backwards" inferencing. Their task was eased by the functional \(side-effect free\) nature)A
72 80 :M
-.076(of their higher order data structures.)A
72 96 :M
1.319 .132(The resolution of overloaded function symbols in strongly-typed languages such as Ada [Ada83] bears much)J
72 107 :M
0(resemblance to type inference. Early researchers feared that overload resolution would require an iterative forwards-)A
72 118 :M
.913 .091(and-backwards process similar to that described here [Ichbiah79,7.5.1], but these fears proved groundless when)J
72 129 :M
-.039(algorithms were found that performed this resolution in a single forward and a single backward pass [Pennello80].)A
72 145 :M
f2_10 sf
.183 .018(Abstract interpretation)J
f0_10 sf
.09 .009( is the name given to the generic process of "executing" a program on a lattice which is much)J
72 156 :M
.999 .1(simpler than the standard execution lattice. This process produces a kind of "homomorphic image" of the real)J
72 167 :M
.202 .02(computation, and is often used for various kinds of static analysis [Cousot77, Mycroft81, Burn87]. Most "forward")J
72 178 :M
.074 .007(type inference, including that performed by Kaplan-Ullman, Beer and ourselves, can be viewed as a form of abstract)J
72 189 :M
1.636 .164(interpretation. However, as Tanenbaum [Tanenbaum74], Kaplan-Ullman [Kaplan80] and we show, forward)J
72 200 :M
.807 .081(inferencing, and hence abstract interpretation, is not strong enough by itself to provide the information which is)J
72 211 :M
-.339(desired.)A
72 227 :M
f2_10 sf
-.066(Constant propagation)A
f0_10 sf
-.06( [Aho86] can be seen as a form of forward type inference or abstract interpretation [Callahan86].)A
72 238 :M
.129 .013(This technique detects and propagates compile-time constants by evaluating expressions \(including function calls, if)J
72 249 :M
.687 .069(possible\) to perform as much of the computation as possible during compilation. A complete implementation of)J
72 260 :M
.269 .027(constant propagation subsumes actual program execution, since the provision of a complete set of input data would)J
72 271 :M
.801 .08(enable the computation of all output at compile time. Since constant propagation necessitates a violation of the)J
72 282 :M
.04 .004(order of evaluation, it has much in common with )J
f2_10 sf
.01(strictness)A
f0_10 sf
.05 .005( analysis in lazy functional languages [Burn87].)J
72 298 :M
.223 .022(Kaplan and Ullman [Kaplan80] provide an algorithm and a characterization of a type inference algorithm for a run-)J
72 309 :M
.675 .067(time data-typed language such as APL or Lisp. Their algorithm is optimum, in that for a class of languages and)J
72 320 :M
.629 .063(programs that he characterizes, it provides the best possible information on the range of types that a variable can)J
72 331 :M
.632 .063(assume. Kaplan shows that both "forward" inferencing \(in the normal direction of computation\) and "backward")J
72 342 :M
1.82 .182(inferencing \(contrary to the normal direction of computation\) is required in order to extract the maximum)J
72 353 :M
-.004(information. Forward type inferencing propagates the type information from subexpressions to the whole expression)A
72 364 :M
.286 .029(by restricting the possibilities for the mathematical range of the subexpression functions; e.g., knowledge about the)J
72 375 :M
1.221 .122(non-negativity of a "square" function might be useful to restrict the possible results from the next step in the)J
72 386 :M
1.423 .142(computation. Backward type inferencing propagates the type information about the mathematical domain of)J
72 397 :M
.688 .069(functions within subexpressions; e.g., if a function computes the reciprocal of a number, then the requirement of)J
72 408 :M
1.018 .102(non-zeroness of that argument must be fed backward through the computation to make sure that the reciprocal)J
72 419 :M
-.019(function will never see a zero argument.)A
72 435 :M
.067 .007(Kaplan's algorithm provides the maximal amount of information, but it depends upon a rather simplified model for a)J
72 446 :M
.066 .007(programming language: a language with variables and iteration, but no recursion or data structures. Furthermore, he)J
72 457 :M
1.991 .199(does not tackle the problem of functional arguments, which makes control flow analysis difficult in Lisp)J
72 468 :M
.223 .022([Shivers88]. The Nimble type inference algorithm extends Kaplan's algorithm to handle the constructs of Common)J
72 479 :M
.181(Lisp.)A
72 495 :M
.103 .01(Most existing Lisp implementations utilize a simple forward inferencing scheme in which declaration information is)J
72 506 :M
1.443 .144(propagated forwards from variables to values, function arguments to function values [Moon74, Teitelman78,)J
72 517 :M
.161 .016(Marti79, Brooks82, Yuasa85]. These schemes are not state-based, and hence cannot handle case-based inferencing.)J
72 528 :M
.152 .015(Furthermore, the lattice typically used tends to be trivial\321e.g., "integer/short-float/long-float/other". Beer [Beer88])J
72 539 :M
-.008(has implemented the forward portion of Kaplan's algorithm for Common Lisp using a more precise, hence indiscrete,)A
72 550 :M
.892 .089(lattice to infer types and numeric bounds. He finds that it is successful at determining the types of 80% of the)J
72 561 :M
.368 .037(variables and expressions at compile-time for an interesting benchmark. More importantly, the program ran 136%)J
72 572 :M
1.609 .161(faster after type inferencing, while only an additional 3.5% improvement was realized when the rest of the)J
72 583 :M
.291 .029(declarations were inserted by hand. We believe that the Nimble two-phase approach is strictly more powerful than)J
72 594 :M
.072 .007(the Beer algorithm, although they are difficult to compare because the Beer algorithm uses heuristics to terminate its)J
72 605 :M
.167(loops.)A
72 621 :M
.859 .086([Bauer74] pointed out the possibility of type inferencing in APL. [Budd88] has implemented an APL compiler)J
72 632 :M
.067 .007(which is successful at inferring the types of most variables and subexpressions within the APL language.)J
72 648 :M
.176 .018([Suzuki81] and [Borning82] attack the problem of type inferencing in the Smalltalk language. In Smalltalk, control)J
72 659 :M
.204 .02(flow and data flow analysis must be done simultaneously, since in many cases, the code executed depends upon the)J
72 670 :M
.786 .079(type and values of the data, and vice versa. They find that Smalltalk also has enough redundancy to make type)J
72 681 :M
-.075(inferencing quite successful.)A
72 697 :M
f2_10 sf
1.546 .155(Range inferencing)J
f0_10 sf
.767 .077( is similar in concept to type inferencing. Here, we would like to narrow the range of values)J
72 708 :M
.252 .025(assumed by a variable or an expression to be less than the whole universe of values of the particular data type. For)J
72 719 :M
.396 .04(example, if a variable is inferred to be an integer, we would like to determine whether its values are restricted to a)J
72 730 :M
1.351 .135(small set of integers, perhaps 0-255, so that additional optimization can be performed. Range inferencing is)J
endp
%%Page: 23 23
%%BeginPageSetup
initializepage
(Henry Baker; page: 23 of 26)setjob
%%EndPageSetup
-31 -31 :T
gS 31 31 552 730 rC
72 45 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
72 749 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
503 749 :M
(23)S
72 69 :M
.128 .013(particularly important in reducing the need for array bounds checking, because bounds checking can slow down and)J
72 80 :M
-.039(possibly defeat several array indexing optimizations.)A
72 96 :M
1.243 .124([Harrison77] is one of the first to report on compile-time range inferencing, with [Suzuki] and [Markstein82])J
72 107 :M
1.35 .135(following. Even though the results these researchers reported were positive, very few commercial compilers)J
72 118 :M
.142 .014(incorporate this sort of analysis, except for Ada compilers [Taffs85], in which range checks are required unless they)J
72 129 :M
.105 .011(can be proved redundant. To avoid the overhead of array bounds checking in those compilers which do not perform)J
72 140 :M
.167 .017(the analysis, the user must turn off all array bounds checking. This practice is too dangerous for applications where)J
72 151 :M
-.026(an error could cause loss of property or life.)A
72 167 :M
1.154 .115(Even in those cases where array-bounds checking cannot be eliminated, a competent type checker can still be)J
72 178 :M
.986 .099(beneficial. The programmer may already have performed his own range check to obtain a more graceful error)J
72 189 :M
.904 .09(recovery than the language system normally provides, and in some of these cases, the type checker can usually)J
72 200 :M
-.086(conclude that an additional check inserted by the compiler would be redundant.)A
72 216 :M
.619 .062(Array bounds checking demonstrates one significant weakness of the Nimble type inference algorithm relative to)J
72 227 :M
.211 .021(strongly-typed languages like Ada [AdaLRM]. Ada is a strongly typed language which has a substantial amount of)J
72 238 :M
.47 .047(machinery for declaring and manipulating variables subject to range constraints. However, unlike Nimble ranges,)J
72 249 :M
.421 .042(whose endpoints must be numeric constants, Ada ranges can have )J
f2_10 sf
.107(variables)A
f0_10 sf
.365 .036( as endpoints, meaning that the size of)J
72 260 :M
.796 .08(the range is not known until run-time. Thus, an Ada compiler can relatively painlessly determine that the array)J
72 271 :M
.045 .004(bounds of v are never violated in the following code, by relying on Ada's strong typing system:)J
72 287 :M
f7_10 sf
.006 .001(type vector is array\(natural range <>\) of float;)J
72 298 :M
.006 .001(function sum foo\(v: vector\) return float is)J
88 309 :M
(total: float := 0;)S
88 320 :M
(begin)S
103 331 :M
(for i in v'range loop)S
136 342 :M
(total := total + v\(i\);)S
136 353 :M
(end loop;)S
103 364 :M
(return total;)S
88 375 :M
(end sum;)S
72 391 :M
f0_10 sf
.41 .041(On the other hand, the current Nimble type inferencer cannot eliminate the bounds checking on )J
f7_10 sf
.156(v)A
f0_10 sf
.376 .038( in the following)J
72 402 :M
-.018(equivalent Common Lisp code due to its inability to represent such )A
f2_10 sf
-.018(variable)A
f0_10 sf
-.02( ranges:)A
72 418 :M
f7_10 sf
(\(defun sum\(v &aux \(total 0\)\))S
88 429 :M
(\(dotimes \(i \(length v\) total\))S
103 440 :M
(\(incf total \(aref v i\)\)\)\))S
72 456 :M
f0_10 sf
1.331 .133(ML-style type inferencing [Milner78] elegantly solves two problems\321typing higher order functions and data)J
72 467 :M
1.143 .114(structures, and avoiding the forward-backward iterations of the dataflow techniques. However, ML-style type)J
72 478 :M
-.024(inferencing also has several deficiencies. It cannot handle case-based inferencing due to its lack of state and it cannot)A
72 489 :M
.161 .016(handle full Lisp-like polymorphism.)J
72 505 :M
.805 .081(The ML-style unification algorithm which comes closest in goals to ours is that of [Suzuki81] for Smalltalk-76.)J
72 516 :M
.997 .1(Suzuki extends the ML algorithm to handle )J
f2_10 sf
.281(unions)A
f0_10 sf
.883 .088( of base types, which are quite similar to our techniques for)J
72 527 :M
.618 .062(representing Common Lisp types. He uses Milner-style unification to solve a set of simultaneous inequalities on)J
72 538 :M
.676 .068(the datatypes of the variable instances instead of the more precise \(and slower\) Scott-style least-fixed-point limit)J
72 549 :M
.203 .02(steps. The Suzuki method may be somewhat faster than our method and it easily extends to higher-order functions,)J
72 560 :M
.913 .091(but it does not produce bounds which are as tight as those produced by the Nimble algorithm. For example, it)J
72 571 :M
.949 .095(cannot conclude that the argument to the factorial function remains a non-negative fixnum if it starts as a non-)J
72 582 :M
.655 .065(negative fixnum, nor can it conclude that the value is always a positive integer if the argument is a non-negative)J
72 593 :M
-.037(integer.)A
72 609 :M
.16 .016([Wand84] describes an ML-style type checker for Scheme, another dialect of Lisp. It handles straight-forward ML-)J
72 620 :M
.737 .074(style polymorphism, and is best characterized as "ML with parentheses". However, this method is not nearly as)J
72 631 :M
.417 .042(powerful as that in [Suzuki81], because it cannot handle the unions of datatypes introduced by Suzuki, and cannot)J
72 642 :M
-.02(therefore handle the polymorphism of real Lisp programs.)A
72 658 :M
.293 .029(The Nimble type inference algorithm could be used in a functional programming environment, where it could infer)J
72 669 :M
1.867 .187(sharper information than the ML unification algorithm. This is because the Nimble algorithm can handle)J
72 680 :M
.539 .054(polymorphism and case-based reasoning in a way that would be impossible for a unification-based algorithm. Its)J
72 691 :M
.278 .028(ability to type builtin functions more accurately than ML will also produce sharper type information. While it )J
f2_10 sf
.153(may)A
72 702 :M
f0_10 sf
.619 .062(be more expensive to run than a unification-based inference algorithm \(although ML typing is itself known to be)J
72 713 :M
-.057(DEXPTIME-complete [Mairson90]\), its better information may yield more efficient programs\321a reasonable trade-off)A
72 724 :M
.602 .06(in some situations.)J
endp
%%Page: 24 24
%%BeginPageSetup
initializepage
(Henry Baker; page: 24 of 26)setjob
%%EndPageSetup
-31 -31 :T
gS 31 31 552 730 rC
72 45 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
72 749 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
503 749 :M
(24)S
72 69 :M
f4_10 sf
3.514 .351(10. CONCLUSIONS AND FUTURE DIRECTIONS)J
72 85 :M
f0_10 sf
.305 .03(Type inferencing in a run-time data typed language such as Lisp or APL is not needed for simple execution. If the)J
72 96 :M
.199 .02(goal is optimized execution, however, then more specific information as to the types of variables and expressions is)J
72 107 :M
.476 .048(necessary. Type inferencing cannot be dispensed with through additional declarations; e.g., declarations force the)J
72 118 :M
.119 .012(same type for an argument in all calls to a procedure, and eliminate the possibility of )J
f2_10 sf
.041(polymorphism)A
f0_10 sf
.123 .012(, or execution of)J
72 129 :M
.269 .027(the same code at different times with different types [Cardelli85]. Type inferencing can be a real boon in checking)J
72 140 :M
-.006(types across procedure call interfaces, and allow for different types to be inferred within a procedure depending upon)A
72 151 :M
-.012(the actual arguments.)A
72 167 :M
.124 .012(Generalized type inferencing would seem to be hopeless. However, while many examples can be contrived to show)J
72 178 :M
1.728 .173(the impossibility of assigning a distinct type to an expression, most real programs have more than enough)J
72 189 :M
-.002(redundancy in the use of the built-in functions and operators to enable most data types to be unambiguously assigned)A
72 200 :M
.297 .03([Beer88]. The consequences of an ambiguous assignment in Lisp is not necessarily an error, but it does reduce the)J
72 211 :M
.064 .006(possibilities for optimization; hence the more tightly the datatypes are constrained, the more efficiently the code will)J
72 222 :M
.057(run.)A
72 238 :M
1.272 .127(We have described a type inference algorithm for Common Lisp which has evolved from the Kaplan-Ullman)J
72 249 :M
.963 .096(algorithm [Kaplan80] to the point that it can handle the entire Common Lisp-84 language [CLtL84]. We have)J
72 260 :M
.136 .014(shown, through a number of examples, that this algorithm uses case-based and state-based reasoning to deduce tight)J
72 271 :M
.998 .1(lattice bounds on polymorphic functions, including recursive functions. We have described a number of novel)J
72 282 :M
.331 .033(techniques for engineering an efficient implementation of the lattice manipulations required by this algorithm. We)J
72 293 :M
.645 .064(have shown how this algorithm is strictly more powerful than other popular techniques such as unification-based)J
72 304 :M
.614 .061(techniques [Milner78] on some examples, and seems more appropriate for highly polymorphic languages such as)J
72 315 :M
1.483 .148(Lisp. While the algorithmic complexity of our inferencer is higher than usual for Lisp compilers, its better)J
72 326 :M
1.032 .103(information can be used for a greater than usual degree of optimization. The fact that this information can be)J
72 337 :M
1.17 .117(extracted in a completely mechanical fashion, and the fact that the kind of processing required can be greatly)J
72 348 :M
-.051(accelerated by parallel computers, mean that the cost of type inference will decrease quickly over time.)A
72 364 :M
2.034 .203(A possible improvement that could be made to the basic Kaplan-Ullman type inference machinery is the)J
72 375 :M
.27 .027(employment of a larger number of lattices. So long as every inner loop in the Kaplan-Ullman algorithm is allowed)J
72 386 :M
.464 .046(to complete, the computed bound can be used as an upper bound on the next stage execution of the inner loop. If)J
72 397 :M
-.001(this next stage uses a more refined lattice, then tighter bounds can be inferred. Therefore, we could conceivably start)A
72 408 :M
.242 .024(with a coarse lattice, distinguishing only between scalars, list cells, functions, etc. The next stage could distinguish)J
72 419 :M
.301 .03(various kinds of numbers, various kinds of list cells, etc. Only in the latest stages would we distinguish among the)J
72 430 :M
.473 .047(higher order kinds of data structures and their components. A large amount of space and time in type inferencing)J
72 441 :M
.005 .001(could be saved by reserving a higher resolution lattice for numbers, only for those variables which have already been)J
72 452 :M
.862 .086(shown to be numbers; a higher resolution lattice for different kinds of list cells could be reserved just for those)J
72 463 :M
.944 .094(variables shown to be only list cells; and so forth. In this way, we could utiliize different lattices for different)J
72 474 :M
.449 .045(variables, which is an improvement that we could also have achieved through strong typing. However, our lattice)J
72 485 :M
-.023(approach allows far more flexibility, because not all variables need be resolved to the same level of refinement.)A
72 501 :M
.994 .099(Since the Nimble type inferencer must deal with the entirety of the Common Lisp-84 language, it must have a)J
72 512 :M
.619 .062(reasonably deep understanding of every one of its datatypes, constructs and functions. One may ask whether the)J
72 523 :M
.477 .048(enormous effort involved in incorporating this knowledge into a static analyzer is worth the effort. The answer is)J
72 534 :M
1.366 .137(yes, if there exist important Common Lisp programs which would be expensive to modify which need to be)J
72 545 :M
-.086(statically analyzed.)A
72 561 :M
.829 .083(In most cases, the Lisp community would be better served by a language which is )J
f2_10 sf
.314(much)A
f0_10 sf
1.08 .108( smaller than Common)J
72 572 :M
-.033(Lisp, since the many different and often redundant features of the language do not contribute either to its efficiency or)A
72 583 :M
.426 .043(to its ease of use. For example, the polymorphic type complexity of the Common Lisp library functions is mostly)J
72 594 :M
.65 .065(gratuitous, and both the efficiency of compiled code and the efficiency of the programmer could be increased by)J
72 605 :M
1.824 .182(rationalizing this complexity. Notions such as dynamic floating-point contagion, multiple-values, complex)J
72 616 :M
.509 .051(argument-passing, and special variables are obsolete in today's world. Most strings and lists in Lisp are used in a)J
72 627 :M
.108 .011(functional manner, yet they are heavily penalized in performance by the remote possibility of side-effects. A major)J
72 638 :M
1.057 .106(advance in the run-time efficiency and ease of static analysis of Lisp-like languages could be achieved if Lisp)J
72 649 :M
-.038(programs and argument lists were constructed from some functional data structure instead of from cons cells.)A
72 665 :M
f4_10 sf
.564(ACKNOWLEDGMENTS)A
72 681 :M
f0_10 sf
.484 .048(The author wishes to thank the Department of Energy for their support and James J. Hirsch for his help in editing)J
72 692 :M
.45 .045(this manuscript.)J
endp
%%Page: 25 25
%%BeginPageSetup
initializepage
(Henry Baker; page: 25 of 26)setjob
%%EndPageSetup
-31 -31 :T
gS 31 31 552 730 rC
72 45 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
72 749 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
503 749 :M
(25)S
72 69 :M
f4_10 sf
.753(REFERENCES)A
72 85 :M
f0_10 sf
-.03(AdaLRM: )A
f2_10 sf
-.026(Reference Manual for the Ada\250 Programming Language)A
f0_10 sf
-.027(. ANSI/MIL-STD-1815A-1983, U.S. Government)A
89 96 :M
.43 .043(Printing Office, Wash., DC, 1983.)J
72 107 :M
.015 .002(Aho, Alfred V.; Sethi, Ravi; and Ullman, Jeffrey D. )J
f2_10 sf
.024 .002(Compilers: Principles, Techniques, and Tools)J
f0_10 sf
.023 .002(. Addison-Wesley,)J
89 118 :M
.125(1986.)A
72 129 :M
.914 .091(Baker92: Baker, Henry. "A Decision Procedure for Common Lisp's SUBTYPEP Predicate". )J
f2_10 sf
1.076 .108(Lisp and Symbolic)J
89 140 :M
.977 .098(Computation 5)J
f0_10 sf
.857 .086(,3 \(Sept.\3121992\), 157-190.)J
72 151 :M
-.016(Baker90b: Baker, Henry. "Unify and Conquer \(Garbage, Updating, Aliasing...\) in Functional Languages". )A
f2_10 sf
-.019(Proc. 1990)A
89 162 :M
.095 .01(ACM Conference on Lisp and Functional Programming)J
f0_10 sf
.072 .007(, June 1990.)J
72 173 :M
.124 .012(Baker90c: Baker, Henry. "Efficient Implementation of Bit-vector Operations in Common Lisp". ACM)J
f2_10 sf
.115 .012( Lisp Pointers)J
89 184 :M
.075(3,)A
f0_10 sf
.5 .05(2-3-4 \(April-June\3121990\), 8-22.)J
72 195 :M
.286 .029(Bauer, Alan M., and Saal, Harry J. "Does APL really need run-time checking?" )J
f2_10 sf
.445 .044(Software Practice and Experience)J
f0_10 sf
(,)S
89 206 :M
.891 .089(v.4, 1974,pp.129-138.)J
72 217 :M
1.224 .122(Beer, Randall D. "The compile-time type inference and type checking of Common Lisp programs: a technical)J
89 228 :M
.692 .069(summary". TR 88-116, Ctr. for Automation and Intelligent Sys. Research, Case Western Reserve Univ., May)J
89 239 :M
.233 .023(1988; also )J
f2_10 sf
.3 .03(LISP Pointers 1)J
f0_10 sf
.377 .038(,2 \(June-July 1987\),5-11.)J
72 250 :M
1.038 .104(Bobrow, et al. "Common Lisp Object System Specification X3J13", )J
f2_10 sf
1.522 .152(ACM SIGPLAN Notices)J
f0_10 sf
.94 .094(, v.23, Sept. 1988;)J
89 261 :M
.262 .026(also X3J13 Document 88-002R, June 1988.)J
72 272 :M
.368 .037(Borning, Alan H. and Ingalls, Daniel H. H. "A Type Declaration and Inference System for Smalltalk" )J
f2_10 sf
.715 .071(ACM POPL)J
89 283 :M
.16(9)A
f0_10 sf
.661 .066(, 1982, pp.133-141.)J
72 294 :M
1.06 .106(Brooks, R., Gabriel, R., Steele, G. "S-1 Common Lisp Implementation". )J
f2_10 sf
.978 .098(Proc. of '82 ACM Symp. on Lisp and)J
89 305 :M
.319 .032(Funct. Prog.)J
f0_10 sf
.311 .031(, \(Aug. 1982\),108-113.)J
72 316 :M
.546 .055(Brooks, R., et al. "Design of an Optimizing, Dynamically Retargetable Compiler for Common Lisp". )J
f2_10 sf
.52 .052(Proc. of '86)J
89 327 :M
.424 .042(ACM Conf. on Lisp and Funct. Prog.)J
f0_10 sf
.566 .057(, \(Aug. 1986\),67-85.)J
72 338 :M
.306 .031(Budd, Timothy. )J
f2_10 sf
.42 .042(An APL Compiler)J
f0_10 sf
.36 .036(. Springer-Verlag, NY, 1988.)J
72 349 :M
.468 .047(Burn, G.L. )J
f2_10 sf
.812 .081(Abstract Interpretation and the Parallel Evaluation of Functional Languages)J
f0_10 sf
.621 .062(. Ph.D. Thesis, Imperial)J
89 360 :M
.314 .031(College, London, 1987.)J
72 371 :M
.517 .052(Callahan, D., Cooper, K.D., Kennedy, K., and Torczon, L. "Interprocedural Constant Propagation". )J
f2_10 sf
.763 .076(Proc. Sigplan)J
89 382 :M
.584 .058('86 Symp. on Compiler Constr.)J
f0_10 sf
.284 .028(, also )J
f2_10 sf
.647 .065(Sigplan Notices 21)J
f0_10 sf
.593 .059(, 7 \(July 1986\),152-161.)J
72 393 :M
1.047 .105(Cardelli, L., and Wegner, P. "On Understanding Types, Data Abstraction, and Polymorphism". ACM Comput.)J
89 404 :M
.339 .034(Surv. 17,4 \(Dec. 1985\),471-522.)J
72 415 :M
.28 .028(Cartwright, R. "User-defined Data Types as an Aid to Verifying Lisp Programs". In Michaelson, S., and Milner, R.)J
89 426 :M
-.021(\(eds.\). )A
f2_10 sf
-.03(Automata, Languages and Programming)A
f0_10 sf
-.027(, Edinburgh Press, Edinburgh,228-256.)A
72 437 :M
.638 .064(CLtL:\312\312Steele, Guy L., Jr. )J
f2_10 sf
1.009 .101(Common Lisp: The Language)J
f0_10 sf
.649 .065(. Digital Press, 1984.)J
72 448 :M
1.327 .133(Cousot, P., and Cousot, R. "Abstract Interpretation: a unified lattice model for static analysis of programs by)J
89 459 :M
.392 .039(construction or approximation of fixpoints". )J
f2_10 sf
.458 .046(4'th ACM POPL)J
f0_10 sf
.604 .06(, 1977,238-252.)J
72 470 :M
.242 .024(Dijkstra, E.W. )J
f2_10 sf
.37 .037(A Discipline of Programming)J
f0_10 sf
.308 .031(. Prentice-Hall, Englewood Cliffs, NJ, 1976.)J
72 481 :M
.256 .026(Ellis, John R. )J
f2_10 sf
.478 .048(Bulldog: A Compiler for VLIW Architectures.)J
f0_10 sf
.383 .038( MIT Press, Cambridge, MA, 1986.)J
72 492 :M
-.028(Ferrante, Jeanne, and Rackoff, Charles W. "A decision procedure for the first order theory of real addition with order".)A
89 503 :M
f2_10 sf
.807 .081(SIAM J. Comput. 4)J
f0_10 sf
.931 .093(, 1 \(1975\),69-76.)J
72 514 :M
.135 .013(Gabriel, Richard P. )J
f2_10 sf
.196 .02(Performance and Evaluation of Lisp Systems)J
f0_10 sf
.159 .016(. MIT Press, Cambridge, MA, 1985.)J
72 525 :M
.366 .037(Harper, R., )J
f2_10 sf
.291 .029(et al)J
f0_10 sf
.449 .045(. "Standard ML". Technical Report ECS-LFCS-86-2, Dept. of Computer Science, Edinburgh, UK,)J
89 536 :M
.193 .019(March, 1986.)J
72 547 :M
.652 .065(Harrison, William. "Compiler Analysis of the Value Ranges for Variables". )J
f2_10 sf
.699 .07(IEEE Trans. Soft. Eng. SE-3)J
f0_10 sf
.815 .082(,3 \(May)J
89 558 :M
.026(1977\),243-250.)A
72 569 :M
1.696 .17(Haynes, Christopher T., and Friedman, Daniel P. "Embedding Continuations in Procedural Objects". )J
f2_10 sf
1.043(ACM)A
89 580 :M
.788 .079(TOPLAS 9)J
f0_10 sf
.74 .074(,4 \(Oct. 1987\),582-598.)J
72 591 :M
.243 .024(Hillis, W. Daniel. )J
f2_10 sf
.463 .046(The Connection Machine)J
f0_10 sf
.298 .03(. The MIT Press, Cambridge, MA, 1985.)J
72 602 :M
1.537 .154(Ichbiah, J. "Rationale for the design of the Ada programming language." )J
f2_10 sf
2.007 .201(ACM Sigplan Notices 14)J
f0_10 sf
1.96 .196(,6 \(June)J
89 613 :M
(1979\),part B.)S
72 624 :M
.184 .018(Intel Corporation. )J
f2_10 sf
.279 .028(i860 64-bit Microprocessor Programmer's Reference Manual.)J
f0_10 sf
.203 .02( Order #240329, Intel Corporation,)J
89 635 :M
.44 .044(Santa Clara, CA, 1989.)J
72 646 :M
.425 .042(Jones, N.D., and Muchnick, S. "Binding time optimization in programming languages". )J
f2_10 sf
.521 .052(3'rd ACM POPL)J
f0_10 sf
.498 .05(, Atlanta,)J
89 657 :M
.032 .003(GA \(1976\),77-94.)J
72 668 :M
1.112 .111(Kanellakis, P.C., and Mitchell, J.C. "Polymorphic unification and ML typing". )J
f2_10 sf
1.27 .127(ACM Funct. Prog. Langs. and)J
89 679 :M
.759 .076(Comp. Arch. \(FPCA\))J
f0_10 sf
.823 .082(, 1989,54-74.)J
72 690 :M
.327 .033(Kaplan, Marc A., and Ullman, Jeffrey D. "A Scheme for the Automatic Inference of Variable Types". )J
f2_10 sf
.638 .064(ACM JACM)J
89 701 :M
.082(27)A
f0_10 sf
.363 .036(,1 \(Jan. 1980\),128-145.)J
72 712 :M
1.279 .128(Katayama, Takuya. "Type Inference and Type Checking for Functional Programming Languages\321A Reduced)J
89 723 :M
.466 .047(Computation Approach". )J
f2_10 sf
.374 .037(1984 ACM Conf. on Lisp and Funct. Prog.)J
f0_10 sf
.522 .052(, Aug. 1984,263-272.)J
endp
%%Page: 26 26
%%BeginPageSetup
initializepage
(Henry Baker; page: 26 of 26)setjob
%%EndPageSetup
-31 -31 :T
gS 31 31 552 730 rC
72 45 :M
f0_12 sf
-.104(The Nimble Type Inferencer for Common Lisp-84)A
72 749 :M
f0_10 sf
.227 .023(\251 1989-1991 Nimble Computer Corporation)J
503 749 :M
(26)S
72 69 :M
1.336 .134(Kranz, D., Kelsey, R., Rees, J., Hudak, P., Philbin, J., and Adams, N. "ORBIT: An Optimizing Compiler for)J
89 80 :M
.38 .038(Scheme". )J
f2_10 sf
.441 .044(Proc. Sigplan '86 Symp. on Compiler Constr., also Sigplan Notices 21)J
f0_10 sf
.481 .048(, 7 \(July 1986\),219-233.)J
72 91 :M
.208 .021(Ma, Kwan-Liu, and Kessler, Robert R. "TICL\321A Type Inference System for Common Lisp". )J
f2_10 sf
.296 .03(SW\321Prac. & Exper.)J
89 102 :M
.151(20)A
f0_10 sf
.902 .09(,6 \(June\3121990\),593-623.)J
72 113 :M
.131 .013(MacLane, Saunders and Birkhoff, Garrett. )J
f2_10 sf
.048(ALGEBRA)A
f0_10 sf
.126 .013(. Macmillan, 1967.)J
72 124 :M
.312 .031(Mairson, H.G. "Deciding ML Typability is Complete for Deterministic Exponential Time". )J
f2_10 sf
.383 .038(17'th ACM POPL)J
f0_10 sf
.265 .026( \(Jan.)J
89 135 :M
.026(1990\),382-401.)A
72 146 :M
.479 .048(Markstein, Victoria; Cocke, John; and Markstein, Peter. "Optimization of Range Checking". )J
f2_10 sf
.557 .056(ACM POPL '82)J
f0_10 sf
.147(,114-)A
89 157 :M
.167(119.)A
72 168 :M
.453 .045(Marti, J., Hearn, A.C., Griss, M.L., and Griss, C. "Standard LISP Report". )J
f2_10 sf
.646 .065(Sigplan Notices 14,)J
f0_10 sf
.518 .052( 10 \(Oct. 1979\),48-)J
89 179 :M
.25(68.)A
72 190 :M
.438 .044(Milner, Robin. "A Theory of Type Polymorphism in Programming" )J
f2_10 sf
.144(JCSS)A
f0_10 sf
.062 .006( )J
f2_10 sf
.137(17)A
f0_10 sf
.768 .077(, 1978,pp.348-375.)J
72 201 :M
.412 .041(Moon, D. )J
f2_10 sf
.771 .077(MACLISP Reference Manual Rev. 0)J
f0_10 sf
.647 .065(. Proj. MAC\321M.I.T., Camb., MA, April 1974.)J
72 212 :M
.423 .042(Morris, J.H. "Types are Not Sets". )J
f2_10 sf
.923 .092(ACM POPL)J
f0_10 sf
.704 .07(, 1973, pp.120-124.)J
72 223 :M
.653 .065(Mycroft, Alan. )J
f2_10 sf
1.025 .102(Abstract Interpretation and Optimising Transformation for Applicative Programs)J
f0_10 sf
.633 .063(. Ph.D. Thesis,)J
89 234 :M
.245 .024(Univ. Edinburgh, Scoitland, 1981.)J
72 245 :M
.157 .016(Pennello, T., and Meyers, R. "A Simplified Operator Identification Scheme in Ada". ACM Sigplan Notices 15, 7&8)J
89 256 :M
.251 .025(\(July-Aug. 1980\),82-87.)J
72 267 :M
.23 .023(Schwartz, J.T. "Optimization of very high level languages\321I. Value transmission and its corollaries". )J
f2_10 sf
.361 .036(J. Computer)J
89 278 :M
.175 .018(Lang. 1)J
f0_10 sf
.264 .026( \(1975\),161-194.)J
72 289 :M
.452 .045(Scott, D. "Data types as lattices". )J
f2_10 sf
.814 .081(SIAM J. Computing)J
f0_10 sf
.626 .063(, 5,3 \(Sept. 1976\), 522-587.)J
72 300 :M
.491 .049(Sethi, Ravi. "Conditional Expressions with Equality Tests". )J
f2_10 sf
.45 .045(J. ACM 25)J
f0_10 sf
.895 .09(,4 \(Oct.\3121978\),667-674.)J
72 311 :M
.5 .05(Shivers, O. "Control Flow Analysis in Scheme". )J
f2_10 sf
.633 .063(ACM Sigplan Conf. '88)J
f0_10 sf
.166(,164-174.)A
72 322 :M
.535 .054(Steele, Guy L., Jr. )J
f2_10 sf
.893 .089(Rabbit: A Compiler for SCHEME \(A Study in Compiler Optimization\))J
f0_10 sf
.871 .087(. AI-TR-474, Artificial)J
89 333 :M
.03 .003(Intelligence Laboratory, MIT, May 1978.)J
72 344 :M
.122 .012(Suzuki, Norihisa. "Implementation of an array bound checker". )J
f2_10 sf
.208 .021(ACM POPL)J
f0_10 sf
.101 .01( ???.)J
72 355 :M
.42 .042(Suzuki, Norihisa. "Inferring Types in Smalltalk". )J
f2_10 sf
.396 .04(ACM POPL 8, )J
f0_10 sf
.128(1981,pp.187-199.)A
72 366 :M
.556 .056(Taffs, D.A., Taffs, M.W., Rienzo, J.C., Hampson, T.R. "The ALS Ada Compiler Global Optimizer". in Barnes &)J
89 377 :M
.353 .035(Fisher, "Ada in Use": Proc. Ada Int'l. Conf., Camb. Univ. Press, 1985,355-366.)J
72 388 :M
.327 .033(Teitelman, W., )J
f2_10 sf
.214 .021(et al)J
f0_10 sf
.06 .006(. )J
f2_10 sf
.461 .046(InterLISP Reference Manual)J
f0_10 sf
.265 .027(. Xerox PARC, Palo Alto, CA, 1978.)J
72 399 :M
1.154 .115(Tenenbaum, A. "Type determination for very high level languages". Ph.D. Thesis, Rep. NSO-3, Courant Inst.)J
89 410 :M
.372 .037(Math. Sci., New York U., New York, 1974.)J
72 421 :M
.452 .045(Thatte, Satish R. "Quasi-static Typing". )J
f2_10 sf
.547 .055(17'th ACM POPL '90)J
f0_10 sf
.642 .064(, Jan. 1990,367-381.)J
72 432 :M
1.565 .156(Wand, M. "A Semantic Prototyping System". )J
f2_10 sf
1.824 .182(Proc. ACM Sigplan '84 Symp. on Compiler Constr., Sigplan)J
89 443 :M
.261 .026(Notices 19)J
f0_10 sf
.28 .028(,6 \(June 1984\),213-221.)J
72 454 :M
1.135 .114(Yuasa, T., and Hagiya, M. Kyoto Common Lisp Report. Research Institute for Mathematical Sciences, Kyoto)J
89 465 :M
.496 .05(University, 1985.)J
endp
%%Trailer
end
%%EOF
*