ocaml/bytecomp/lambda.ml

673 lines
20 KiB
OCaml

(***********************************************************************)
(* *)
(* OCaml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* en Automatique. All rights reserved. This file is distributed *)
(* under the terms of the Q Public License version 1.0. *)
(* *)
(***********************************************************************)
open Misc
open Path
open Asttypes
type compile_time_constant =
| Big_endian
| Word_size
| Int_size
| Max_wosize
| Ostype_unix
| Ostype_win32
| Ostype_cygwin
type loc_kind =
| Loc_FILE
| Loc_LINE
| Loc_MODULE
| Loc_LOC
| Loc_POS
type immediate_or_pointer =
| Immediate
| Pointer
type initialization_or_assignment =
| Initialization
| Assignment
type primitive =
Pidentity
| Pignore
| Prevapply of Location.t
| Pdirapply of Location.t
| Ploc of loc_kind
(* Globals *)
| Pgetglobal of Ident.t
| Psetglobal of Ident.t
(* Operations on heap blocks *)
| Pmakeblock of int * mutable_flag
| Pfield of int
| Psetfield of int * immediate_or_pointer * initialization_or_assignment
| Pfloatfield of int
| Psetfloatfield of int * initialization_or_assignment
| Pduprecord of Types.record_representation * int
(* Force lazy values *)
| Plazyforce
(* External call *)
| Pccall of Primitive.description
(* Exceptions *)
| Praise of raise_kind
(* Boolean operations *)
| Psequand | Psequor | Pnot
(* Integer operations *)
| Pnegint | Paddint | Psubint | Pmulint | Pdivint | Pmodint
| Pandint | Porint | Pxorint
| Plslint | Plsrint | Pasrint
| Pintcomp of comparison
| Poffsetint of int
| Poffsetref of int
(* Float operations *)
| Pintoffloat | Pfloatofint
| Pnegfloat | Pabsfloat
| Paddfloat | Psubfloat | Pmulfloat | Pdivfloat
| Pfloatcomp of comparison
(* String operations *)
| Pstringlength | Pstringrefu | Pstringsetu | Pstringrefs | Pstringsets
(* Array operations *)
| Pmakearray of array_kind * mutable_flag
| Pduparray of array_kind * mutable_flag
| Parraylength of array_kind
| Parrayrefu of array_kind
| Parraysetu of array_kind
| Parrayrefs of array_kind
| Parraysets of array_kind
(* Test if the argument is a block or an immediate integer *)
| Pisint
(* Test if the (integer) argument is outside an interval *)
| Pisout
(* Bitvect operations *)
| Pbittest
(* Operations on boxed integers (Nativeint.t, Int32.t, Int64.t) *)
| Pbintofint of boxed_integer
| Pintofbint of boxed_integer
| Pcvtbint of boxed_integer (*source*) * boxed_integer (*destination*)
| Pnegbint of boxed_integer
| Paddbint of boxed_integer
| Psubbint of boxed_integer
| Pmulbint of boxed_integer
| Pdivbint of boxed_integer
| Pmodbint of boxed_integer
| Pandbint of boxed_integer
| Porbint of boxed_integer
| Pxorbint of boxed_integer
| Plslbint of boxed_integer
| Plsrbint of boxed_integer
| Pasrbint of boxed_integer
| Pbintcomp of boxed_integer * comparison
(* Operations on big arrays: (unsafe, #dimensions, kind, layout) *)
| Pbigarrayref of bool * int * bigarray_kind * bigarray_layout
| Pbigarrayset of bool * int * bigarray_kind * bigarray_layout
(* size of the nth dimension of a big array *)
| Pbigarraydim of int
(* load/set 16,32,64 bits from a string: (unsafe)*)
| Pstring_load_16 of bool
| Pstring_load_32 of bool
| Pstring_load_64 of bool
| Pstring_set_16 of bool
| Pstring_set_32 of bool
| Pstring_set_64 of bool
(* load/set 16,32,64 bits from a
(char, int8_unsigned_elt, c_layout) Bigarray.Array1.t : (unsafe) *)
| Pbigstring_load_16 of bool
| Pbigstring_load_32 of bool
| Pbigstring_load_64 of bool
| Pbigstring_set_16 of bool
| Pbigstring_set_32 of bool
| Pbigstring_set_64 of bool
(* Compile time constants *)
| Pctconst of compile_time_constant
(* byte swap *)
| Pbswap16
| Pbbswap of boxed_integer
(* Integer to external pointer *)
| Pint_as_pointer
(* Inhibition of optimisation *)
| Popaque
and comparison =
Ceq | Cneq | Clt | Cgt | Cle | Cge
and array_kind =
Pgenarray | Paddrarray | Pintarray | Pfloatarray
and boxed_integer = Primitive.boxed_integer =
Pnativeint | Pint32 | Pint64
and bigarray_kind =
Pbigarray_unknown
| Pbigarray_float32 | Pbigarray_float64
| Pbigarray_sint8 | Pbigarray_uint8
| Pbigarray_sint16 | Pbigarray_uint16
| Pbigarray_int32 | Pbigarray_int64
| Pbigarray_caml_int | Pbigarray_native_int
| Pbigarray_complex32 | Pbigarray_complex64
and bigarray_layout =
Pbigarray_unknown_layout
| Pbigarray_c_layout
| Pbigarray_fortran_layout
and raise_kind =
| Raise_regular
| Raise_reraise
| Raise_notrace
type structured_constant =
Const_base of constant
| Const_pointer of int
| Const_block of int * structured_constant list
| Const_float_array of string list
| Const_immstring of string
type inline_attribute =
| Always_inline (* [@inline] or [@inline always] *)
| Never_inline (* [@inline never] *)
| Unroll of int (* [@unroll x] *)
| Default_inline (* no [@inline] attribute *)
type specialise_attribute =
| Always_specialise (* [@specialise] or [@specialise always] *)
| Never_specialise (* [@specialise never] *)
| Default_specialise (* no [@specialise] attribute *)
type function_kind = Curried | Tupled
type let_kind = Strict | Alias | StrictOpt | Variable
type meth_kind = Self | Public | Cached
type shared_code = (int * int) list
type function_attribute = {
inline : inline_attribute;
specialise : specialise_attribute;
is_a_functor: bool;
}
type lambda =
Lvar of Ident.t
| Lconst of structured_constant
| Lapply of lambda_apply
| Lfunction of lfunction
| Llet of let_kind * Ident.t * lambda * lambda
| Lletrec of (Ident.t * lambda) list * lambda
| Lprim of primitive * lambda list
| Lswitch of lambda * lambda_switch
| Lstringswitch of lambda * (string * lambda) list * lambda option
| Lstaticraise of int * lambda list
| Lstaticcatch of lambda * (int * Ident.t list) * lambda
| Ltrywith of lambda * Ident.t * lambda
| Lifthenelse of lambda * lambda * lambda
| Lsequence of lambda * lambda
| Lwhile of lambda * lambda
| Lfor of Ident.t * lambda * lambda * direction_flag * lambda
| Lassign of Ident.t * lambda
| Lsend of meth_kind * lambda * lambda * lambda list * Location.t
| Levent of lambda * lambda_event
| Lifused of Ident.t * lambda
and lfunction =
{ kind: function_kind;
params: Ident.t list;
body: lambda;
attr: function_attribute; } (* specified with [@inline] attribute *)
and lambda_apply =
{ ap_func : lambda;
ap_args : lambda list;
ap_loc : Location.t;
ap_should_be_tailcall : bool;
ap_inlined : inline_attribute;
ap_specialised : specialise_attribute; }
and lambda_switch =
{ sw_numconsts: int;
sw_consts: (int * lambda) list;
sw_numblocks: int;
sw_blocks: (int * lambda) list;
sw_failaction : lambda option}
and lambda_event =
{ lev_loc: Location.t;
lev_kind: lambda_event_kind;
lev_repr: int ref option;
lev_env: Env.summary }
and lambda_event_kind =
Lev_before
| Lev_after of Types.type_expr
| Lev_function
| Lev_pseudo
type program =
{ code : lambda;
main_module_block_size : int; }
let const_unit = Const_pointer 0
let lambda_unit = Lconst const_unit
let default_function_attribute = {
inline = Default_inline;
specialise = Default_specialise;
is_a_functor = false;
}
(* Build sharing keys *)
(*
Those keys are later compared with Pervasives.compare.
For that reason, they should not include cycles.
*)
exception Not_simple
let max_raw = 32
let make_key e =
let count = ref 0 (* Used for controling size *)
and make_key = Ident.make_key_generator () in
(* make_key is used for normalizing let-bound variables *)
let rec tr_rec env e =
incr count ;
if !count > max_raw then raise Not_simple ; (* Too big ! *)
match e with
| Lvar id ->
begin
try Ident.find_same id env
with Not_found -> e
end
| Lconst (Const_base (Const_string _)) ->
(* Mutable constants are not shared *)
raise Not_simple
| Lconst _ -> e
| Lapply ap ->
Lapply {ap with ap_func = tr_rec env ap.ap_func;
ap_args = tr_recs env ap.ap_args;
ap_loc = Location.none}
| Llet (Alias,x,ex,e) -> (* Ignore aliases -> substitute *)
let ex = tr_rec env ex in
tr_rec (Ident.add x ex env) e
| Llet (str,x,ex,e) ->
(* Because of side effects, keep other lets with normalized names *)
let ex = tr_rec env ex in
let y = make_key x in
Llet (str,y,ex,tr_rec (Ident.add x (Lvar y) env) e)
| Lprim (p,es) ->
Lprim (p,tr_recs env es)
| Lswitch (e,sw) ->
Lswitch (tr_rec env e,tr_sw env sw)
| Lstringswitch (e,sw,d) ->
Lstringswitch
(tr_rec env e,
List.map (fun (s,e) -> s,tr_rec env e) sw,
tr_opt env d)
| Lstaticraise (i,es) ->
Lstaticraise (i,tr_recs env es)
| Lstaticcatch (e1,xs,e2) ->
Lstaticcatch (tr_rec env e1,xs,tr_rec env e2)
| Ltrywith (e1,x,e2) ->
Ltrywith (tr_rec env e1,x,tr_rec env e2)
| Lifthenelse (cond,ifso,ifnot) ->
Lifthenelse (tr_rec env cond,tr_rec env ifso,tr_rec env ifnot)
| Lsequence (e1,e2) ->
Lsequence (tr_rec env e1,tr_rec env e2)
| Lassign (x,e) ->
Lassign (x,tr_rec env e)
| Lsend (m,e1,e2,es,loc) ->
Lsend (m,tr_rec env e1,tr_rec env e2,tr_recs env es,Location.none)
| Lifused (id,e) -> Lifused (id,tr_rec env e)
| Lletrec _|Lfunction _
| Lfor _ | Lwhile _
(* Beware: (PR#6412) the event argument to Levent
may include cyclic structure of type Type.typexpr *)
| Levent _ ->
raise Not_simple
and tr_recs env es = List.map (tr_rec env) es
and tr_sw env sw =
{ sw with
sw_consts = List.map (fun (i,e) -> i,tr_rec env e) sw.sw_consts ;
sw_blocks = List.map (fun (i,e) -> i,tr_rec env e) sw.sw_blocks ;
sw_failaction = tr_opt env sw.sw_failaction ; }
and tr_opt env = function
| None -> None
| Some e -> Some (tr_rec env e) in
try
Some (tr_rec Ident.empty e)
with Not_simple -> None
(***************)
let name_lambda strict arg fn =
match arg with
Lvar id -> fn id
| _ -> let id = Ident.create "let" in Llet(strict, id, arg, fn id)
let name_lambda_list args fn =
let rec name_list names = function
[] -> fn (List.rev names)
| (Lvar id as arg) :: rem ->
name_list (arg :: names) rem
| arg :: rem ->
let id = Ident.create "let" in
Llet(Strict, id, arg, name_list (Lvar id :: names) rem) in
name_list [] args
let iter_opt f = function
| None -> ()
| Some e -> f e
let iter f = function
Lvar _
| Lconst _ -> ()
| Lapply{ap_func = fn; ap_args = args} ->
f fn; List.iter f args
| Lfunction{kind; params; body} ->
f body
| Llet(str, id, arg, body) ->
f arg; f body
| Lletrec(decl, body) ->
f body;
List.iter (fun (id, exp) -> f exp) decl
| Lprim(p, args) ->
List.iter f args
| Lswitch(arg, sw) ->
f arg;
List.iter (fun (key, case) -> f case) sw.sw_consts;
List.iter (fun (key, case) -> f case) sw.sw_blocks;
iter_opt f sw.sw_failaction
| Lstringswitch (arg,cases,default) ->
f arg ;
List.iter (fun (_,act) -> f act) cases ;
iter_opt f default
| Lstaticraise (_,args) ->
List.iter f args
| Lstaticcatch(e1, (_,vars), e2) ->
f e1; f e2
| Ltrywith(e1, exn, e2) ->
f e1; f e2
| Lifthenelse(e1, e2, e3) ->
f e1; f e2; f e3
| Lsequence(e1, e2) ->
f e1; f e2
| Lwhile(e1, e2) ->
f e1; f e2
| Lfor(v, e1, e2, dir, e3) ->
f e1; f e2; f e3
| Lassign(id, e) ->
f e
| Lsend (k, met, obj, args, _) ->
List.iter f (met::obj::args)
| Levent (lam, evt) ->
f lam
| Lifused (v, e) ->
f e
module IdentSet =
Set.Make(struct
type t = Ident.t
let compare = compare
end)
let free_ids get l =
let fv = ref IdentSet.empty in
let rec free l =
iter free l;
fv := List.fold_right IdentSet.add (get l) !fv;
match l with
Lfunction{kind; params; body} ->
List.iter (fun param -> fv := IdentSet.remove param !fv) params
| Llet(str, id, arg, body) ->
fv := IdentSet.remove id !fv
| Lletrec(decl, body) ->
List.iter (fun (id, exp) -> fv := IdentSet.remove id !fv) decl
| Lstaticcatch(e1, (_,vars), e2) ->
List.iter (fun id -> fv := IdentSet.remove id !fv) vars
| Ltrywith(e1, exn, e2) ->
fv := IdentSet.remove exn !fv
| Lfor(v, e1, e2, dir, e3) ->
fv := IdentSet.remove v !fv
| Lassign(id, e) ->
fv := IdentSet.add id !fv
| Lvar _ | Lconst _ | Lapply _
| Lprim _ | Lswitch _ | Lstringswitch _ | Lstaticraise _
| Lifthenelse _ | Lsequence _ | Lwhile _
| Lsend _ | Levent _ | Lifused _ -> ()
in free l; !fv
let free_variables l =
free_ids (function Lvar id -> [id] | _ -> []) l
let free_methods l =
free_ids (function Lsend(Self, Lvar meth, obj, _, _) -> [meth] | _ -> []) l
(* Check if an action has a "when" guard *)
let raise_count = ref 0
let next_raise_count () =
incr raise_count ;
!raise_count
let negative_raise_count = ref 0
let next_negative_raise_count () =
decr negative_raise_count ;
!negative_raise_count
(* Anticipated staticraise, for guards *)
let staticfail = Lstaticraise (0,[])
let rec is_guarded = function
| Lifthenelse( cond, body, Lstaticraise (0,[])) -> true
| Llet(str, id, lam, body) -> is_guarded body
| Levent(lam, ev) -> is_guarded lam
| _ -> false
let rec patch_guarded patch = function
| Lifthenelse (cond, body, Lstaticraise (0,[])) ->
Lifthenelse (cond, body, patch)
| Llet(str, id, lam, body) ->
Llet (str, id, lam, patch_guarded patch body)
| Levent(lam, ev) ->
Levent (patch_guarded patch lam, ev)
| _ -> fatal_error "Lambda.patch_guarded"
(* Translate an access path *)
let rec transl_normal_path = function
Pident id ->
if Ident.global id then Lprim(Pgetglobal id, []) else Lvar id
| Pdot(p, s, pos) ->
Lprim(Pfield pos, [transl_normal_path p])
| Papply(p1, p2) ->
fatal_error "Lambda.transl_path"
(* Translation of value identifiers *)
let transl_path ?(loc=Location.none) env path =
transl_normal_path (Env.normalize_path (Some loc) env path)
(* Compile a sequence of expressions *)
let rec make_sequence fn = function
[] -> lambda_unit
| [x] -> fn x
| x::rem ->
let lam = fn x in Lsequence(lam, make_sequence fn rem)
(* Apply a substitution to a lambda-term.
Assumes that the bound variables of the lambda-term do not
belong to the domain of the substitution.
Assumes that the image of the substitution is out of reach
of the bound variables of the lambda-term (no capture). *)
let subst_lambda s lam =
let rec subst = function
Lvar id as l ->
begin try Ident.find_same id s with Not_found -> l end
| Lconst sc as l -> l
| Lapply ap ->
Lapply{ap with ap_func = subst ap.ap_func;
ap_args = List.map subst ap.ap_args}
| Lfunction{kind; params; body; attr} ->
Lfunction{kind; params; body = subst body; attr}
| Llet(str, id, arg, body) -> Llet(str, id, subst arg, subst body)
| Lletrec(decl, body) -> Lletrec(List.map subst_decl decl, subst body)
| Lprim(p, args) -> Lprim(p, List.map subst args)
| Lswitch(arg, sw) ->
Lswitch(subst arg,
{sw with sw_consts = List.map subst_case sw.sw_consts;
sw_blocks = List.map subst_case sw.sw_blocks;
sw_failaction = subst_opt sw.sw_failaction; })
| Lstringswitch (arg,cases,default) ->
Lstringswitch
(subst arg,List.map subst_strcase cases,subst_opt default)
| Lstaticraise (i,args) -> Lstaticraise (i, List.map subst args)
| Lstaticcatch(e1, io, e2) -> Lstaticcatch(subst e1, io, subst e2)
| Ltrywith(e1, exn, e2) -> Ltrywith(subst e1, exn, subst e2)
| Lifthenelse(e1, e2, e3) -> Lifthenelse(subst e1, subst e2, subst e3)
| Lsequence(e1, e2) -> Lsequence(subst e1, subst e2)
| Lwhile(e1, e2) -> Lwhile(subst e1, subst e2)
| Lfor(v, e1, e2, dir, e3) -> Lfor(v, subst e1, subst e2, dir, subst e3)
| Lassign(id, e) -> Lassign(id, subst e)
| Lsend (k, met, obj, args, loc) ->
Lsend (k, subst met, subst obj, List.map subst args, loc)
| Levent (lam, evt) -> Levent (subst lam, evt)
| Lifused (v, e) -> Lifused (v, subst e)
and subst_decl (id, exp) = (id, subst exp)
and subst_case (key, case) = (key, subst case)
and subst_strcase (key, case) = (key, subst case)
and subst_opt = function
| None -> None
| Some e -> Some (subst e)
in subst lam
let rec map f lam =
let lam =
match lam with
| Lvar v -> lam
| Lconst cst -> lam
| Lapply { ap_func; ap_args; ap_loc; ap_should_be_tailcall;
ap_inlined; ap_specialised } ->
Lapply {
ap_func = map f ap_func;
ap_args = List.map (map f) ap_args;
ap_loc;
ap_should_be_tailcall;
ap_inlined;
ap_specialised;
}
| Lfunction { kind; params; body; attr; } ->
Lfunction { kind; params; body = map f body; attr; }
| Llet (str, v, e1, e2) ->
Llet (str, v, map f e1, map f e2)
| Lletrec (idel, e2) ->
Lletrec (List.map (fun (v, e) -> (v, map f e)) idel, map f e2)
| Lprim (p, el) ->
Lprim (p, List.map (map f) el)
| Lswitch (e, sw) ->
Lswitch (map f e,
{ sw_numconsts = sw.sw_numconsts;
sw_consts = List.map (fun (n, e) -> (n, map f e)) sw.sw_consts;
sw_numblocks = sw.sw_numblocks;
sw_blocks = List.map (fun (n, e) -> (n, map f e)) sw.sw_blocks;
sw_failaction = Misc.may_map (map f) sw.sw_failaction;
})
| Lstringswitch (e, sw, default) ->
Lstringswitch (
map f e,
List.map (fun (s, e) -> (s, map f e)) sw,
Misc.may_map (map f) default)
| Lstaticraise (i, args) ->
Lstaticraise (i, List.map (map f) args)
| Lstaticcatch (body, id, handler) ->
Lstaticcatch (map f body, id, map f handler)
| Ltrywith (e1, v, e2) ->
Ltrywith (map f e1, v, map f e2)
| Lifthenelse (e1, e2, e3) ->
Lifthenelse (map f e1, map f e2, map f e3)
| Lsequence (e1, e2) ->
Lsequence (map f e1, map f e2)
| Lwhile (e1, e2) ->
Lwhile (map f e1, map f e2)
| Lfor (v, e1, e2, dir, e3) ->
Lfor (v, map f e1, map f e2, dir, map f e3)
| Lassign (v, e) ->
Lassign (v, map f e)
| Lsend (k, m, o, el, loc) ->
Lsend (k, map f m, map f o, List.map (map f) el, loc)
| Levent (l, ev) ->
Levent (map f l, ev)
| Lifused (v, e) ->
Lifused (v, map f e)
in
f lam
(* To let-bind expressions to variables *)
let bind str var exp body =
match exp with
Lvar var' when Ident.same var var' -> body
| _ -> Llet(str, var, exp, body)
and commute_comparison = function
| Ceq -> Ceq| Cneq -> Cneq
| Clt -> Cgt | Cle -> Cge
| Cgt -> Clt | Cge -> Cle
and negate_comparison = function
| Ceq -> Cneq| Cneq -> Ceq
| Clt -> Cge | Cle -> Cgt
| Cgt -> Cle | Cge -> Clt
let raise_kind = function
| Raise_regular -> "raise"
| Raise_reraise -> "reraise"
| Raise_notrace -> "raise_notrace"
let lam_of_loc kind loc =
let loc_start = loc.Location.loc_start in
let (file, lnum, cnum) = Location.get_pos_info loc_start in
let enum = loc.Location.loc_end.Lexing.pos_cnum -
loc_start.Lexing.pos_cnum + cnum in
match kind with
| Loc_POS ->
Lconst (Const_block (0, [
Const_immstring file;
Const_base (Const_int lnum);
Const_base (Const_int cnum);
Const_base (Const_int enum);
]))
| Loc_FILE -> Lconst (Const_immstring file)
| Loc_MODULE ->
let filename = Filename.basename file in
let name = Env.get_unit_name () in
let module_name = if name = "" then "//"^filename^"//" else name in
Lconst (Const_immstring module_name)
| Loc_LOC ->
let loc = Printf.sprintf "File %S, line %d, characters %d-%d"
file lnum cnum enum in
Lconst (Const_immstring loc)
| Loc_LINE -> Lconst (Const_base (Const_int lnum))
let reset () =
raise_count := 0