Initial snarf.
[shack.git] / arch / mir / util / mir_memory.ml
blobf3aff5dac975eacf61085ac503b7f9e82f3a4a7d
1 (*
2 Insert Reserve and CopyOnWrite instructions into the MIR
3 Copyright (C) 2001 Justin David Smith, Caltech
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 WARNING WARNING WARNING WARNING WARNING WARNING
24 If you modify this file, you NEED to run the test/fc/memory
25 test cases. Obscure errors tend to pop up in this file when
26 new MIR primitives are added (especially when the primitive
27 is an alloc op) (both Jason and myself have been bitten by
28 this at least once). Only test/fc/memory really exercises
29 the Reserve statements to ensure they are correct.
30 -Justin, 2002.02.09
32 WARNING WARNING WARNING WARNING WARNING WARNING
37 WARNING WARNING WARNING WARNING WARNING WARNING
39 There is a serious logical flaw in Reserve statement scheduling.
40 Consider the following snippet of code, for example:
42 let v_index = ptr[off] in
43 /* assume ptr is now dead */ /*1*/
44 let v = PtrOfIndex v_index in
45 ...
47 Suppose ptr is dead after it is used; but v is live for awhile.
48 If a Reserve statement is placed after v is bound, then everything
49 is fine; we list v as a live pointer, but ptr is not listed because
50 it is dead. Here's the problem: Suppose a Reserve statement is
51 scheduled at /*1*/. Note that ptr is already dead; furthermore,
52 while we will decode v_index into a pointer real soon, it is NOT
53 YET A POINTER, and will NOT be included in the reserve live ptrs!
54 Therefore, we end up with code like this:
56 let v_index = ptr[off] in
57 Reserve([], #bytes, #ptrs);
58 let v = PtrOfIndex v_index in
59 ...
61 Note that in this code, v is a pointer, but it was NOT listed in
62 the most recent reserve; furthermore, ptr is dead in the reserve
63 so if the only reference to v was in ptr, it is possible that v
64 will be garbage-collected, causing a probable segfault when we
65 attempt to use v. This is bad!
67 The problem here is that between the two let's, v is a ``live''
68 pointer in the sense that the block will be referenced, but it is
69 not yet a real pointer in a register, so Reserve statements placed
70 here will miss block v as a live block. Currently, this is not a
71 problem -- this type of expression only occurs when we're reading
72 a pointer from memory, and the dereference is always followed by
73 the PtrOfIndex operation -- there is nothing that will cause a
74 reserve to be scheduled here *IF* res_cross_conditionals is true.
75 So we are okay.
77 However, if Reserve scheduling gets more sophisticated, or if the
78 res_cross_conditionals is set to false, it is possible that a
79 Reserve will be scheduled between the two lets, which will cause
80 serious problems. There are also a few other contexts where this
81 problem shows up (e.g. the SysMigrate faults), so be careful!
83 A short-term hack if this problem occurs is to prevent the Reserve
84 from crossing a certain boundary; e.g. place a Reserve of zero bytes
85 after the second let. This will prevent a Reserve from being
86 scheduled between the lets, but we pay a performance penalty for
87 doing this. A better solution would be to mark the two lets as
88 being a ``unit'' in the sense that no instructions can interleave
89 with them.
91 Damn, this is a subtle bug. We need a proper solution to it.
92 -Justin, 2002.01.15
96 (* Useful modules *)
97 open Debug
98 open Symbol
99 open Frame_type
100 open Sizeof_const
102 open Mir
103 open Mir_ds
104 open Mir_pos
105 open Mir_exn
106 open Mir_memory_sig
108 module Pos = MakePos (struct let name = "Mir_memory" end)
109 open Pos
112 module MirMemory (Frame : BackendSig) : MirMemorySig =
113 struct
114 module MirUtil = Mir_util.MirUtil (Frame)
115 module MirEnv = Mir_env.MirEnv (Frame)
116 open Frame
117 open MirUtil
120 (*** Utilities ***)
123 let exp_pos pos =
124 string_pos "Mir_memory" (exp_pos pos)
126 let raw_block_header_size = Rawint.of_int precision_native_int false block_header_size
129 (*** Propagation policies ***)
132 (* These constants control the propagation policies of Reserve and
133 CopyOnWrite statements. Note that later optimizations may attempt
134 to further refine Reserve/COW placement, so here we only need a
135 rough metric. At this time it is not yet clear which policy will
136 be best.
138 The cross_conditionals variables determine whether a statement
139 should be propagated above a conditional boundary -- if false,
140 then any pending Reserve/COW will be placed only once we're CERTAIN
141 the statement is needed; if true, then these statements may cross
142 boundaries where it is LIKELY the statement is needed. *)
143 let res_cross_conditionals = true
144 let cow_cross_conditionals = true
146 (* If cow_local_set is set, then we attempt to track LOCAL pointers
147 (pointers which we know are allocated within the current atomic
148 scope, or aliases of similar pointers). These pointers do not
149 need to be checked. *)
150 let cow_local_set = true
153 (*** Variable Environments ***)
156 (* Symbol sets *)
157 let env_empty = SymbolSet.empty
158 let env_add = SymbolSet.add
159 let env_mem = SymbolSet.mem
160 let env_remove env v =
161 try SymbolSet.remove env v with
162 Not_found -> env
163 let env_fold = SymbolSet.fold
164 let env_union = SymbolSet.union
167 (* Live sets *)
168 let live_empty = env_empty
169 let live_union = env_union
170 let live_list = SymbolSet.to_list
171 let live_remove = env_remove
174 (* Variable type environments *)
175 let venv_find = SymbolTable.find
176 let venv_lookup venv pos v =
177 try venv_find venv v with
178 Not_found -> raise (MirException (pos, UnboundVar v))
181 (* variable_is_pointer *)
182 let variable_is_pointer venv v =
184 let ac = venv_find venv v in
185 atom_class_may_be_pointer ac
186 with
187 Not_found ->
188 (* Note: some variables (e.g. global variables,
189 variables in context) are not added to the
190 venv. These variables should be ignored anywho
191 since they are usually handled separately and
192 can contain pointers that are outside of the
193 normal heap. *)
194 false
197 (*** Atom Functions, and Live Set ***)
200 (* var_in_atom
201 Returns true iff the specified variable name occurs within
202 the atom specified. *)
203 let rec var_in_atom v = function
204 AtomVar (_, v')
205 | AtomFunVar (_, v') ->
206 Symbol.eq v v'
207 | AtomInt _
208 | AtomRawInt _
209 | AtomFloat _ ->
210 false
211 | AtomUnop (_, a) ->
212 var_in_atom v a
213 | AtomBinop (_, a1, a2) ->
214 var_in_atom v a1 || var_in_atom v a2
217 (* live_add_var
218 Adds a variable to the live environment, *if* the variable
219 is a pointer variable as dictated by the variable environment. *)
220 let live_add_var venv globals pos live v =
221 let pos = string_pos "live_add_var" pos in
222 if variable_is_pointer venv v && not (SymbolTable.mem globals v) then
223 env_add live v
224 else
225 live
227 let live_add_vars venv globals pos = List.fold_left (live_add_var venv globals pos)
230 (* live_add_atom
231 Adds variables occurring in this atom to the live set. *)
232 let rec live_add_atom venv globals pos live a =
233 let pos = string_pos "live_add_atom" pos in
234 match a with
235 AtomVar (_, v)
236 | AtomFunVar (_, v) ->
237 live_add_var venv globals pos live v
238 | AtomInt _
239 | AtomRawInt _
240 | AtomFloat _ ->
241 live
242 | AtomUnop (_, a) ->
243 live_add_atom venv globals pos live a
244 | AtomBinop (_, a1, a2) ->
245 live_add_atom venv globals pos (live_add_atom venv globals pos live a1) a2
247 let live_add_atoms venv globals pos = List.fold_left (live_add_atom venv globals pos)
250 (* live_add_alloc_op
251 Adds variables occurring in this alloc operation to live set. *)
252 let live_add_alloc_op venv globals pos live op =
253 let pos = string_pos "live_add_alloc_op" pos in
254 match op with
255 AllocTuple atoms
256 | AllocArray atoms
257 | AllocUnion (_, atoms) ->
258 live_add_atoms venv globals pos live atoms
259 | AllocMArray (atoms, a) ->
260 live_add_atoms venv globals pos (live_add_atom venv globals pos live a) atoms
261 | AllocMalloc a ->
262 live_add_atom venv globals pos live a
265 (*** Copy-on-Write Management ***)
268 (* cow_empty_reserve
269 Used by COW because we don't want to deal with reserve logic yet,
270 at this point. Instead we take the lazy route and create a dummy
271 reserve info until reserve_expr can be run. *)
272 let cow_zero = AtomRawInt (Rawint.of_int Rawint.Int32 false 0)
273 let cow_empty_reserve = (new_symbol_string "uninitialized_cow_reserve", [], cow_zero, cow_zero)
276 (* cow_binding_occurrence
277 A binding occurrence of a variable has occurred. If this var
278 appears in the list of written pointers, we must insert a
279 CopyOnWrite check *now*. *)
280 let cow_binding_occurrence venv pos loc v wrptrs e =
281 let pos = string_pos "cow_binding_occurrence" pos in
282 if env_mem wrptrs v then
283 (* Binding occurrence here *)
284 let ac = venv_lookup venv pos v in
285 let wrptrs = env_remove wrptrs v in
286 let e = CopyOnWrite (cow_empty_reserve, AtomVar (ac, v), e) in
287 wrptrs, make_exp loc e
288 else
289 (* Binding occurrence does not affect us *)
290 wrptrs, e
293 (* cow_set_mem
294 Process a setmem expression. The atom is the pointer that is
295 actually being written to. Pointers in the local set are assumed
296 to have been allocated within the current atomic scope, and are
297 therefore never added to wrptrs. *)
298 let cow_set_mem loc a wrptrs local e =
299 match a with
300 AtomVar (_, v) ->
301 if cow_local_set && env_mem local v then
302 (* Do not add this local pointer. *)
303 wrptrs, e
304 else
305 env_add wrptrs v, e
306 | _ ->
307 (* Arbitrary expression; we have to do the COW here *)
308 let e = CopyOnWrite (cow_empty_reserve, a, e) in
309 wrptrs, make_exp loc e
312 (* cow_expr
313 Inserts copy-on-write operations into the expression given. The
314 function returns a list of pointers which are written to, but have
315 not yet been checked for copy-on-write, at a given level. When we
316 hit a binding occurence of a variable in this list, we must insert
317 a CopyOnWrite check for the pointer. The reserve information (which
318 includes the current set of live pointers) is not computed here; you
319 *must* run reserve_expr after running this function.
321 Note that in the current implementation, a list of local allocations
322 is maintained; this list must be cleared across an atomic boundary.
323 For any pointer in this list, we KNOW that we have allocated it within
324 the current atomic scope, therefore we don't need to check for COW.
325 These pointers are therefore not added to the wrptrs list. *)
326 let rec cow_expr venv local e =
327 let pos = string_pos "cow_expr" (exp_pos e) in
328 let loc = loc_of_exp e in
329 match dest_exp_core e with
330 LetAtom (v, ac, a, e) ->
331 let wrptrs, e = cow_expr venv local e in
332 let wrptrs, e = cow_binding_occurrence venv pos loc v wrptrs e in
333 wrptrs, make_exp loc (LetAtom (v, ac, a, e))
334 | TailCall (op, f, atoms) ->
335 env_empty, make_exp loc (TailCall (op, f, atoms))
336 | Match (a, ac, cases) ->
337 cow_match_expr venv local pos loc a ac cases
338 | IfThenElse (op, a1, a2, e1, e2) ->
339 cow_ifthenelse_expr venv local pos loc op a1 a2 e1 e2
340 | IfType (obj, names, name, v, e1, e2) ->
341 cow_iftype_expr venv local pos loc obj names name v e1 e2
342 | LetExternal (v, ac, f, atoms, e) ->
343 let wrptrs, e = cow_expr venv local e in
344 let wrptrs, e = cow_binding_occurrence venv pos loc v wrptrs e in
345 wrptrs, make_exp loc (LetExternal (v, ac, f, atoms, e))
346 | LetExternalReserve (rinfo, v, ac, f, atoms, e) ->
347 let wrptrs, e = cow_expr venv local e in
348 let wrptrs, e = cow_binding_occurrence venv pos loc v wrptrs e in
349 wrptrs, make_exp loc (LetExternalReserve (rinfo, v, ac, f, atoms, e))
350 | Reserve (reserve_info, e) ->
351 let wrptrs, e = cow_expr venv local e in
352 wrptrs, make_exp loc (Reserve (reserve_info, e))
353 | LetAlloc (v, op, e) ->
354 let local = env_add local v in
355 let wrptrs, e = cow_expr venv local e in
356 let wrptrs, e = cow_binding_occurrence venv pos loc v wrptrs e in
357 wrptrs, make_exp loc (LetAlloc (v, op, e))
358 | SetMem (ac, ptrty, a1, a2, a3, e) ->
359 let wrptrs, e = cow_expr venv local e in
360 let wrptrs, e = cow_set_mem loc a1 wrptrs local e in
361 wrptrs, make_exp loc (SetMem (ac, ptrty, a1, a2, a3, e))
362 | SetGlobal (ac, v, a, e) ->
363 (* BUG: have to decide what to do here --jyh *)
364 let wrptrs, e = cow_expr venv local e in
365 wrptrs, make_exp loc (SetGlobal (ac, v, a, e))
366 | BoundsCheck (label, ptrty, ptr, start, stop, e) ->
367 let wrptrs, e = cow_expr venv local e in
368 wrptrs, make_exp loc (BoundsCheck (label, ptrty, ptr, start, stop, e))
369 | LowerBoundsCheck (label, ptrty, ptr, start, e) ->
370 let wrptrs, e = cow_expr venv local e in
371 wrptrs, make_exp loc (LowerBoundsCheck (label, ptrty, ptr, start, e))
372 | UpperBoundsCheck (label, ptrty, ptr, stop, e) ->
373 let wrptrs, e = cow_expr venv local e in
374 wrptrs, make_exp loc (UpperBoundsCheck (label, ptrty, ptr, stop, e))
375 | PointerIndexCheck (label, ptrty, v, ac, index, e) ->
376 let wrptrs, e = cow_expr venv local e in
377 let wrptrs, e = cow_binding_occurrence venv pos loc v wrptrs e in
378 wrptrs, make_exp loc (PointerIndexCheck (label, ptrty, v, ac, index, e))
379 | FunctionIndexCheck (label, funty, v, ac, index, e) ->
380 let wrptrs, e = cow_expr venv local e in
381 let wrptrs, e = cow_binding_occurrence venv pos loc v wrptrs e in
382 wrptrs, make_exp loc (FunctionIndexCheck (label, funty, v, ac, index, e))
383 | Memcpy (ptrty, dst_base, dst_off, src_base, src_off, size, e) ->
384 let wrptrs, e = cow_expr venv local e in
385 let wrptrs, e = cow_set_mem loc dst_base wrptrs local e in
386 wrptrs, make_exp loc (Memcpy (ptrty, dst_base, dst_off, src_base, src_off, size, e))
387 | Debug (info, e) ->
388 let wrptrs, e = cow_expr venv local e in
389 wrptrs, make_exp loc (Debug (info, e))
390 | PrintDebug (v1, v2, atoms, e) ->
391 let wrptrs, e = cow_expr venv local e in
392 wrptrs, make_exp loc (PrintDebug (v1, v2, atoms, e))
393 | CommentFIR (fir, e) ->
394 let wrptrs, e = cow_expr venv local e in
395 wrptrs, make_exp loc (CommentFIR (fir, e))
396 | SysMigrate (label, dptr, doff, f, env) ->
397 env_empty, make_exp loc (SysMigrate (label, dptr, doff, f, env))
398 | Atomic (f, i, e) ->
399 env_empty, make_exp loc (Atomic (f, i, e))
400 | AtomicRollback (level, i) ->
401 env_empty, make_exp loc (AtomicRollback (level, i))
402 | AtomicCommit (level, e) ->
403 (* I'm currently paranoid, and don't allow COW to
404 interact across an atomic COMMIT boundary. *)
405 let e = cow_top_expr venv env_empty e in
406 env_empty, make_exp loc (AtomicCommit (level, e))
407 | CopyOnWrite (reserve_info, ptr, e) ->
408 let wrptrs, e = cow_expr venv local e in
409 wrptrs, make_exp loc (CopyOnWrite (reserve_info, ptr, e))
410 | SpecialCall _ ->
411 raise (MirException (pos, InternalError "SpecialCall not allowed by this point"))
414 (* cow_match_expr
415 Handles a conditional expression in COW analysis. *)
416 and cow_match_expr venv local pos loc a ac cases =
417 let pos = string_pos "cow_match_expr" pos in
418 let process_case (wrptrs, cases) (i, e) =
419 let wrptrs', e = cow_cond_top_expr venv local e in
420 let wrptrs = env_union wrptrs wrptrs' in
421 let cases = (i, e) :: cases in
422 wrptrs, cases
423 in (* End of process_case *)
424 let wrptrs, cases = List.fold_left process_case (env_empty, []) cases in
425 let cases = List.rev cases in
426 wrptrs, make_exp loc (Match (a, ac, cases))
429 (* cow_ifthenelse_expr
430 Handles a conditional expression in COW analysis. *)
431 and cow_ifthenelse_expr venv local pos loc op a1 a2 e1 e2 =
432 let pos = string_pos "cow_ifthenelse_expr" pos in
433 let wrptrs1, e1 = cow_cond_top_expr venv local e1 in
434 let wrptrs2, e2 = cow_cond_top_expr venv local e2 in
435 let wrptrs = env_union wrptrs1 wrptrs2 in
436 wrptrs, make_exp loc (IfThenElse (op, a1, a2, e1, e2))
439 (* cow_iftype_expr
440 Handles a conditional type expression in COW analysis. *)
441 and cow_iftype_expr venv local pos loc obj names name v e1 e2 =
442 let pos = string_pos "cow_iftype_expr" pos in
443 let wrptrs1, e1 = cow_cond_top_expr venv local e1 in
444 let wrptrs2, e2 = cow_cond_top_expr venv local e2 in
445 (* Note: v is a binding occurrence for e1 ONLY *)
446 let wrptrs1, e1 = cow_binding_occurrence venv pos loc v wrptrs1 e1 in
447 let wrptrs = env_union wrptrs1 wrptrs2 in
448 wrptrs, make_exp loc (IfType (obj, names, name, v, e1, e2))
451 (* cow_top_expr
452 Similar to above function, except we assume e is the top-most
453 expression in its scope (unconditional block, function, etc).
454 All pending COW's must be prepended here. *)
455 and cow_top_expr venv local e =
456 let pos = string_pos "cow_top_expr" (exp_pos e) in
457 let wrptrs, e = cow_expr venv local e in
458 let loc = loc_of_exp e in
459 let prepend_cow_check e ptr =
460 let ac = venv_lookup venv pos ptr in
461 make_exp loc (CopyOnWrite (cow_empty_reserve, AtomVar (ac, ptr), e))
463 env_fold prepend_cow_check e wrptrs
466 (* cow_cond_top_expr
467 Similar to cow_top_expr, except this is used at the top of a
468 conditional block. This code checks to see if we are allowed
469 to cross conditional boundaries. *)
470 and cow_cond_top_expr venv local e =
471 if cow_cross_conditionals then
472 cow_expr venv local e
473 else
474 env_empty, cow_top_expr venv local e
477 (* cow_top_expr
478 Interface to copy-on-write system. *)
479 let cow_top_expr venv e = cow_top_expr venv env_empty e
482 (*** Reserve Expressions ***)
485 (* res_arith
486 Defines simple arithmetic on reserve expressions. *)
487 type res_arith =
488 Add of res_arith list
489 | Mul of res_arith list
490 | Max of res_arith list
491 | Value of atom
494 (* Define the default state, indicating no reservation needed *)
495 let raw_zero = Rawint.of_int precision_pointer false 0
496 let raw_one = Rawint.of_int precision_pointer false 1
497 let raw_header = Rawint.of_int precision_native_int false block_header_size
498 let reserve_null = Value (AtomRawInt raw_zero), Value (AtomRawInt raw_zero)
500 (* Some convenient sizes for the headers and whatnot *)
501 let size_zero = Value (AtomRawInt raw_zero)
502 let size_one = Value (AtomRawInt raw_one)
503 let size_header = Value (AtomRawInt raw_header)
506 (* res_add
507 res_max
508 Adds a new addition or maximization operation to reserve. *)
509 let res_add res1 res2 =
510 match res1, res2 with
511 Add ls1, Add ls2 ->
512 Add (ls1 @ ls2)
513 | Add ls1, _ ->
514 Add (res2 :: ls1)
515 | _, Add ls2 ->
516 Add (res1 :: ls2)
517 | _ ->
518 Add [res1; res2]
520 let res_mul res1 res2 =
521 match res1, res2 with
522 Mul ls1, Mul ls2 ->
523 Mul (ls1 @ ls2)
524 | Mul ls1, _ ->
525 Mul (res2 :: ls1)
526 | _, Mul ls2 ->
527 Mul (res1 :: ls2)
528 | _ ->
529 Mul [res1; res2]
531 let res_max res1 res2 =
532 match res1, res2 with
533 Max ls1, Max ls2 ->
534 Max (ls1 @ ls2)
535 | Max ls1, _ ->
536 Max (res2 :: ls1)
537 | _, Max ls2 ->
538 Max (res1 :: ls2)
539 | _ ->
540 Max [res1; res2]
543 (* res_simplify
544 Simplifies a reserve expression by folding constant operations
545 where possible, and simplifying the remaining expressions. *)
546 let rec res_simplify res =
547 (* Fold the constant values together. arithop is the arithmetic
548 operator to apply to the constants, and ident is the identity
549 value for the operator. construct is the res_arith constructor
550 corresponding to this operation. *)
551 let fold_constants arithop ident ls construct =
552 let ls = List.map res_simplify ls in
553 let result, ls = List.fold_left (fun (result, ls) res ->
554 match res with
555 Value (AtomRawInt i) ->
556 arithop result i, ls
557 | _ ->
558 result, res :: ls) (ident, []) ls
560 if ls = [] then
561 Value (AtomRawInt result)
562 else if Rawint.compare result ident = 0 then
563 construct ls
564 else
565 construct (Value (AtomRawInt result) :: ls)
567 match res with
568 Add ls ->
569 fold_constants Rawint.add raw_zero ls (fun ls -> Add ls)
570 | Mul ls ->
571 fold_constants Rawint.mul raw_one ls (fun ls -> Mul ls)
572 | Max ls ->
573 fold_constants Rawint.max raw_zero ls (fun ls -> Max ls)
574 | Value a ->
575 Value a
578 (* atom_of_res
579 Transforms a simple res expression into an atom expression. *)
580 let rec atom_of_res res =
581 let res = res_simplify res in
582 let rec atom_of_list op = function
583 res :: [] ->
584 atom_of_res res
585 | res :: ls ->
586 AtomBinop (op, atom_of_res res, atom_of_list op ls)
587 | [] ->
588 AtomRawInt raw_zero
590 match res with
591 Add ls ->
592 atom_of_list (PlusOp ac_native_unsigned) ls
593 | Mul ls ->
594 atom_of_list (MulOp ac_native_unsigned) ls
595 | Max ls ->
596 atom_of_list (MaxOp ac_native_unsigned) ls
597 | Value a ->
601 (* var_in_res
602 Returns true if the named variable occurs in res. This is
603 used in Reserve statement scheduling to determine if we can
604 propagate a Reserve upward past a variable binding occurrence. *)
605 let rec var_in_res v = function
606 Add ls
607 | Mul ls
608 | Max ls ->
609 List.exists (var_in_res v) ls
610 | Value a ->
611 var_in_atom v a
614 (* sizeof_alloc_op
615 Determines the size of the alloc operation, for use by reserve.
616 A pair is returned, of the total bytes required and the number
617 of pointer entries required. *)
618 let sizeof_alloc_op = function
619 AllocTuple atoms
620 | AllocArray atoms
621 | AllocUnion (_, atoms) ->
622 (* ML blocks contain a pointer/int for each of the args.
623 These blocks _are_ saved in the pointer table, so
624 we reserve space for them. *)
625 let size_tuple = sizeof_atom_list atoms in
626 let size_tuple = Int32.add size_tuple (Int32.of_int block_header_size) in
627 let size_tuple = Rawint.of_int32 precision_pointer false size_tuple in
628 Value (AtomRawInt size_tuple), size_one
629 | AllocMalloc (AtomRawInt i) ->
630 (* The atom specifies a constant number of bytes to alloc.
631 This is an indexed pointer, so we need a ptr space. *)
632 let size = Rawint.add i raw_block_header_size in
633 Value (AtomRawInt size), size_one
634 | AllocMalloc a ->
635 (* The atom specifies a variable number of bytes to alloc.
636 This is an indexed pointer, so we need a ptr space. *)
637 Add [Value a; size_header], size_one
638 | AllocMArray (dimens, _) ->
639 (* The dimens specifies a multi-dimensional array.
640 Add together the total size. *)
641 let rec collect dimens =
642 match dimens with
643 a :: dimens ->
644 let bytes, ptrs = collect dimens in
645 let bytes = Add [Mul [Value a; bytes]; size_header] in
646 let ptrs = Add [Mul [Value a; ptrs]; size_one] in
647 bytes, ptrs
648 | [] ->
649 reserve_null
651 collect dimens
654 (* build_reserve_info
655 Builds the reserve info from resmem, resptr information
656 and the live pointers list. You can also provide a base
657 name for the unique label that is generated here. *)
658 let build_reserve_info name live (resmem, resptr) =
659 let resmem = atom_of_res resmem in
660 let resptr = atom_of_res resptr in
661 let label = new_symbol_string name in
662 let live = live_list live in
663 (label, live, resmem, resptr)
666 (* build_reserve_expr
667 Constructs a reserve expression (if warranted) *)
668 let build_reserve_expr live (resmem, resptr) e =
669 let resmem = res_simplify resmem in
670 let resptr = res_simplify resptr in
671 let loc = loc_of_exp e in
672 match resmem, resptr with
673 Value (AtomRawInt i), Value (AtomRawInt j)
674 when Rawint.compare i raw_zero = 0 && Rawint.compare j raw_zero = 0 ->
675 (* No reserve required here *)
677 | _ ->
678 let rinfo = build_reserve_info "reserve" live (resmem, resptr) in
679 make_exp loc (Reserve (rinfo, e))
682 (* reserve_binding_occurrence
683 A binding occurrence of v occurs here. Check to see if v is used
684 anywhere in the reserve expression; if so, then we must emit a
685 new reserve statement here. *)
686 let reserve_binding_occurrence live (resmem, resptr) v e =
687 if var_in_res v resmem || var_in_res v resptr then
688 (* Variable occurs in reserve expression; must emit Reserve *)
689 let e = build_reserve_expr live (resmem, resptr) e in
690 let live = live_remove live v in
691 live, reserve_null, e
692 else
693 (* Variable does not occur; we can pass the reserve up *)
694 let live = live_remove live v in
695 live, (resmem, resptr), e
698 (*** Reserve (Expressions) ***)
701 (* reserve_expr
702 Inserts memory operations into the expression given. Several sets
703 of information are maintained here, to be explicit on what they are:
704 * venv (passed down) contains the set of variables with their types
705 * live (passed up) contains information on what variables are live.
706 * resmem (passed up) - memory reservation requirements from below.
707 This is a list of atoms containing size expressions. When any
708 variable mentioned in the list is bound (going up), we must insert
709 a new reserve expression.
710 * resptr (passed up) - a number indicating the number of pointers
711 required by expressions below. *)
712 let rec reserve_expr venv globals e =
713 let pos = string_pos "reserve_expr" (exp_pos e) in
714 let loc = loc_of_exp e in
715 match dest_exp_core e with
716 LetAtom (v, ac, a, e) ->
717 let live, res, e = reserve_expr venv globals e in
718 let live, res, e = reserve_binding_occurrence live res v e in
719 let live = live_add_atom venv globals pos live a in
720 live, res, make_exp loc (LetAtom (v, ac, a, e))
721 | TailCall (op, f, atoms) ->
722 let live = live_add_atoms venv globals pos live_empty atoms in
723 live, reserve_null, make_exp loc (TailCall (op, f, atoms))
724 | Match (a, ac, cases) ->
725 reserve_match_expr venv globals pos loc a ac cases
726 | IfThenElse (op, a1, a2, e1, e2) ->
727 reserve_ifthenelse_expr venv globals pos loc op a1 a2 e1 e2
728 | IfType (obj, names, name, v, e1, e2) ->
729 reserve_iftype_expr venv globals pos loc obj names name v e1 e2
730 | LetExternal (v, ac, f, atoms, e) ->
731 let live, res, e = reserve_expr venv globals e in
732 let live, res, e = reserve_binding_occurrence live res v e in
733 let live = live_add_atoms venv globals pos live atoms in
734 live, res, make_exp loc (LetExternal (v, ac, f, atoms, e))
735 | LetExternalReserve (rinfo, v, ac, f, atoms, e) ->
736 reserve_external_reserve_expr venv globals pos loc rinfo v ac f atoms e
737 | Reserve (rinfo, e) ->
738 let _, _, mem, ptr = rinfo in
739 let live, e = reserve_top_expr venv globals e in
740 let live = live_add_atoms venv globals pos live [mem; ptr] in
741 live, reserve_null, make_exp loc (Reserve (rinfo, e))
742 | LetAlloc (v, op, e) ->
743 reserve_alloc_expr venv globals pos loc v op e
744 | SetMem (ac, ptrty, a1, a2, a3, e) ->
745 let live, res, e = reserve_expr venv globals e in
746 let live = live_add_atoms venv globals pos live [a1; a2; a3] in
747 live, res, make_exp loc (SetMem (ac, ptrty, a1, a2, a3, e))
748 | SetGlobal (ac, v, a, e) ->
749 let live, res, e = reserve_expr venv globals e in
750 let live = live_add_atom venv globals pos live a in
751 live, res, make_exp loc (SetGlobal (ac, v, a, e))
752 | BoundsCheck (label, ptrty, ptr, start, stop, e) ->
753 let live, res, e = reserve_expr venv globals e in
754 let live = live_add_atoms venv globals pos live [ptr; start; stop] in
755 live, res, make_exp loc (BoundsCheck (label, ptrty, ptr, start, stop, e))
756 | LowerBoundsCheck (label, ptrty, ptr, start, e) ->
757 let live, res, e = reserve_expr venv globals e in
758 let live = live_add_atoms venv globals pos live [ptr; start] in
759 live, res, make_exp loc (LowerBoundsCheck (label, ptrty, ptr, start, e))
760 | UpperBoundsCheck (label, ptrty, ptr, stop, e) ->
761 let live, res, e = reserve_expr venv globals e in
762 let live = live_add_atoms venv globals pos live [ptr; stop] in
763 live, res, make_exp loc (UpperBoundsCheck (label, ptrty, ptr, stop, e))
764 | PointerIndexCheck (label, ptrty, v, ac, index, e) ->
765 let live, res, e = reserve_expr venv globals e in
766 let live, res, e = reserve_binding_occurrence live res v e in
767 let live = live_add_atom venv globals pos live index in
768 live, res, make_exp loc (PointerIndexCheck (label, ptrty, v, ac, index, e))
769 | FunctionIndexCheck (label, funty, v, ac, index, e) ->
770 let live, res, e = reserve_expr venv globals e in
771 let live, res, e = reserve_binding_occurrence live res v e in
772 let live = live_add_atom venv globals pos live index in
773 live, res, make_exp loc (FunctionIndexCheck (label, funty, v, ac, index, e))
774 | Memcpy (ptrty, dst_base, dst_off, src_base, src_off, size, e) ->
775 let live, res, e = reserve_expr venv globals e in
776 let live = live_add_atoms venv globals pos live [dst_base; dst_off; src_base; src_off; size] in
777 live, res, make_exp loc (Memcpy (ptrty, dst_base, dst_off, src_base, src_off, size, e))
778 | Debug (info, e) ->
779 let live, res, e = reserve_expr venv globals e in
780 live, res, make_exp loc (Debug (info, e))
781 | PrintDebug (v1, v2, atoms, e) ->
782 let live, res, e = reserve_expr venv globals e in
783 let live = live_add_atoms venv globals pos live atoms in
784 let live = live_add_vars venv globals pos live [v1; v2] in
785 live, res, make_exp loc (PrintDebug (v1, v2, atoms, e))
786 | CommentFIR (fir, e) ->
787 let live, res, e = reserve_expr venv globals e in
788 live, res, make_exp loc (CommentFIR (fir, e))
789 | SysMigrate (label, dptr, doff, f, env) ->
790 let live = live_add_atoms venv globals pos live_empty [dptr; doff; env] in
791 live, reserve_null, make_exp loc (SysMigrate (label, dptr, doff, f, env))
792 | Atomic (f, i, e) ->
793 let live = live_add_atoms venv globals pos live_empty [i; e] in
794 live, reserve_null, make_exp loc (Atomic (f, i, e))
795 | AtomicRollback (level, i) ->
796 let live = live_add_atoms venv globals pos live_empty [level; i] in
797 live, reserve_null, make_exp loc (AtomicRollback (level, i))
798 | AtomicCommit (level, e) ->
799 let live, res, e = reserve_expr venv globals e in
800 let live = live_add_atom venv globals pos live level in
801 live, res, make_exp loc (AtomicCommit (level, e))
802 | CopyOnWrite (rinfo, ptr, e) ->
803 reserve_copyonwrite_expr venv globals pos loc rinfo ptr e
804 | SpecialCall _ ->
805 raise (MirException (pos, InternalError "SpecialCall not allowed by this point"))
808 (* reserve_match_expr
809 Match statements (like other conditional expressions) require
810 a maximization operation. *)
811 and reserve_match_expr venv globals pos loc a ac cases =
812 let pos = string_pos "reserve_match_expr" pos in
814 (* process_case
815 Processes an individual case. The live set will be the
816 union of live sets of each case; the reserves are the
817 maximizations of each individual case. *)
818 let process_case (live, (resmem, resptr), cases) (i, e) =
819 let live', res', e = reserve_cond_top_expr venv globals e in
820 let resmem', resptr' = res' in
821 let live = live_union live live' in
822 let resmem = res_max resmem resmem' in
823 let resptr = res_max resptr resptr' in
824 let cases = (i, e) :: cases in
825 live, (resmem, resptr), cases
826 in (* End of process_case *)
828 (* Processes all cases to pass up. *)
829 let fold_src = live_empty, reserve_null, [] in
830 let live, res, cases = List.fold_left process_case fold_src cases in
831 let cases = List.rev cases in
832 let live = live_add_atom venv globals pos live a in
833 live, res, make_exp loc (Match (a, ac, cases))
836 (* reserve_ifthenelse_expr
837 Similar to above code, except we only have two cases here. *)
838 and reserve_ifthenelse_expr venv globals pos loc op a1 a2 e1 e2 =
839 let pos = string_pos "reserve_ifthenelse_expr" pos in
840 let live1, res1, e1 = reserve_cond_top_expr venv globals e1 in
841 let live2, res2, e2 = reserve_cond_top_expr venv globals e2 in
842 let resmem1, resptr1 = res1 in
843 let resmem2, resptr2 = res2 in
844 let live = live_union live1 live2 in
845 let live = live_add_atoms venv globals pos live [a1; a2] in
846 let resmem = res_max resmem1 resmem2 in
847 let resptr = res_max resptr1 resptr2 in
848 live, (resmem, resptr), make_exp loc (IfThenElse (op, a1, a2, e1, e2))
851 (* reserve_iftype_expr
852 Similar to above code for ifthenelse, except for two concerns:
853 1. There is a binding occurrence of v here, along e1.
854 2. There is an implicit allocation of 2 words (plus header)
855 along the e1 branch; note this means we must do the
856 reserve UNCONDITIONALLY to ensure memory has been set
857 aside if we attempt the allocation here. *)
858 and reserve_iftype_expr venv globals pos loc obj names name v e1 e2 =
859 let pos = string_pos "reserve_iftype_expr" pos in
861 (* Get the live sets and current reserves for each branch *)
862 let live1, res1, e1 = reserve_cond_top_expr venv globals e1 in
863 let live2, res2, e2 = reserve_cond_top_expr venv globals e2 in
865 (* Note the binding occurence of v along e1 branch *)
866 let live1, res1, e1 = reserve_binding_occurrence live1 res1 v e1 in
868 (* Unify the live information and add the new atoms *)
869 let live = live_union live1 live2 in
870 let live = live_add_atoms venv globals pos live [obj; names; name] in
872 (* Unify the reserve information for each branch *)
873 let resmem1, resptr1 = res1 in
874 let resmem2, resptr2 = res2 in
875 let resmem = res_max resmem1 resmem2 in
876 let resptr = res_max resptr1 resptr2 in
878 (* NOW we can add the implicit allocation of 8 bytes plus header *)
879 let tuple_size = Rawint.of_int precision_native_int false (2 * sizeof_int32) in
880 let resmem' = Value (AtomRawInt (Rawint.add tuple_size raw_header)) in
881 let resptr' = Value (AtomRawInt raw_one) in
882 let resmem = res_add resmem resmem' in
883 let resptr = res_add resptr resptr' in
884 live, (resmem, resptr), make_exp loc (IfType (obj, names, name, v, e1, e2))
887 (* reserve_alloc_expr
888 Reservation has to be enhanced to take into account this
889 allocation operation. *)
890 and reserve_alloc_expr venv globals pos loc v op e =
891 let pos = string_pos "reserve_alloc_expr" pos in
893 (* Compute live and reserves for rest of program *)
894 let live, res, e = reserve_expr venv globals e in
895 let live, res, e = reserve_binding_occurrence live res v e in
897 (* Update live set to include atoms in alloc operator *)
898 let live = live_add_alloc_op venv globals pos live op in
900 (* Update reserve parametres to include this allocation *)
901 let resmem, resptr = res in
902 let resmem', resptr' = sizeof_alloc_op op in
903 let resmem = res_add resmem resmem' in
904 let resptr = res_add resptr resptr' in
905 live, (resmem, resptr), make_exp loc (LetAlloc (v, op, e))
908 (* reserve_copyonwrite_expr
909 Copy-on-write can allocate new memory in the heap; the COW
910 code will call GC if it needs to alloc more memory, but to
911 do this we have to know how much memory is going to be
912 allocated further down (we need the reserve parametres).
913 The GC isn't guaranteed to be called, so any reserve params
914 from here have to be passed up, as well. *)
915 and reserve_copyonwrite_expr venv globals pos loc rinfo ptr e =
916 let pos = string_pos "reserve_copyonwrite_expr" pos in
917 let live, res, e = reserve_expr venv globals e in
919 (* Generate the reserve_info record *)
920 let rinfo = build_reserve_info "copy_on_write" live res in
922 (* Update the live set to contain this atom *)
923 let live = live_add_atom venv globals pos live ptr in
924 live, res, make_exp loc (CopyOnWrite (rinfo, ptr, e))
927 (* reserve_external_reserve_expr
928 This external call may require a garbage collection.
930 BUG JYH: this is incorrect. We need to force a reserve
931 after the external call. The reserve_expr should be
932 reserve_top_expr, but I'm confused about the reserve
933 binding occurrences...
935 JDS TEMP: I think I have fixed this.
937 To explain more: LetExternalReserve is a reserve barrier.
938 Since we don't know if the extcall will allocate, we
939 must assume that it leaves no free space on the heap.
940 That means we need a new reserve before the body e. *)
941 and reserve_external_reserve_expr venv globals pos loc rinfo v ac f atoms e =
942 let pos = string_pos "reserve_external_reserve_expr" pos in
943 let live, e = reserve_top_expr venv globals e in
945 (* reserve_top_expr forces a Reserve statement before e, so we have
946 no allocation request to pass up at this point. Therefore we will
947 revert to reserve_null. *)
948 let res = reserve_null in
949 let live, res, e = reserve_binding_occurrence live res v e in
951 (* Generate the reserve_info record *)
952 let rinfo = build_reserve_info "external_reserve" live res in
954 (* Update the live set to include the arguments *)
955 let live = live_add_atoms venv globals pos live atoms in
956 live, res, make_exp loc (LetExternalReserve (rinfo, v, ac, f, atoms, e))
959 (* reserve_top_expr
960 Similar to above function, but forces a reserve expression
961 at the beginning of the expression list. Use for the first
962 expression of a function, or when another barrier forces
963 us to place a reserve here. The live set is still returned. *)
964 and reserve_top_expr venv globals e =
965 let live, res, e = reserve_expr venv globals e in
966 let e = build_reserve_expr live res e in
967 live, e
970 (* reserve_cond_top_expr
971 Conditional expressions use this at the top of each case.
972 This checks whether reserves are permitted to cross a cond
973 boundary. *)
974 and reserve_cond_top_expr venv globals e =
975 if res_cross_conditionals then
976 reserve_expr venv globals e
977 else
978 let live, e = reserve_top_expr venv globals e in
979 live, reserve_null, e
982 (*** Interface Code ***)
985 (* mir_memory
986 Adds copy-on-write and reserve instructions to a MIR expression *)
987 let mir_memory venv globals e =
988 let e = cow_top_expr venv e in
989 let _, e = reserve_top_expr venv globals e in
993 (* memory *)
994 let memory prog =
995 let funs = prog.prog_funs in
996 let globals = prog.prog_globals in
997 let venv = MirEnv.mir_venv_prog prog in
998 let memory_fun _ (line, args, e) =
999 let e = mir_memory venv globals e in
1000 (line, args, e)
1002 let funs = SymbolTable.mapi memory_fun funs in
1003 let prog = { prog with prog_funs = funs } in
1004 if debug Mir_state.debug_print_mir then
1005 Mir_print.debug_prog "After memory" prog;
1006 prog
1008 let memory = Fir_state.profile "Mir_memory.memory" memory
1011 end (* struct *)