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