My working unpac repository
at opam/upstream/seq 116 lines 4.5 kB view raw
1(**************************************************************************) 2(* *) 3(* OCaml *) 4(* *) 5(* Xavier Leroy, projet Cambium, INRIA Paris *) 6(* *) 7(* Copyright 2023 Institut National de Recherche en Informatique et *) 8(* en Automatique. *) 9(* *) 10(* All rights reserved. This file is distributed under the terms of *) 11(* the GNU Lesser General Public License version 2.1, with the *) 12(* special exception on linking described in the file LICENSE. *) 13(* *) 14(**************************************************************************) 15 16(* Compute the parameters needed for allocating and managing stack frames 17 in the Emit phase. *) 18 19open Mach 20 21type analysis_result = { 22 contains_nontail_calls: bool; 23 frame_required: bool; 24 extra_stack_used: int; 25} 26 27class virtual stackframe_generic = object (self) 28 29(* Size of an exception handler block on the stack. 30 To be provided for each target. *) 31 32method virtual trap_handler_size : int 33 34(* Determine if an instruction performs a call that requires 35 the return address to be saved in the stack frame, and a stack frame to 36 be allocated. 37 38 At a minimum, these instructions include all non-tail calls, 39 both to OCaml functions or to C functions. 40 41 For exception-raising constructs, we get better stack backtraces 42 by treating them as non-tail calls, even if they are implemented as 43 tail calls. 44 45 This method can be overridden in [Stackframe] to implement target-specific 46 behaviors. *) 47 48method is_call = function 49 | Iop (Icall_ind | Icall_imm _ | Iextcall _) -> true 50 | Iop (Ialloc _) | Iop (Ipoll _) -> true 51 (* caml_alloc*, caml_garbage_collection (incl. polls) *) 52 | Iop (Iintop (Icheckbound) | Iintop_imm(Icheckbound, _)) -> !Clflags.debug 53 (* caml_ml_array_bound_error *) 54 | Iraise Lambda.Raise_notrace -> false 55 | Iraise (Lambda.Raise_regular | Lambda.Raise_reraise) -> true 56 (* caml_stash_backtrace; having a frame gives better stack backtrace *) 57 | Itrywith _ -> true 58 | _ -> false 59 60(* Determine if a function requires a stack frame to be allocated. 61 This is the case if it contains calls, but also if it allocates 62 variables on the stack. 63 64 This method can be overridden in [Stackframe] to implement target-specific 65 behaviors. *) 66 67method frame_required f contains_calls = 68 contains_calls || 69 f.fun_num_stack_slots.(0) > 0 || f.fun_num_stack_slots.(1) > 0 70 71(* Analyze the body of a Mach function to determine 72 - whether it contains non-tail-calls to OCaml functions 73 - whether it requires allocating a stack frame and saving the return address 74 - how much extra stack space is needed for exception handlers 75 and for passing parameters to C function on stack. 76*) 77 78method analyze f = 79 let contains_nontail_calls = ref false 80 and contains_calls = ref false 81 and extra_space = ref 0 in 82 let rec analyze sp i = 83 if sp > !extra_space then extra_space := sp; 84 contains_calls := !contains_calls || self#is_call i.desc; 85 match i.desc with 86 | Iend -> () 87 | Iop (Istackoffset delta) -> 88 analyze (sp + delta) i.next 89 | Iop (Itailcall_ind | Itailcall_imm _) -> () 90 | Iop (Icall_ind | Icall_imm _) -> 91 contains_nontail_calls := true; 92 analyze sp i.next 93 | Iop _ -> 94 analyze sp i.next 95 | Ireturn -> () 96 | Iifthenelse(_, ifso, ifnot) -> 97 analyze sp ifso; analyze sp ifnot; analyze sp i.next 98 | Iswitch(_, branches) -> 99 Array.iter (analyze sp) branches; analyze sp i.next 100 | Icatch(_, handlers, body) -> 101 List.iter (fun (_, handler) -> analyze sp handler) handlers; 102 analyze sp body; 103 analyze sp i.next 104 | Iexit _ -> () 105 | Itrywith(body, handler) -> 106 analyze (sp + self#trap_handler_size) body; 107 analyze sp handler; 108 analyze sp i.next 109 | Iraise _ -> () 110 in 111 analyze 0 f.fun_body; 112 { contains_nontail_calls = !contains_nontail_calls; 113 frame_required = self#frame_required f !contains_calls; 114 extra_stack_used = !extra_space } 115 116end