-- Copyright (c) 1991-2002, The Numerical ALgorithms Group Ltd.
-- All rights reserved.
-- Copyright (C) 2007-2015, Gabriel Dos Reis.
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are
-- met:
--
-- - Redistributions of source code must retain the above copyright
-- notice, this list of conditions and the following disclaimer.
--
-- - Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in
-- the documentation and/or other materials provided with the
-- distribution.
--
-- - Neither the name of The Numerical ALgorithms Group Ltd. nor the
-- names of its contributors may be used to endorse or promote products
-- derived from this software without specific prior written permission.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-- IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-- PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-- OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import g_-util
namespace BOOT
--%
$optimizableConstructorNames := $SystemInlinableConstructorNames
++ Return true if the domain `dom' is an instance of a functor
++ that has been nominated for inlining.
optimizableDomain? dom ==
symbolMember?(opOf dom,$optimizableConstructorNames)
++ Register the domain `dom' for inlining.
nominateForInlining dom ==
$optimizableConstructorNames := [opOf dom,:$optimizableConstructorNames]
--%
++ return the template of the instantiating functor for
++ the domain form `dom'.
getDomainTemplate dom ==
dom isnt [.,:.] => nil
getInfovec first dom
++ Emit code for an indirect call to domain-wide Spad function.
++ This is usually the case for exported functions.
emitIndirectCall(fn,args,x) ==
x.first := "SPADCALL"
fn.first := '%tref
x.rest := [:args,fn]
x
--% OPTIMIZER
++ Return the name of the iterator variable `it'.
++ NOTES: iterator variables are usually of the form `[sc,:id]'
++ where `sc' is a storage class, i.e. `%local' or `%free'.
++ Occasionally, they are just symbols with the storage
++ class `%local' implied.
iteratorName it ==
ident? it => it
rest it
++ Subroutine of changeVariableDefinitionToStore.
++ Same semantics as changeVariableDefinitionToStore for a loop
++ with body `body', iterator list `iters', return value `val' and
++ currently bound variable list `vars'.
changeLoopVarDefsToStore(iters,body,val,vars) ==
vars' := vars
-- Iterator variables are local to the the loop.
for it in iters repeat
it is ['STEP,idx,start,inc,:final] =>
vars' := changeVariableDefinitionToStore(start,vars')
vars' := changeVariableDefinitionToStore(inc,vars')
vars' := changeVariableDefinitionToStore(final,vars')
vars' := [iteratorName it,:vars']
it is [op,v,l] and op in '(IN ON) =>
vars' := changeVariableDefinitionToStore(l,vars')
vars' := [iteratorName it,:vars']
vars' := changeVariableDefinitionToStore(it,vars')
changeVariableDefinitionToStore(val,vars')
-- Variables defined in the loop body are also local to the loop
changeVariableDefinitionToStore(body,vars')
vars
++ Change (%LET id expr) to (%store id expr) if `id' is being
++ updated as opposed to being defined. `vars' is the list of
++ all variable definitions in scope.
changeVariableDefinitionToStore(form,vars) ==
atomic? form or form.op in '(CLOSEDFN XLAM) => vars
form is ['%LET,v,expr] =>
vars := changeVariableDefinitionToStore(expr,vars)
do
symbolMember?(v,vars) => form.op := '%store
vars := [v,:vars]
vars
form is ['%scope,.,expr] and expr is ['%seq,:.] =>
changeVariableDefinitionToStore(expr,vars)
vars
form is ['%when,:.] =>
for clause in form.args repeat
-- variable defined in clause predicates are visible
-- in subsequent predicates.
vars := changeVariableDefinitionToStore(first clause,vars)
-- but those defined in branches are local.
changeVariableDefinitionToStore(rest clause,vars)
vars
-- recursive binding
form is ['%bind,:.] =>
vars' := vars
for [v,init] in second form repeat
vars' := changeVariableDefinitionToStore(init,vars')
vars' := [v,:vars']
changeVariableDefinitionToStore(third form,vars')
vars
-- non-recursive binding.
form is ['LET,:.] =>
vars' := nil
for [v,init] in second form repeat
changeVariableDefinitionToStore(init,vars')
vars' := [v,:vars']
changeVariableDefinitionToStore(third form,[:vars',:vars])
vars
abstraction? form =>
changeVariableDefinitionToStore(form.absBody,[:form.absParms,:vars])
vars
form is ['%repeat,:iters,body,val] =>
changeLoopVarDefsToStore(iters,body,val,vars)
form is ['%closure,fun,env] =>
changeVariableDefinitionToStore(fun,vars)
vars
if form is ['%seq,:.] then
form.args := spliceSeqArgs form.args
for x in form repeat
vars := changeVariableDefinitionToStore(x,vars)
vars
++ Return true if `x' contains control transfer to a point outside itself.
jumpToToplevel? x ==
atomic? x => false
abstraction? x or op is '%closure => false
op := x.op
op is '%seq => CONTAINED('%leave,x.args) -- FIXME: what about GO?
op in '(%exit %leave) => true
or/[jumpToToplevel? x' for x' in x]
++ Return true if `form' is just one assignment expression.
nonExitingSingleAssignment? form ==
form is ['%LET,.,rhs]
and not CONTAINED('%LET,rhs) and not jumpToToplevel? rhs
++ Turns `form' into a `%bind'-expression if it starts with a
++ a sequence of first-time variable definitions.
groupVariableDefinitions form ==
atomic? form => form
form is ['%when,[p,s1],['%otherwise,s2]] and sideEffectFree? p =>
[form.op,[p,groupVariableDefinitions s1],
['%otherwise,groupVariableDefinitions s2]]
form.op is '%when =>
-- FIXME: we should not be generating store-modifying predicates
for clause in form.args while not CONTAINED('%LET, first clause) repeat
second(clause) := groupVariableDefinitions second clause
form
form is ['%scope,tag,expr] =>
mkScope(tag,groupVariableDefinitions expr)
form is ['%bind,inits,expr] =>
mkBind(inits,groupVariableDefinitions expr)
form is ['%lambda,:.] =>
[form.absKind,form.absParms,groupVariableDefinitions form.absBody]
form is ['%repeat,:iters,body,val] =>
[form.op,:iters,groupVariableDefinitions body,val]
form isnt ['%seq,:stmts,['%exit,val]] => form
form.args = nil => nil
form.args is [s] => groupVariableDefinitions s
defs := [s.args for s in stmts while nonExitingSingleAssignment? s]
defs = nil => form
stmts := drop(#defs,stmts)
expr :=
stmts = nil => val
['%seq,:stmts,['%exit,val]]
mkBind(defs,expr)
++ Group all %LET-definitions of artificial/temporary variables
++ into %bind-forms, appropriate for inlining in later stages.
groupTranscients! x == walkWith!(x,function f) where
f x ==
x is ['%scope,tag,y] and y is ['%seq,:.] =>
defs := [s.args for s in y.args while s is ['%LET,z,u]
and gensym? z and hasNoLeave?(u,tag)]
defs = nil => x
resetTo(x,mkBind(defs,mkScope(tag,mkSeq drop(#defs,y.args))))
x
++ Reduce all applications of XLAM-abstractions to arguments.
++ This is done before simplifyVMForm to expose more opportunities
++ for further reductions.
reduceXLAM! x == walkWith!(x,function f) where
f x ==
x is ['%call,y,:args] and y is ['XLAM,:.] =>
resetTo(x,doInlineCall(args,y.absParms,copyTree y.absBody))
x
++ Remove throw-away expressions, inline one-time temporaries
++ abstracting conditionals (generated by the parser) and remove
++ unnecessary %seq boxes around singleton expressions.
++ NOTES: this transformation must be run before any grouping
++ of intermediate result bindings.
removeJunk! x == walkWith!(x,function f) where
f x ==
x is ['%seq,:.] =>
x.args := g x.args
x.args is [y] => resetTo(x,y)
x
x
g xs ==
xs = nil => nil
x := first xs
x is "/throwAway" => g rest xs -- skip garbages
x is ['%LET,y,u] and gensym? y and numOfOccurencesOf(y,rest xs) = 1 =>
g substitute!(u,y,rest xs)
x is ['%seq,:.] => -- splice sub-sequences
ys := x.args =>
lastNode(ys).rest := rest xs
g ys
g rest xs -- skip empty statements
rest xs = nil => xs
sideEffectFree? x => g rest xs -- skip effect-less statements.
xs.rest := g rest xs
xs
inlineLocals! x == walkWith!(x,function f) where
f x ==
x is ['%bind,inits,:.] =>
kept := nil
while inits is [u,:inits] repeat
[y,z] := u
usesVariable?(z,y) or usesVariable?(inits,y) => kept := [u,:kept]
or/[usesVariable?(z,v) for [v,.] in kept] => kept := [u,:kept]
canInlineVarDefinition(y,z,x.absBody) =>
x.absBody := substitute!(z,y,x.absBody)
kept := [u,:kept]
kept = nil => resetTo(x,x.absBody)
x.absParms := reverse! kept
x
x
++ Transform an IF-expression into a tower of %when-form.
transformIF! x == walkWith!(x,function f) where
f x ==
x is ['IF,p,s1,s2] =>
s2 is '%noBranch => resetTo(x,['%when,[p,s1]])
s1 is '%noBranch => resetTo(x,['%when,[['%not,p],s2]])
s2 is ['%when,:.] => resetTo(x,['%when,[p,s1],:s2.args])
resetTo(x,['%when,[p,s1],['%otherwise,s2]])
x
++ Subroutine of packWhen!
mkDefault(g,l) ==
l = nil => ['CONDERR]
mkScope(g,mkSeq l)
coagulateWhenSeries(l,tag) ==
f(l,tag) where
f(l,tag) ==
cl := nil
while l is [s,:.] and (cl' := g(s,tag)) repeat
cl := [:cl,:cl']
l := rest l
cl = nil => nil
[cl,l]
g(s,tag) ==
-- return list of reduced clauses if they all exit from same scope.
s is ['%when,:.] => exitClauses(s.args,tag)
nil
++ Return non-nil if the expression `x' exist the scope with tag `g'
++ by normal local transfer or by exiting the enclosing function
++ with a `return' statement. If non-nil, the return value is the
++ expression operand to the normal exit.
exitScope?(x,g) ==
x is ['%leave,=g,y] and hasNoLeave?(y,g) => y
x is ['%return,:.] => x
nil
unnestWhen! x == f x where
f x ==
x is ['%scope,tag,y] =>
y is ['%seq,:stmts] =>
while stmts is [s,:.] repeat
s := f s
s is ['%when,[p,u],['%otherwise,v]] and exitScope?(u,tag) =>
stmts.first := ['%when,[p,u]]
stmts.rest := spliceSeqArgs [f v,:rest stmts]
stmts.first := s
stmts := rest stmts
x
y is ['%when,[p,u:=['%leave,=tag,.]],['%otherwise,v]] =>
reset(x,f mkScope(tag,mkSeq [['%when,[p,u]],f v]))
second(x.args) := f y
x
do
abstraction? x => x.absBody := f x.absBody
x is ['%leave,.,y] or x is ['%return,.,y] => second(x.args) := f y
x is ['%when,:.] =>
for cl in x.args repeat
second(cl) := f second cl
x is ['%seq,:.] =>
for stmts in tails x.args repeat
stmts.first := f first stmts
x is ['%repeat,:.] =>
x.loopBody := unnestWhen! x.loopBody
x
++ Transform nested-to-tower.
packWhen! x == walkWith!(x,function f) where
f x ==
x is ['%when,[p1,['%when,[p2,s]]]] =>
resetTo(x,f ['%when,[['%and,p1,p2],s]])
x is ['%when,:cl,['%otherwise,y]] and y is ['%when,:.] =>
resetTo(x,f ['%when,:cl,:y.args])
x is ['%leave,g,['%when,[p,['%leave,=g,s]]]] =>
resetTo(x,['%leave,g,['%when,[p,s]]])
x is ['%scope,g,['%seq,:w]] and coagulateWhenSeries(w,g) is [u,v] =>
resetTo(x,f ['%when,:u,['%otherwise,f mkDefault(g,v)]])
x
spliceSeq! x == walkWith!(x,function f) where
f x ==
if x is ['%seq,:.] then
x.args := spliceSeqArgs x.args
x
++ Return the list of reduced clauses if every branch is a simple
++ normal exiting expression. Otherwise, return nil.
exitClauses(l,g) ==
[[p,x] for cl in l | cl is [p,u] and (x := exitScope?(u,g)) or leave nil]
++ Remove superfluous cancel/scope pairs exposed by running
++ removeJunk!.
cancelScopeLeave! x == walkWith!(x,function f) where
f x ==
x is ['%scope,g,u] =>
u is ['%when,:.] and (v := exitClauses(u.args,g)) =>
resetTo(x,[u.op,:v])
y := exitScope?(u,g) => resetTo(x,f y)
x
x
++ Remove redundant %leave opode around %return forms.
removeLeave! x == walkWith!(x,function f) where
f x ==
x is ['%leave,.,y] and y is ['%return,:.] => resetTo(x,y)
x
freeIteratorFirstValues iters ==
[u for it in iters | u := f it] where
f it ==
it is ['STEP,['%free,:id],lo,:.] => ['%LET,id,lo]
it is ['ON,['%free,:id],l] => ['%LET,id,l]
nil
cleanLoop! x == prefixWalk!(x,function f) where
f x ==
x is ['%scope,tag,['%repeat,:itl,body,val]] =>
body := g(body,tag)
val := g(val,tag)
firstVals := freeIteratorFirstValues itl =>
resetTo(x,mkSeq [:firstVals,f ['%repeat,:itl,body,val]])
resetTo(x,f ['%repeat,:itl,body,val])
x
g(x,tag) ==
atomic? x => x
x is ['%leave,=tag,y] =>
first(x.args) := nil
second(x.args) := g(y,tag)
x
for xs in tails x repeat
xs.first := g(first xs,tag)
x
removeScope! x == prefixWalk!(x,function f) where
f x ==
x is ['%scope,g,y] =>
y is ['%when,[p,['%leave,=g,u]],['%leave,=g,v]] =>
resetTo(x,['%when,[p,u],['%otherwise,v]])
hasNoLeave?(y,g) => resetTo(x,f y)
x
x
++ Transform an intermediate form (output of the elaborator) to
++ a lower intermediate form, applying several transformations
++ generaly intended to improve quality and efficiency.
optimize! x ==
x := unnestWhen! spliceSeq! packWhen! transformIF! removeLeave! cleanLoop! x
changeVariableDefinitionToStore(x,nil)
simplifyVMForm removeScope! cancelScopeLeave! spliceSeq! packWhen!
inlineLocals! groupTranscients! cancelScopeLeave!
removeJunk! reduceXLAM! x
++ A non-mutating version of `optimize!'.
optimize x ==
optimize! copyTree x
optimizeFunctionDef(def) ==
if $reportOptimization then
sayBrightlyI bright '"Original LISP code:"
pp def
expr := optimize! copyTree second def
if $reportOptimization then
sayBrightlyI bright '"Intermediate VM code:"
pp expr
[first def,expr]
resetTo(x,y) ==
y isnt [.,:.] => y
sameObject?(x,y) => y
x.first := y.first
x.rest := y.rest
x
++ Simplify the VM form `x'
simplifyVMForm x ==
x is '%icst0 => 0
x is '%icst1 => 1
atomic? x => x
x.op is 'CLOSEDFN => x
x.op isnt [.,:.] =>
symbol? x.op and abstractionOperator? x.op =>
x.absBody := groupVariableDefinitions simplifyVMForm x.absBody
x
for args in tails x.args repeat
args.first := simplifyVMForm first args
opt := subrname x.op has OPTIMIZE => resetTo(x,apply(opt,[x]))
x
for xs in tails x repeat
xs.first := simplifyVMForm first xs
x
subrname u ==
ident? u => u
COMPILED_-FUNCTION_-P u or MBPIP u => BPINAME u
nil
changeLeaveToExit(s,g) ==
atomic? s => nil
s.op in '(QUOTE %seq REPEAT COLLECT %collect %repeat) => nil
s is ['%leave, =g,:u] => (s.first := '%exit; s.rest := u)
changeLeaveToExit(first s,g)
changeLeaveToExit(rest s,g)
changeLeaveToGo(s,g) ==
s isnt [.,:.] or s.op is 'QUOTE => nil
s is ['%leave, =g,u] =>
changeLeaveToGo(u,g)
s.first := "PROGN"
s.rest := [["%LET",second g,u],["GO",second g]]
changeLeaveToGo(first s,g)
changeLeaveToGo(rest s,g)
optScope (x is ['%scope,g,a]) ==
a isnt [.,:.] => a
if a is ['%seq,:s,['%leave,=g,u]] then
changeLeaveToExit(s,g)
a.rest := [:s,['%exit,u]]
a := simplifyVMForm a
a isnt [.,:.] => a
do
hasNoLeave?(a,g) => resetTo(x,a)
changeLeaveToGo(a,g)
x.first := '%seq
x.rest := [['%exit,a],second g,['%exit,second g]]
x
optSPADCALL(form is ['SPADCALL,:argl]) ==
not $InteractiveMode => form
-- last arg is function/env, but may be a form
argl is [:argl,fun] and fun is ["ELT",dom,slot] =>
optCall ['%call,['ELT,dom,slot],:argl]
form
++ Inline a call with arguments `args' to a simple function with
++ parameter-list `parms' and body `body'.
doInlineCall(args,parms,body) ==
-- 1. almost constant functions are easy
parms = nil => body
-- 2. identity functions too
parms is [=body] => first args
-- 3. We know that the body is essentially side-effect-free (at best)
-- or simple expression (at worst.) The issue is whether it is
-- OK to directly substitute the arguments which may be
-- arbitrary expressions. The ideal semantics calls for introducting
-- a (fresh) temporary to hold the values of the argument expressions.
-- We attempt to short-circuit the native evaluation as follows:
-- a. side-effect-free arguments go by themselves.
-- b. Simple expression arguments go by themselves as long as
-- the corresponding parameter is used linearly in the body.
-- c. Others get evaluated to temporaries.
tmps := nil -- list of temporaries
inits := nil -- list of local bindings
subst := nil -- arguments substitution.
for arg in args for parm in parms repeat
g := gensym()
tmps := [g,:tmps]
n := numOfOccurencesOf(parm,body)
atomic? arg or (sideEffectFree? arg and n < 2) or n = 1 =>
subst := [[g,:arg],:subst]
inits := [[g,arg],:inits]
-- 4. Alpha-rename the body and substitute simple expression arguments.
body := applySubst(pairList(parms,reverse! tmps),body)
body := applySubst!(subst,body)
-- 5. Deliver.
inits = nil => body
['%bind,reverse! inits,body]
optCall (x is ['%call,:u]) ==
u is [['XLAM,vars,body],:args] =>
vars isnt [.,:.] => body
#vars > #args => systemErrorHere ['optCall,x]
resetTo(x,doInlineCall(args,vars,body))
[fn,:a] := u
fn isnt [.,:.] =>
opt := fn has OPTIMIZE => resetTo(x,apply(opt,[u]))
resetTo(x,u)
fn is ['%apply,name] =>
do
ident? name =>
x.first := '%funcall
x.rest := [['%head,name],:a,['%tail,name]]
x.first := 'SPADCALL
x.rest := [:a,name]
x
fn is ['%closure,['%function,op],env] =>
x.first := op
x.rest := [:a,env]
x
fn is ['%external,op] =>
x.first := op
x.rest := a
opt := op has OPTIMIZE => resetTo(x,apply(opt,[x]))
x
fn is ['ELT,:.] => emitIndirectCall(fn,a,x)
fn is ['CONST,R,n] => ['spadConstant,R,n]
systemErrorHere ['optCall,x]
optClosure(x is ['%closure,fun,env]) ==
fun is ['%function,['%lambda,vars,body]] =>
do
vars is [:vars',=env] =>
body is [op,: =vars] => x.args := [['%function,op],env]
not usesVariable?(body,env) => x.args := [fun,'%nil]
x
x
optCons (x is ["CONS",a,b]) ==
a is "NIL" =>
b is 'NIL => (x.first := 'QUOTE; x.rest := ['NIL,:'NIL]; x)
b is ['QUOTE,:c] => (x.first := 'QUOTE; x.rest := ['NIL,:c]; x)
x
a is ['QUOTE,a'] =>
b is 'NIL => (x.first := 'QUOTE; x.rest := [a',:'NIL]; x)
b is ['QUOTE,:c] => (x.first := 'QUOTE; x.rest := [a',:c]; x)
x
x
optCond (x is ['%when,:l]) ==
l is [['%true,c],:.] => c
l is [['%false,.],:.] => optCond ['%when,:rest l]
l is [['%otherwise,c]] => c
if l is [a,[aa,b]] and aa is '%otherwise and b is ['%when,:c] then
x.rest.rest := c
if l is [[p1,:c1],[p2,:c2],:.] then
if (p1 is ['%not,=p2]) or (p2 is ['%not,=p1]) then
l:=[[p1,:c1],['%otherwise,:c2]]
x.rest := l
c1 is ['NIL] and p2 is '%otherwise and first c2 is '%otherwise =>
return optNot ['%not,p1]
l is [[p1,['%when,[p2,c2]]]] => optCond ['%when,[['%and,p1,p2],c2]]
l is [[p1,c1],['%otherwise,'%false]] => optAnd ['%and,p1,c1]
l is [[p1,c1],['%otherwise,'%true]] => optOr ['%or,optNot ['%not,p1],c1]
l is [[p1,'%false],['%otherwise,c2]] => optAnd ['%and,optNot ['%not,p1],c2]
l is [[p1,'%true],['%otherwise,c2]] => optOr ['%or,p1,c2]
l is [[p1,:c1],[p2,:c2],[p3,:c3]] and p3 is '%otherwise =>
EqualBarGensym(c1,c3) =>
optCond ['%when,[['%or,p1,optNot ['%not,p2]],:c1],['%otherwise,:c2]]
EqualBarGensym(c1,c2) =>
optCond ['%when,[['%or,p1,p2],:c1],['%otherwise,:c3]]
x
for y in tails l repeat
while y is [[a1,c1],[a2,c2],:y'] and EqualBarGensym(c1,c2) repeat
a := ['%or,a1,a2]
first(y).first := a
y.rest := y'
x
AssocBarGensym(key,l) ==
for x in l repeat
cons? x =>
EqualBarGensym(key,first x) => return x
EqualBarGensym(x,y) ==
$GensymAssoc: local := nil
fn(x,y) where
fn(x,y) ==
x=y => true
gensym? x and gensym? y =>
z:= assoc(x,$GensymAssoc) => y=rest z
$GensymAssoc:= [[x,:y],:$GensymAssoc]
true
null x => y is [g] and gensym? g
null y => x is [g] and gensym? g
x isnt [.,:.] or y isnt [.,:.] => false
fn(first x,first y) and fn(rest x,rest y)
optSuchthat [.,:u] == ["SUCHTHAT",:u]
++ List of VM side effect free operators.
$VMsideEffectFreeOperators ==
'(SPADfirst ASH FLOAT FLOAT_-SIGN %function %nullStream %nonNullStream
%nothing %when %false %true %otherwise %2bit %2bool
%and %or %not %peq %ieq %ilt %ile %igt %ige %head %tail %integer?
%beq %blt %ble %bgt %bge %bitand %bitior %bitxor %bitnot %bcompl
%ilength %ibit %icst0 %icst1 %icstmin %icstmax
%imul %iadd %isub %igcd %ilcm %ipow %imin %imax %ieven? %iodd? %iinc
%idec %irem %iquo %idivide %idec %irandom %imulmod %iaddmod %isubmod
%ilshift %irshift %ibigrandom
%feq %flt %fle %fgt %fge %fmul %fadd %fsub %fexp %fmin %fmax %float?
%fpowi %fdiv %fneg %i2f %fminval %fmaxval %fbase %fprec %ftrunc
%fsqrt %fpowf %flog %flog2 %flog10 %fmanexp %fNaN? %fdecode
%fsin %fcos %ftan %fcot
%fasin %facos %fatan %facot
%fsinh %fcosh %ftanh
%fasinh %facosh %fatanh
%val2z %z2val %zlit %zreal %zimag
%zexp %zlog %zsin %zcos %ztan %zasin %zacos %zatan
%zsinh %zcosh %ztanh %zasinh %zacosh %zatanh
%nil %pair %list %pair? %lconcat %llength %lfirst %lsecond %lthird
%lreverse %lempty? %lcopy %hash %ismall? %string? %f2s STRINGIMAGE
%ccst %ccstmax %ceq %clt %cle %cgt %cge %c2i %i2c %s2c %c2s %cup %cdown
%sname
%strlength %streq %i2s %schar %strlt %strconc
%strcopy %bytevec2str %str2bytevec
%array %simpleArray %emptyArray %list2array %array2list
%initialElement %initialContents
%vector %aref %vref %vlength %vcopy
%bitvector
%bitvecnot %bitvecand %bitvecnand %bivecor %bitvecnor %bitvecxor
%bitveccopy %bitvecconc %bitveclength %bitvecref %bitveceq
%before? %equal %sptreq %ident? %property %tref
%void %retract %pullback %lambda %closure %external
%type2form)
++ List of simple VM operators
$simpleVMoperators ==
[:$VMsideEffectFreeOperators,
:['SPADCALL,'%apply, '%funcall, '%gensym, '%lreverse!, '%strstc]]
++ Return true if the `form' is semi-simple with respect to
++ to the list of operators `ops'.
semiSimpleRelativeTo?(form,ops) ==
atomic? form => true
not symbol?(form.op) or not symbolMember?(form.op,ops) => false
abstraction? form => true -- always, regardless of body
form.op is '%when =>
and/[sideEffectFree? p and semiSimpleRelativeTo?(c,ops)
for [p,c] in form.args]
and/[semiSimpleRelativeTo?(f,ops) for f in form.args]
++ Return true if `form' os a side-effect free form.
sideEffectFree? form ==
semiSimpleRelativeTo?(form,$VMsideEffectFreeOperators)
++ Return true if `form' is a VM form whose evaluation does not depend
++ on the program point where it is evaluated.
$FloatableOperators ==
['%bind,:$VMsideEffectFreeOperators]
floatableVMForm?: %Code -> %Boolean
floatableVMForm? form ==
semiSimpleRelativeTo?(form,$FloatableOperators)
++ Return true if the VM form `form' is one that we certify to
++ evaluate to a (compile time) constant. Note that this is a
++ fairly conservative approximation of compile time constants.
isVMConstantForm: %Code -> %Boolean
isVMConstantForm form ==
integer? form or string? form => true
form isnt [op,:args] => false
op in '(%list %pair %vector) => false
symbolMember?(op,$VMsideEffectFreeOperators) and
"and"/[isVMConstantForm arg for arg in args]
++ Return the set of free variables in the VM form `form'.
findVMFreeVars form ==
ident? form => [form]
form isnt [op,:args] => nil
op is 'QUOTE => nil
vars := union/[findVMFreeVars arg for arg in args]
op isnt [.,:.] => vars
setUnion(findVMFreeVars op,vars)
++ Return true is `var' is the left hand side of an assignment
++ or a sort of binding in `form'.
modified?(var,form) ==
atomic? form => false
form is [op,var',expr,:.] and op in '(%LET LETT SETQ %store) =>
modified?(var,expr) => true
symbol? var' => var' = var -- whole var is assigned
var' is [.,=var,:.] -- only part of it is modified
form is ['%bind,:.] and
(or/[symbolEq?(var,var') for [var',.] in form.absParms]) => true
form is ['%repeat,:iters,.,.] and
(or/[symbolEq?(var,iteratorName i) for i in iters]) => true
abstraction? form and symbolMember?(var,form.absParms) => true
or/[modified?(var,f) for f in form]
++ Return the list of variables referenced in `expr'.
dependentVars expr == main(expr,nil) where
main(x,vars) ==
ident? x =>
symbolMember?(x,vars) => vars
[x,:vars]
atomic? x => vars
x' :=
cons? x.op => x
x.args
for y in x' repeat
vars := main(y,vars)
vars
++ Subroutine of optBind. Return true if the variable `var' locally
++ defined in a binding form can be safely replaced by its initalization
++ `expr' in the `body' of the binding form.
canInlineVarDefinition(var,expr,body) ==
-- Occasional situation where `expr' evaluates to itself
gensym? var and sameObject?(var,body) => true
-- FIXME: We should not be inlining a side-effecting initializer.
-- If the variable is assigned to, that is a no no.
modified?(var,body) => false
-- Similarly, if the initial value depends on variables that are
-- side-effected latter, it is also a no no.
or/[modified?(x,body) for x in dependentVars expr] => false
-- If the initializer is a variable and not modified in body,
-- and the new var is not modified, then we can inline.
-- FIXME: except if the modification is done via normal function calls.
ident? expr => true
-- Conversatively stay out of loops
cons? body and body.op in '(%repeat %collect) => false
-- Linearly used internal temporaries should be replaced, and
-- so should side-effet free initializers for linear variables.
usageCount := numOfOccurencesOf(var,body)
usageCount < 2 and floatableVMForm? expr => true
gensym? var and usageCount = 1 => true
-- If the initializer is a variable and the body is
-- a series of choices with floatable predicates, then
-- no harm is done by inlining the local `var'.
ident? expr and body is ['%when,:branches] =>
and/[floatableVMForm? pred for [pred,:.] in branches]
false
++ Implement simple-minded LET-inlining. It seems we can't count
++ on Lisp implementations to do this simple transformation.
++ This transformation will probably be more effective when all
++ type informations are still around. Which is why we should
++ have a type directed compilation throughout.
optBind form ==
form isnt ['%bind,inits,.] => form -- accept only simple bodies
while inits ~= nil repeat
[var,expr] := first inits
usesVariable?(rest inits,var) => leave nil -- no dependency, please.
body := third form
canInlineVarDefinition(var,expr,body) =>
third(form) := substitute!(expr,var,body)
inits := rest inits
leave nil
null inits => third form -- no local var left
second(form) := inits
form
optTry form ==
form isnt ['%try,e,hs,f] or not(floatableVMForm? e) or f ~= nil => form
e
optList form ==
form is ['%list] => '%nil
literalElts := [(x is ['QUOTE,y] => y; leave "failed") for x in form.args]
literalElts is "failed" => form
quote literalElts
quoteMode x ==
x isnt [.,:.] => x
ident? x.op and constructor? x.op =>
['%list,quote x.op,:[quoteMode a for a in x.args]]
[quoteMode y for y in x]
++ Translate retraction of a value denoted by `e' to sub-domain `m'
++ defined by predicate `pred',
optRetract ["%retract",e,m,pred] ==
e isnt [.,:.] =>
cond := simplifyVMForm substitute(e,"#1",pred)
cond is '%true => e
['%when,[cond,e],['%otherwise,['moanRetract,e,quoteMode m]]]
g := gensym()
['%bind,[[g,e]],
['%when,[substitute(g,"#1",pred),g],
['%otherwise,['moanRetract,g,quoteMode m]]]]
++ We have an expression `x' of some Union type. Expand an attempted
++ pullback to the `n'th branch of type `t'.
optPullback ['%pullback,x,t,n] ==
y :=
ident? x => x
gensym()
expr :=
['%when,[['%ieq,['%head,y],n],['%tail,y]],
['%otherwise,['moanRetract,y,quoteMode t]]]
symbolEq?(x,y) => expr
['%bind,[[y,x]],expr]
--% Boolean expression transformers
optNot(x is ['%not,a]) ==
a is '%true => '%false
a is '%false => '%true
a is ['%not,b] => b
a is ['%when,:.] =>
optCond [a.op, :[[p,optNot ['%not,c]] for [p,c] in a.args]]
x
optAnd(x is ['%and,a,b]) ==
a is '%true => b
b is '%true => a
a is '%false => '%false
x
optOr(x is ['%or,a,b]) ==
a is '%false => b
b is '%false => a
a is '%true => '%true
x
-- Boolean <-> bit conversion.
opt2bit ['%2bit,a] ==
optCond ['%when,[a,'%icst1],['%otherwise,'%icst0]]
opt2bool ['%2bool,a] ==
a is '%icst0 => '%false
a is '%icst1 => '%true
optIeq ['%ieq,a,'%icst1]
optIeq(x is ['%ieq,a,b]) ==
integer? a and integer? b =>
scalarEq?(a,b) => '%true
'%false
sameObject?(a,b) => '%true
x
optIlt(x is ['%ilt,a,b]) ==
-- 1. Don't delay if both operands are literals.
integer? a and integer? b =>
a < b => '%true
'%false
-- 2. max(a,b) cannot be negative if either a or b is zero.
b = 0 and a is ['%imax,:.] and (second a = 0 or third a = 0) => '%false
-- 3. min(a,b) cannot be positive if either a or b is zero.
a = 0 and b is ['%imin,:.] and (second b = 0 or third b = 0) => '%false
x
optIle(x is ['%ile,a,b]) ==
optNot ['%not,optIlt ['%ilt,b,a]]
optIgt x ==
optIlt ['%ilt,third x, second x]
optIge x ==
optNot ['%not,optIlt ['%ilt,second x,third x]]
--% Byte operations
optBle ['%ble,a,b] ==
optNot ['%not,['%blt,b,a]]
optBgt ['%bgt,a,b] ==
['%blt,b,a]
optBge ['%bge,a,b] ==
optBle ['%ble,b,a]
--% Integer operations
optIadd(x is ['%iadd,a,b]) ==
integer? a and integer? b => a + b
integer? a =>
a = 0 => b
b is [op,b1,b2] and op in '(%iadd %isub) =>
integer? b1 => simplifyVMForm [op,['%iadd,a,b1],b2]
integer? b2 => simplifyVMForm ['%iadd,b1,[op,a,b2]]
x
x
integer? b =>
b = 0 => a
a is [op,a1,a2] and op in '('%iadd %isub) =>
integer? a1 => simplifyVMForm [op,['%iadd,a1,b],a2]
integer? a2 => simplifyVMForm ['%iadd,a1,[op,b,a2]]
x
x
x
optIaddmod ['%iaddmod,a,b,c] ==
optIrem ['%irem,optIadd ['%iadd,a,b],c]
optIinc(x is ['%iinc,a]) ==
integer? a => a + 1
a is [op,b,c] and op in '(%isub %iadd) =>
integer? b => simplifyVMForm [op,[op,b,1],c]
integer? c => simplifyVMForm [op,b,[op,c,1]]
x
x
optIsub(x is ['%isub,a,b]) ==
integer? a and integer? b => a - b
integer? a =>
a = 0 => ['%ineg,b]
b is ['%iadd,b1,b2] =>
integer? b1 => simplifyVMForm ['%isub,['%isub,a,b1],b2]
integer? b2 => simplifyVMForm ['%isub,['%isub,a,b2],b1]
x
b is ['%isub,b1,b2] =>
integer? b1 => simplifyVMForm ['%iadd,['%isub,a,b1],b2]
integer? b2 => simplifyVMForm ['%isub,['%iadd,a,b2],b1]
x
x
integer? b =>
b = 0 => a
a is ['%iadd,a1,a2] =>
integer? a1 => simplifyVMForm ['%iadd,['%isub,a1,b],a2]
integer? a2 => simplifyVMForm ['%iadd,a1,['%isub,a2,b]]
x
a is ['%isub,a1,a2] =>
integer? a1 => simplifyVMForm ['%isub,['%isub,a1,b],a2]
integer? a2 => simplifyVMForm ['%isub,a1,['%iadd,a2,b]]
x
x
x
optIsubmod ['%isubmod,a,b,c] ==
optIrem ['%irem,optIsub ['%isub,a,b],c]
optIdec(x is ['%idec,a]) ==
integer? a => a - 1
a is ['%iadd,b,c] =>
integer? b => simplifyVMForm ['%iadd,['%isub,b,1],c]
integer? c => simplifyVMForm ['%iadd,b,['%isub,c,1]]
x
a is ['%isub,b,c] =>
integer? b => simplifyVMForm ['%isub,['%isub,b,1],c]
integer? c => simplifyVMForm ['%isub,b,['%iadd,c,1]]
x
x
optImul(x is ['%imul,a,b]) ==
integer? a and integer? b => a * b
integer? a and a = 1 => b
integer? b and b = 1 => a
x
optImulmod(x is ['%imulmod,a,b,c]) ==
optIrem ['%irem,optImul ['%imul,a,b],c]
optIneg(x is ['%ineg,a]) ==
integer? a => -a
x
optIrem(x is ['%irem,a,b]) ==
integer? a and integer? b => a rem b
x
optIquo(x is ['%iquo,a,b]) ==
integer? a and integer? b => a quo b
x
--% Arrays
optEmptyArray ['%emptyArray,t] ==
['%array,'%icst0,'%elementType,['%type2form,t]]
optSimpleArray ['%simpleArray,t,n,x] ==
['%array,n,'%elementType,['%type2form,t],'%initialElement,x]
optList2Array ['%list2array,l,t] ==
['%array,['%llength, l],'%elementType,['%type2form,t],'%initialContents,l]
--%
--% optimizer hash table
--%
for x in '((%call optCall) _
(%bind optBind)_
(%try optTry)_
(%not optNot)_
(%and optAnd)_
(%or optOr)_
(%ble optBle)_
(%bgt optBgt)_
(%bge optBge)_
(%ieq optIeq)_
(%ilt optIlt)_
(%ile optIle)_
(%igt optIgt)_
(%ige optIge)_
(%ineg optIneg)_
(%iadd optIadd)_
(%iaddmod optIaddmod)_
(%iinc optIinc)_
(%isub optIsub)_
(%isubmod optIsubmod)_
(%irem optIrem)_
(%iquo optIquo)_
(%imul optImul)_
(%imulmod optImulmod)_
(%2bit opt2bit)_
(%2bool opt2bool)_
(%list optList)_
(SPADCALL optSPADCALL)_
(%closure optClosure)_
(_| optSuchthat)_
(%scope optScope)_
(%when optCond)_
(%retract optRetract)_
(%pullback optPullback)_
(%emptyArray optEmptyArray)_
(%simpleArray optSimpleArray)_
(%list2array optList2Array)) _
repeat property(first x,'OPTIMIZE) := second x
--much quicker to call functions if they have an SBC