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