1(*
2    Copyright (c) 2012,13,16,18-20 David C.J. Matthews
3
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License version 2.1 as published by the Free Software Foundation.
7    
8    This library is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11    Lesser General Public License for more details.
12    
13    You should have received a copy of the GNU Lesser General Public
14    License along with this library; if not, write to the Free Software
15    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
16*)
17
18(* Miscellaneous construction and operation functions on the code-tree. *)
19
20functor CODETREE_FUNCTIONS(
21    structure BASECODETREE: BaseCodeTreeSig
22    structure STRONGLY:
23        sig val stronglyConnectedComponents: {nodeAddress: 'a -> int, arcs: 'a -> int list } -> 'a list -> 'a list list end
24) : CodetreeFunctionsSig
25=
26struct
27    open BASECODETREE
28    open STRONGLY
29    open Address
30    exception InternalError = Misc.InternalError
31    
32    fun mkEnv([], exp) = exp
33    |   mkEnv(decs, exp) = Newenv(decs, exp)
34
35    val word0 = toMachineWord 0
36    and word1 = toMachineWord 1
37
38    val False = word0  
39    and True  = word1 
40
41    val F_mutable_words : Word8.word = Word8.orb (F_words, F_mutable)
42
43    val CodeFalse = Constnt(False, [])
44    and CodeTrue  = Constnt(True, [])
45    and CodeZero  = Constnt(word0, [])
46   
47    (* Properties of code.  This indicates the extent to which the
48       code has side-effects (i.e. where even if the result is unused
49       the code still needs to be produced) or is applicative
50       (i.e. where its value depends only arguments and can safely
51       be reordered). *)
52
53    (* The RTS has a table of properties for RTS functions.  The 103 call
54       returns these Or-ed into the register mask. *)
55    val PROPWORD_NORAISE  = 0wx40000000
56    and PROPWORD_NOUPDATE = 0wx20000000
57    and PROPWORD_NODEREF  = 0wx10000000
58
59    (* Since RTS calls are being eliminated leave residual versions of these. *)
60    fun earlyRtsCall _ = false
61    and sideEffectFreeRTSCall _ = false
62
63    local
64        infix orb andb
65        val op orb = Word.orb and op andb = Word.andb
66        val noSideEffect = PROPWORD_NORAISE orb PROPWORD_NOUPDATE
67        val applicative = noSideEffect orb PROPWORD_NODEREF
68    in
69        fun codeProps (Lambda _) = applicative
70
71        |   codeProps (Constnt _) = applicative
72
73        |   codeProps (Extract _) = applicative
74
75        |   codeProps (TagTest{ test, ... }) = codeProps test
76
77        |   codeProps (Cond(i, t, e)) = codeProps i andb codeProps t andb codeProps e
78
79        |   codeProps (Newenv(decs, exp)) =
80                List.foldl (fn (d, r) => bindingProps d andb r) (codeProps exp) decs
81
82        |   codeProps (Handle { exp, handler, ... }) =
83                (* A handler processes all the exceptions in the body *)
84                (codeProps exp orb PROPWORD_NORAISE) andb codeProps handler
85
86        |   codeProps (Tuple { fields, ...}) = testList fields
87
88        |   codeProps (Indirect{base, ...}) = codeProps base
89
90            (* A built-in function may be side-effect free.  This can
91               occur if we have, for example, "if exp1 orelse exp2"
92               where exp2 can be reduced to "true", typically because it's
93               inside an inline function and some of the arguments to the
94               function are constants.  This then gets converted to
95               (exp1; true) and we can eliminate exp1 if it is simply
96               a comparison. *)
97        |   codeProps (Unary{oper, arg1}) =
98            let
99                open BuiltIns
100                val operProps =
101                    case oper of
102                        NotBoolean => applicative
103                    |   IsTaggedValue => applicative
104                    |   MemoryCellLength => applicative
105                        (* MemoryCellFlags could return a different result if a mutable cell was locked. *)
106                    |   MemoryCellFlags => applicative
107                    |   ClearMutableFlag => Word.orb(PROPWORD_NODEREF, PROPWORD_NORAISE)
108                    |   AtomicIncrement => PROPWORD_NORAISE
109                    |   AtomicDecrement => PROPWORD_NORAISE
110                    |   AtomicReset => Word.orb(PROPWORD_NODEREF, PROPWORD_NORAISE)
111                    |   LongWordToTagged => applicative
112                    |   SignedToLongWord => applicative
113                    |   UnsignedToLongWord => applicative
114                    |   RealAbs _ => applicative (* Does not depend on rounding setting. *)
115                    |   RealNeg _ => applicative (* Does not depend on rounding setting. *)
116                        (* If we float a 64-bit int to a 64-bit floating point value we may
117                           lose precision so this depends on the current rounding mode. *)
118                    |   RealFixedInt _ => Word.orb(PROPWORD_NOUPDATE, PROPWORD_NORAISE)
119                    |   FloatToDouble => applicative
120                        (* The rounding mode is set explicitly. *)
121                    |   DoubleToFloat _ => applicative
122                        (* May raise the overflow exception *)
123                    |   RealToInt _ => PROPWORD_NOUPDATE orb PROPWORD_NODEREF
124                    |   TouchAddress => PROPWORD_NORAISE (* Treat as updating a notional reference count. *)
125                    |   AllocCStack => PROPWORD_NORAISE
126            in
127                operProps andb codeProps arg1
128            end
129
130        |   codeProps (Binary{oper, arg1, arg2}) =
131            let
132                open BuiltIns
133                val mayRaise = PROPWORD_NOUPDATE orb PROPWORD_NODEREF
134                val operProps =
135                    case oper of
136                        WordComparison _ => applicative
137                    |   FixedPrecisionArith _ => mayRaise
138                    |   WordArith _ => applicative (* Quot and Rem don't raise exceptions - zero checking is done before. *)
139                    |   WordLogical _ => applicative
140                    |   WordShift _ => applicative
141                    |   AllocateByteMemory => Word.orb(PROPWORD_NOUPDATE, PROPWORD_NORAISE)
142                            (* Allocation returns a different value on each call. *)
143                    |   LargeWordComparison _ => applicative
144                    |   LargeWordArith _ => applicative (* Quot and Rem don't raise exceptions - zero checking is done before. *)
145                    |   LargeWordLogical _ => applicative
146                    |   LargeWordShift _ => applicative
147                    |   RealComparison _ => applicative
148                        (* Real arithmetic operations depend on the current rounding setting. *)
149                    |   RealArith _ => Word.orb(PROPWORD_NOUPDATE, PROPWORD_NORAISE)
150                    |   FreeCStack => PROPWORD_NORAISE orb PROPWORD_NODEREF
151                    |   PointerEq => applicative
152            in
153                operProps andb codeProps arg1 andb codeProps arg2
154            end
155
156        |   codeProps (Nullary{oper=BuiltIns.GetCurrentThreadId}) = Word.orb(PROPWORD_NOUPDATE, PROPWORD_NORAISE)
157        |   codeProps (Nullary{oper=BuiltIns.CheckRTSException}) = PROPWORD_NOUPDATE
158
159        |   codeProps (Arbitrary{shortCond, arg1, arg2, longCall, ...}) =
160                (* Arbitrary precision operations are applicative but the longCall is
161                   a function call.  It should never have a side-effect so it might
162                   be better to remove it. *)
163                codeProps shortCond andb codeProps arg1 andb codeProps arg2 andb codeProps longCall
164
165        |   codeProps (AllocateWordMemory {numWords, flags, initial}) =
166            let
167                val operProps = Word.orb(PROPWORD_NOUPDATE, PROPWORD_NORAISE)
168            in
169                operProps andb codeProps numWords andb codeProps flags andb codeProps initial
170            end
171
172        |   codeProps (Eval _) = 0w0
173
174        |   codeProps(Raise exp) = codeProps exp andb (Word.notb PROPWORD_NORAISE)
175
176            (* Treat these as unsafe at least for the moment. *)
177        |   codeProps(BeginLoop _) = 0w0
178
179        |   codeProps(Loop _) = 0w0
180
181        |   codeProps (SetContainer _) = 0w0
182
183        |   codeProps (LoadOperation {address, kind}) =
184            let
185                val operProps =
186                    case kind of
187                        LoadStoreMLWord {isImmutable=true} => applicative
188                    |   LoadStoreMLByte {isImmutable=true} => applicative
189                    |   _ => Word.orb(PROPWORD_NOUPDATE, PROPWORD_NORAISE)
190            in
191                operProps andb addressProps address
192            end
193
194        |   codeProps (StoreOperation {address, value, ...}) =
195                Word.orb(PROPWORD_NODEREF, PROPWORD_NORAISE) andb addressProps address andb codeProps value
196        
197        |   codeProps (BlockOperation {kind, sourceLeft, destRight, length}) =
198            let
199                val operProps =
200                    case kind of
201                    BlockOpMove _ => PROPWORD_NORAISE
202                |   BlockOpEqualByte => applicative
203                |   BlockOpCompareByte => applicative
204            in
205                operProps andb addressProps sourceLeft andb addressProps destRight andb codeProps length
206            end
207
208        and testList t = List.foldl(fn (c, r) => codeProps c andb r) applicative t
209    
210        and bindingProps(Declar{value, ...}) = codeProps value
211        |   bindingProps(RecDecs _) = applicative (* These should all be lambdas *)
212        |   bindingProps(NullBinding c) = codeProps c
213        |   bindingProps(Container{setter, ...}) = codeProps setter
214        
215        and addressProps{base, index=NONE, ...} = codeProps base
216        |   addressProps{base, index=SOME index, ...} = codeProps base andb codeProps index
217
218        (* sideEffectFree - does not raise an exception or make an assignment. *)
219        fun sideEffectFree c = (codeProps c andb noSideEffect) = noSideEffect
220        (* reorderable - does not raise an exception or access a reference. *)
221        and reorderable c = codeProps c = applicative
222    end
223
224    (* Return the inline property if it is set. *)
225    fun findInline [] = EnvSpecNone
226    |   findInline (h::t) =
227            if Universal.tagIs CodeTags.inlineCodeTag h
228            then Universal.tagProject CodeTags.inlineCodeTag h
229            else findInline t
230
231    (* Makes a constant value from an expression which is known to be
232       constant but may involve inline functions, tuples etc. *)
233    fun makeConstVal (cVal:codetree) =
234    let
235        fun makeVal (c as Constnt _) = c
236             (* should just be a tuple  *)
237            (* Get a vector, copy the entries into it and return it as a constant. *)
238        |   makeVal (Tuple {fields= [], ...}) = CodeZero (* should have been optimised already! *)
239        |   makeVal (Tuple {fields, ...}) =
240            let
241                val tupleSize = List.length fields
242                val vec : address = allocWordData(Word.fromInt tupleSize, F_mutable_words, word0)
243                val fieldCode = map makeVal fields
244      
245                fun copyToVec ([], _) = []
246                |   copyToVec (Constnt(w, prop) :: t, locn) =
247                    (
248                        assignWord (vec, locn, w);
249                        prop :: copyToVec (t, locn + 0w1)
250                    )
251                |   copyToVec _ = raise InternalError "not constant"
252                
253                val props = copyToVec(fieldCode, 0w0)
254                (* If any of the constants have properties create a tuple property
255                   for the result. *)
256                val tupleProps =
257                    if List.all null props
258                    then []
259                    else
260                    let
261                        (* We also need to construct an EnvSpecTuple property because findInline
262                           does not look at tuple properties. *)
263                        val inlineProps = map findInline props
264                        val inlineProp =
265                            if List.all (fn EnvSpecNone => true | _ => false) inlineProps
266                            then []
267                            else
268                            let
269                                fun tupleEntry n =
270                                    (EnvGenConst(loadWord(vec, Word.fromInt n), List.nth(props, n)),
271                                     List.nth(inlineProps, n))
272                            in
273                                [Universal.tagInject CodeTags.inlineCodeTag (EnvSpecTuple(tupleSize, tupleEntry))]
274                            end
275                    in
276                        Universal.tagInject CodeTags.tupleTag props :: inlineProp
277                    end
278            in
279                lock vec;
280                Constnt(toMachineWord vec, tupleProps)
281            end
282        |   makeVal _ = raise InternalError "makeVal - not constant or tuple"
283    in
284        makeVal cVal
285    end
286
287    local
288        fun allConsts []       = true
289        |   allConsts (Constnt _ :: t) = allConsts t
290        |   allConsts _ = false
291        
292        fun mkRecord isVar xp =
293        let
294            val tuple = Tuple{fields = xp, isVariant = isVar }
295        in
296            if allConsts xp
297            then (* Make it now. *) makeConstVal tuple
298            else tuple
299        end;
300        
301    in  
302        val mkTuple = mkRecord false
303        and mkDatatype = mkRecord true
304    end
305
306    (* Set the inline property.  If the property is already
307       present it is replaced.  If the property we are setting is
308       EnvSpecNone no property is set. *)
309    fun setInline p (h::t) =
310            if Universal.tagIs CodeTags.inlineCodeTag h
311            then setInline p t
312            else h :: setInline p t
313    |   setInline EnvSpecNone [] = []
314    |   setInline p [] = [Universal.tagInject CodeTags.inlineCodeTag p]
315
316    (* These are very frequently used and it might be worth making
317       special bindings for values such as 0, 1, 2, 3 etc to reduce
318       garbage. *)
319    fun checkNonZero n = if n < 0 then raise InternalError "mkLoadxx: argument negative" else n
320    val mkLoadLocal = Extract o LoadLocal o checkNonZero
321    and mkLoadArgument = Extract o LoadArgument o checkNonZero
322    and mkLoadClosure = Extract o LoadClosure o checkNonZero
323
324    (* Set the container to the fields of the record.  Try to push this
325       down as far as possible. *)
326    fun mkSetContainer(container, Cond(ifpt, thenpt, elsept), filter) =
327        Cond(ifpt, mkSetContainer(container, thenpt, filter),
328            mkSetContainer(container, elsept, filter))
329
330    |  mkSetContainer(container, Newenv(decs, exp), filter) =
331            Newenv(decs, mkSetContainer(container, exp, filter))
332
333    |  mkSetContainer(_, r as Raise _, _) =
334        r (* We may well have the situation where one branch of an "if" raises an
335             exception.  We can simply raise the exception on that branch. *)
336
337    |   mkSetContainer(container, Handle {exp, handler, exPacketAddr}, filter) =
338            Handle{exp=mkSetContainer(container, exp, filter),
339                   handler=mkSetContainer(container, handler, filter),
340                   exPacketAddr = exPacketAddr}
341
342    |   mkSetContainer(container, tuple, filter) =
343            SetContainer{container = container, tuple = tuple, filter = filter }
344
345    local
346        val except: exn = InternalError "Invalid load encountered in compiler"
347        (* Exception value to use for invalid cases.  We put this in the code
348           but it should never actually be executed.  *)
349        val raiseError = Raise (Constnt (toMachineWord except, []))
350    in
351        (* Look for an entry in a tuple. Used in both the optimiser and in mkInd. *)
352        fun findEntryInBlock (Tuple { fields, isVariant, ...}, offset, isVar) =
353            (
354                isVariant = isVar orelse raise InternalError "findEntryInBlock: tuple/datatype mismatch";
355                if offset < List.length fields
356                then List.nth(fields, offset)
357                (* This can arise if we're processing a branch of a case discriminating on
358                   a datatype which won't actually match at run-time. e.g. Tests/Succeed/Test030. *)
359                else if isVar
360                then raiseError
361                else raise InternalError "findEntryInBlock: invalid address"
362            )
363
364        |   findEntryInBlock (Constnt (b, props), offset, isVar) =
365            let
366                (* Find the tuple property if it is present and extract the field props. *)
367                val fieldProps =
368                    case List.find(Universal.tagIs CodeTags.tupleTag) props of
369                        NONE => []
370                    |   SOME p => List.nth(Universal.tagProject CodeTags.tupleTag p, offset)
371            in
372                case findInline props of
373                    EnvSpecTuple(_, env) =>
374                    (* Do the selection now.  This is especially useful if we
375                       have a global structure  *)
376                    (* At the moment at least we assume that we can get all the
377                       properties from the tuple selection. *)
378                    (
379                        case env offset of
380                            (EnvGenConst(w, p), inl) => Constnt(w, setInline inl p)
381                        (* The general value from selecting a field from a constant tuple must be a constant. *)
382                        |   _ => raise InternalError "findEntryInBlock: not constant"
383                    )
384                |   _ =>
385                      (* The ML compiler may generate loads from invalid addresses as a
386                         result of a val binding to a constant which has the wrong shape.
387                         e.g. val a :: b = nil
388                         It will always result in a Bind exception being generated 
389                         before the invalid load, but we have to be careful that the
390                         optimiser does not fall over.  *)
391                    if isShort b
392                        orelse not (Address.isWords (toAddress b))
393                        orelse Address.length (toAddress b) <= Word.fromInt offset
394                    then if isVar
395                    then raiseError
396                    else raise InternalError "findEntryInBlock: invalid address"
397                    else Constnt (loadWord (toAddress b, Word.fromInt offset), fieldProps)
398            end
399
400        |   findEntryInBlock(base, offset, isVar) =
401                Indirect {base = base, offset = offset, indKind = if isVar then IndVariant else IndTuple} (* anything else *)
402     end
403        
404    (* Exported indirect load operation i.e. load a field from a tuple.
405       We can't use  findEntryInBlock in every case since that discards
406       unused entries in a tuple and at this point we haven't checked
407       that the unused entries don't have
408       side-effects/raise exceptions e.g. #1 (1, raise Fail "bad") *)
409    local
410        fun mkIndirect isVar (addr, base as Constnt _) = findEntryInBlock(base, addr, isVar)
411        |   mkIndirect isVar (addr, base) =
412                Indirect {base = base, offset = addr, indKind = if isVar then IndVariant else IndTuple}
413    
414    in
415        val mkInd = mkIndirect false and mkVarField = mkIndirect true
416    end
417    
418    fun mkIndContainer(addr, base) = Indirect{offset=addr, base=base, indKind=IndContainer}
419
420    (* Create a tuple from a container. *)
421    fun mkTupleFromContainer(addr, size) =
422        Tuple{fields = List.tabulate(size, fn n => mkIndContainer(n, mkLoadLocal addr)), isVariant = false}
423
424    (* Get the value from the code. *)
425    fun evalue (Constnt(c, _)) = SOME c
426    |   evalue _ = NONE
427
428    (* This is really to simplify the change from mkEnv taking a codetree list to
429       taking a codeBinding list * code.  This extracts the last entry which must
430       be a NullBinding and packages the declarations with it. *)
431    fun decSequenceWithFinalExp decs =
432    let
433        fun splitLast _ [] = raise InternalError "decSequenceWithFinalExp: empty"
434        |   splitLast decs [NullBinding exp] = (List.rev decs, exp)
435        |   splitLast _ [_] = raise InternalError "decSequenceWithFinalExp: last is not a NullDec"
436        |   splitLast decs (hd::tl) = splitLast (hd:: decs) tl
437    in
438        mkEnv(splitLast [] decs)
439    end
440    
441    local
442        type node = { addr: int, lambda: lambdaForm, use: codeUse list }
443        fun nodeAddress({addr, ...}: node) = addr
444        and arcs({lambda={closure, ...}, ...}: node) =
445            List.foldl(fn (LoadLocal addr, l) => addr :: l | (_, l) => l) [] closure
446    in
447        val stronglyConnected = stronglyConnectedComponents{nodeAddress=nodeAddress, arcs=arcs}
448    end
449
450    (* In general any mutually recursive declaration can refer to any
451       other.  It's better to partition the recursive declarations into
452       strongly connected components i.e. those that actually refer
453       to each other.  *)
454    fun partitionMutualBindings(RecDecs rlist) =
455        let
456            val processed = stronglyConnected rlist
457            (* Convert the result.  Note that stronglyConnectedComponents returns the
458               dependencies in the reverse order i.e. if X depends on Y but not the other
459               way round then X will appear before Y in the list.  We need to reverse
460               it so that X goes after Y. *)
461            fun rebuild ([{lambda, addr, use}], tl) =
462                   Declar{addr=addr, use=use, value=Lambda lambda} :: tl
463            |   rebuild (multiple, tl) = RecDecs multiple :: tl
464        in
465            List.foldl rebuild [] processed
466        end
467        (* This is only intended for RecDecs but it's simpler to handle all bindings. *)
468    |   partitionMutualBindings other = [other]
469
470
471    (* Functions to help in building a closure. *)
472    datatype createClosure = Closure of (loadForm * int) list ref
473    
474    fun makeClosure() = Closure(ref [])
475
476        (* Function to build a closure.  Items are added to the closure if they are not already there. *)
477    fun addToClosure (Closure closureList) (ext: loadForm): loadForm =
478        case (List.find (fn (l, _) => l = ext) (!closureList), ! closureList) of
479            (SOME(_, n), _) => (* Already there *) LoadClosure n
480        |   (NONE, []) => (* Not there - first *) (closureList := [(ext, 0)]; LoadClosure 0)
481        |   (NONE, cl as (_, n) :: _) => (closureList := (ext, n+1) :: cl; LoadClosure(n+1))
482
483    fun extractClosure(Closure (ref closureList)) =
484        List.foldl (fn ((ext, _), l) => ext :: l) [] closureList
485
486    datatype inlineTest =
487        TooBig
488    |   NonRecursive
489    |   TailRecursive of bool vector
490    |   NonTailRecursive of bool vector
491
492    fun evaluateInlining(function, numArgs, maxInlineSize) =
493    let
494        (* This checks for the possibility of inlining a function.  It sees if it is
495           small enough according to some rough estimate of the cost and it also looks
496           for recursive uses of the function.
497           Typically if the function is small enough to inline there will be only
498           one recursive use but we consider the possibility of more than one.  If
499           the only uses are tail recursive we can replace the recursive calls by
500           a Loop with a BeginLoop outside it.  If there are non-tail recursive
501           calls we may be able to lift out arguments that are unchanged.  For
502           example for fun map f [] = [] | map f (a::b) = f a :: map f b 
503           it may be worth lifting out f and generating specific mapping
504           functions for each application. *)
505        val hasRecursiveCall = ref false (* Set to true if rec call *)
506        val allTail = ref true (* Set to false if non recursive *)
507        (* An element of this is set to false if the actual value if anything
508           other than the original argument.  At the end we are then
509           left with the arguments that are unchanged. *)
510        val argMod = Array.array(numArgs, true)
511
512        infix 6 --
513        (* Subtract y from x but return 0 rather than a negative number. *)
514        fun x -- y = if x >= y then x-y else 0
515
516        (* Check for the code size and also recursive references.  N,B. We assume in hasLoop
517           that tail recursion applies only with Cond, Newenv and Handler. *)
518        fun checkUse _ (_, 0, _) = 0 (* The function is too big to inline. *)
519 
520        |   checkUse isMain (Newenv(decs, exp), cl, isTail) =
521            let
522                fun checkBind (Declar{value, ...}, cl) = checkUse isMain(value, cl, false)
523                |   checkBind (RecDecs decs, cl) = List.foldl(fn ({lambda, ...}, n) => checkUse isMain (Lambda lambda, n, false)) cl decs
524                |   checkBind (NullBinding c, cl) = checkUse isMain (c, cl, false)
525                |   checkBind (Container{setter, ...}, cl) = checkUse isMain(setter, cl -- 1, false)
526            in
527                checkUse isMain (exp, List.foldl checkBind cl decs, isTail)
528            end
529
530        |   checkUse _      (Constnt(w, _), cl, _) = if isShort w then cl else cl -- 1
531
532            (* A recursive reference in any context other than a call prevents any inlining. *)
533        |   checkUse true   (Extract LoadRecursive, _, _) = 0
534        |   checkUse _      (Extract _, cl, _) = cl -- 1
535
536        |   checkUse isMain (Indirect{base, ...}, cl, _) = checkUse isMain (base, cl -- 1, false)
537
538        |   checkUse _      (Lambda {body, argTypes, closure, ...}, cl, _) =
539                (* For the moment, any recursive use in an inner function prevents inlining. *)
540                if List.exists (fn LoadRecursive => true | _ => false) closure
541                then 0
542                else checkUse false (body, cl -- (List.length argTypes + List.length closure), false)
543
544        |   checkUse true (Eval{function = Extract LoadRecursive, argList, ...}, cl, isTail) =
545            let
546                (* If the actual argument is anything but the original argument
547                   then the corresponding entry in the array is set to false. *)
548                fun testArg((exp, _), n) =
549                (
550                    if (case exp of Extract(LoadArgument a) => n = a | _ => false)
551                    then ()
552                    else Array.update(argMod, n, false);
553                    n+1
554                )
555            in
556                List.foldl testArg 0 argList;
557                hasRecursiveCall := true;
558                if isTail then () else allTail := false;
559                List.foldl(fn ((e, _), n) => checkUse true (e, n, false)) (cl--3) argList
560            end
561
562        |   checkUse isMain (Eval{function, argList, ...}, cl, _) =
563                checkUse isMain (function, List.foldl(fn ((e, _), n) => checkUse isMain (e, n, false)) (cl--2) argList, false)
564
565        |   checkUse _ (Nullary _, cl, _) = cl -- 1
566        |   checkUse isMain (Unary{arg1, ...}, cl, _) = checkUse isMain (arg1, cl -- 1, false)
567        |   checkUse isMain (Binary{arg1, arg2, ...}, cl, _) = checkUseList isMain ([arg1, arg2], cl -- 1)
568        |   checkUse isMain (Arbitrary{arg1, arg2, ...}, cl, _) = checkUseList isMain ([arg1, arg2], cl -- 4)
569        |   checkUse isMain (AllocateWordMemory {numWords, flags, initial}, cl, _) =
570                checkUseList isMain ([numWords, flags, initial], cl -- 1)
571
572        |   checkUse isMain (Cond(i, t, e), cl, isTail) =
573                checkUse isMain (i, checkUse isMain (t, checkUse isMain (e, cl -- 2, isTail), isTail), false)
574        |   checkUse isMain (BeginLoop { loop, arguments, ...}, cl, _) =
575                checkUse isMain (loop, List.foldl (fn (({value, ...}, _), n) => checkUse isMain (value, n, false)) cl arguments, false)
576        |   checkUse isMain (Loop args, cl, _) = List.foldl(fn ((e, _), n) => checkUse isMain (e, n, false)) cl args
577        |   checkUse isMain (Raise c, cl, _) = checkUse isMain (c, cl -- 1, false)
578        |   checkUse isMain (Handle {exp, handler, ...}, cl, isTail) =
579                checkUse isMain (exp, checkUse isMain (handler, cl, isTail), false)
580        |   checkUse isMain (Tuple{ fields, ...}, cl, _) = checkUseList isMain (fields, cl)
581
582        |   checkUse isMain (SetContainer{container, tuple = Tuple { fields, ...}, ...}, cl, _) =
583                (* This can be optimised *)
584                checkUse isMain (container, checkUseList isMain (fields, cl), false)
585        |   checkUse isMain (SetContainer{container, tuple, filter}, cl, _) =
586                checkUse isMain (container, checkUse isMain (tuple, cl -- (BoolVector.length filter), false), false)
587
588        |   checkUse isMain (TagTest{test, ...}, cl, _) = checkUse isMain (test, cl -- 1, false)
589
590        |   checkUse isMain (LoadOperation{address, ...}, cl, _) = checkUseAddress isMain (address, cl -- 1)
591
592        |   checkUse isMain (StoreOperation{address, value, ...}, cl, _) =
593                checkUse isMain (value, checkUseAddress isMain (address, cl -- 1), false)
594
595        |   checkUse isMain (BlockOperation{sourceLeft, destRight, length, ...}, cl, _) =
596                checkUse isMain (length,
597                    checkUseAddress isMain (destRight, checkUseAddress isMain (sourceLeft, cl -- 1)), false)
598        
599        and checkUseList isMain (elems, cl) =
600            List.foldl(fn (e, n) => checkUse isMain (e, n, false)) cl elems
601
602        and checkUseAddress isMain ({base, index=NONE, ...}, cl) = checkUse isMain (base, cl, false)
603        |   checkUseAddress isMain ({base, index=SOME index, ...}, cl) = checkUseList isMain ([base, index], cl)
604        
605        val costLeft = checkUse true (function, maxInlineSize, true)
606    in
607        if costLeft = 0
608        then TooBig
609        else if not (! hasRecursiveCall) 
610        then NonRecursive
611        else if ! allTail then TailRecursive(Array.vector argMod)
612        else NonTailRecursive(Array.vector argMod)
613    end
614    
615    structure Sharing =
616    struct
617        type codetree = codetree
618        and codeBinding = codeBinding
619        and loadForm = loadForm
620        and createClosure = createClosure
621        and envSpecial = envSpecial
622    end
623
624end;
625