1(* 2 Copyright (c) 2013, 2015 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(* 19If a function has an empty closure it can be code-generated immediately. That may allow 20other functions or tuples to be generated immediately as well. As well as avoiding 21run-time allocations this also allows the code-generator to use calls/jumps to constant 22addresses. 23*) 24functor CODETREE_CODEGEN_CONSTANT_FUNCTIONS ( 25 structure BASECODETREE: BaseCodeTreeSig 26 structure CODETREE_FUNCTIONS: CodetreeFunctionsSig 27 structure BACKEND: CodegenTreeSig 28 structure DEBUG: DEBUGSIG 29 structure PRETTY : PRETTYSIG 30 31 sharing 32 BASECODETREE.Sharing 33 = CODETREE_FUNCTIONS.Sharing 34 = BACKEND.Sharing 35 = PRETTY.Sharing 36): 37sig 38 type codetree 39 type machineWord = Address.machineWord 40 val codeGenerate: codetree * int * Universal.universal list -> (unit -> machineWord) * Universal.universal list 41 structure Foreign: FOREIGNCALLSIG 42 structure Sharing: sig type codetree = codetree end 43end = 44struct 45 open BASECODETREE 46 open CODETREE_FUNCTIONS 47 open Address 48 49 exception InternalError = Misc.InternalError 50 51 datatype lookupVal = EnvGenLoad of loadForm | EnvGenConst of machineWord * Universal.universal list 52 53 type cgContext = 54 { 55 lookupAddr: loadForm -> lookupVal, 56 enterConstant: int * (machineWord * Universal.universal list) -> unit, 57 debugArgs: Universal.universal list 58 } 59 60 (* Code-generate a function or set of mutually recursive functions that contain no free variables 61 and run the code to return the address. This allows us to further fold the address as 62 a constant if, for example, it is used in a tuple. *) 63 fun codeGenerateToConstant(lambda, debugSwitches, closure) = 64 let 65 val () = 66 if DEBUG.getParameter DEBUG.codetreeAfterOptTag debugSwitches 67 then PRETTY.getCompilerOutput debugSwitches (BASECODETREE.pretty(Lambda lambda)) else () 68 69 val props = BACKEND.codeGenerate(lambda, debugSwitches, closure) 70 val () = Address.lock closure 71 in 72 props 73 end 74 75 (* If we are code-generating a function immediately we make a one-word 76 mutable cell that will subsequently contain the address of the code. 77 After it is locked this becomes the closure of the function. By creating 78 it here we can turn recursive references into constant references before 79 we actually compile the function. *) 80 fun makeConstantClosure () = 81 let 82 open Address 83 in 84 allocWordData(0w1, Word8.orb(F_mutable, F_words), toMachineWord 0w1) 85 end 86 87 fun cgFuns ({ lookupAddr, ...}: cgContext) (Extract ext) = 88 ( 89 (* Look up the entry. It may now be a constant. If it isn't it may still 90 have changed if it is a closure entry and other closure entries have 91 been replaced by constants. *) 92 case lookupAddr ext of 93 EnvGenLoad load => SOME(Extract load) 94 | EnvGenConst w => SOME(Constnt w) 95 ) 96 97 | cgFuns (context as {debugArgs, ...}) (Lambda lambda) = 98 let 99 val copied as { closure=resultClosure, ...} = cgLambda(context, lambda, EnvGenLoad LoadRecursive) 100 in 101 case resultClosure of 102 [] => 103 let 104 (* Create a "closure" for the function. *) 105 val closure = makeConstantClosure() 106 (* Replace any recursive references by references to the closure. There 107 may be inner functions that only make recursive calls to this. By turning 108 the recursive references into constants we may be able to compile 109 them immediately as well. *) 110 val repLambda = cgLambda(context, lambda, EnvGenConst(toMachineWord closure, [])) 111 val props = codeGenerateToConstant(repLambda, debugArgs, closure) 112 in 113 SOME(Constnt(toMachineWord closure, props)) 114 end 115 | _ => SOME(Lambda copied) 116 end 117 118 | cgFuns (context as { enterConstant, debugArgs, ...}) (Newenv(envBindings, envExp)) = 119 let 120 (* First expand out any mutually-recursive bindings. This ensures that if 121 we have any RecDecs left *) 122 val expandedBindings = 123 List.foldr (fn (d, l) => partitionMutableBindings d @ l) [] envBindings 124 125 fun processBindings(Declar{value, addr, use} :: tail) = 126 ( 127 (* If this is a constant put it in the table otherwise create a binding. *) 128 case mapCodetree (cgFuns context) value of 129 Constnt w => (enterConstant(addr, w); processBindings tail) 130 | code => Declar{value=code, addr=addr, use=use} :: processBindings tail 131 ) 132 133 | processBindings(NullBinding c :: tail) = 134 NullBinding(mapCodetree (cgFuns context) c) :: processBindings tail 135 136 | processBindings(RecDecs[{addr, lambda, use}] :: tail) = 137 (* Single recursive bindings - treat as simple binding *) 138 processBindings(Declar{addr=addr, value=Lambda lambda, use = use} :: tail) 139 140 | processBindings(RecDecs recdecs :: tail) = 141 let 142 (* We know that this forms a strongly connected component so it is only 143 possible to code-generate the group if no function has a free-variable 144 outside the group. Each function must have at least one free 145 variable which is part of the group. *) 146 fun processEntry {addr, lambda, use} = 147 {addr=addr, lambda=cgLambda(context, lambda, EnvGenLoad LoadRecursive), use=use} 148 val processedGroup = map processEntry recdecs 149 150 (* If every free variable is another member of the group we can 151 code-generate the group. *) 152 local 153 fun closureItemInGroup(LoadLocal n) = 154 List.exists(fn{addr, ...} => n = addr) processedGroup 155 | closureItemInGroup _ = false 156 157 fun onlyInGroup{lambda={closure, ...}, ...} = List.all closureItemInGroup closure 158 in 159 val canCodeGen = List.all onlyInGroup processedGroup 160 end 161 in 162 if canCodeGen 163 then 164 let 165 open Address 166 (* Create "closures" for each entry. Add these as constants to the table. *) 167 fun createAndEnter {addr, ...} = 168 let val c = makeConstantClosure() in enterConstant(addr, (Address.toMachineWord c, [])); c end 169 val closures = List.map createAndEnter processedGroup 170 (* Code-generate each of the lambdas and store the code in the closure. *) 171 fun processLambda({lambda, addr, ...}, closure) = 172 let 173 val closureAsMachineWord = Address.toMachineWord closure 174 val repLambda = 175 cgLambda(context, lambda, EnvGenConst(closureAsMachineWord, [])) 176 val props = codeGenerateToConstant(repLambda, debugArgs, closure) 177 in 178 (* Include any properties we may have added *) 179 enterConstant(addr, (closureAsMachineWord, props)) 180 end 181 val () = ListPair.appEq processLambda (processedGroup, closures) 182 in 183 processBindings tail (* We've done these *) 184 end 185 186 else RecDecs processedGroup :: processBindings tail 187 end 188 189 | processBindings(Container{addr, use, size, setter} :: tail) = 190 Container{addr=addr, use=use, size=size, 191 setter = mapCodetree (cgFuns context) setter} :: processBindings tail 192 193 | processBindings [] = [] 194 195 val bindings = processBindings expandedBindings 196 val body = mapCodetree (cgFuns context) envExp 197 in 198 case bindings of 199 [] => SOME body 200 | _ => SOME(Newenv(bindings, body)) 201 end 202 203 | cgFuns context (Tuple{ fields, isVariant }) = 204 (* Create any constant tuples that have arisen because they contain 205 constant functions. *) 206 SOME((if isVariant then mkDatatype else mkTuple)(map (mapCodetree (cgFuns context)) fields)) 207 208 | cgFuns _ _ = NONE 209 210 and cgLambda({lookupAddr, debugArgs, ...}, 211 { body, isInline, name, closure, argTypes, resultType, localCount, recUse}, 212 loadRecursive) = 213 let 214 val cArray = Array.array(localCount, NONE) 215 val newClosure = makeClosure() 216 217 fun lookupLocal(load as LoadLocal n) = 218 ( 219 case Array.sub(cArray, n) of 220 NONE => EnvGenLoad load 221 | SOME w => EnvGenConst w 222 ) 223 | lookupLocal(LoadClosure n) = 224 ( 225 case lookupAddr(List.nth (closure, n)) of 226 EnvGenLoad load => EnvGenLoad(addToClosure newClosure load) 227 | c as EnvGenConst _ => c 228 ) 229 | lookupLocal LoadRecursive = loadRecursive 230 | lookupLocal load = EnvGenLoad load (* Argument *) 231 232 val context = 233 { 234 lookupAddr = lookupLocal, 235 enterConstant = fn (n, w) => Array.update(cArray, n, SOME w), 236 debugArgs = debugArgs 237 } 238 239 (* Process the body to deal with any sub-functions and also to bind 240 in any constants from free variables. *) 241 val newBody = mapCodetree (cgFuns context) body 242 (* Build the resulting lambda. *) 243 val resultClosure = extractClosure newClosure 244 in 245 { 246 body = newBody, isInline = isInline, name = name, closure = resultClosure, 247 argTypes = argTypes, resultType = resultType, localCount = localCount, 248 recUse = recUse 249 } 250 end 251 252 fun codeGenerate(original, nLocals, debugArgs) = 253 let 254 val cArray = Array.array(nLocals, NONE) 255 fun lookupAddr(load as LoadLocal n) = 256 ( 257 case Array.sub(cArray, n) of 258 NONE => EnvGenLoad load 259 | SOME w => EnvGenConst w 260 ) 261 | lookupAddr _ = raise InternalError "lookupConstant: top-level reached" 262 263 val context = 264 { 265 lookupAddr = lookupAddr, 266 enterConstant = fn (n, w) => Array.update(cArray, n, SOME w), 267 debugArgs = debugArgs 268 } 269 270 val resultCode = mapCodetree (cgFuns context) original 271 (* Turn this into a lambda to code-generate. *) 272 val lambda:lambdaForm = 273 { 274 body = resultCode, 275 isInline = NonInline, 276 name = "<top level>", 277 closure = [], 278 argTypes = [(GeneralType, [])], 279 resultType = GeneralType, 280 localCount = nLocals, 281 recUse = [] 282 } 283 val closure = makeConstantClosure() 284 285 val props = BACKEND.codeGenerate(lambda, debugArgs, closure) 286 287 (* The code may consist of tuples (i.e. compiled ML structures) containing 288 a mixture of Loads, where the values are yet to be compiled, and 289 Constants, where the code has now been compiled. We need to extract 290 any properties from the constants and return the whole lot as 291 tuple properties. *) 292 fun extractProps(Constnt(_, p)) = p 293 | extractProps(Extract ext) = 294 ( 295 case lookupAddr ext of 296 EnvGenLoad _ => [] 297 | EnvGenConst(_, p) => p 298 ) 299 | extractProps(Tuple{fields, ...}) = 300 let 301 val fieldProps = map extractProps fields 302 in 303 if List.all null fieldProps 304 then [] 305 else [Universal.tagInject CodeTags.tupleTag fieldProps] 306 end 307 | extractProps(Newenv(_, exp)) = extractProps exp 308 | extractProps _ = [] 309 310 val newProps = extractProps original 311 (* Cast this as a function. It is a function with a single argument. *) 312 val resultFunction: unit -> machineWord = RunCall.unsafeCast closure 313 314 in 315 (resultFunction, CodeTags.mergeTupleProps(newProps, props)) 316 end 317 318 structure Foreign = BACKEND.Foreign 319 320 structure Sharing = struct type codetree = codetree end 321end; 322