csharpfftfsharpintegrationinterpolationlinear-algebramathdifferentiationmatrixnumericsrandomregressionstatisticsmathnet
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1357 lines
59 KiB
1357 lines
59 KiB
// (c) Microsoft Corporation. All rights reserved
|
|
#light
|
|
|
|
module internal Microsoft.FSharp.Compiler.Tlr
|
|
|
|
open Internal.Utilities
|
|
open Internal.Utilities.Pervasives
|
|
open Microsoft.FSharp.Compiler.AbstractIL
|
|
open Microsoft.FSharp.Compiler.AbstractIL.Internal
|
|
open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library
|
|
|
|
open Microsoft.FSharp.Compiler
|
|
open Microsoft.FSharp.Compiler.AbstractIL.Diagnostics
|
|
open Microsoft.FSharp.Compiler.Ast
|
|
open Microsoft.FSharp.Compiler.ErrorLogger
|
|
open Microsoft.FSharp.Compiler.Tast
|
|
open Microsoft.FSharp.Compiler.Tastops
|
|
open Microsoft.FSharp.Compiler.Env
|
|
open Microsoft.FSharp.Compiler.Layout
|
|
open Microsoft.FSharp.Compiler.Detuple.GlobalUsageAnalysis
|
|
open Microsoft.FSharp.Compiler.Lib
|
|
|
|
let verboseTLR = false
|
|
|
|
/// Turns on explicit lifting of TLR constants to toplevel
|
|
/// e.g. use true if want the TLR constants to be initialised once.
|
|
///
|
|
/// NOTE: liftTLR is incomplete and disabled
|
|
/// Approach is to filter Top* let binds whilst "under lambdas",
|
|
/// and wrap them around that expr ASAP (when get to TopLevel position).
|
|
/// However, for arity assigned public vals (not TLR at moment),
|
|
/// assumptions that their RHS are lambdas get broken since the
|
|
/// lambda can be wrapped with bindings...
|
|
let liftTLR = ref false
|
|
|
|
//-------------------------------------------------------------------------
|
|
// library helpers
|
|
//-------------------------------------------------------------------------
|
|
|
|
let internalError str = dprintf "Error: %s\n" str;raise (Failure str)
|
|
|
|
module Zmap =
|
|
let force k mp (str,soK) = try Zmap.find k mp with e -> dprintf "forceM': %s %s\n" str (soK k); raise e
|
|
|
|
let forceM' k mp (str,soK) = try Zmap.find k mp with e -> dprintf "forceM: %s %s\n" str (soK k); raise e
|
|
|
|
//-------------------------------------------------------------------------
|
|
// misc
|
|
//-------------------------------------------------------------------------
|
|
|
|
/// tree, used to store dec sequence
|
|
type 'a tree =
|
|
| TreeNode of 'a tree list
|
|
| LeafNode of 'a
|
|
|
|
let fringeTR tr =
|
|
let rec collect tr acc =
|
|
match tr with
|
|
| TreeNode subts -> List.foldBack collect subts acc
|
|
| LeafNode x -> x::acc
|
|
|
|
collect tr []
|
|
|
|
let emptyTR = TreeNode[]
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// misc
|
|
//-------------------------------------------------------------------------
|
|
|
|
/// Collapse reclinks on app and combine apps if possible
|
|
/// recursive ids are inside reclinks and maybe be type instanced with a TExpr_app
|
|
|
|
// CLEANUP NOTE: mk_appl ensures applications are kept in a collapsed
|
|
// and combined form, so this function should not be needed
|
|
let DestApp (f,fty,tys,args,m) =
|
|
match strip_expr f with
|
|
| TExpr_app (f2,fty2,tys2,[] ,m2) -> (f2,fty2,tys2 @ tys,args,m)
|
|
| TExpr_app (f2,fty2,tys2,argtys2,m2) -> (f,fty,tys,args,m) (* has args, so not combine ty args *)
|
|
| f -> (f,fty,tys,args,m)
|
|
|
|
let soTyparSet tps = showL (commaListL (List.map TyparL (Zset.elements tps)))
|
|
let soTyp t = (DebugPrint.showType t)
|
|
let soLength xs = string (List.length xs)
|
|
|
|
// CLEANUP NOTE: don't like the look of this function - this distinction
|
|
// should never be needed
|
|
let isDelayedRepr (f:Val) e =
|
|
let tps,vss,b,rty = dest_top_lambda (e,f.Type)
|
|
List.length vss>0
|
|
|
|
|
|
// REVIEW: these should just be replaced by direct calls to mk_local, mk_compgen_local etc.
|
|
// REVIEW: However these set an arity whereas the others don't
|
|
let mkLocalNameTypeArity compgen m name ty topValInfo =
|
|
NewVal(ident(name,m),ty,Immutable,compgen,topValInfo,None,taccessPublic,ValNotInRecScope,None,NormalVal,[],OptionalInline,emptyXmlDoc,false,false,false,false,None,ParentNone)
|
|
|
|
let mkLocal m name ty = mkLocalNameTypeArity true m name ty None
|
|
|
|
//-------------------------------------------------------------------------
|
|
// definitions: TLR, arity, arity-met, arity-short
|
|
//-------------------------------------------------------------------------
|
|
|
|
(* DEFN: An f is TLR with arity wf if
|
|
(a) it's repr is "LAM tps. lam x1...xN. body" and have N<=wf (i.e. have enough args)
|
|
(b) it has no free tps
|
|
(c) for g:freevars(repr), both
|
|
(1) g is TLR with arity wg, and
|
|
(2) g occurs in arity-met occurance.
|
|
(d) if N=0, then further require that body be a TLR-constant.
|
|
|
|
Conditions (a-c) are required if f is to have a static method/field represenation.
|
|
Condition (d) chooses which constants can be lifted. (no effects, non-trivial).
|
|
|
|
DEFN: An arity-met occurance of g is a g application with enough args supplied,
|
|
ie. (g tps args) where wg <= |args|.
|
|
|
|
DEFN: An arity-short occurance does not have enough args.
|
|
|
|
DEFN: A TLR-constant:
|
|
- can have constructors (tuples, datatype, records, exn).
|
|
- should be non-trivial (says, causes allocation).
|
|
- if calls are allowed, they must be effect free (since eval point is moving).
|
|
*)
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// OVERVIEW
|
|
//-------------------------------------------------------------------------
|
|
|
|
(* Overview of passes (over term) and steps (not over term):
|
|
|
|
pass1 - decide which f will be TLR and determine their arity.
|
|
pass2 - what closures are needed? Finds etps(f) and envReq(f) for TLR f.
|
|
Depends on the arity choice, so must follow pass1.
|
|
step3 - choose env packing, create fHats.
|
|
pass4 - rewrite term fixing up definitions and callsites.
|
|
Depends on closure and env packing, so must follow pass2 (and step 3).
|
|
pass5 - copy_expr call to topexpr to ensure all bound ids are unique.
|
|
For complexity reasons, better to re-recurse over expr once.
|
|
pass6 - sanity check, confirm that all TLR marked bindings meet DEFN.
|
|
|
|
*)
|
|
|
|
//-------------------------------------------------------------------------
|
|
// pass1: GetValsBoundUnderMustInline (see comment further below)
|
|
//-------------------------------------------------------------------------
|
|
|
|
let GetValsBoundUnderMustInline ccu xinfo =
|
|
let accRejectFrom (v:Val) repr rejectS =
|
|
if v.InlineInfo = PseudoValue then
|
|
Zset.union (GetValsBoundInExpr repr) rejectS
|
|
else rejectS
|
|
let rejectS = Zset.empty val_spec_order
|
|
let rejectS = Zmap.fold accRejectFrom xinfo.xinfo_eqns rejectS
|
|
rejectS
|
|
|
|
//-------------------------------------------------------------------------
|
|
// pass1: isTLRConstant
|
|
//-------------------------------------------------------------------------
|
|
|
|
(*
|
|
let rec trivialExpr x =
|
|
match x with
|
|
| TExpr_val _ -> true
|
|
| TExpr_op(TOp_ucase (_),tyargs,[],_) -> true
|
|
| TExpr_const _ -> true
|
|
| TExpr_app((f0,f0ty),tyargsl,[],m) -> not (is_tyfunc_vref_expr f0) && trivialExpr f0
|
|
| _ -> false
|
|
|
|
let rec const_expr x =
|
|
(* For now: constructions around constants
|
|
* Later, can also refer to *PRIOR* TLR constants (i.e. CSE) - need declaration order.
|
|
*)
|
|
match x with
|
|
| TExpr_const _ -> true
|
|
| TExpr_op(TOp_tuple, tys,args,m) -> const_exprs args
|
|
| TExpr_op(TOp_ucase cr,tinst,args,m) -> const_exprs args && not (tcref_alloc_observable (fst cr))
|
|
| TExpr_op(TOp_recd (ctor,tcr),tinst,args,m) -> ctor = None && const_exprs args && not (tcref_alloc_observable tcr)
|
|
(*| TExpr_lambda _ | TExpr_tlambda _ -> true -- these should be ok *)
|
|
(*| could also allow calls to functions which have no known effect *)
|
|
| _ -> false
|
|
and const_exprs es = List.forall const_expr es
|
|
|
|
let isTLRConstant x = const_expr x && not (trivialExpr x)
|
|
*)
|
|
|
|
//-------------------------------------------------------------------------
|
|
// pass1: IsRefusedTLR
|
|
//-------------------------------------------------------------------------
|
|
|
|
let IsRefusedTLR g (f:Val) =
|
|
let mutableVal = f.IsMutable
|
|
// things marked NeverInline are special
|
|
let dllImportStubOrOtherNeverInline = (f.InlineInfo = NeverInline)
|
|
// Cannot have static fields of byref type
|
|
let byrefVal = is_byref_typ g f.Type
|
|
// Special values are instance methods etc. on .NET types. For now leave these alone
|
|
let specialVal = isSome(f.MemberInfo)
|
|
let alreadyChosen = f.TopValInfo.IsSome
|
|
let refuseTest = alreadyChosen || mutableVal || byrefVal || specialVal || dllImportStubOrOtherNeverInline
|
|
refuseTest
|
|
|
|
let IsMandatoryTopLevel (f:Val) =
|
|
let specialVal = isSome(f.MemberInfo)
|
|
let isModulBinding = f.IsMemberOrModuleBinding
|
|
specialVal || isModulBinding
|
|
|
|
let IsMandatoryNonTopLevel g (f:Val) =
|
|
let byrefVal = is_byref_typ g f.Type
|
|
byrefVal
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// pass1: decide which f are to be TLR? and if so, arity(f)
|
|
//-------------------------------------------------------------------------
|
|
|
|
module Pass1_DetermineTLRAndArities =
|
|
|
|
let GetMaxNumArgsAtUses xinfo f =
|
|
match Zmap.tryfind f xinfo.xinfo_uses with
|
|
| None -> 0 (* no call sites *)
|
|
| Some sites ->
|
|
sites |> List.map (fun (accessors,tinst,args) -> List.length args) |> List.max
|
|
|
|
let SelectTLRVals g xinfo f e =
|
|
if IsRefusedTLR g f then None
|
|
else if Zset.mem f xinfo.xinfo_dtree then None
|
|
else
|
|
(* Could the binding be TLR? with what arity? *)
|
|
let atTopLevel = Zset.mem f xinfo.xinfo_toplevel
|
|
let tps,vss,b,rty = dest_top_lambda (e,f.Type)
|
|
let nFormals = List.length vss
|
|
let nMaxApplied = GetMaxNumArgsAtUses xinfo f
|
|
let arity = Operators.min nFormals nMaxApplied
|
|
if atTopLevel or arity<>0 or nonNil tps then Some (f,arity)
|
|
else None
|
|
|
|
/// Check if f involves any value recursion (so can skip those).
|
|
/// ValRec considered: recursive && some f in mutual binding is not bound to a lambda
|
|
let IsValueRecursionFree xinfo f =
|
|
|
|
let hasDelayedRepr f = isDelayedRepr f (forceM' f xinfo.xinfo_eqns ("IsValueRecursionFree - hasDelayedRepr",name_of_val))
|
|
let recursive,mudefs = forceM' f xinfo.xinfo_mubinds ("IsValueRecursionFree",name_of_val)
|
|
not recursive || FlatList.forall hasDelayedRepr mudefs
|
|
|
|
let DumpArity arityM =
|
|
let dump f n = dprintf "tlr: arity %50s = %d\n" (showL (valL f)) n
|
|
Zmap.iter dump arityM
|
|
|
|
let DetermineTLRAndArities ccu g expr =
|
|
if verboseTLR then dprintf "DetermineTLRAndArities------\n";
|
|
let xinfo = GetUsageInfoOfImplFile g expr
|
|
let fArities = Zmap.chooseL (SelectTLRVals g xinfo) xinfo.xinfo_eqns
|
|
let fArities = List.filter (fst >> IsValueRecursionFree xinfo) fArities
|
|
// Do not TLR v if it is bound under a mustinline defn
|
|
// There is simply no point - the original value will be duplicated and TLR'd anyway
|
|
// However we could report warnings for such values as they lead to duplication
|
|
// which could be avoided by making inlineable versions of the TLR'd values
|
|
// also available.
|
|
let rejectS = GetValsBoundUnderMustInline ccu xinfo
|
|
let fArities = List.filter (fun (v,_) -> not (Zset.mem v rejectS)) fArities
|
|
(*-*)
|
|
let tlrS = Zset.of_list val_spec_order (List.map fst fArities)
|
|
let topValS = xinfo.xinfo_toplevel in (* genuinely top level *)
|
|
let topValS = Zset.filter (IsMandatoryNonTopLevel g >> not) topValS in (* restrict *)
|
|
(*
|
|
let fUsedOnce = Zmap.List.choose (selectUsedOnce g xinfo) xinfo.xinfo_eqns
|
|
let fUsedOnce = List.filter (isInRecursiveBinding xinfo >> not) fUsedOnce
|
|
let fUsedOnce = List.filter (fun v -> not (Zset.mem v rejectS)) fUsedOnce
|
|
let fUsedOnce = List.filter (fun v -> not (Zset.mem v tlrS)) fUsedOnce
|
|
let fUsedOnce = List.filter (fun v -> not (Zset.mem v topValS)) fUsedOnce
|
|
dprintf "#fUsedOnce = %d\n" (List.length fUsedOnce);
|
|
fUsedOnce |> List.iter (fun v -> v.Data.val_mustinline <- PseudoValue);
|
|
*)
|
|
(* REPORT MISSED CASES *)
|
|
begin
|
|
if verboseTLR then
|
|
let missed = Zset.diff xinfo.xinfo_toplevel tlrS
|
|
missed |> Zset.iter (fun v -> dprintf "TopLevel but not TLR = %s\n" v.MangledName)
|
|
end;
|
|
(* REPORT OVER *)
|
|
let arityM = Zmap.of_list val_spec_order fArities
|
|
if verboseTLR then DumpArity arityM;
|
|
tlrS,topValS, arityM
|
|
|
|
|
|
|
|
(* NOTES:
|
|
For constants,
|
|
Want to fold in a declaration order,
|
|
so can make decisions about TLR given TLR-knowledge about prior constants.
|
|
Assuming ilxgen will fix up initialisations.
|
|
So,
|
|
xinfo to be extended to include some scoping representation.
|
|
Maybe a telescope tree which can be walked over.
|
|
*)
|
|
|
|
//-------------------------------------------------------------------------
|
|
// pass2: determine etps(f) and envreq(f) - notes
|
|
//-------------------------------------------------------------------------
|
|
|
|
/// What are the closing types/values for {f1,f2...} mutally defined?
|
|
///
|
|
// Note: arity-met g-applications (g TLR) will translated as:
|
|
// [[g @ tps ` args]] -> gHAT @ etps(g) tps ` env(g) args
|
|
// so they require availability of closing types/values for g.
|
|
//
|
|
// If g is free wrt f1,f2... then g's closure must be included.
|
|
//
|
|
// Note: mutual definitions have a common closure.
|
|
//
|
|
// For f1,f2,... = fBody1,fbody2... mutual bindings:
|
|
//
|
|
// DEFN: The generators are the free-values of fBody1,fBody2...
|
|
//
|
|
// What are the closure equations?
|
|
//
|
|
// etps(f1,f2..) includes free-tps(f)
|
|
// etps(f1,f2..) includes etps(g) if fBody has arity-met g-occurance (g TLR).
|
|
//
|
|
// envReq(f1,f2...) includes ReqSubEnv(g) if fBody has arity-met g-occurance (g TLR)
|
|
// envReq(f1,f2...) includes ReqVal(g) if fBody has arity-short g-occurance (g TLR)
|
|
// envReq(f1,f2...) includes ReqVal(g) if fBody has g-occurance (g not TLR)
|
|
//
|
|
// and only collect requirements if g is a generator (see next notes).
|
|
//
|
|
// Note: "env-availability"
|
|
// In the translated code, env(h) will be defined at the h definition point.
|
|
// So, where-ever h could be called (recursive or not),
|
|
// the env(h) will be available (in scope).
|
|
//
|
|
// Note (subtle): "sub-env-requirement-only-for-generators"
|
|
// If have an arity-met call to h inside fBody, but h is not a freevar for f,
|
|
// then h does not contribute env(h) to env(f), the closure for f.
|
|
// It is true that env(h) will be required at the h call-site,
|
|
// but the env(h) will be available there (by "env-availability"),
|
|
// since h must be bound inside the fBody since h was not a freevar for f.
|
|
// .
|
|
// [note, f and h may mutally recurse and formals of f may be in env(h),
|
|
// so env(f) may be properly inside env(h),
|
|
// so better not have env(h) in env(f)!!!].
|
|
|
|
|
|
/// The subset of ids from a mutal binding that are chosen to be TLR.
|
|
/// They share a common env.
|
|
/// [Each fclass has an env, the fclass are the handles to envs.]
|
|
type fclass =
|
|
FC of FlatVals
|
|
|
|
let fclass_order ccu = orderOn (fun (FC fs) -> fs) (FlatList.order val_spec_order)
|
|
let fclassPairs ((FC fs) as fc) = fs |> FlatList.map (fun f -> (f,fc))
|
|
let memFC (f:Val) (FC fs) = fs |> FlatList.exists (fun v -> v.Stamp = f.Stamp)
|
|
let isEmptyFC (FC fs) = FlatList.isEmpty fs
|
|
let showFC (FC fs) = "+" + String.concat "+" (FlatList.map name_of_val fs)
|
|
|
|
/// It is required to make the TLR closed wrt it's freevars (the env generators).
|
|
/// For g a generator,
|
|
/// An arity-met g occurance contributes the env required for that g call.
|
|
/// Other occurances contribute the value g.
|
|
type envItem =
|
|
| ReqSubEnv of Val
|
|
| ReqVal of Val
|
|
|
|
let envItem_order ccu =
|
|
let rep = function
|
|
ReqSubEnv v -> true ,v
|
|
| ReqVal v -> false,v
|
|
|
|
orderOn rep (Pair.order (Bool.order,val_spec_order))
|
|
|
|
/// An env says what is needed to close the corresponding defn(s).
|
|
/// The etps are the free etps of the defns, and those required by any direct TLR arity-met calls.
|
|
/// The envReq are the ids/subEnvs required from calls to freeVars.
|
|
type env =
|
|
{ etps : Typar Zset.set;
|
|
envReq : envItem Zset.set;
|
|
m : Range.range; }
|
|
|
|
let env0 ccu m =
|
|
{etps = Zset.empty typar_spec_order;
|
|
envReq = Zset.empty (envItem_order ccu);
|
|
m = m }
|
|
|
|
let extendEnv (typars,items) env =
|
|
{env with
|
|
etps = Zset.addList typars env.etps;
|
|
envReq = Zset.addList items env.envReq}
|
|
|
|
let envSubEnvs env =
|
|
let select = function ReqSubEnv f -> Some f | ReqVal _ -> None
|
|
List.choose select (Zset.elements env.envReq)
|
|
|
|
let envVals env =
|
|
let select = function ReqSubEnv f -> None | ReqVal f -> Some f
|
|
List.choose select (Zset.elements env.envReq)
|
|
|
|
(*--debug-stuff--*)
|
|
|
|
let showEnvItem = function
|
|
| ReqSubEnv f -> "&" ^ f.MangledName
|
|
| ReqVal f -> f.MangledName
|
|
|
|
let soEnv env =
|
|
(showL (commaListL (List.map TyparL (Zset.elements (env.etps))))) ^ "--" ^
|
|
(String.concat "," (List.map showEnvItem (Zset.elements env.envReq)))
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// pass2: collector - state
|
|
//-------------------------------------------------------------------------
|
|
|
|
type generators = Val Zset.set
|
|
|
|
/// check a named function value applied to sufficient arguments
|
|
let IsArityMet (vref:ValRef) wf tys args =
|
|
(List.length tys = vref.Typars.Length) && (wf <= List.length args)
|
|
|
|
|
|
module Pass2_DetermineTLREnvs =
|
|
|
|
|
|
// IMPLEMENTATION PLAN:
|
|
//
|
|
// fold over expr.
|
|
//
|
|
// - at an instance g,
|
|
// - (a) g arity-met, LogRequiredFrom g - ReqSubEnv(g) -- direct call will require env(g) and etps(g)
|
|
// - (b) g arity-short, LogRequiredFrom g - ReqVal(g) -- remains g call
|
|
// - (c) g non-TLR, LogRequiredFrom g - ReqVal(g) -- remains g
|
|
// where
|
|
// LogRequiredFrom g ... = logs info into (generators,env) if g in generators.
|
|
//
|
|
// - at some mu-bindings, f1,f2... = fBody1,fBody2,...
|
|
// "note generators, push (generators,env), fold-over bodies, pop, fold rest"
|
|
//
|
|
// - let fclass = ff1,... be the fi which are being made TLR.
|
|
// - required to find an env for these.
|
|
// - start a new envCollector:
|
|
// freetps = freetypars of (fBody1,fBody2,...)
|
|
// freevs = freevars of ..
|
|
// initialise:
|
|
// etps = freetps
|
|
// envReq = [] -- info collected from generator occurances in bindings
|
|
// generators = freevs
|
|
// - fold bodies, collecting info for generators.
|
|
// - pop and save env.
|
|
// - note: - etps(fclass) are only the freetps
|
|
// - they need to include etps(g) for each direct call to g (g a generator for fclass)
|
|
// - the etps(g) may not yet be known,
|
|
// e.g. if we are inside the definition of g and had recursively called it.
|
|
// - so need to FIX up the etps(-) function when collected info for all fclass.
|
|
// - fold rest (after binding)
|
|
//
|
|
// fix up etps(-) according to direct call dependencies.
|
|
//
|
|
|
|
|
|
/// This state collects:
|
|
/// envM - fclass -> env
|
|
/// fclassM - f -> fclass
|
|
/// declist - fclass list
|
|
/// recShortCallS - the f which are "recursively-called" in arity short instance.
|
|
///
|
|
/// When walking expr, at each mutual binding site,
|
|
/// push a (generator,env) collector frame on stack.
|
|
/// If occurances in body are relevant (for a generator) then it's contribution is logged.
|
|
///
|
|
/// recShortCalls to f will require a binding for f in terms of fHat within the fHatBody.
|
|
type state =
|
|
{ stack : (fclass * generators * env) list;
|
|
envM : Zmap.map<fclass,env>;
|
|
fclassM : Zmap.map<Val,fclass>;
|
|
revDeclist : fclass list;
|
|
recShortCallS : Zset.set<Val>;
|
|
}
|
|
|
|
let state0 ccu =
|
|
{ stack = [];
|
|
envM = Zmap.empty (fclass_order ccu);
|
|
fclassM = Zmap.empty val_spec_order;
|
|
revDeclist = [];
|
|
recShortCallS = Zset.empty val_spec_order; }
|
|
|
|
let soVSet fs = (showL (commaListL (List.map valL (Zset.elements fs))))
|
|
|
|
/// PUSH = start collecting for fclass
|
|
let PushFrame ccu fclass (etps0,generators,m) state =
|
|
if isEmptyFC fclass then state else
|
|
( (if verboseTLR then dprintf "PushFrame: %s\n - generators = %s\n" (showFC fclass) (soVSet generators));
|
|
{state with
|
|
revDeclist = fclass :: state.revDeclist;
|
|
stack = (let env = extendEnv (etps0,[]) (env0 ccu m) in (fclass,generators,env)::state.stack); })
|
|
|
|
/// POP & SAVE = end collecting for fclass and store
|
|
let SaveFrame fclass state =
|
|
if isEmptyFC fclass then state else
|
|
if verboseTLR then dprintf "SaveFrame: %s\n" (showFC fclass);
|
|
match state.stack with
|
|
| [] -> internalError "trl: popFrame has empty stack"
|
|
| (fclass,generators,env)::stack -> (* ASSERT: same fclass *)
|
|
{state with
|
|
stack = stack;
|
|
envM = Zmap.add fclass env state.envM;
|
|
fclassM = FlatList.fold (fun mp (k,v) -> Zmap.add k v mp) state.fclassM (fclassPairs fclass) }
|
|
|
|
/// Log requirements for g in the relevant stack frames
|
|
let LogRequiredFrom g items state =
|
|
let logIntoFrame (fclass,generators,env) =
|
|
let env =
|
|
if Zset.mem g generators then
|
|
// dprintf " : logging for generators=%s\n" (soVSet generators);
|
|
let typars = []
|
|
extendEnv (typars,items) env
|
|
else env
|
|
|
|
fclass,generators,env
|
|
|
|
{state with stack = List.map logIntoFrame state.stack}
|
|
|
|
let LogShortCall g state =
|
|
let frameFor g (fclass,generators,env) = memFC g fclass
|
|
if List.exists (frameFor g) state.stack then
|
|
((if verboseTLR then dprintf "shortCall: rec: %s\n" g.MangledName);
|
|
(* Have short call to g within it's (mutual) definition(s) *)
|
|
{state with
|
|
recShortCallS = Zset.add g state.recShortCallS})
|
|
else
|
|
((if verboseTLR then dprintf "shortCall: not-rec: %s\n" g.MangledName);
|
|
state)
|
|
|
|
let getEnv f state =
|
|
match Zmap.tryfind f state.fclassM with
|
|
| Some fclass -> (* env(f) is known, f prior *)
|
|
((if verboseTLR then dprintf "getEnv: fclass=%s\n" (showFC fclass));
|
|
let env = forceM' fclass state.envM ("getEnv",showFC)
|
|
Some env)
|
|
| None -> (* env(f) unknown, perhaps in body of it's defn *)
|
|
None
|
|
|
|
|
|
|
|
let FreeInBindings bs = FlatList.fold (foldOn (free_in_rhs CollectTyparsAndLocals) union_freevars) empty_freevars bs
|
|
|
|
/// Intercepts selected exprs.
|
|
/// "letrec f1,f2,... = fBody1,fBody2,... in rest" -
|
|
/// "val v" - free occurance
|
|
/// "app (f,tps,args)" - occurance
|
|
///
|
|
/// On intercepted nodes, must exprF fold to collect from subexpressions.
|
|
let ExprEnvIntercept ccu (tlrS,arityM) exprF z expr =
|
|
let accInstance z (fvref,tps,args) (* f known local *) =
|
|
let f = deref_val fvref
|
|
match Zmap.tryfind f arityM with
|
|
|
|
| Some wf ->
|
|
// f is TLR with arity wf
|
|
if IsArityMet fvref wf tps args then
|
|
// arity-met call to a TLR g
|
|
LogRequiredFrom f [ReqSubEnv f] z
|
|
else
|
|
// arity-short instance
|
|
let z = LogRequiredFrom f [ReqVal f] z
|
|
// LogShortCall - logs recursive short calls
|
|
let z = LogShortCall f z
|
|
z
|
|
|
|
| None ->
|
|
// f is non-TLR
|
|
LogRequiredFrom f [ReqVal f] z
|
|
|
|
let accBinds m z (binds: Bindings) =
|
|
let tlrBs,nonTlrBs = binds |> FlatList.partition (fun b -> Zset.mem b.Var tlrS)
|
|
// For bindings marked TLR, collect implied env
|
|
let fclass = FC (vars_of_Bindings tlrBs)
|
|
// what determines env?
|
|
let frees = FreeInBindings tlrBs
|
|
let etps0 = frees.FreeTyvars.FreeTypars |> Zset.elements (* put in env *)
|
|
// occurances contribute to env
|
|
let generators = (frees.FreeLocals |> Zset.elements)
|
|
// tlrBs are not generators for themselves
|
|
let generators = List.filter (fun g -> not (memFC g fclass)) generators
|
|
let generators = Zset.of_list val_spec_order generators
|
|
// collect into env over bodies
|
|
let z = PushFrame ccu fclass (etps0,generators,m) z
|
|
let z = FlatList.fold (foldOn rhs_of_bind exprF) z tlrBs
|
|
let z = SaveFrame fclass z
|
|
(* for bindings not marked TRL, collect *)
|
|
let z = FlatList.fold (foldOn rhs_of_bind exprF) z nonTlrBs
|
|
z
|
|
|
|
match expr with
|
|
| TExpr_val (v,_,m) ->
|
|
let z = accInstance z (v,[],[])
|
|
Some z
|
|
| TExpr_op (TOp_lval_op (_,v),tys,args,m) ->
|
|
let z = accInstance z (v,[],[])
|
|
let z = List.fold exprF z args
|
|
Some z
|
|
| TExpr_app (f,fty,tys,args,m) ->
|
|
let f,fty,tys,args,m = DestApp (f,fty,tys,args,m)
|
|
match f with
|
|
| TExpr_val (f,_,_) ->
|
|
// // YES: APP vspec tps args - log
|
|
let z = accInstance z (f,tys,args)
|
|
let z = List.fold exprF z args
|
|
Some z
|
|
| _ ->
|
|
(* NO: app, but function is not val - no log *)
|
|
None
|
|
| TExpr_letrec (binds,body,m,_) ->
|
|
let z = accBinds m z binds
|
|
let z = exprF z body
|
|
Some z
|
|
| TExpr_let (bind,body,m,_) ->
|
|
let z = accBinds m z (FlatList.one bind)
|
|
let z = exprF z body
|
|
Some z
|
|
| _ -> None (* NO: no intercept *)
|
|
|
|
|
|
/// Initially, etps(fclass) = freetps(bodies).
|
|
/// For each direct call to a g, a generator for fclass,
|
|
/// Required to include the etps(g) in etps(fclass).
|
|
let CloseEnvETps fclassM envM =
|
|
if verboseTLR then dprintf "CloseEnvETps------\n";
|
|
let etpsFor envM f =
|
|
let fc = forceM' f fclassM ("etpsFor",name_of_val)
|
|
let env = forceM' fc envM ("etpsFor",showFC)
|
|
env.etps
|
|
|
|
let closeStep envM changed fc env =
|
|
let directCallFs = envSubEnvs env
|
|
let directCallETps = List.map (etpsFor envM) directCallFs
|
|
let etps0 = env.etps
|
|
let etps = List.fold Zset.union etps0 directCallETps
|
|
let changed = changed || (not (Zset.equal etps0 etps))
|
|
let env = {env with etps = etps}
|
|
if verboseTLR then
|
|
dprintf "closeStep: fc=%30s nSubs=%d etps0=%s etps=%s\n" (showFC fc) directCallFs.Length (soTyparSet etps0) (soTyparSet etps);
|
|
directCallFs |> List.iter (fun f -> dprintf "closeStep: dcall f=%s\n" f.MangledName)
|
|
directCallFs |> List.iter (fun f -> dprintf "closeStep: dcall fc=%s\n" (showFC (Zmap.find f fclassM)))
|
|
directCallETps |> List.iter (fun etps -> dprintf "closeStep: dcall etps=%s\n" (soTyparSet etps0))
|
|
changed,env
|
|
|
|
let rec fixpoint envM =
|
|
let changed = false
|
|
let changed,envM = Zmap.fmap (closeStep envM) changed envM
|
|
if changed then
|
|
fixpoint envM
|
|
else
|
|
envM
|
|
|
|
fixpoint envM
|
|
|
|
let DumpEnvM envM =
|
|
let dump fc env = dprintf "CLASS=%s\n env=%s\n" (showFC fc) (soEnv env)
|
|
Zmap.iter dump envM
|
|
|
|
let DetermineTLREnvs ccu (tlrS,arityM) expr =
|
|
if verboseTLR then dprintf "DetermineTLREnvs------\n";
|
|
let folder = {ExprFolder0 with exprIntercept = ExprEnvIntercept ccu (tlrS,arityM)}
|
|
let z = state0 ccu
|
|
let z = FoldImplFile folder z expr
|
|
(* project *)
|
|
let envM = z.envM
|
|
let fclassM = z.fclassM
|
|
let declist = List.rev z.revDeclist
|
|
let recShortCallS = z.recShortCallS
|
|
(* diagnostic dump *)
|
|
(if verboseTLR then DumpEnvM envM);
|
|
(* close the etps under the subEnv reln *)
|
|
let envM = CloseEnvETps fclassM envM
|
|
(* filter out trivial fclass - with no TLR defns *)
|
|
let envM = Zmap.remove (FC FlatList.empty) envM
|
|
(* restrict declist to those with envM bindings (the non-trivial ones) *)
|
|
let declist = List.filter (Zmap.mem_of envM) declist
|
|
(* diagnostic dump *)
|
|
if verboseTLR then
|
|
DumpEnvM envM;
|
|
declist |> List.iter (fun fc -> dprintf "Declist: %s\n" (showFC fc))
|
|
recShortCallS |> Zset.iter (fun f -> dprintf "RecShortCall: %s\n" f.MangledName)
|
|
|
|
envM,fclassM,declist,recShortCallS
|
|
|
|
//-------------------------------------------------------------------------
|
|
// step3: envPack
|
|
//-------------------------------------------------------------------------
|
|
|
|
/// Each env is represented by some carrier values, the aenvs.
|
|
/// An env packing defines these, and the pack/unpack bindings.
|
|
/// The bindings are in terms of the fvs directly.
|
|
///
|
|
/// When defining a new TLR f definition,
|
|
/// the fvs will become bound by the unpack bindings,
|
|
/// the aenvs will become bound by the new lam, and
|
|
/// the etps will become bound by the new LAM.
|
|
/// For uniqueness of bound ids,
|
|
/// all these ids (Typar/Val) will need to be freshened up.
|
|
/// It is OK to break the uniqueness-of-bound-ids rule during the rw,
|
|
/// provided it is fixed up via a copy_expr call on the final expr.
|
|
|
|
type envPack =
|
|
{ /// The actual typars
|
|
ep_etps : typars;
|
|
/// The actual env carrier values
|
|
ep_aenvs : Val list;
|
|
/// Sequentially define the aenvs in terms of the fvs
|
|
ep_pack : Bindings;
|
|
/// Sequentially define the fvs in terms of the aenvs
|
|
ep_unpack : Bindings;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// step3: FlatEnvPacks
|
|
//-------------------------------------------------------------------------
|
|
|
|
exception AbortTLR of Range.range
|
|
|
|
/// A naive packing of environments.
|
|
/// Chooses to pass all env values as explicit args (no tupling).
|
|
/// Note, tupling would cause an allocation,
|
|
/// so, unless arg lists get very long, this flat packing will be preferable.
|
|
|
|
/// Given (fclass,env).
|
|
/// Have env = ReqVal vj, ReqSubEnv subEnvk -- ranging over j,k
|
|
/// Define vals(env) = {vj}|j union vals(subEnvk)|k -- trans closure of vals of env.
|
|
/// Define <vi,aenvi> for each vi in vals(env).
|
|
/// This is the cmap for the env.
|
|
|
|
/// etps = env.etps
|
|
/// carriers = aenvi|i
|
|
/// pack = TBIND(aenvi = vi) for each (aenvi,vi) in cmap
|
|
/// unpack = TBIND(vj = aenvFor(vj)) for each vj in reqvals(env).
|
|
/// and TBIND(asubEnvi = aenvFor(v)) for each (asubEnvi,v) in cmap(subEnvk) ranging over required subEnvk.
|
|
/// where
|
|
/// aenvFor(v) = aenvi where (v,aenvi) in cmap.
|
|
let FlatEnvPacks g ccu fclassM topValS declist envM =
|
|
let fclassOf f = forceM' f fclassM ("fclassM",name_of_val)
|
|
let packEnv carrierMaps (fc:fclass) =
|
|
if verboseTLR then dprintf "\ntlr: packEnv fc=%s\n" (showFC fc);
|
|
let env = forceM' fc envM ("packEnv",showFC)
|
|
|
|
// carrierMaps = (fclass,(v,aenv)map)map
|
|
let carrierMapFor f = forceM' (fclassOf f) carrierMaps ("carrierMapFor",showFC)
|
|
let valsSubEnvFor f = Zmap.keys (carrierMapFor f)
|
|
|
|
// determine vals(env) - transclosure
|
|
let vals = envVals env @ List.collect valsSubEnvFor (envSubEnvs env) in // list, with repeats
|
|
let vals = List.noRepeats val_spec_order vals // noRepeats
|
|
let vals = vals |> FlatList.of_list
|
|
|
|
// Remove genuinely toplevel, need not close over
|
|
let vals = vals |> FlatList.filter (IsMandatoryTopLevel >> not)
|
|
let vals = vals |> FlatList.filter (Zset.mem_of topValS >> not)
|
|
|
|
// Carrier sets cannot include constrained polymorphic values. We can't just take such a value out, so for the moment
|
|
// we'll just abandon TLR altogether and give a warning about this condition.
|
|
(match vals |> FlatList.tryfind (IsGenericValWithGenericContraints g) with None -> () | Some v -> raise (AbortTLR v.Range));
|
|
|
|
// build cmap for env
|
|
let cmapPairs = vals |> FlatList.mapi (fun i v -> (v,(mk_compgen_local env.m v.MangledName v.Type |> fst)))
|
|
let cmap = Zmap.of_FlatList val_spec_order cmapPairs
|
|
let aenvFor v = forceM' v cmap ("aenvFor",name_of_val)
|
|
let aenvExprFor v = expr_for_val env.m (aenvFor v)
|
|
|
|
// build envPack
|
|
let etps = env.etps
|
|
let aenvs = Zmap.values cmap
|
|
let pack = cmapPairs |> FlatList.map (fun (v,aenv) -> mk_invisible_bind aenv (expr_for_val env.m v))
|
|
let unpack =
|
|
let unpackCarrier (v,aenv) = mk_invisible_bind (set_val_has_no_arity v) (expr_for_val env.m aenv)
|
|
let unpackSubenv f =
|
|
let subCMap = carrierMapFor f
|
|
let vaenvs = Zmap.to_list subCMap
|
|
vaenvs |> List.map (fun (subv,subaenv) -> mk_bind NoSequencePointAtInvisibleBinding subaenv (aenvExprFor subv))
|
|
List.map unpackCarrier (Zmap.to_list cmap) @
|
|
List.collect unpackSubenv (envSubEnvs env)
|
|
|
|
// extend carrierMaps
|
|
let carrierMaps = Zmap.add fc cmap carrierMaps
|
|
|
|
// dump
|
|
if verboseTLR then
|
|
dprintf "tlr: packEnv envVals =%s\n" (showL (listL valL (envVals env)));
|
|
dprintf "tlr: packEnv envSubs =%s\n" (showL (listL valL (envSubEnvs env)));
|
|
dprintf "tlr: packEnv vals =%s\n" (showL (listL valL (FlatList.to_list vals)));
|
|
dprintf "tlr: packEnv aenvs =%s\n" (showL (listL valL aenvs));
|
|
dprintf "tlr: packEnv pack =%s\n" (showL (listL BindingL (FlatList.to_list pack)));
|
|
dprintf "tlr: packEnv unpack =%s\n" (showL (listL BindingL unpack))
|
|
|
|
// result
|
|
carrierMaps,
|
|
(fc, { ep_etps = Zset.elements etps;
|
|
ep_aenvs = aenvs;
|
|
ep_pack = pack;
|
|
ep_unpack = FlatList.of_list unpack})
|
|
|
|
let carriedMaps = Zmap.empty (fclass_order ccu)
|
|
let carriedMaps,envPacks = List.fmap packEnv carriedMaps declist (* List.fmap in dec order *)
|
|
let envPacks = Zmap.of_list (fclass_order ccu) envPacks
|
|
envPacks
|
|
|
|
|
|
(*-------------------------------------------------------------------------
|
|
* step3: chooseEnvPacks
|
|
*-------------------------------------------------------------------------*)
|
|
|
|
let DumpEnvPackM envPackM =
|
|
let dump fc envPack =
|
|
dprintf "envPack: fc = %s\n" (showFC fc);
|
|
dprintf " etps = %s\n" (showL (commaListL (List.map TyparL envPack.ep_etps)));
|
|
dprintf " aenvs = %s\n" (showL (commaListL (List.map valL envPack.ep_aenvs)));
|
|
dprintf " pack = %s\n" (showL (semiListL (FlatList.to_list (FlatList.map BindingL envPack.ep_pack))));
|
|
dprintf " unpack = %s\n" (showL (semiListL (FlatList.to_list (FlatList.map BindingL envPack.ep_unpack))));
|
|
dprintf "\n"
|
|
|
|
Zmap.iter dump envPackM
|
|
|
|
/// For each fclass, have an env.
|
|
/// Required to choose an envPack,
|
|
/// e.g. deciding whether to tuple up the environment or not.
|
|
/// e.g. deciding whether to use known values for required sub environments.
|
|
///
|
|
/// Scope for optimisating env packing here.
|
|
/// For now, pass all environments via arguments since aiming to eliminate allocations.
|
|
/// Later, package as tuples if arg lists get too long.
|
|
let ChooseEnvPackings g ccu fclassM topValS declist envM =
|
|
if verboseTLR then dprintf "ChooseEnvPackings------\n";
|
|
let envPackM = FlatEnvPacks g ccu fclassM topValS declist envM
|
|
let envPackM : (fclass,envPack) Zmap.map = envPackM
|
|
if verboseTLR then DumpEnvPackM envPackM;
|
|
envPackM
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// step3: CreateFHatM
|
|
//-------------------------------------------------------------------------
|
|
|
|
/// arity info where nothing is untupled
|
|
(* REVIEW: could do better here by preserving names *)
|
|
let MkSimpleArityInfo tps n = TopValInfo (TopValInfo.InferTyparInfo tps,List.replicate n TopValInfo.unnamedTopArg,TopValInfo.unnamedRetVal)
|
|
|
|
let CreateFHatM g ccu tlrS arityM fclassM envPackM =
|
|
if verboseTLR then dprintf "CreateFHatM------\n";
|
|
let createFHat (f:Val) =
|
|
let wf = forceM' f arityM ("createFHat - wf",(fun v -> showL (valL v)))
|
|
let fc = forceM' f fclassM ("createFHat - fc",name_of_val)
|
|
let envp = forceM' fc envPackM ("CreateFHatM - envp",showFC)
|
|
let name = f.MangledName (* ^ "_TLR_" ^ string wf *)
|
|
let m = f.Range
|
|
let tps,tau = f.TypeScheme
|
|
let argtys,res = strip_fun_typ g tau
|
|
let newTps = envp.ep_etps @ tps
|
|
let fHatTy =
|
|
let newArgtys = List.map type_of_val envp.ep_aenvs @ argtys
|
|
mk_lambda_ty newTps newArgtys res
|
|
let fHatArity = MkSimpleArityInfo newTps (List.length envp.ep_aenvs + wf)
|
|
let fHatName = globalNng.FreshCompilerGeneratedName(name,m)
|
|
|
|
let fHat = mkLocalNameTypeArity f.IsCompilerGenerated m fHatName fHatTy (Some fHatArity)
|
|
(if verboseTLR then dprintf "new %50s : %s\n" fHat.MangledName ((DebugPrint.showType fHat.Type)));
|
|
fHat
|
|
|
|
let fs = Zset.elements tlrS
|
|
let ffHats = List.map (fun f -> f,createFHat f) fs
|
|
let fHatM = Zmap.of_list val_spec_order ffHats
|
|
fHatM
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// pass4: rewrite - penv
|
|
//-------------------------------------------------------------------------
|
|
|
|
type penv =
|
|
{ ccu : ccu;
|
|
g : Env.TcGlobals;
|
|
tlrS : Zset.set<Val> ;
|
|
topValS : Zset.set<Val> ;
|
|
arityM : Zmap.map<Val,int> ;
|
|
fclassM : Zmap.map<Val,fclass> ;
|
|
recShortCallS : Zset.set<Val> ;
|
|
envPackM : Zmap.map<fclass,envPack>;
|
|
/// The mapping from 'f' values to 'fHat' values
|
|
fHatM : Zmap.map<Val,Val> ;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// pass4: rwstate (z state)
|
|
//-------------------------------------------------------------------------
|
|
|
|
type IsRecursive = IsRec | NotRec
|
|
type LiftedDeclaration = IsRecursive * Bindings (* where bool=true if letrec *)
|
|
|
|
/// This state is related to lifting to top-level (which is actually disabled right now)
|
|
/// This is to ensure the TLR constants get initialised once.
|
|
///
|
|
/// Top-level status ends when stepping inside a lambda, where a lambda is:
|
|
/// TExpr_tlambda, TExpr_lambda, TExpr_obj (and tmethods).
|
|
/// [... also, try_catch handlers, and switch targets...]
|
|
///
|
|
/// Top* repr bindings already at top-level do not need moving...
|
|
/// [and should not be, since they may lift over unmoved defns on which they depend].
|
|
/// Any TLR repr bindings under lambdas can be filtered out (and collected),
|
|
/// giving pre-declarations to insert before the outermost lambda expr.
|
|
type rwstate =
|
|
{ rws_mustinline: bool;
|
|
/// counts level of enclosing "lambdas"
|
|
rws_innerLevel : int;
|
|
/// collected preDecs (fringe is in-order)
|
|
rws_preDecs : tree<LiftedDeclaration>
|
|
}
|
|
|
|
let rws0 = {rws_mustinline=false;rws_innerLevel=0;rws_preDecs=emptyTR}
|
|
|
|
// move in/out of lambdas (or lambda containing construct)
|
|
let EnterInner z = {z with rws_innerLevel = z.rws_innerLevel + 1}
|
|
let ExitInner z = {z with rws_innerLevel = z.rws_innerLevel - 1}
|
|
|
|
let EnterMustInline b z f =
|
|
let orig = z.rws_mustinline
|
|
let z',x = f (if b then {z with rws_mustinline = true } else z)
|
|
{z' with rws_mustinline = orig },x
|
|
|
|
/// extract PreDecs (iff at top-level)
|
|
let ExtractPreDecs z =
|
|
// If level=0, so at top-level, then pop decs,
|
|
// else keep until get back to a top-level point.
|
|
if z.rws_innerLevel=0 then
|
|
// at top-level, extract preDecs
|
|
let preDecs = fringeTR z.rws_preDecs
|
|
{z with rws_preDecs=emptyTR}, preDecs
|
|
else
|
|
// not yet top-level, keep decs
|
|
z,[]
|
|
|
|
/// pop and set preDecs as "LiftedDeclaration tree"
|
|
let PopPreDecs z = {z with rws_preDecs=emptyTR},z.rws_preDecs
|
|
let SetPreDecs z pdt = {z with rws_preDecs=pdt}
|
|
|
|
/// collect Top* repr bindings - if needed...
|
|
let LiftTopBinds isRec penv z binds =
|
|
let isTopBind bind = isSome (chosen_arity_of_bind bind)
|
|
let topBinds,otherBinds = FlatList.partition isTopBind binds
|
|
let liftTheseBindings =
|
|
!liftTLR && // lifting enabled
|
|
not z.rws_mustinline && // can't lift bindings in a mustinline context - they would become private an not inlined
|
|
z.rws_innerLevel>0 && // only collect Top* bindings when at inner levels (else will drop them!)
|
|
not (FlatList.isEmpty topBinds) // only collect topBinds if there are some!
|
|
|
|
if liftTheseBindings then
|
|
let LiftedDeclaration = isRec,topBinds // LiftedDeclaration Top* decs
|
|
let z = {z with rws_preDecs = TreeNode [z.rws_preDecs;LeafNode LiftedDeclaration]} // logged at end
|
|
z,otherBinds
|
|
else
|
|
z,binds (* not "topBinds @ otherBinds" since that has changed order... *)
|
|
|
|
/// Wrap preDecs (in order) over an expr - use letrec/let as approp
|
|
let MakePreDec m (isRec,binds) expr =
|
|
if isRec=IsRec then
|
|
mk_letrec_binds m binds expr
|
|
else
|
|
mk_lets_from_Bindings m binds expr
|
|
|
|
let MakePreDecs m preDecs expr = List.foldBack (MakePreDec m) preDecs expr
|
|
|
|
let RecursivePreDecs pdsA pdsB =
|
|
let pds = fringeTR (TreeNode[pdsA;pdsB])
|
|
let decs = pds |> List.collect (fun (_,x) -> FlatList.to_list x) |> FlatList.of_list
|
|
LeafNode (IsRec,decs)
|
|
|
|
|
|
(*-------------------------------------------------------------------------
|
|
* pass4: lowertop - convert_vterm_bind on TopLevel binds
|
|
*-------------------------------------------------------------------------*)
|
|
|
|
let ConvertBind g (TBind(v,repr,_) as bind) =
|
|
match v.TopValInfo with
|
|
| None -> v.Data.val_top_repr_info <- Some (InferArityOfExprBinding g v repr )
|
|
| Some _ -> ()
|
|
|
|
bind
|
|
|
|
(*-------------------------------------------------------------------------
|
|
* pass4: transBind (translate)
|
|
*-------------------------------------------------------------------------*)
|
|
|
|
// Transform
|
|
// let f<tps> vss = f_body[<f_freeTypars>,f_freeVars]
|
|
// To
|
|
// let f<tps> vss = fHat<f_freeTypars> f_freeVars vss
|
|
// let fHat<tps> f_freeVars vss = f_body[<f_freeTypars>,f_freeVars]
|
|
let TransTLRBindings penv (binds:Bindings) =
|
|
if FlatList.isEmpty binds then FlatList.empty,FlatList.empty else
|
|
let fc = FC (vars_of_Bindings binds)
|
|
let envp = forceM' fc penv.envPackM ("TransTLRBindings",showFC)
|
|
|
|
let fRebinding (TBind(fOrig,b,letSeqPtOpt)) =
|
|
let m = fOrig.Range
|
|
let tps,vss,b,rty = dest_top_lambda (b,fOrig.Type)
|
|
let aenvExprs = envp.ep_aenvs |> List.map (expr_for_val m)
|
|
let vsExprs = vss |> List.map (mk_tupled_vars penv.g m)
|
|
let fHat = forceM' fOrig penv.fHatM ("fRebinding",name_of_val)
|
|
let w = 0
|
|
(* REVIEW: is this mutation really, really necessary? *)
|
|
(* Why are we applying TLR if the thing already has an arity? *)
|
|
let fOrig = set_val_has_no_arity fOrig
|
|
let fBind =
|
|
mk_multi_lambda_bind fOrig letSeqPtOpt m tps vss
|
|
(mk_appl penv.g
|
|
(typed_expr_for_val m fHat,
|
|
[List.map mk_typar_ty (envp.ep_etps @ tps)],
|
|
aenvExprs @ vsExprs,m),rty)
|
|
fBind
|
|
|
|
let fHatNewBinding (shortRecBinds:Bindings) (TBind(f,b,letSeqPtOpt)) =
|
|
let wf = forceM' f penv.arityM ("fHatNewBinding - arityM",name_of_val)
|
|
let fHat = forceM' f penv.fHatM ("fHatNewBinding - fHatM",name_of_val)
|
|
// Take off the variables
|
|
let tps,vss,b,rty = dest_top_lambda (b,f.Type)
|
|
// Don't take all the variables - only up to length wf
|
|
let vssTake,vssDrop = List.chop wf vss
|
|
// put the variables back on
|
|
let b,rty = mk_multi_lambdas_core (range_of_expr b) vssDrop (b,rty)
|
|
// fHat, args
|
|
let m = fHat.Range
|
|
// Add the type variables to the front
|
|
let fHat_tps = envp.ep_etps @ tps
|
|
// Add the 'aenv' and original taken variables to the front
|
|
let fHat_args = List.map List.singleton envp.ep_aenvs @ vssTake
|
|
let fHat_body = mk_lets_from_Bindings m envp.ep_unpack b
|
|
let fHat_body = mk_lets_from_Bindings m shortRecBinds fHat_body // bind "f" if have short recursive calls (somewhere)
|
|
// fHat binding, f rebinding
|
|
let wfHat = List.length envp.ep_aenvs + wf
|
|
let fHatBind = mk_multi_lambda_bind fHat letSeqPtOpt m fHat_tps fHat_args (fHat_body,rty)
|
|
fHatBind
|
|
let rebinds = binds |> FlatList.map fRebinding
|
|
let shortRecBinds = rebinds |> FlatList.filter (fun b -> penv.recShortCallS.Contains(b.Var))
|
|
let newBinds = binds |> FlatList.map (fHatNewBinding shortRecBinds)
|
|
newBinds,rebinds
|
|
|
|
let GetAEnvBindings penv fc =
|
|
match Zmap.tryfind fc penv.envPackM with
|
|
| None -> FlatList.empty // no env for this mutual binding
|
|
| Some envp -> envp.ep_pack // environment pack bindings
|
|
|
|
let TransBindings xisRec penv (binds:Bindings) =
|
|
let tlrBs,nonTlrBs = binds |> FlatList.partition (fun b -> Zset.mem b.Var penv.tlrS)
|
|
let fclass = FC (vars_of_Bindings tlrBs)
|
|
// Trans each TLR f binding into fHat and f rebind
|
|
let newTlrBinds,tlrRebinds = TransTLRBindings penv tlrBs
|
|
let aenvBinds = GetAEnvBindings penv fclass
|
|
// lower nonTlrBs if they are GTL
|
|
// QUERY: we repeat this logic in Lowertop. Do we really need to do this here?
|
|
// QUERY: yes and no - if we don't, we have an unrealizable term, and many decisions must
|
|
// QUERY: correlate with Lowertop.
|
|
let forceTopBindToHaveArity (bind:Binding) =
|
|
if penv.topValS.Contains(bind.Var) then ConvertBind penv.g bind
|
|
else bind
|
|
|
|
let nonTlrBs = nonTlrBs |> FlatList.map forceTopBindToHaveArity
|
|
let tlrRebinds = tlrRebinds |> FlatList.map forceTopBindToHaveArity
|
|
// assemble into replacement bindings
|
|
let bindAs,rebinds =
|
|
match xisRec with
|
|
| IsRec -> FlatList.to_list newTlrBinds @ FlatList.to_list tlrRebinds @ FlatList.to_list nonTlrBs @ FlatList.to_list aenvBinds,[] (* note: aenv last, order matters in letrec! *)
|
|
| NotRec -> FlatList.to_list aenvBinds @ FlatList.to_list newTlrBinds, FlatList.to_list tlrRebinds @ FlatList.to_list nonTlrBs (* note: aenv go first, they may be used *)
|
|
FlatList.of_list bindAs, FlatList.of_list rebinds
|
|
|
|
|
|
(*-------------------------------------------------------------------------
|
|
* pass4: TransApp (translate)
|
|
*-------------------------------------------------------------------------*)
|
|
|
|
let TransApp penv (fx,fty,tys,args,m) =
|
|
// Is it a val app, where the val f is TLR with arity wf?
|
|
// CLEANUP NOTE: should be using a mk_appl to make all applications
|
|
match fx with
|
|
| TExpr_val (fvref,_,m) when
|
|
(Zset.mem (deref_val fvref) penv.tlrS) &&
|
|
(let wf = forceM' (deref_val fvref) penv.arityM ("TransApp - wf",name_of_val)
|
|
IsArityMet fvref wf tys args) ->
|
|
|
|
let f = deref_val fvref
|
|
(* replace by direct call to corresponding fHat (and additional closure args) *)
|
|
let fc = forceM' f penv.fclassM ("TransApp - fc",name_of_val)in
|
|
let envp = forceM' fc penv.envPackM ("TransApp - envp",showFC)
|
|
let fHat = forceM' f penv.fHatM ("TransApp - fHat",name_of_val)in
|
|
let tys = (List.map mk_typar_ty envp.ep_etps) @ tys
|
|
let aenvExprs = List.map (expr_for_val m) envp.ep_aenvs
|
|
let args = aenvExprs @ args
|
|
mk_appl penv.g (typed_expr_for_val m fHat,[tys],args,m) (* change, direct fHat call with closure (etps,aenvs) *)
|
|
| _ ->
|
|
if isNil tys && isNil args then
|
|
fx
|
|
else TExpr_app (fx,fty,tys,args,m)
|
|
(* no change, f is expr *)
|
|
|
|
(*-------------------------------------------------------------------------
|
|
* pass4: pass (over expr)
|
|
*-------------------------------------------------------------------------*)
|
|
|
|
/// Must WrapPreDecs around every construct that could do EnterInner (which filters TLR decs).
|
|
/// i.e. let,letrec (bind may...), ilobj, lambda, tlambda.
|
|
let WrapPreDecs m pds x =
|
|
MakePreDecs m pds x
|
|
|
|
/// At bindings, fixup any TLR bindings.
|
|
/// At applications, fixup calls if they are arity-met instances of TLR.
|
|
/// At free vals, fixup 0-call if it is an arity-met constant.
|
|
/// Other cases rewrite structurally.
|
|
let rec TransExpr (penv:penv) z expr =
|
|
match expr with
|
|
// Use TransLinearExpr with a rebuild-continuation for some forms to avoid stack overflows on large terms *)
|
|
| TExpr_letrec _ | TExpr_let _ ->
|
|
TransLinearExpr penv z expr (fun res -> res)
|
|
|
|
// app - call sites may require z.
|
|
// - match the app (collapsing reclinks and type instances).
|
|
// - patch it.
|
|
| TExpr_app (f,fty,tys,args,m) ->
|
|
// pass over f,args subexprs
|
|
let z,f = TransExpr penv z f
|
|
let z,args = List.fmap (TransExpr penv) z args
|
|
// match app, and fixup if needed
|
|
let f,fty,tys,args,m = DestApp (f,fty,tys,args,m)
|
|
let expr = TransApp penv (f,fty,tys,args,m)
|
|
z,expr
|
|
|
|
| TExpr_val (v,_,m) ->
|
|
// consider this a trivial app
|
|
let fx,fty = expr,v.Type
|
|
let expr = TransApp penv (fx,fty,[],[],m)
|
|
z,expr
|
|
|
|
// reclink - suppress
|
|
| TExpr_link r ->
|
|
TransExpr penv z (!r)
|
|
|
|
// ilobj - has implicit lambda exprs and recursive/base references
|
|
| TExpr_obj (_,ty,basev,basecall,overrides,iimpls,m,_) ->
|
|
let z,basecall = TransExpr penv z basecall
|
|
let z,overrides = List.fmap (TransMethod penv) z overrides
|
|
let z,iimpls = List.fmap (fmap2'2 (List.fmap (TransMethod penv))) z iimpls
|
|
let expr = TExpr_obj(new_uniq(),ty,basev,basecall,overrides,iimpls,m,SkipFreeVarsCache())
|
|
let z,pds = ExtractPreDecs z
|
|
z,WrapPreDecs m pds expr (* if TopLevel, lift preDecs over the ilobj expr *)
|
|
|
|
// lambda, tlambda - explicit lambda terms
|
|
| TExpr_lambda(_,basevopt,argvs,body,m,rty,_) ->
|
|
let z = EnterInner z
|
|
let z,body = TransExpr penv z body
|
|
let z = ExitInner z
|
|
let z,pds = ExtractPreDecs z
|
|
z,WrapPreDecs m pds (mk_basev_multi_lambda m basevopt argvs (body,rty))
|
|
|
|
| TExpr_tlambda(_,argtyvs,body,m,rty,_) ->
|
|
let z = EnterInner z
|
|
let z,body = TransExpr penv z body
|
|
let z = ExitInner z
|
|
let z,pds = ExtractPreDecs z
|
|
z,WrapPreDecs m pds (mk_tlambda m argtyvs (body,rty))
|
|
|
|
/// Lifting TLR out over constructs (disabled)
|
|
/// Lift minimally to ensure the defn is not lifted up and over defns on which it depends (disabled)
|
|
| TExpr_match(spBind,exprm,dtree,targets,m,ty,_) ->
|
|
let targets = Array.to_list targets
|
|
let z,dtree = TransDecisionTree penv z dtree
|
|
let z,targets = List.fmap (TransDecisionTreeTarget penv) z targets
|
|
// TransDecisionTreeTarget wraps EnterInner/exitInnter, so need to collect any top decs
|
|
let z,pds = ExtractPreDecs z
|
|
z,WrapPreDecs m pds (mk_and_optimize_match spBind exprm m ty dtree targets)
|
|
|
|
// all others - below - rewrite structurally - so boiler plate code after this point...
|
|
| TExpr_const _ -> z,expr (* constant wrt Val *)
|
|
| TExpr_quote (a,{contents=Some(argTypes,argExprs,data)},m,ty) ->
|
|
let z,argExprs = List.fmap (TransExpr penv) z argExprs
|
|
z,TExpr_quote(a,{contents=Some(argTypes,argExprs,data)},m,ty)
|
|
| TExpr_quote (a,{contents=None},m,ty) ->
|
|
z,TExpr_quote(a,{contents=None},m,ty)
|
|
| TExpr_op (c,tyargs,args,m) ->
|
|
let z,args = List.fmap (TransExpr penv) z args
|
|
z,TExpr_op(c,tyargs,args,m)
|
|
| TExpr_seq (e1,e2,dir,spSeq,m) ->
|
|
let z,e1 = TransExpr penv z e1
|
|
let z,e2 = TransExpr penv z e2
|
|
z,TExpr_seq(e1,e2,dir,spSeq,m)
|
|
| TExpr_static_optimization (constraints,e2,e3,m) ->
|
|
let z,e2 = TransExpr penv z e2
|
|
let z,e3 = TransExpr penv z e3
|
|
z,TExpr_static_optimization(constraints,e2,e3,m)
|
|
| TExpr_tchoose (_,_,m) -> error(Error("Unexpected TExpr_tchoose",m))
|
|
|
|
/// Walk over linear structured terms in tail-recursive loop, using a continuation
|
|
/// to represent the rebuild-the-term stack
|
|
and TransLinearExpr penv z expr contf =
|
|
match expr with
|
|
// letrec - pass_recbinds does the work
|
|
| TExpr_letrec (binds,e,m,_) ->
|
|
let z = EnterInner z
|
|
// For letrec, preDecs from RHS must mutually recurse with those from the bindings
|
|
let z,pdsPrior = PopPreDecs z
|
|
let z,binds = FlatList.fmap (TransBindingRhs penv) z binds
|
|
let z,pdsRhs = PopPreDecs z
|
|
let binds,rebinds = TransBindings IsRec penv binds
|
|
let z,binds = LiftTopBinds IsRec penv z binds in (* factor Top* repr binds *)
|
|
let z,rebinds = LiftTopBinds IsRec penv z rebinds
|
|
let z,pdsBind = PopPreDecs z
|
|
let z = SetPreDecs z (TreeNode [pdsPrior;RecursivePreDecs pdsBind pdsRhs])
|
|
let z = ExitInner z
|
|
let z,pds = ExtractPreDecs z
|
|
TransLinearExpr penv z e (contf << (fun (z,e) ->
|
|
let e = mk_lets_from_Bindings m rebinds e
|
|
z,WrapPreDecs m pds (TExpr_letrec (binds,e,m,NewFreeVarsCache()))))
|
|
|
|
// let - can consider the mu-let bindings as mu-letrec bindings - so like as above
|
|
| TExpr_let (bind,e,m,_) ->
|
|
|
|
// For let, preDecs from RHS go before those of bindings, which is collection order
|
|
let z,bind = TransBindingRhs penv z bind
|
|
let binds,rebinds = TransBindings NotRec penv (FlatList.one bind)
|
|
// factor Top* repr binds
|
|
let z,binds = LiftTopBinds NotRec penv z binds
|
|
let z,rebinds = LiftTopBinds NotRec penv z rebinds
|
|
// any lifted PreDecs from binding, if so wrap them...
|
|
let z,pds = ExtractPreDecs z
|
|
TransLinearExpr penv z e (contf << (fun (z,e) ->
|
|
let e = mk_lets_from_Bindings m rebinds e
|
|
z,WrapPreDecs m pds (mk_lets_from_Bindings m binds e)))
|
|
|
|
| _ ->
|
|
contf (TransExpr penv z expr)
|
|
|
|
and TransMethod penv z (TObjExprMethod(slotsig,tps,vs,e,m)) =
|
|
let z = EnterInner z
|
|
let z,e = TransExpr penv z e
|
|
let z = ExitInner z
|
|
z,TObjExprMethod(slotsig,tps,vs,e,m)
|
|
|
|
and TransBindingRhs penv z (TBind(v,e,letSeqPtOpt)) =
|
|
let mustInline = v.MustInline
|
|
let z,e = EnterMustInline mustInline z (fun z -> TransExpr penv z e)
|
|
z,TBind (v,e,letSeqPtOpt)
|
|
|
|
and TransDecisionTree penv z x =
|
|
match x with
|
|
| TDSuccess (es,n) ->
|
|
let z,es = FlatList.fmap (TransExpr penv) z es
|
|
z,TDSuccess(es,n)
|
|
| TDBind (bind,rest) ->
|
|
let z,bind = TransBindingRhs penv z bind
|
|
let z,rest = TransDecisionTree penv z rest
|
|
z,TDBind(bind,rest)
|
|
| TDSwitch (e,cases,dflt,m) ->
|
|
let z,e = TransExpr penv z e
|
|
let TransDecisionTreeCase penv z (TCase (discrim,dtree)) =
|
|
let z,dtree = TransDecisionTree penv z dtree
|
|
z,TCase(discrim,dtree)
|
|
|
|
let z,cases = List.fmap (TransDecisionTreeCase penv) z cases
|
|
let z,dflt = Option.fmap (TransDecisionTree penv) z dflt
|
|
z,TDSwitch (e,cases,dflt,m)
|
|
|
|
and TransDecisionTreeTarget penv z (TTarget(vs,e,spTarget)) =
|
|
let z = EnterInner z
|
|
let z,e = TransExpr penv z e
|
|
let z = ExitInner z
|
|
z,TTarget(vs,e,spTarget)
|
|
|
|
and TransValBinding penv z bind = TransBindingRhs penv z bind
|
|
and TransValBindings penv z binds = FlatList.fmap (TransValBinding penv) z binds
|
|
and TransModuleExpr penv z x =
|
|
match x with
|
|
| TMTyped(mty,def,m) ->
|
|
let z,def = TransModuleDef penv z def
|
|
z,TMTyped(mty,def,m)
|
|
|
|
and TransModuleDefs penv z x = List.fmap (TransModuleDef penv) z x
|
|
and TransModuleDef penv (z:rwstate) x =
|
|
match x with
|
|
| TMDefRec(tycons,binds,mbinds,m) ->
|
|
let z,binds = TransValBindings penv z binds
|
|
let z,mbinds = TransModuleBindings penv z mbinds
|
|
z,TMDefRec(tycons,binds,mbinds,m)
|
|
| TMDefLet(bind,m) ->
|
|
let z,bind = TransValBinding penv z bind
|
|
z,TMDefLet(bind,m)
|
|
| TMDefDo(e,m) ->
|
|
let z,bind = TransExpr penv z e
|
|
z,TMDefDo(e,m)
|
|
| TMDefs(defs) ->
|
|
let z,defs = TransModuleDefs penv z defs
|
|
z,TMDefs(defs)
|
|
| TMAbstract(mexpr) ->
|
|
let z,mexpr = TransModuleExpr penv z mexpr
|
|
z,TMAbstract(mexpr)
|
|
and TransModuleBindings penv z binds = List.fmap (TransModuleBinding penv) z binds
|
|
and TransModuleBinding penv z (TMBind(nm, rhs)) =
|
|
let z,rhs = TransModuleDef penv z rhs
|
|
z,TMBind(nm,rhs)
|
|
|
|
let TransImplFile penv z mv = fmapTImplFile (TransModuleExpr penv) z mv
|
|
|
|
let TransAssembly penv z (TAssembly(mvs)) =
|
|
let z,mvs = List.fmap (TransImplFile penv) z mvs
|
|
TAssembly(mvs)
|
|
(*-------------------------------------------------------------------------
|
|
* pass5: copy_expr
|
|
*-------------------------------------------------------------------------*)
|
|
|
|
let RecreateUniqueBounds g expr =
|
|
copy_ImplFile g OnlyCloneExprVals expr
|
|
|
|
(*-------------------------------------------------------------------------
|
|
* entry point
|
|
*-------------------------------------------------------------------------*)
|
|
|
|
let MakeTLRDecisions ccu g expr =
|
|
try
|
|
// pass1: choose the f to be TLR with arity(f)
|
|
let tlrS,topValS, arityM = Pass1_DetermineTLRAndArities.DetermineTLRAndArities ccu g expr
|
|
|
|
// pass2: determine the typar/freevar closures, f->fclass and fclass declist
|
|
let envM,fclassM,declist,recShortCallS = Pass2_DetermineTLREnvs.DetermineTLREnvs ccu (tlrS,arityM) expr
|
|
|
|
// pass3
|
|
let envPackM = ChooseEnvPackings g ccu fclassM topValS declist envM
|
|
let fHatM = CreateFHatM g ccu tlrS arityM fclassM envPackM
|
|
|
|
// pass4: rewrite
|
|
if verboseTLR then dprintf "TransExpr(rw)------\n";
|
|
let penv = {ccu=ccu; g=g; tlrS=tlrS; topValS=topValS; arityM=arityM; fclassM=fclassM; recShortCallS=recShortCallS; envPackM=envPackM; fHatM=fHatM}
|
|
let z = rws0
|
|
let _,expr = TransImplFile penv z expr
|
|
|
|
// pass5: copy_expr to restore "each bound is unique" property
|
|
// aka, copy_expr
|
|
if verboseTLR then dprintf "copy_expr------\n";
|
|
let expr = RecreateUniqueBounds g expr
|
|
if verboseTLR then dprintf "TLR-done------\n";
|
|
|
|
// Summary:
|
|
// GTL = genuine top-level
|
|
// TLR = TopLevelRep = identified by this pass
|
|
// Note, some GTL are skipped until sort out the initial env...
|
|
// if verboseTLR then dprintf "note: tlr = %d inner-TLR + %d GenuineTopLevel-TLR + %d GenuineTopLevel skipped TLR (public)\n"
|
|
// (lengthS (Zset.diff tlrS topValS))
|
|
// (lengthS (Zset.inter topValS tlrS))
|
|
// (lengthS (Zset.diff topValS tlrS))
|
|
|
|
// DONE
|
|
expr
|
|
with AbortTLR m ->
|
|
warning(Error("Note: Lambda-lifting optimizations have not been applied because of the use of this local constrained generic function as a first class value. Adding type constraints may resolve this condition",m));
|
|
expr
|
|
|