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.
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
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
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.
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
91 Damn, this is a subtle bug. We need a proper solution to it.
108 module Pos
= MakePos
(struct let name = "Mir_memory" end)
112 module MirMemory
(Frame
: BackendSig
) : MirMemorySig
=
114 module MirUtil
= Mir_util.MirUtil
(Frame
)
115 module MirEnv
= Mir_env.MirEnv
(Frame
)
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
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 ***)
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
163 let env_fold = SymbolSet.fold
164 let env_union = SymbolSet.union
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
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
197 (*** Atom Functions, and Live Set ***)
201 Returns true iff the specified variable name occurs within
202 the atom specified. *)
203 let rec var_in_atom v = function
205 | AtomFunVar
(_
, v'
) ->
213 | AtomBinop
(_
, a1
, a2
) ->
214 var_in_atom v a1
|| var_in_atom v a2
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
227 let live_add_vars venv globals
pos = List.fold_left
(live_add_var venv globals
pos)
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
236 | AtomFunVar
(_
, v) ->
237 live_add_var venv globals
pos live
v
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)
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
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
262 live_add_atom venv globals
pos live a
265 (*** Copy-on-Write Management ***)
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
289 (* Binding occurrence does not affect us *)
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 =
301 if cow_local_set && env_mem local
v then
302 (* Do not add this local pointer. *)
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
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))
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))
411 raise
(MirException
(pos, InternalError
"SpecialCall not allowed by this point"))
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
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
))
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
))
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
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
474 env_empty, cow_top_expr venv
local e
478 Interface to copy-on-write system. *)
479 let cow_top_expr venv
e = cow_top_expr venv
env_empty e
482 (*** Reserve Expressions ***)
486 Defines simple arithmetic on reserve expressions. *)
488 Add
of res_arith list
489 | Mul
of res_arith list
490 | Max
of res_arith list
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)
508 Adds a new addition or maximization operation to reserve. *)
509 let res_add res1 res2
=
510 match res1
, res2
with
520 let res_mul res1 res2
=
521 match res1
, res2
with
531 let res_max res1 res2
=
532 match res1
, res2
with
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
->
555 Value
(AtomRawInt i
) ->
558 result, res
:: ls) (ident
, []) ls
561 Value
(AtomRawInt
result)
562 else if Rawint.compare
result ident
= 0 then
565 construct
(Value
(AtomRawInt
result) :: ls)
569 fold_constants Rawint.add
raw_zero ls (fun ls -> Add
ls)
571 fold_constants Rawint.mul
raw_one ls (fun ls -> Mul
ls)
573 fold_constants Rawint.max
raw_zero ls (fun ls -> Max
ls)
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
586 AtomBinop
(op
, atom_of_res res, atom_of_list op
ls)
592 atom_of_list (PlusOp ac_native_unsigned
) ls
594 atom_of_list (MulOp ac_native_unsigned
) ls
596 atom_of_list (MaxOp ac_native_unsigned
) ls
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
609 List.exists
(var_in_res v) ls
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
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
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
=
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
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 *)
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
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) ***)
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))
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
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
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))
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
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
974 and reserve_cond_top_expr venv globals
e =
975 if res_cross_conditionals then
976 reserve_expr venv globals
e
978 let live, e = reserve_top_expr venv globals
e in
979 live, reserve_null, e
982 (*** Interface Code ***)
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
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
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;
1008 let memory = Fir_state.profile
"Mir_memory.memory" memory