Math.NET Numerics
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.
 
 
 

6210 lines
321 KiB

// (c) Microsoft Corporation. All rights reserved
#light
//--------------------------------------------------------------------------
// The ILX generator.
//
// NOTE: unit have NULL storage (no point storing units).
//--------------------------------------------------------------------------
module (* internal *) Microsoft.FSharp.Compiler.Ilxgen
open System.IO
open Internal.Utilities
open Internal.Utilities.Pervasives
open Microsoft.FSharp.Compiler.AbstractIL
open Microsoft.FSharp.Compiler.AbstractIL.IL
open Microsoft.FSharp.Compiler.AbstractIL.Internal
open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library
open Microsoft.FSharp.Compiler.AbstractIL.Extensions.ILX
open Microsoft.FSharp.Compiler.AbstractIL.Extensions.ILX.Types
open Microsoft.FSharp.Compiler.AbstractIL.Diagnostics
open Microsoft.FSharp.Compiler
open Microsoft.FSharp.Compiler.Tast
open Microsoft.FSharp.Compiler.Tastops
open Microsoft.FSharp.Compiler.Range
open Microsoft.FSharp.Compiler.Ast
open Microsoft.FSharp.Compiler.ErrorLogger
open Microsoft.FSharp.Compiler.PrettyNaming
open Microsoft.FSharp.Compiler.Env
open Microsoft.FSharp.Compiler.Layout
open Microsoft.FSharp.Compiler.Lib
open Microsoft.FSharp.Compiler.Typrelns
module Ilx = Microsoft.FSharp.Compiler.AbstractIL.Extensions.ILX.Types
let verbose = false // (System.Environment.GetEnvironmentVariable("VERBOSE_ILXGEN") <> null)
let generatePublicAsInternal = ref false // flag can be set, see fscopts.ml
let IsNonErasedTypar (tp:Typar) = not tp.IsErased
let DropErasedTypars (tps:Typar list) = tps |> List.filter IsNonErasedTypar
let DropErasedTyargs tys = tys |> List.filter (fun ty -> match ty with TType_measure _ -> false | _ -> true)
let AddSpecialNameFlag (mdef:ILMethodDef) = { mdef with mdSpecialName = true }
let AddNonUserCompilerGeneratedAttribs g (mdef:ILMethodDef) = add_mdef_generated_attrs g.ilg mdef
let debugDisplayMethodName = "__DebugDisplay"
//--------------------------------------------------------------------------
// misc
//--------------------------------------------------------------------------
let i_pop = I_arith AI_pop
let i_nop = I_arith AI_nop
let i_dup = I_arith AI_dup
let i_ldnull = I_arith AI_ldnull
let i_ldc_i32_0 = I_arith (AI_ldc (DT_I4,NUM_I4 0))
let mk_ldc_i64 i = I_arith (AI_ldc (DT_I8,NUM_I8 i))
let mk_ldc_double i = I_arith (AI_ldc (DT_R8,NUM_R8 i))
let mk_ldc_single i = I_arith (AI_ldc (DT_R4,NUM_R4 i))
/// Make a method that simply loads a field
let mk_ldfld_method_def (methnm,reprAccess,stat,ilty,fldName,propType) =
let il_fspec = mk_fspec_in_typ(ilty,fldName,propType)
let ret = mk_return propType
let mdef =
if stat then
mk_static_nongeneric_mdef (methnm,reprAccess,[],ret,MethodBody_il(mk_ilmbody(true,[],2,nonbranching_instrs_to_code([mk_normal_ldsfld il_fspec]),None)))
else
mk_instance_mdef (methnm,reprAccess,[],ret,MethodBody_il(mk_ilmbody (true,[],2,nonbranching_instrs_to_code ([ ldarg_0; mk_normal_ldfld il_fspec]),None)))
mdef |> AddSpecialNameFlag
let ChooseParamNames fieldNamesAndTypes =
let takenFieldNames = fieldNamesAndTypes |> List.map p23 |> Set.of_list
fieldNamesAndTypes
|> List.map (fun (propName,fldName,propType) ->
let lowerPropName = String.uncapitalize propName
let paramName = if takenFieldNames.Contains(lowerPropName) then propName else lowerPropName
paramName,fldName,propType)
let markup s = s |> Seq.mapi (fun i x -> i,x)
// See prim-types.fs
let SourceLevelConstruct_SumType = 1
let SourceLevelConstruct_RecordType = 2
let SourceLevelConstruct_ObjectType = 3
let SourceLevelConstruct_Field = 4
let SourceLevelConstruct_Exception = 5
let SourceLevelConstruct_Closure = 6
let SourceLevelConstruct_Module = 7
let SourceLevelConstruct_Alternative = 8
let SourceLevelConstruct_Value = 9
let SourceLevelConstruct_PrivateRepresentation = 32
// Approximation for purposes of optimization and giving a warning when compiling definition-only files as EXEs
let rec CheckCodeDoesSomething code =
match code with
| ILBasicBlock bb -> Array.fold (fun x i -> x || match i with I_arith (AI_ldnull | AI_nop | AI_pop) | I_ret | I_seqpoint _ -> false | _ -> true) false bb.bblockInstrs
| GroupBlock (_,codes) -> List.exists CheckCodeDoesSomething codes
| RestrictBlock (_,code) -> CheckCodeDoesSomething code
| TryBlock (code,seh) -> true
let ChooseFreeVarNames takenNames ts =
let tns = List.map (fun t -> (t,None)) ts
let rec chooseName names (t,nOpt) =
let tn = match nOpt with None -> t | Some n -> t^string n
if Zset.mem tn names then
chooseName names (t,Some(match nOpt with None -> 0 | Some n -> (n+1)))
else
let names = Zset.add tn names
names,tn
let string_order = (compare : string -> string -> int)
let names = Zset.empty string_order |> Zset.addList takenNames
let names,ts = List.fmap chooseName names tns
ts
let ilxgenGlobalNng = NiceNameGenerator ()
(* cannot tailcall to methods taking byrefs *)
let is_byref = function Type_byref _ -> true | _ -> false
let mainMethName = CompilerGeneratedName "main"
type AttributeDecoder(namedArgs) =
let nameMap = NameMap.of_list (List.map (fun (AttribNamedArg(s,a,b,c)) -> s,c) namedArgs)
let findConst x = match NameMap.tryfind x nameMap with | Some(AttribExpr(_,TExpr_const(c,_,_))) -> Some c | _ -> None
let findAppTr x = match NameMap.tryfind x nameMap with | Some(AttribExpr(_,TExpr_app(_,_,[TType_app(tr,ti)],_,_))) -> Some tr | _ -> None
member self.FindInt16 x dflt = match findConst x with | Some(TConst_int16 x) -> x | _ -> dflt
member self.FindInt32 x dflt = match findConst x with | Some(TConst_int32 x) -> x | _ -> dflt
member self.FindBool x dflt = match findConst x with | Some(TConst_bool x) -> x | _ -> dflt
member self.FindString x dflt = match findConst x with | Some(TConst_string x) -> x | _ -> dflt
member self.FindTypeName x dflt = match findAppTr x with | Some(tr) -> tr.DisplayName | _ -> dflt
//--------------------------------------------------------------------------
// Statistics
//--------------------------------------------------------------------------
let report_ref = ref (fun oc -> ())
let add_report f = let old = report_ref.contents in report_ref := (fun oc -> old oc; f oc)
let report (oc:TextWriter) = report_ref.contents oc
let NewCounter(nm) =
let count = ref 0
add_report (fun oc -> if !count <> 0 then output_string oc (string !count ^ " "^nm^"\n"));
(fun () -> incr count)
let CountClosure = NewCounter "closures"
let CountMethodDef = NewCounter "IL method defintitions corresponding to values"
let CountStaticFieldDef = NewCounter "IL field defintitions corresponding to values"
let CountCallFuncInstructions = NewCounter "callfunc instructions (indirect calls)"
//-------------------------------------------------------------------------
// Part of the last-minute tranformation performed by this file
// is to eliminate variables of static type "unit". These are
// utility functions related to this.
//-------------------------------------------------------------------------
let BindUnitVars g (mvs:Val list,paramInfos,body) =
match mvs,paramInfos with
| [v],[] ->
assert is_unit_typ g v.Type
[], mk_let NoSequencePointAtInvisibleBinding v.Range v (mk_unit g v.Range) body
| _ -> mvs,body
//--------------------------------------------------------------------------
// Compilation environment for compiling a whole a module
//--------------------------------------------------------------------------
[<StructuralEquality(false); StructuralComparison(false)>]
type cenv =
{ g: Env.TcGlobals;
viewCcu: ccu;
fragName: string;
generateFilterBlocks: bool;
workAroundReflectionEmitBugs: bool;
emitConstantArraysUsingStaticDataBlobs:bool;
amap: Import.ImportMap;
(* mainMethodInfo: if this is set, then the last module becomes the "main" module and its toplevel bindings are executed at startup *)
mainMethodInfo: Tast.Attribs option;
localOptimizationsAreOn: bool;
debug: bool;
emptyProgramOk : bool; }
type EmitSequencePointState = SPAlways | SPSuppress
//--------------------------------------------------------------------------
// scope, cloc, visibility
// Referencing other stuff, and descriptions of where items are to be placed
// within the generated IL namespace/typespace. A bit of a mess.
//--------------------------------------------------------------------------
type cloc =
(* cloc = compilation location = path to a ccu, namespace or class *)
{ clocScope: IL.ILScopeRef;
clocTopImplQualifiedName: string;
clocNamespace: string option;
clocEncl: string list;
clocQualifiedNameOfFile : string }
//--------------------------------------------------------------------------
// Access this and other assemblies
//--------------------------------------------------------------------------
let mk_il_name pos n = match pos with [] -> n | _ -> String.concat "." pos^"."^n
let mk_private_name n = (CompilerGeneratedName n)
let scoref_for_cloc cloc = cloc.clocScope
let CompLocForFragment fragName (ccu:ccu) =
{ clocQualifiedNameOfFile =fragName;
clocTopImplQualifiedName= fragName;
clocScope=ccu.ILScopeRef;
clocNamespace=None;
clocEncl=[]}
let CompLocForCcu (ccu:ccu) = CompLocForFragment ccu.AssemblyName ccu
let mk_topname ns n = String.concat "." (match ns with Some x -> [x;n] | None -> [n])
let CompLocForSubModuleOrNamespace cloc (submod:ModuleOrNamespace) =
let n = submod.MangledName
match submod.ModuleOrNamespaceType.ModuleOrNamespaceKind with
| FSharpModuleWithSuffix | FSharpModule -> { cloc with clocEncl= cloc.clocEncl @ [n]}
| Namespace -> {cloc with clocNamespace=Some (mk_topname cloc.clocNamespace n)}
let CompLocForFixedPath fragName qname (CompPath(sref,cpath)) =
let ns,t = List.takeUntil (fun (_,mkind) -> mkind <> Namespace) cpath
let ns = List.map fst ns
let ns = text_of_path ns
let encl = List.map (fun (s ,mkind)-> s) t
if verbose then dprintn ("CompLocForFixedPath, ns = '"^ns^"', encl = '"^text_of_path encl^"'");
let ns = if ns = "" then None else Some ns
{ clocQualifiedNameOfFile =fragName;
clocTopImplQualifiedName=qname;
clocScope=sref;
clocNamespace=ns;
clocEncl=encl }
let CompLocForFixedModule fragName qname (mspec:ModuleOrNamespace) =
let cloc = CompLocForFixedPath fragName qname mspec.CompilationPath
let cloc = CompLocForSubModuleOrNamespace cloc mspec
cloc
let NestedTypeRefForCompLoc cloc n =
match cloc.clocEncl with
| [] ->
let tyname = mk_topname cloc.clocNamespace n
mk_tref(scoref_for_cloc cloc,tyname)
| h::t -> mk_nested_tref(scoref_for_cloc cloc,mk_topname cloc.clocNamespace h :: t,n)
let NestedTypeSpecForCompLoc cloc n tinst =
mk_tspec (NestedTypeRefForCompLoc cloc n,tinst)
let TypeNameForStatupCode cloc = "<StartupCode$"^(cloc.clocQualifiedNameOfFile.Replace(".","-"))^">.$"^cloc.clocTopImplQualifiedName
let TypeNameForPrivateImplementationDetails cloc = "<PrivateImplementationDetails$"^(cloc.clocQualifiedNameOfFile.Replace(".","-"))^">"
let CompLocForStartupCode cloc =
{cloc with clocEncl=[TypeNameForStatupCode cloc];clocNamespace=None}
let CompLocForPrivateImplementationDetails cloc =
{cloc with
clocEncl=[TypeNameForPrivateImplementationDetails cloc];clocNamespace=None}
let rec TypeRefForCompLoc cloc =
match cloc.clocEncl with
| [] ->
mk_tref(scoref_for_cloc cloc,TypeNameForPrivateImplementationDetails cloc)
| [h] ->
let tyname = mk_topname cloc.clocNamespace h
mk_tref(scoref_for_cloc cloc,tyname)
| _ ->
let encl,n = List.frontAndBack cloc.clocEncl
NestedTypeRefForCompLoc {cloc with clocEncl=encl} n
let TypeSpecForCompLoc cloc =
mk_nongeneric_tspec (TypeRefForCompLoc cloc)
// Under --publicasinternal change Public to Internal,
// except when mayChange=false, e.g. for override implementations (no accessibility restriction permitted).
let ComputePublicMemberAccess mayChange = if !generatePublicAsInternal && mayChange then MemAccess_assembly else MemAccess_public
let ComputeMemberAccess mayChange hidden = if hidden then MemAccess_assembly else ComputePublicMemberAccess mayChange
// Under --publicasinternal change types from Public to Private (internal for types)
let ComputePublicTypeAccess() = if !generatePublicAsInternal then TypeAccess_private else TypeAccess_public
let ComputeTypeAccess (tref:ILTypeRef) hidden =
match tref.Enclosing with
| [] -> if hidden then TypeAccess_private else ComputePublicTypeAccess()
| _ -> TypeAccess_nested (ComputeMemberAccess true hidden)
//--------------------------------------------------------------------------
// Representation of type constructors etc.
//
// How are module kinds, type parameters, local type constructors
// etc. are mapped to IL types and IL type variables
//--------------------------------------------------------------------------
[<StructuralEquality(false); StructuralComparison(false)>]
type TypeReprEnv =
{ typar_reprs: (Typar * uint16 (* static_item_repr *) ) list;
typar_count: int; (* How many type variables are in scope? *)
tyenv_nativeptr_as_nativeint: bool (* Do we compile the "nativeptr<'a>" type as a machine integer, e.g. in closures? *) }
//--------------------------------------------------------------------------
// Lookup tyenv
//--------------------------------------------------------------------------
let repr_of_typar m tp tyenv =
try ListAssoc.find typar_ref_eq tp tyenv.typar_reprs
with Not_found ->
errorR(InternalError("Undefined or unsolved type variable: "^showL(TyparL tp),m));
uint16 666 (* random value for post-hoc diagnostic analysis on generated tree *)
//--------------------------------------------------------------------------
// Type parameters and the environment
//--------------------------------------------------------------------------
let add_typar_as tyenv tp y = {tyenv with typar_reprs=(tp,y) :: tyenv.typar_reprs }
let add_typar tyenv (tp:Typar) =
if IsNonErasedTypar tp then { (add_typar_as tyenv tp (uint16 tyenv.typar_count)) with typar_count= tyenv.typar_count + 1 } else tyenv
let add_typars tyenv tps = List.fold add_typar tyenv tps
let empty_tyenv = { typar_count=0;
typar_reprs=[];
tyenv_nativeptr_as_nativeint=false}
let tyenv_for_typars tps = add_typars empty_tyenv tps
let tyenv_for_tycon (tycon:Tycon) = tyenv_for_typars (tycon.TyparsNoRange)
let tyenv_for_tcref tcref = tyenv_for_tycon (deref_tycon tcref)
//--------------------------------------------------------------------------
// Generate type references
//--------------------------------------------------------------------------
let GenTcref (tcref:TyconRef) =
assert(not tcref.IsTypeAbbrev);
tcref.CompiledRepresentation
type VoidNotOK = VoidNotOK | VoidOK
#if DEBUG
let voidCheck m g permits ty =
if permits=VoidNotOK && is_void_typ g ty then
error(InternalError("System.Void unexpectedly detected in IL code generation. This should not occur.",m))
#endif
// When generating parameter and return types generate precise .NET IL pointer types
// These can't be generated for generic instantiations, since .NET generics doesn't
// permit this. But for 'naked' values (locals, parameters, return values etc.) machine
// integer values and native pointer values are compatible (though the code is unverifiable).
type PtrsOK =
| PtrTypesOK
| PtrTypesNotOK
// An F# multi dimension array type "int32[,]" should normally map to the ILDASM type "int32[0...,0...]", just like C#.
//
// However, System.Reflection.Emit has a nasty bug that means it can't emit calls to C# generic methods involving multi-dimensional arrays
// void M<T>(int32[,])
// because MakeGenericMethod on this method returns a handle that causes an invalid call to be emitted by the IL code generator for dynamic assemblies
//
// We have to pay a price here, either:
// -- always emit no bounds, i.e. the ILDASM type "int32[,]" (without lower bounds), and not be able to implement C# virtual slots involving multi-dimensional array types
// -- always emit bounds, i.e. the ILDASM type "int32[0...,0...]" (without lower bounds), and not be able to call C# or F# generic code such as the Array2 module
// -- emit no bounds within the signatures of F# generic methods
// We follow the last one
type MultiDimArrayEmitFlag =
| EmitMultiDimArrayTypesWithoutBounds
| EmitMultiDimArrayTypesNormally
let rec GenTyargAux m g tyenv multiDimFlag tyarg = GenTypeAux m g tyenv VoidNotOK PtrTypesNotOK multiDimFlag tyarg
and GenTyargsAux m g tyenv multiDimFlag tyargs = List.map (GenTyargAux m g tyenv multiDimFlag) (DropErasedTyargs tyargs)
and GenTyAppAux m g tyenv multiDimFlag repr tinst =
let ilTypeInst = GenTyargsAux m g tyenv multiDimFlag tinst
match repr with
| TyrepOpen ty ->
let ty = IL.inst_typ ilTypeInst ty
match multiDimFlag,ty with
| EmitMultiDimArrayTypesWithoutBounds, Type_array(ILArrayShape(arrayBounds),elemTy) when arrayBounds.Length > 1 ->
Type_array(ILArrayShape(arrayBounds |> List.map (fun _ -> (None,None))), elemTy)
| _ -> ty
| TyrepNamed (tref,boxity) -> IL.mk_typ boxity (mk_tspec (tref,ilTypeInst))
and GenNamedTyAppAux m g tyenv ptrsOK multiDimFlag tcref tinst =
let tinst = DropErasedTyargs tinst in
(* See above note on ptrsOK *)
if ptrsOK = PtrTypesOK && tcref_eq g tcref g.nativeptr_tcr then
GenNamedTyAppAux m g tyenv ptrsOK multiDimFlag g.ilsigptr_tcr tinst
else
GenTyAppAux m g tyenv multiDimFlag (GenTcref tcref) tinst
and GenTypeAux m g tyenv voidOK ptrsOK multiDimFlag ty =
#if DEBUG
voidCheck m g voidOK ty;
#endif
(* if verbose then dprintf "generating type '%s'\n" ((DebugPrint.showType ty)); *)
match strip_tpeqns_and_tcabbrevs_and_measureable g ty with
| TType_app(tcref,tinst) -> GenNamedTyAppAux m g tyenv ptrsOK multiDimFlag tcref tinst
| TType_tuple(args) -> GenTypeAux m g tyenv VoidNotOK ptrsOK multiDimFlag (compiled_tuple_ty g args)
| TType_fun(dty,returnTy) -> Pubclo.typ_Func1 g.ilxPubCloEnv (GenTyargAux m g tyenv multiDimFlag dty) (GenTyargAux m g tyenv multiDimFlag returnTy)
| TType_ucase(ucref,args) ->
let cuspec,idx = GenUnionCaseSpec m g tyenv ucref args
EraseIlxClassunions.typ_of_alt cuspec idx
| TType_forall(tps,tau) ->
let tps = DropErasedTypars tps
let tyenv = (add_typars tyenv tps)
List.foldBack (GenGenericParam m g tyenv >> Pubclo.typ_TyFunc g.ilxPubCloEnv) tps (GenTypeAux m g tyenv VoidNotOK ptrsOK multiDimFlag tau)
| TType_var(tp) -> Type_tyvar (repr_of_typar m tp tyenv)
| TType_measure u -> g.ilg.typ_int32
| _ -> failwith "GenTypeAux m: unexpected naked Unknown/Struct/Named type"
and GenGenericParam m g tyenv (tp:Typar) =
let subTypeConstraints = tp.Constraints |> List.choose (function | TTyparCoercesToType(ty,m) -> Some(ty) | _ -> None) |> List.map (GenTypeAux m g tyenv VoidNotOK PtrTypesNotOK EmitMultiDimArrayTypesNormally)
let refTypeConstraint = tp.Constraints |> List.exists (function TTyparIsReferenceType _ -> true | TTyparSupportsNull _ -> true | _ -> false)
let notNullableValueTypeConstraint = tp.Constraints |> List.exists (function TTyparIsNotNullableValueType _ -> true | _ -> false)
let defaultConstructorConstraint = tp.Constraints |> List.exists (function TTyparRequiresDefaultConstructor _ -> true | _ -> false)
{ gpName=
(let nm :string = tp.Name
if nm.TrimEnd([| '0' .. '9' |]).Length = 1 then nm
//elif nm.Length >= 1 && System.Char.IsLower nm.[0] then "T"^nm >= "T" && nm <= "Z" then nm
else "T"^(String.capitalize nm)); (* ^(if tp.IsCompilerGenerated then string tp.Stamp else "") *)
gpConstraints=subTypeConstraints;
gpVariance=NonVariant;
gpReferenceTypeConstraint=refTypeConstraint;
gpNotNullableValueTypeConstraint=notNullableValueTypeConstraint;
gpDefaultConstructorConstraint= defaultConstructorConstraint }
//--------------------------------------------------------------------------
// Generate ILX references to closures, classunions etc. given a tyenv
//--------------------------------------------------------------------------
and GenUnionCaseRef m g tyenv i (fspecs:RecdField array) =
fspecs |> Array.mapi (fun j fspec ->
let fieldDef = IL.mk_instance_fdef(fspec.Name,GenType m g tyenv fspec.FormalType, None, ComputePublicMemberAccess true)
{ fieldDef with
// These properties on the "field" of an alternative end up going on a property generated by cu_erase.ml
fdCustomAttrs = mk_custom_attrs [(mk_CompilationMappingAttrWithVariantNumAndSeqNum g SourceLevelConstruct_Field i j )] } )
and GenUnionRef m g tcref =
let tycon = (deref_tycon tcref)
assert(not tycon.IsTypeAbbrev);
match tycon.UnionInfo with
| None -> failwith "GenUnionRef m"
| Some funion ->
cached funion.funion_ilx_repr (fun () ->
let tyenvinner = tyenv_for_tycon tycon
match tcref.CompiledRepresentation with
| TyrepOpen _ -> failwith "GenUnionRef m: unexpected ASM tyrep"
| TyrepNamed (tref,_) ->
let alternatives =
tycon.UnionCasesArray |> Array.mapi (fun i cspec ->
{ altName=cspec.ucase_il_name;
altCustomAttrs=mk_custom_attrs [];
altFields=GenUnionCaseRef m g tyenvinner i cspec.RecdFieldsArray })
let nullPermitted = IsUnionTypeWithNullAsTrueValue g tycon
Ilx.IlxUnionRef(tref,alternatives,nullPermitted))
and GenUnionSpec m g tyenv tcref tyargs =
let curef = GenUnionRef m g tcref
let tinst = GenTypeArgs m g tyenv tyargs
Ilx.IlxUnionSpec(curef,tinst)
and GenUnionCaseSpec m g tyenv (ucref:UnionCaseRef) tyargs =
let cuspec = GenUnionSpec m g tyenv ucref.TyconRef tyargs
let idx = ucref_index ucref
cuspec, idx
and GenType m g tyenv ty = (GenTypeAux m g tyenv VoidNotOK PtrTypesNotOK EmitMultiDimArrayTypesNormally ty)
and GenTypes m g tyenv tys = List.map (GenType m g tyenv) tys
and GenTypePermitVoid m g tyenv ty = (GenTypeAux m g tyenv VoidOK PtrTypesNotOK EmitMultiDimArrayTypesNormally ty)
and GenTypesPermitVoid m g tyenv tys = List.map (GenTypePermitVoid m g tyenv) tys
and GenTyApp m g tyenv repr tyargs = GenTyAppAux m g tyenv EmitMultiDimArrayTypesNormally repr tyargs
and GenNamedTyApp m g tyenv tcref tinst = GenNamedTyAppAux m g tyenv PtrTypesNotOK EmitMultiDimArrayTypesNormally tcref tinst
/// IL pointer types are only generated for DLL Import signatures *
/// IL void types are only generated for return types
and ComputePtrTypesOK isDllImport = (if isDllImport then PtrTypesOK else PtrTypesNotOK)
and ComputeMultiDimArrayEmitFlag isGeneric =
#if FX_ATLEAST_40
// in Dev10, the CLR has fixed the bug (see comment above "type MultiDimArrayEmitFlag" earlier in this file)
EmitMultiDimArrayTypesNormally
#else
(if isGeneric then EmitMultiDimArrayTypesWithoutBounds else EmitMultiDimArrayTypesNormally)
#endif
and GenReturnType m g tyenv isDllImport isGeneric returnTyOpt =
match returnTyOpt with
| None -> Type_void
| Some returnTy -> GenTypeAux m g tyenv VoidNotOK(*1*) (ComputePtrTypesOK isDllImport) (ComputeMultiDimArrayEmitFlag isGeneric) returnTy (*1: generate void from unit, but not accept void *)
and GenParamType m g tyenv isDllImport isGeneric ty =
ty |> GenTypeAux m g tyenv VoidNotOK (ComputePtrTypesOK isDllImport) (ComputeMultiDimArrayEmitFlag isGeneric)
and GenParamTypes m g tyenv isDllImport isGeneric tys =
tys |> List.map (GenTypeAux m g tyenv VoidNotOK (ComputePtrTypesOK isDllImport) (ComputeMultiDimArrayEmitFlag isGeneric))
and GenTypeArgs m g tyenv tyargs = GenTyargsAux m g tyenv EmitMultiDimArrayTypesNormally tyargs
let GenericParamHasConstraint gp =
nonNil gp.gpConstraints ||
gp.gpVariance <> NonVariant ||
gp.gpReferenceTypeConstraint ||
gp.gpNotNullableValueTypeConstraint ||
gp.gpDefaultConstructorConstraint
let repr_of_named_type cloc nm boxity =
TyrepNamed (NestedTypeRefForCompLoc cloc nm,boxity)
(* Static fields generally go in a private StartupCode section. This is to ensure all static
fields are initialized only in their class constructors (we generate one primary
cctor for each file to ensure initialization coherence across the file, regardless
of how many modules are in the file). This means F# passes an extra check applied by SQL Server when it
verifies stored procedures: SQL Server checks that all 'initionly' static fields are only initialized from
their own class constructor.
However, mutable static fields must be accessible across compilation units. This means we place them in their "natural" location
which may be in a nested module etc. This means mutable static fields can't be used in code to be loaded by SQL Server. *)
let UseGenuineStaticField g (vspec:Val) =
let mut = vspec.IsMutable
let attribs = vspec.Attribs
let hasLiteralAttr = HasAttrib g g.attrib_LiteralAttribute attribs
mut || hasLiteralAttr
let GenFieldSpecForStaticField g ilTypeSpecForProperty vspec cloc fieldName il_ty =
/// Where does the field live?
let tspec =
if UseGenuineStaticField g vspec then ilTypeSpecForProperty
else TypeSpecForCompLoc (CompLocForStartupCode cloc)
mk_fspec_in_boxed_tspec (tspec,fieldName, il_ty)
(* REVIEW: this logic is also duplicated in tc.ml's attribute type checking code *)
let GenFieldName tycon fld = gen_field_name tycon fld
let GenRecdFieldRef m cenv tyenv (rfref:RecdFieldRef) tyargs =
let tyenvinner = tyenv_for_tycon rfref.Tycon
mk_fspec_in_typ(GenTyApp m cenv.g tyenv rfref.TyconRef.CompiledRepresentation tyargs,
GenFieldName rfref.Tycon rfref.RecdField,
GenType m cenv.g tyenvinner rfref.RecdField.FormalType)
let GenExnType m g tyenv (ecref:TyconRef) = GenTyApp m g tyenv ecref.CompiledRepresentation []
//--------------------------------------------------------------------------
// Closure summaries
//--------------------------------------------------------------------------
type arityInfo = int list
[<StructuralEquality(false); StructuralComparison(false)>]
type IlxClosureInfo =
{ clo_expr: expr;
clo_name: string;
clo_arity_info: arityInfo;
clo_formal_il_rty: ILType;
clo_il_frees: Ilx.IlxClosureFreeVar list;
clo_clospec: Ilx.IlxClosureSpec;
clo_attribs: Attribs;
clo_il_gparams: IL.ILGenericParameterDefs;
clo_freevars: Val list; (* nb. the freevars we actually close over *)
clo_lambdas: Ilx.IlxClosureLambdas;
(* local type func support *)
/// The free type parameters occuring in the type of the closure (and not just its body)
/// This is used for local type functions, whose contract class must use these types
/// type Contract<'fv> =
/// abstract DirectInvoke : ty['fv]
/// type Implementation<'fv,'fv2> : Contract<'fv> =
/// override DirectInvoke : ty['fv] = expr['fv,'fv2]
///
/// At the callsite we generate
/// unbox ty['fv]
/// callvirt clo.DirectInvoke
ltyfunc_contract_il_gactuals: ILType list;
ltyfunc_contract_ftyvs: Typar list;
ltyfunc_direct_il_gparams: IL.ILGenericParameterDefs
ltyfunc_internal_ftyvs: Typar list;}
//--------------------------------------------------------------------------
// Representation of term declarations = Environments for compiling expressions.
//--------------------------------------------------------------------------
[<StructuralEquality(false); StructuralComparison(false)>]
type storage =
/// Indicates the value is always null
| Null
/// Indicates the value is not stored, and no value is created
| Unrealized
/// Indicates the value is stored in a static field.
| StaticField of ILFieldSpec * ValRef * (*hasLiteralAttr:*)bool * ILTypeSpec * string * string * ILType * ILMethodRef * ILMethodRef * OptionalShadowLocal
/// Indicates the value is "stored" as a IL static method (in a "main" class for a F#
/// compilation unit, or as a member) according to its inferred or specified arity.
| Method of ValTopReprInfo * ValRef * ILMethodSpec * Range.range * TopArgInfo list * TopArgInfo
/// Indicates the value is stored at the given position in the closure environment accessed via "ldarg 0"
| Env of ILTypeSpec * int * NamedLocalIlxClosureInfo ref option
/// Indicates that the value is an argument of a method being generated
| Arg of int
/// Indicates that the value is stored in local of the method being generated. NamedLocalIlxClosureInfo is normally empty.
/// It is non-empty for 'local type functions', see comments on definition of NamedLocalIlxClosureInfo.
| Local of int * NamedLocalIlxClosureInfo ref option
and OptionalShadowLocal =
| NoShadowLocal
| ShadowLocal of storage
/// The representation of a NamedLocalClosure is based on a cloinfo. However we can't generate a cloinfo until we've
/// decided the representations of other items in the recursive set. Hence we use two phases to decide representations in
/// a recursive set. Yuck.
and NamedLocalIlxClosureInfo =
| NamedLocalIlxClosureInfoGenerator of (ilxGenEnv -> IlxClosureInfo)
| NamedLocalIlxClosureInfoGenerated of IlxClosureInfo
and ModuleStorage =
{ storage_vals: Lazy<NameMap<storage>> ;
storage_submoduls: Lazy<NameMap<ModuleStorage>>; }
/// BranchCallItems are those where a call to the value can be implemented as
/// a branch. At the moment these are only used for generating branch calls back to
/// the entry label of the method currently being generated.
and BranchCallItem =
| BranchCallClosure of arityInfo
| BranchCallMethod of
// Argument counts for compiled form of F# method or value
arityInfo *
// Arg infos for compiled form of F# method or value
(Tast.typ * TopArgInfo) list list *
// Typars for F# method or value
Tast.typars *
// Typars for F# method or value
int *
// num obj args
int
and mark = Mark of ILCodeLabel (* places we can branch to *)
and ilxGenEnv =
{ tyenv: TypeReprEnv;
someTspecInThisModule: ILTypeSpec;
/// Where to place the stuff we're currently generating
cloc: cloc;
/// Hiding information down the signature chain, used to compute what's public to the assembly
sigToImplRemapInfo: (Remap * SignatureHidingInfo) list;
/// All values in scope
valsInScope: ValMap<Lazy<storage>>;
/// For optimizing direct tail recusion to a loop - mark says where to branch to. Length is 0 or 1.
/// REVIEW: generalize to arbitrary nested local loops??
innerVals: (ValRef * (BranchCallItem * mark)) list;
/// Full list of enclosing bound values. First non-compiler-generated element is used to help give nice names for closures and other expressions.
letBoundVars: ValRef list;
/// The set of IL local variable indexes currently in use by lexically scoped variables, to allow reuse on different branches.
/// Really an integer set.
liveLocals: unit Imap.t;
/// Are we under the scope of a try, catch or finally? If so we can't tailcall. SEH = structured exception handling
withinSEH: bool; }
let replace_tyenv tyenv (eenv: ilxGenEnv) = {eenv with tyenv = tyenv}
let env_for_typars tps eenv = replace_tyenv (tyenv_for_typars tps) eenv
let AddTyparsToEnv typars (eenv:ilxGenEnv) = {eenv with tyenv = add_typars eenv.tyenv typars}
let AddSignatureRemapInfo msg (rpi,mhi) eenv =
if verbose then dprintf "AddSignatureRemapInfo, %s, #tycons = %s\n" msg (showL (Layout.sepListL (wordL ";") (List.map tyconL (Zset.elements mhi.mhiTycons))));
if verbose then dprintf "AddSignatureRemapInfo, %s, #rpi.mrpiTycons = %d, #tyconReprs = %s\n" msg (List.length rpi.mrpiTycons) (showL (Layout.sepListL (wordL ";") (List.map tyconL (Zset.elements mhi.mhiTyconReprs))));
if verbose then dprintf "AddSignatureRemapInfo, %s, #vals = %s\n" msg (showL (Layout.sepListL (wordL ";") (List.map valL (Zset.elements mhi.mhiVals))));
if verbose then dprintf "AddSignatureRemapInfo, %s, #rfrefs = %s\n" msg (showL (Layout.sepListL (wordL ";") (List.map recdFieldRefL (Zset.elements mhi.mhiRecdFields))));
{ eenv with sigToImplRemapInfo = (mk_repackage_remapping rpi,mhi) :: eenv.sigToImplRemapInfo }
//--------------------------------------------------------------------------
// Print eenv
//--------------------------------------------------------------------------
let output_storage pps s =
match s with
| StaticField _ -> output_string pps "(top)"
| Method _ -> output_string pps "(top)"
| Local _ -> output_string pps "(local)"
| Arg _ -> output_string pps "(arg)"
| Env _ -> output_string pps "(env)"
| Null -> output_string pps "(null)"
| Unrealized -> output_string pps "(no real value required)"
//--------------------------------------------------------------------------
// Augment eenv with values
//--------------------------------------------------------------------------
let AddStorageForVal g (v,s) eenv =
if verbose then dprintf "adding %s to value table\n" (showL (valL v));
let eenv = { eenv with valsInScope = vspec_map_add v s eenv.valsInScope }
// when compiling fslib also add an entry under the results of a non-local lookup
if g.compilingFslib then
match v.PublicPath with
| None -> eenv
| Some pp ->
match try_deref_val (rescope_val_pubpath g.fslibCcu pp v) with
| None -> eenv
| Some gv ->
if verbose then dprintf "adding remapped %s to value table\n" (showL (valL gv));
{ eenv with valsInScope = vspec_map_add gv s eenv.valsInScope }
else
eenv
let AddStorageForLocalVals g vals eenv = List.foldBack (fun (v,s) acc -> AddStorageForVal g (v,notlazy s) acc) vals eenv
//--------------------------------------------------------------------------
// Lookup eenv
//--------------------------------------------------------------------------
open Microsoft.FSharp.Compiler.AbstractIL
open Microsoft.FSharp.Compiler.AbstractIL.Internal
open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library
let storage_for_val m v eenv =
let v =
try vspec_map_find v eenv.valsInScope
with Not_found ->
(* REVIEW: The binary will probably still be written under these error conditions.
* That is useful when debugging the compiler, but not in Retail mode.
* Fail with an internal error if Retail? *)
(* // Diagnostics for bug://4046
* let vals = eenv.valsInScope.imap |> Zmap.to_list
* vals |> List.iter (printf "v,s = %A\n")
*)
assert false
errorR(Error(sprintf "undefined value: %s" (showL(vspecAtBindL v)),m));
notlazy (Arg 668(* random value for post-hoc diagnostic analysis on generated tree *) )
v.Force()
let storage_for_vref m v eenv = storage_for_val m (deref_val v) eenv
//--------------------------------------------------------------------------
// Imported modules and the environment
//
// How a top level value is represented depends on its type. If it's a
// function or is polymorphic, then it gets represented as a
// method (possibly and instance method). Otherwise it gets represented as a
// static field.
//--------------------------------------------------------------------------
let vref_isDllImport g (vref:ValRef) =
vref.Attribs |> HasAttrib g g.attrib_DllImportAttribute
let GetMethodSpecForMemberVal g memberInfo (vref:ValRef) =
let m = vref.Range
let tps,curriedArgInfos,returnTy,retInfo =
assert(vref.TopValInfo.IsSome);
GetTopValTypeInCompiledForm g (the vref.TopValInfo) vref.Type m
let tyenv_under_typars = tyenv_for_typars tps
let flatArgInfos = List.concat curriedArgInfos
let isDllImport = vref_isDllImport g vref
let isCtor = (memberInfo.MemberFlags.MemberKind = MemberKindConstructor)
let cctor = (memberInfo.MemberFlags.MemberKind = MemberKindClassConstructor)
let parentTcref = vref.MemberActualParent
let parentTypars = parentTcref.TyparsNoRange
let numParentTypars = parentTypars.Length
if tps.Length < numParentTypars then error(InternalError("CodeGen check: type checking did not ensure that this method is sufficiently generic", m));
let ctps,mtps = List.chop numParentTypars tps
let isGeneric = nonNil (DropErasedTypars tps)
let isCompiledAsInstance = ValRefIsCompiledAsInstanceMember g vref
let ilActualRetTy =
let ilRetTy = GenReturnType m g tyenv_under_typars isDllImport isGeneric returnTy
if isCtor || cctor then Type_void else ilRetTy
let ilTy = GenType m g tyenv_under_typars (mk_tyapp_ty parentTcref (List.map mk_typar_ty ctps))
if isCompiledAsInstance || isCtor then
// Find the 'this' argument type if any
let thisTy,flatArgInfos =
if isCtor then (GetFSharpViewOfReturnType g returnTy),flatArgInfos
else
match flatArgInfos with
| [] -> error(InternalError("This instance method '"^vref.MangledName^"' has no arguments", m))
| (h,_):: t -> h,t
let thisTy = if is_byref_typ g thisTy then dest_byref_typ g thisTy else thisTy
let thisArgTys = tinst_of_stripped_typ g thisTy
if ctps.Length <> thisArgTys.Length then
warning(InternalError(Printf.sprintf "CodeGen check: type checking did not quantify the correct number of type variables for this method, #parentTypars = %d, #ctps = %d, #mtps = %d, #thisArgTys = %d" numParentTypars ctps.Length mtps.Length (List.length thisArgTys),m))
else
List.iter2
(fun gtp ty2 ->
if not (type_equiv g (mk_typar_ty gtp) ty2) then
warning(InternalError("CodeGen check: type checking did not quantify the correct type variables for this method: generalization list contained "^gtp.Name^"#"^string gtp.Stamp^" and list from 'this' pointer contained "^ (showL(typeL ty2)), m)))
ctps
thisArgTys;
let methodArgTys,paramInfos = List.unzip flatArgInfos
let ilMethodArgTys = GenParamTypes m g tyenv_under_typars isDllImport isGeneric methodArgTys
let ilMethodInst = GenTypeArgs m g tyenv_under_typars (List.map mk_typar_ty mtps)
let mspec = mk_instance_mspec_in_typ (ilTy,memberInfo.CompiledName,ilMethodArgTys,ilActualRetTy,ilMethodInst)
mspec,ctps,mtps,paramInfos,retInfo
else
let methodArgTys,paramInfos = List.unzip flatArgInfos
let ilMethodArgTys = GenParamTypes m g tyenv_under_typars isDllImport isGeneric methodArgTys
let ilMethodInst = GenTypeArgs m g tyenv_under_typars (List.map mk_typar_ty mtps)
let mspec = mk_static_mspec_in_typ (ilTy,memberInfo.CompiledName,ilMethodArgTys,ilActualRetTy,ilMethodInst)
mspec,ctps,mtps,paramInfos,retInfo
// This called via 2 routes.
// (a) alloc_or_import_{ccu,modval,top_vspec} - for vref from modulespec mtyp_vals.
// (b) AllocStorageForBind - if arity specified for vref. This route includes some compiler generated temporaries.
//
/// This function decides the storage for the val.
/// The decision is based on arityInfo.
let ComputeStorageForTopVal g optShadowLocal (vref:ValRef) cloc =
if is_unit_typ g vref.Type && not vref.IsMemberOrModuleBinding && not vref.IsMutable then Null else
let topValInfo =
match vref.TopValInfo with
| None -> error(InternalError("ComputeStorageForTopVal: no arity found for "^showL(ValRefL vref),vref.Range))
| Some a -> a
let m = vref.Range
let nm = vref.UniqueCompiledName
// This call to GetTopValTypeInFSharpForm is only needed to determine if this is a (type) function or a value
// We should just look at the arity
match GetTopValTypeInFSharpForm g topValInfo vref.Type vref.Range with
| [],[], returnTy,retInfo when not vref.IsMember ->
// Mutable and literal static fields must have stable names and live in the "public" location
// See notes on GenFieldSpecForStaticField above.
let vspec = (deref_val vref)
let fieldName = if UseGenuineStaticField g vspec then nm else ilxgenGlobalNng.FreshCompilerGeneratedName (nm,m)
let il_ty = GenType m g empty_tyenv returnTy in (* empty_tyenv ok: not a field in a generic class *)
let ilTypeSpecForProperty = TypeSpecForCompLoc cloc
let attribs = vspec.Attribs
let mut = vspec.IsMutable
let hasLiteralAttr = HasAttrib g g.attrib_LiteralAttribute attribs
let tref = ilTypeSpecForProperty.TypeRef
let ilGetterMethRef = mk_mref(tref,ILCallingConv.Static,"get_"^nm,0,[],il_ty)
let ilSetterMethRef = mk_mref(tref,ILCallingConv.Static,"set_"^nm,0,[il_ty],Type_void)
let fspec = GenFieldSpecForStaticField g ilTypeSpecForProperty vspec cloc fieldName il_ty
StaticField (fspec,vref,hasLiteralAttr,ilTypeSpecForProperty,fieldName,nm,il_ty,ilGetterMethRef,ilSetterMethRef,optShadowLocal)
| _ ->
match vref.MemberInfo with
| Some(memberInfo) when not vref.IsExtensionMember ->
let mspec,_,_,paramInfos,retInfo = GetMethodSpecForMemberVal g memberInfo vref
Method (topValInfo,vref,mspec, m,paramInfos,retInfo)
| _ ->
let (tps,curriedArgInfos,returnTy,retInfo) = GetTopValTypeInCompiledForm g topValInfo vref.Type m
let tyenv_under_typars = tyenv_for_typars tps
let (methodArgTys,paramInfos) = curriedArgInfos |> List.concat |> List.unzip
let isDllImport = vref_isDllImport g vref
let isGeneric = nonNil (DropErasedTypars tps)
let ilMethodArgTys = GenParamTypes m g tyenv_under_typars isDllImport isGeneric methodArgTys
let ilRetTy = GenReturnType m g tyenv_under_typars isDllImport isGeneric returnTy
let tspec = TypeSpecForCompLoc cloc
let ilMethodInst = GenTypeArgs m g tyenv_under_typars (List.map mk_typar_ty tps)
let mspec = mk_static_mspec_in_boxed_tspec (tspec,nm,ilMethodArgTys,ilRetTy,ilMethodInst)
Method(topValInfo,vref,mspec, m,paramInfos,retInfo)
let ComputeAndAddStorageForLocalTopVal g cloc optShadowLocal (v:Val) eenv =
let storage = ComputeStorageForTopVal g optShadowLocal (mk_local_vref v) cloc
AddStorageForVal g (v,notlazy storage) eenv
let ComputeStorageForNonLocalTopVal g cloc modref (v:Val) =
//if inline_info_of_val v = PseudoValue then Unrealized else
match v.TopValInfo with
| None -> error(InternalError("ComputeStorageForNonLocalTopVal, expected an arity for "^v.MangledName,v.Range))
| Some _ -> ComputeStorageForTopVal g NoShadowLocal (mk_vref_in_modref modref v) cloc
let rec ComputeStorageForNonLocalModuleOrNamespaceRef g cloc (modref:ModuleOrNamespaceRef) (modul:ModuleOrNamespace) acc =
if verbose then dprintn ("ComputeStorageForNonLocalModuleOrNamespaceRef for module "^demangled_name_of_modul modul);
NameMap.foldRange
(fun v acc -> AddStorageForVal g (v, lazy (ComputeStorageForNonLocalTopVal g cloc modref v)) acc)
modul.ModuleOrNamespaceType.AllValuesAndMembers
(NameMap.foldRange
(fun smodul acc -> ComputeStorageForNonLocalModuleOrNamespaceRef g (CompLocForSubModuleOrNamespace cloc smodul) (MakeNestedTcref modref smodul) smodul acc)
modul.ModuleOrNamespaceType.ModulesAndNamespacesByDemangledName
acc)
let ComputeStorageForExternalCcu g eenv (ccu:ccu) =
if not ccu.IsFSharp then eenv else
let cloc = CompLocForCcu ccu
if verbose then dprintn ("ComputeStorageForExternalCcu, ccu = "^ccu.AssemblyName);
let eenv =
NameMap.foldRange
(fun smodul acc ->
let cloc = CompLocForSubModuleOrNamespace cloc smodul
let modref = mk_nonlocal_ccu_top_tcref ccu smodul
ComputeStorageForNonLocalModuleOrNamespaceRef g cloc modref smodul acc)
ccu.TopModulesAndNamespaces
eenv
eenv
let rec AddBindingsForLocalModuleType allocVal cloc eenv (mty:ModuleOrNamespaceType) =
let eenv = NameMap.foldRange (fun submodul eenv -> AddBindingsForLocalModuleType allocVal (CompLocForSubModuleOrNamespace cloc submodul) eenv submodul.ModuleOrNamespaceType) mty.ModulesAndNamespacesByDemangledName eenv
let eenv = NameMap.foldRange (allocVal cloc) mty.AllValuesAndMembers eenv
eenv
let AddExternalCcusToIlxGenEnv g eenv ccus = List.fold (ComputeStorageForExternalCcu g) eenv ccus
let AddBindingsForTycon allocVal (cloc:cloc) (tycon:Tycon) eenv =
let unrealized_slots =
if tycon.IsFSharpObjectModelTycon
then tycon.FSharpObjectModelTypeInfo.fsobjmodel_vslots
else []
List.foldBack (fun vref eenv -> allocVal cloc (deref_val vref) eenv) unrealized_slots eenv
let rec AddBindingsForModuleDefs allocVal (cloc:cloc) eenv mdefs =
List.fold (AddBindingsForModuleDef allocVal cloc) eenv mdefs
and AddBindingsForModuleDef allocVal cloc eenv x =
match x with
| TMDefRec(tycons,vbinds,mbinds,m) ->
let eenv = FlatList.foldBack (allocVal cloc) (vars_of_Bindings vbinds) eenv
(* Virtual don't have 'let' bindings and must be added to the environment *)
let eenv = List.foldBack (AddBindingsForTycon allocVal cloc) tycons eenv
let eenv = List.foldBack (AddBindingsForSubModules allocVal cloc) mbinds eenv
eenv
| TMDefLet(bind,m) ->
allocVal cloc bind.Var eenv
| TMDefDo(e,m) ->
eenv
| TMAbstract(TMTyped(mtyp,_,_)) ->
AddBindingsForLocalModuleType allocVal cloc eenv mtyp
| TMDefs(mdefs) ->
AddBindingsForModuleDefs allocVal cloc eenv mdefs
and AddBindingsForSubModules allocVal cloc (TMBind(mspec, mdef)) eenv =
let cloc =
if mspec.IsNamespace then cloc
else CompLocForFixedModule cloc.clocQualifiedNameOfFile cloc.clocTopImplQualifiedName mspec
AddBindingsForModuleDef allocVal cloc eenv mdef
and AddBindingsForModuleTopVals g allocVal cloc eenv vs =
FlatList.foldBack allocVal vs eenv
// Put the partial results for a generated fragment (i.e. a part of a CCU generated by FSI)
// into the stored results for the whole CCU.
// isIncrementalExtension = true --> "#use or typed input"
// isIncrementalExtension = false --> "#load"
let AddIncrementalLocalAssmblyFragmentToIlxGenEnv isIncrementalExtension g ccu fragName eenv (TAssembly impls) =
let cloc = CompLocForFragment fragName ccu
let allocVal cloc v = ComputeAndAddStorageForLocalTopVal g cloc NoShadowLocal v
List.fold (fun eenv (TImplFile(qname,_,mexpr)) ->
let cloc = { cloc with clocTopImplQualifiedName = qname.Text }
if isIncrementalExtension then
match mexpr with
| TMTyped(_,mdef,_) -> AddBindingsForModuleDef allocVal cloc eenv mdef
(* | TMTyped(mtyp,_,m) -> error(Error("don't expect inner defs to have a constraint",m)) *)
else
AddBindingsForLocalModuleType allocVal cloc eenv (mtyp_of_mexpr mexpr)
) eenv impls
//--------------------------------------------------------------------------
// Generate debugging marks
//--------------------------------------------------------------------------
let GenILSourceMarker g m =
Some (ILSourceMarker.Create(document=g.memoize_file (file_idx_of_range m),
line=start_line_of_range m,
/// NOTE: .NET && VS measure first column as column 1
column= (start_col_of_range m)+1,
endLine= (end_line_of_range m),
endColumn=(end_col_of_range m)+1))
let GenPossibleILSourceMarker cenv m = if cenv.debug then GenILSourceMarker cenv.g m else None
//--------------------------------------------------------------------------
// Helpers for merging property definitions
//--------------------------------------------------------------------------
let hashtbl_range ht =
let res = ref []
Hashtbl.iter (fun _ x -> res := x :: !res) ht;
!res
let merge_options m o1 o2 =
match o1,o2 with
| Some x, None | None, Some x -> Some x
| None, None -> None
| Some x, Some _ ->
#if DEBUG
// This warning fires on some code that also triggers this warning:
// warning(Error("The implementation of a specified generic interface required a method implementation not fully supported by F# Interactive. In the unlikely event that the resulting class fails to load then compile the interface type into a statically-compiled DLL and reference it using '#r'",m));
// THe code is OK so we don't print this.
errorR(InternalError("merge_options: two values given",m));
#endif
Some x
let merge_pdefs m pd pdef =
{pd with propGet=merge_options m pd.propGet pdef.propGet;
propSet=merge_options m pd.propSet pdef.propSet; }
let add_pdef_to_hash m ht pdef =
let nm = pdef.propName
if Hashtbl.mem ht nm then
let pd = Hashtbl.find ht nm
Hashtbl.replace ht nm (merge_pdefs m pd pdef)
else
Hashtbl.add ht nm pdef
/// Merge a whole group of properties all at once
let merge_pdef_list m propertyDefs =
let ht = Hashtbl.create 0
propertyDefs |> List.iter (add_pdef_to_hash m ht);
hashtbl_range ht
//--------------------------------------------------------------------------
// Buffers for compiling modules. The entire assembly gets compiled via an AssemblyBuilder
//--------------------------------------------------------------------------
/// Information collected imperatively for each type definition
type TypeDefBuilder(tdef) =
let gbasic = tdef
let gmethods = new ResizeArray<ILMethodDef>(0)
let gfields = new ResizeArray<ILFieldDef>(0)
let gproperties : Hashtbl.t<string,ILPropertyDef> = Hashtbl.create 0
let gevents = new ResizeArray<ILEventDef>(0)
let gnested = new TypeDefsBuilder()
member b.Close() =
{ tdef with
tdMethodDefs = mk_mdefs (dest_mdefs tdef.tdMethodDefs @ ResizeArray.to_list gmethods);
tdFieldDefs = mk_fdefs (dest_fdefs tdef.tdFieldDefs @ ResizeArray.to_list gfields);
tdProperties = mk_properties (dest_pdefs tdef.tdProperties @ hashtbl_range gproperties);
tdEvents = mk_events (dest_edefs tdef.tdEvents @ ResizeArray.to_list gevents);
tdNested = mk_tdefs (dest_tdefs tdef.tdNested @ gnested.Close()) }
member b.AddEventDef(edef) = ResizeArray.add gevents edef
member b.AddFieldDef(fieldDef) = ResizeArray.add gfields fieldDef
member b.AddMethodDef(mdef) = ResizeArray.add gmethods mdef
member b.NestedTypeDefs = gnested
member b.GetCurrentFields() = gfields |> Seq.readonly
/// Merge Get and Set property nodes, which we generate independently for F# code
/// when we come across their corresponding methods.
member b.AddOrMergePropertyDef(pdef,m) = add_pdef_to_hash m gproperties pdef
member b.PrependInstructionsToSpecificMethodDef(cond,instrs,tag) =
match ResizeArray.tryfind_index cond gmethods with
| Some idx -> gmethods.[idx] <- prepend_instrs_to_mdef instrs gmethods.[idx]
| None -> gmethods.Add(mk_cctor (mk_impl (false,[],1,nonbranching_instrs_to_code instrs,tag)))
and TypeDefsBuilder() =
let tdefs : Hashtbl.t<string,TypeDefBuilder> = Hashtbl.create 0
member b.Close() = tdefs |> hashtbl_range |> List.map (fun b -> b.Close())
member b.FindTypeDefBuilder(nm) =
try Hashtbl.find tdefs nm
with Not_found -> failwith ("find_gtdef: "^nm^" not found")
member b.FindNestedTypeDefsBuilder(path) =
List.fold (fun (acc:TypeDefsBuilder) x -> acc.FindTypeDefBuilder(x).NestedTypeDefs) b path
member b.FindNestedTypeDefBuilder(tref:ILTypeRef) =
b.FindNestedTypeDefsBuilder(tref.Enclosing).FindTypeDefBuilder(tref.Name)
member b.AddTypeDef(tdef:ILTypeDef) =
Hashtbl.add tdefs tdef.tdName (new TypeDefBuilder(tdef))
/// Assembly generation buffers
type AssemblyBuilder(cenv:cenv) as mgbuf =
// The Abstract IL table of types
let gtdefs= new TypeDefsBuilder()
// The definitions of top level values, as quotations.
let mutable reflectedDefinitions : (Tast.Val * Tast.expr) list = []
// A memoization table for generating value types for big constant arrays
let vtgenerator=
new MemoizationTable<(cloc * int) , ILTypeSpec>
(fun (cloc,size) ->
let name = mk_private_name ("T" ^ string(new_uniq()) ^ "_" ^ string size ^ "Bytes") // Type names ending ...$T<unique>_37Bytes
let vtdef = mk_rawdata_vtdef cenv.g.ilg (name,size,0us)
let vtspec = NestedTypeSpecForCompLoc cloc vtdef.tdName []
let vtref = vtspec.TypeRef
let vtdef = {vtdef with tdAccess= ComputeTypeAccess vtref true}
mgbuf.AddTypeDef(vtref,vtdef);
vtspec)
let mutable explicitEntryPointInfo : ILTypeRef option = None
member mgbuf.GenerateRawDataValueType(cloc,size) =
// Byte array literals require a ValueType of size the required number of bytes.
// With fsi.exe, S.R.Emit TypeBuilder CreateType has restrictions when a ValueType VT is nested inside a type T, and T has a field of type VT.
// To avoid this situation, these ValueTypes are generated under the private implementation rather than in the current cloc. [was bug 1532].
let cloc = CompLocForPrivateImplementationDetails cloc
vtgenerator.Apply((cloc,size))
member mgbuf.AddTypeDef(tref:ILTypeRef,tdef) =
gtdefs.FindNestedTypeDefsBuilder(tref.Enclosing).AddTypeDef(tdef)
member mgbuf.GetCurrentFields(tref:ILTypeRef) =
gtdefs.FindNestedTypeDefBuilder(tref).GetCurrentFields();
member mgbuf.AddReflectedDefinition(vspec,expr) =
reflectedDefinitions <- (vspec,expr) :: reflectedDefinitions
member mgbuf.AddMethodDef(tref:ILTypeRef,mdef) =
gtdefs.FindNestedTypeDefBuilder(tref).AddMethodDef(mdef);
if mdef.mdEntrypoint then
explicitEntryPointInfo <- Some(tref)
member mgbuf.AddExplicitInitToSpecificMethodDef(cond,tref,fspec,m) =
let instrs =
[ mk_ldc_i32 0;
mk_normal_stsfld fspec;
mk_normal_ldsfld fspec;
i_pop]
gtdefs.FindNestedTypeDefBuilder(tref).PrependInstructionsToSpecificMethodDef(cond,instrs,m)
member mgbuf.AddEventDef(tref,edef) =
gtdefs.FindNestedTypeDefBuilder(tref).AddEventDef(edef)
member mgbuf.AddFieldDef(tref,fieldDef) =
gtdefs.FindNestedTypeDefBuilder(tref).AddFieldDef(fieldDef)
member mgbuf.AddOrMergePropertyDef(tref,pdef,m) =
gtdefs.FindNestedTypeDefBuilder(tref).AddOrMergePropertyDef(pdef,m)
member mgbuf.Close() = gtdefs.Close(), reflectedDefinitions
member mgbuf.cenv = cenv
member mgbuf.GetExplicitEntryPointInfo() = explicitEntryPointInfo
let code_label_of_mark (Mark(lab)) = lab
/// Record the types of the things on the evaluation stack.
/// Used for the few times we have to flush the IL evaluation stack and to compute maxStack.
type pushpop =
| Push of ILType
| Pop
let push ty = Push ty
/// Buffers for IL code generation
type CodeGenBuffer(m:range,
mgbuf: AssemblyBuilder,
methodName,
entryPointInfo: (ValRef * BranchCallItem) list,
alreadyUsedArgs:int,
alreadyUsedLocals:int,
zapFirstSeqPointToStart:bool) =
let locals: ResizeArray<((string * (mark * mark)) list * ILType)> = ResizeArray.create 10
let codebuf : ResizeArray<ILInstr> = ResizeArray.create 200
let exnSpecs: ResizeArray<ILExceptionSpec> = ResizeArray.create 10
// Keep track of the current stack so we can spill stuff when we hit a "try" when some stuff
// is on the stack.
let mutable stack : ILType list = []
let mutable nstack=0
let mutable maxStack=0
let mutable seqpoint= None
let codeLabels : Hashtbl.t<ILCodeLabel,int> =Hashtbl.create 10
let mutable lastSeqPoint = None
// Add a nop to make way for the first sequence point. There is always such a
// sequence point even when zapFirstSeqPointToStart=false
do if mgbuf.cenv.debug then codebuf.Add(i_nop);
member cgbuf.DoAction a =
match a with
| Push ty ->
stack <- ty :: stack;
nstack <- nstack + 1;
maxStack <- Operators.max maxStack nstack
| Pop ->
match stack with
| [] ->
warning(InternalError("pop on empty stack during code generation\n",m));
| h::t ->
stack <- t;
nstack <- nstack - 1
member cgbuf.GetCurrentStack() = stack
member cgbuf.AssertEmptyStack() =
if nonNil stack then warning(InternalError("stack flush didn't work, or extraneous expressions left on stack before stack restore",m));
()
member cgbuf.EmitInstr(pps,i) =
pps |> List.iter cgbuf.DoAction;
ResizeArray.add codebuf i
member cgbuf.EmitInstrs (pps,is) =
pps |> List.iter cgbuf.DoAction;
is |> List.iter (ResizeArray.add codebuf)
member cgbuf.GetLastSequencePoint() =
lastSeqPoint
member cgbuf.EmitSeqPoint(src) =
if mgbuf.cenv.debug then
// Always add a nop between sequence points to help .NET get the stepping right
if (codebuf.Count > 0 && match codebuf.[codebuf.Count-1] with I_seqpoint _ -> true | _ -> false) then
codebuf.Add(i_nop);
let attr = GenILSourceMarker mgbuf.cenv.g src
assert(isSome(attr));
let i = I_seqpoint (the attr)
codebuf.Add(i);
// Save the first sequence point away to snap it to the top of the method
match seqpoint with
| Some _ -> ()
| None -> seqpoint <- Some i
// Save the last sequence point away so we can make a decision graph look consistent (i.e. reassert the sequence point at each target)
lastSeqPoint <- Some src
member cgbuf.EmitExceptionClause(clause) =
exnSpecs.Add clause
member cgbuf.EmitDelayMark(nm) =
let lab = generate_code_label()
//if verbose then dprintf " --> generated code label %s with name %s\n" (string_of_code_label lab) nm;
Mark lab
member cgbuf.SetCodeLabelToPC(lab,pc) =
//if verbose then dprintf " --> setting label %s to pc %d\n" (string_of_code_label lab) pc;
if codeLabels.ContainsKey(lab) then
warning(InternalError(sprintf "two values for given for label %s" (string_of_code_label lab),m));
codeLabels.[lab] <- pc
member cgbuf.SetMark (Mark lab1,Mark lab2) =
let pc =
try codeLabels.[lab2]
with Not_found ->
error(InternalError(sprintf "cgbuf.SetMark code label has no pc specified yet",m))
cgbuf.SetCodeLabelToPC(lab1,pc)
member cgbuf.SetMarkToHere (Mark lab) =
cgbuf.SetCodeLabelToPC(lab,codebuf.Count)
member cgbuf.SetStack(s) =
stack <- s;
nstack <- List.length s
member cgbuf.Mark(s) =
let res = cgbuf.EmitDelayMark(s)
cgbuf.SetMarkToHere(res);
res
member cgbuf.mgbuf = mgbuf
member cgbuf.MethodName = methodName
member cgbuf.PreallocatedArgCount = alreadyUsedArgs
member cgbuf.AllocLocal(ranges,ty) =
let j = locals.Count
locals.Add((ranges,ty));
j
member cgbuf.ReallocLocal(cond,ranges,ty) =
let j =
match ResizeArray.tryfind_indexi cond locals with
| Some j ->
let (prevRanges,prevType) = locals.[j]
locals.[j] <- ((ranges@prevRanges),ty);
j
| None ->
cgbuf.AllocLocal(ranges,ty)
let j = j + alreadyUsedLocals
j
member cgbuf.Close() =
let instrs = ResizeArray.to_array codebuf
let instrs =
// If we omitted ANY sequence points, then promote the first sequence point to be the first instruction in the
// method. A bit ugly but .NET debuggers only honour "step into" if the sequence point is the first in the method.
//
match seqpoint with
| Some(I_seqpoint sp as i) ->
let i =
if zapFirstSeqPointToStart then
i
else
// This special dummy sequence point seems to be the magic to indicate that the head of the
// method has no sequence point
I_seqpoint (ILSourceMarker.Create(document = sp.Document,
line = 0x00feefee,
column = 0,
endLine = 0x00feefee,
endColumn = 0))
// Note we use physical equality '==' to compare the instruction objects. Nasty.
instrs |> Array.mapi (fun idx i2 -> if idx = 0 then i else if i == i2 then i_nop else i2)
| _ ->
instrs
ResizeArray.to_list locals ,
maxStack,
codeLabels,
instrs,
ResizeArray.to_list exnSpecs,
isSome seqpoint
module CG =
let EmitInstr (cgbuf:CodeGenBuffer) pps i = cgbuf.EmitInstr(pps,i)
let EmitInstrs (cgbuf:CodeGenBuffer) pps is = cgbuf.EmitInstrs(pps,is)
let EmitSeqPoint (cgbuf:CodeGenBuffer) src = cgbuf.EmitSeqPoint(src)
let EmitDelayMark (cgbuf:CodeGenBuffer) nm = cgbuf.EmitDelayMark(nm)
let SetMark (cgbuf:CodeGenBuffer) m1 m2 = cgbuf.SetMark(m1,m2)
let SetMarkToHere (cgbuf:CodeGenBuffer) m1 = cgbuf.SetMarkToHere(m1)
let SetStack (cgbuf:CodeGenBuffer) s = cgbuf.SetStack(s)
let GenerateMark (cgbuf:CodeGenBuffer) s = cgbuf.Mark(s)
open CG
//--------------------------------------------------------------------------
// Compile constants
//--------------------------------------------------------------------------
let GenString cenv cgbuf m s =
CG.EmitInstrs cgbuf [Push cenv.g.ilg.typ_String] [ I_ldstr s ]
let GenConstArray cenv (cgbuf:CodeGenBuffer) eenv m ilElementType (data:'a[]) (write : Bytes.Bytebuf.t -> 'a -> unit) =
let buf = Bytes.Bytebuf.create data.Length
data |> Array.iter (write buf);
let bytes = Bytes.Bytebuf.close buf
let ilArrayType = mk_sdarray_ty ilElementType
let len = data.Length
if data.Length = 0 then
CG.EmitInstrs cgbuf [Push cenv.g.ilg.typ_int32; Push ilArrayType; Pop] [ mk_ldc_i32 (0); I_newarr (Rank1ArrayShape,ilElementType); ]
else
let vtspec = cgbuf.mgbuf.GenerateRawDataValueType(eenv.cloc,bytes.Length)
let fldName = mk_private_name ("field"^string(new_uniq()))
let fty = Type_value vtspec
let fieldDef = mk_static_fdef (fldName,fty, None, Some bytes, MemAccess_assembly)
let fieldDef = { fieldDef with fdCustomAttrs = mk_custom_attrs [ mk_DebuggerBrowsableNeverAttribute cenv.g.ilg ] }
let fspec = mk_fspec_in_boxed_tspec (TypeSpecForCompLoc eenv.cloc,fldName, fty)
CountStaticFieldDef();
cgbuf.mgbuf.AddFieldDef(fspec.EnclosingTypeRef,fieldDef);
CG.EmitInstrs cgbuf
[ Push cenv.g.ilg.typ_int32; Pop; Push ilArrayType;
Push ilArrayType; Push cenv.g.ilg.typ_RuntimeFieldHandle;
Pop; Pop]
[ mk_ldc_i32 data.Length;
I_newarr (Rank1ArrayShape,ilElementType);
i_dup;
I_ldtoken (Token_field fspec);
mk_normal_call (mspec_RuntimeHelpers_InitializeArray cenv.g.ilg) ]
//--------------------------------------------------------------------------
// We normally generate in the context of a "what to do next" continuation
//--------------------------------------------------------------------------
type sequel =
| EndFilter (* integer says which local to save result in *)
| LeaveHandler of (bool (* finally? *) * int * mark) (* integer says which local to save result in *)
| Br of mark
| CmpThenBrOrContinue of pushpop list * ILInstr
| Continue
| DiscardThen of sequel
| Return
| EndLocalScope of sequel * mark (* used at end of 'let' and 'let rec' blocks to get tail recursive setting of end-of-scope marks *)
(*
| DiscardAndBr of mark
| discardAndReturnVoid
*)
| ReturnVoid
let discard = DiscardThen Continue
let discardAndReturnVoid = DiscardThen ReturnVoid
//-------------------------------------------------------------------------
// This is the main code generation routine. It is used to generate
// the bodies of methods in a couple of places
//-------------------------------------------------------------------------
let CodeGenThen cenv mgbuf (zapFirstSeqPointToStart,entryPointInfo,methodName,eenv,alreadyUsedArgs,alreadyUsedLocals,codeGenFunction,m) =
let cgbuf = new CodeGenBuffer(m,mgbuf,methodName,entryPointInfo,alreadyUsedArgs,alreadyUsedLocals,zapFirstSeqPointToStart)
let start = CG.GenerateMark cgbuf "mstart"
let innerVals = entryPointInfo |> List.map (fun (v,kind) -> (v,(kind,start)))
(* Call the given code generator *)
codeGenFunction cgbuf {eenv with withinSEH=false;
liveLocals=Imap.empty();
innerVals = innerVals};
let finish = CG.GenerateMark cgbuf "mfinish"
let locals,maxStack,codeLabels,code,exnSpecs,hasSequencePoints = cgbuf.Close()
let localDebugSpecs =
locals
|> List.mapi (fun i (nms,_) -> List.map (fun nm -> (i,nm)) nms)
|> List.concat
|> List.map (fun (i,(nm,(start,finish))) ->
{ locRange=(code_label_of_mark start, code_label_of_mark finish);
locInfos= [{ localNum=i; localName=nm }] })
if debug && List.length locals > 64 then dprintn ("Note, method "^methodName^" has "^string (List.length locals)^" locals (even before conversion from ILX to IL).");
(List.map (snd >> IL.mk_local) locals,
maxStack,
codeLabels,
code,
exnSpecs,
localDebugSpecs,
hasSequencePoints)
let CodeGenMethod cenv mgbuf (zapFirstSeqPointToStart,entryPointInfo,methodName,eenv,alreadyUsedArgs,alreadyUsedLocals,codeGenFunction,m) =
(* Codegen the method. REVIEW: change this to generate the AbsIL code tree directly... *)
if verbose then dprintf "----------\ncodegen method %s\n" methodName;
let locals,maxStack,codeLabels,instrs,exns,localDebugSpecs,hasSequencePoints =
CodeGenThen cenv mgbuf (zapFirstSeqPointToStart,entryPointInfo,methodName,eenv,alreadyUsedArgs,alreadyUsedLocals,codeGenFunction,m)
let dump() =
instrs |> Array.iteri (fun i instr -> dprintf "%s: %d: %A\n" methodName i instr);
if verbose then dump();
let code =
// Build an Abstract IL code tree from the raw information
let lab2pc lbl =
try Hashtbl.find codeLabels lbl
with Not_found -> errorR(Error("label "^string_of_code_label lbl^" not found",m)); dump(); 676767
build_code methodName lab2pc instrs exns localDebugSpecs
let code = check_code code
// Attach a source range to the method. Only do this is it has some sequence points, because .NET 2.0/3.5
// ILDASM has issues if you emit symbols with a source range but without any sequence points
let sourceRange = if hasSequencePoints then GenPossibleILSourceMarker cenv m else None
// Build an Abstract IL method
mk_ilmbody (true,locals,maxStack,code, sourceRange)
let StartDelayedLocalScope nm cgbuf =
let startScope = CG.EmitDelayMark cgbuf ("start_"^nm)
let endScope = CG.EmitDelayMark cgbuf ("end_"^nm)
startScope,endScope
let StartLocalScope nm cgbuf =
let startScope = CG.GenerateMark cgbuf ("start_"^nm)
let endScope = CG.EmitDelayMark cgbuf ("end_"^nm)
startScope,endScope
let LocalScope nm cgbuf (f : (mark * mark) -> 'a) : 'a =
let startScope,endScope as scopeMarks = StartLocalScope nm cgbuf
let res = f scopeMarks
CG.SetMarkToHere cgbuf endScope;
res
let compileSequenceExpressions = true // try (System.Environment.GetEnvironmentVariable("COMPILED_SEQ") <> null) with _ -> false
//-------------------------------------------------------------------------
// Generate expressions
//-------------------------------------------------------------------------
let bindHasSeqPt = function (TBind(_,_,SequencePointAtBinding _)) -> true | _ -> false
let bindIsInvisible = function (TBind(_,_,NoSequencePointAtInvisibleBinding _)) -> true | _ -> false
let AlwaysSuppressSequencePoint sp expr =
match sp with
| SPAlways ->
// These extra cases have historically always had their sequence point suppressed
// However they do not fall under the definition of 'DoesExprCodeGenDefinitelyStartWithSequencePoint = false'
match expr with
| TExpr_let (bind,_,_,_) when bindIsInvisible(bind) -> true
| TExpr_letrec(binds,_,_,_) when (binds |> FlatList.exists bindHasSeqPt) || (binds |> FlatList.forall bindIsInvisible) -> true
| TExpr_seq _
| TExpr_match _ -> true
| TExpr_op((TOp_label _ | TOp_goto _),_,_,_) -> true
| _ -> false
| SPSuppress ->
true
// This is the list of composite statement expressions where we're about to emit a sequence
// point for sure. They get sequence points on their sub-expressions
//
// Determine if expression code generation certainly starts with a sequence point. An approximation used
// to prevent the generation of duplicat sequence points for conditionals and pattern matching
let rec WillGenerateSequencePoint sp expr =
match sp with
| SPAlways ->
let definiteSequencePoint =
match expr with
| TExpr_let (bind,expr,_,_)
-> bindHasSeqPt(bind) ||
(bind.Var.IsCompiledAsTopLevel && WillGenerateSequencePoint sp expr)
| TExpr_letrec(binds,expr,_,_)
-> (binds |> FlatList.forall (fun bind -> bind.Var.IsCompiledAsTopLevel)) && WillGenerateSequencePoint sp expr
| TExpr_seq (_, _, NormalSeq,spSeq,_) ->
(match spSeq with
| SequencePointsAtSeq -> true
| SuppressSequencePointOnExprOfSequential -> true
| SuppressSequencePointOnStmtOfSequential -> false)
| TExpr_match (SequencePointAtBinding _,_,_,_,_,_,_) -> true
| TExpr_op(( TOp_try_catch (SequencePointAtTry _,_)
| TOp_try_finally (SequencePointAtTry _,_)
| TOp_for (SequencePointAtForLoop _,_)
| TOp_while (SequencePointAtWhileLoop _)),_,_,_) -> true
| _ -> false
definiteSequencePoint
| SPSuppress ->
false
let DoesGenExprStartWithSequencePoint sp expr =
WillGenerateSequencePoint sp expr || not (AlwaysSuppressSequencePoint sp expr)
let rec GenExpr cenv (cgbuf:CodeGenBuffer) eenv sp expr sequel =
if verbose then dprintf "GenExpr@%a, #stack = %A, sequel = %s\n" output_range (range_of_expr expr) (cgbuf.GetCurrentStack()) (StringOfSequel sequel);
(* if verbose then dprintf "GenExpr@%a, #stack = %d, expr = %s, sequel = %s\n" output_range (range_of_expr expr) (List.length cgbuf.stack) (showL (ExprL expr)) (StringOfSequel sequel); *)
let expr = strip_expr expr
if not (WillGenerateSequencePoint sp expr) && not (AlwaysSuppressSequencePoint sp expr) then
CG.EmitSeqPoint cgbuf (range_of_expr expr);
match (if compileSequenceExpressions then Lowertop.LowerSeqExpr cenv.g cenv.amap expr else None) with
| Some info ->
GenSequenceExpr cenv cgbuf eenv info sequel
| None ->
match expr with
| TExpr_const(c,m,ty) ->
GenConstant cenv cgbuf eenv (c,m,ty) sequel
| TExpr_match (spBind,exprm,tree,targets,m,ty,_) ->
GenMatch cenv cgbuf eenv (spBind,exprm,tree,targets,m,ty) sequel
| TExpr_seq(e1,e2,dir,spSeq,m) ->
GenSequential cenv cgbuf eenv sp (e1,e2,dir,spSeq,m) sequel
| TExpr_letrec (binds,body,m,_) ->
GenLetRec cenv cgbuf eenv (binds,body,m) sequel
| TExpr_let (bind,body,m,_) ->
(* This case implemented here to get a guaranteed tailcall *)
// Make sure we generate the sequence point outside the scope of the variable
let startScope,endScope as scopeMarks = StartDelayedLocalScope "let" cgbuf
let eenv = AllocStorageForBind cenv cgbuf scopeMarks eenv bind
let sp = GenSequencePointForBind cenv cgbuf eenv bind
CG.SetMarkToHere cgbuf startScope;
GenBindAfterSequencePoint cenv cgbuf eenv sp bind;
let sp = if bindHasSeqPt bind || bindIsInvisible bind then SPAlways else SPSuppress
GenExpr cenv cgbuf eenv sp body (EndLocalScope(sequel,endScope))
| TExpr_lambda _ | TExpr_tlambda _ ->
GenLambda cenv cgbuf eenv false None expr sequel
| TExpr_app(f,fty,tyargs,args,m) ->
GenApp cenv cgbuf eenv (f,fty,tyargs,args,m) sequel
| TExpr_val(v,flags,m) ->
GenGetVal cenv cgbuf eenv (v,m) sequel
| TExpr_op(op,tyargs,args,m) ->
begin match op,args,tyargs with
| TOp_exnconstr(c),_,_ ->
GenAllocExn cenv cgbuf eenv (c,args,m) sequel
| TOp_ucase(c),_,_ ->
GenAllocUnionCase cenv cgbuf eenv (c,tyargs,args,m) sequel
| TOp_recd(isCtor,tycon),_,_ ->
GenAllocRecd cenv cgbuf eenv isCtor (tycon,tyargs,args,m) sequel
| TOp_tuple_field_get(n),[e],_ ->
GenGetTupleField cenv cgbuf eenv (e,tyargs,n,m) sequel
| TOp_exnconstr_field_get(constr,n),[e],_ ->
GenGetExnField cenv cgbuf eenv (e,constr,n,m) sequel
| TOp_ucase_field_get(constr,n),[e],_ ->
GenGetUnionCaseField cenv cgbuf eenv (e,constr,tyargs,n,m) sequel
| TOp_ucase_tag_get(constr),[e],_ ->
GenGetUnionCaseTag cenv cgbuf eenv (e,constr,tyargs,m) sequel
| TOp_ucase_proof(constr),[e],_ ->
GenUnionCaseProof cenv cgbuf eenv (e,constr,tyargs,m) sequel
| TOp_exnconstr_field_set(constr,n),[e;e2],_ ->
GenSetExnField cenv cgbuf eenv (e,constr,n,e2,m) sequel
| TOp_ucase_field_set(constr,n),[e;e2],_ ->
GenSetUnionCaseField cenv cgbuf eenv (e,constr,tyargs,n,e2,m) sequel
| TOp_rfield_get(f),[e],_ ->
GenGetRecdField cenv cgbuf eenv (e,f,tyargs,m) sequel
| TOp_rfield_get(f),[],_ ->
GenGetStaticField cenv cgbuf eenv (f,tyargs,m) sequel
| TOp_field_get_addr(f),[e],_ ->
GenGetRecdFieldAddr cenv cgbuf eenv (e,f,tyargs,m) sequel
| TOp_field_get_addr(f),[],_ ->
GenGetStaticFieldAddr cenv cgbuf eenv (f,tyargs,m) sequel
| TOp_rfield_set(f),[e1;e2],_ ->
GenSetRecdField cenv cgbuf eenv (e1,f,tyargs,e2,m) sequel
| TOp_rfield_set(f),[e2],_ ->
GenSetStaticField cenv cgbuf eenv (f,tyargs,e2,m) sequel
| TOp_tuple,_,_ ->
GenAllocTuple cenv cgbuf eenv (args,tyargs,m) sequel
| TOp_asm(code,rtys),_,_ ->
GenAsmCode cenv cgbuf eenv (code,tyargs,args,rtys,m) sequel
| TOp_while sp,[TExpr_lambda(_,_,[_],e1,_,_,_);TExpr_lambda(_,_,[_],e2,_,_,_)],[] ->
GenWhileLoop cenv cgbuf eenv (sp,e1,e2,m) sequel
| TOp_for(spStart,dir),[TExpr_lambda(_,_,[_],e1,_,_,_);TExpr_lambda(_,_,[_],e2,_,_,_);TExpr_lambda(_,_,[v],e3,_,_,_)],[] ->
GenForLoop cenv cgbuf eenv (spStart,v,e1,dir,e2,e3,m) sequel
| TOp_try_finally(spTry,spFinally),[TExpr_lambda(_,_,[_],e1,_,_,_); TExpr_lambda(_,_,[_],e2,_,_,_)],[resty] ->
GenTryFinally cenv cgbuf eenv (e1,e2,m,resty,spTry,spFinally) sequel
| TOp_try_catch(spTry,spWith),[TExpr_lambda(_,_,[_],e1,_,_,_); TExpr_lambda(_,_,[vf],ef,_,_,_);TExpr_lambda(_,_,[vh],eh,_,_,_)],[resty] ->
GenTryCatch cenv cgbuf eenv (e1,vf,ef,vh,eh,m,resty,spTry,spWith) sequel
| TOp_ilcall(meth,enclTypeArgs,methTypeArgs,rtys),args,[] ->
GenIlCall cenv cgbuf eenv (meth,enclTypeArgs,methTypeArgs,args,rtys,m) sequel
| TOp_get_ref_lval,[e],[ty] -> GenGetAddrOfRefCellField cenv cgbuf eenv (e,ty,m) sequel
| TOp_coerce,[e],[tgty;srcty] -> GenCoerce cenv cgbuf eenv (e,tgty,m,srcty) sequel
| TOp_rethrow,[],[rtnty] -> GenRethrow cenv cgbuf eenv (rtnty,m) sequel
| TOp_trait_call(ss),args,[] -> GenTraitCall cenv cgbuf eenv (ss,args, m) expr sequel
| TOp_lval_op(LSet,v),[e],[] -> GenSetVal cenv cgbuf eenv (v,e,m) sequel
| TOp_lval_op(LByrefGet,v),[],[] -> GenGetByref cenv cgbuf eenv (v,m) sequel
| TOp_lval_op(LByrefSet,v),[e],[] -> GenSetByref cenv cgbuf eenv (v,e,m) sequel
| TOp_lval_op(LGetAddr,v),[],[] -> GenGetValAddr cenv cgbuf eenv (v,m) sequel
| TOp_array,elems,[argty] -> GenNewArray cenv cgbuf eenv (elems,argty,m) sequel
| TOp_bytes bytes,[],[] ->
if cenv.emitConstantArraysUsingStaticDataBlobs then
GenConstArray cenv cgbuf eenv m cenv.g.ilg.typ_uint8 bytes Bytes.Bytebuf.emit_byte;
GenSequel cenv eenv.cloc cgbuf sequel
else
GenNewArraySimple cenv cgbuf eenv (List.of_array (Array.map (mk_byte cenv.g m) bytes),cenv.g.byte_ty,m) sequel
| TOp_uint16s arr,[],[] ->
if cenv.emitConstantArraysUsingStaticDataBlobs then
GenConstArray cenv cgbuf eenv m cenv.g.ilg.typ_uint16 arr Bytes.Bytebuf.emit_u16;
GenSequel cenv eenv.cloc cgbuf sequel
else
GenNewArraySimple cenv cgbuf eenv (List.of_array (Array.map (mk_uint16 cenv.g m) arr),cenv.g.uint16_ty,m) sequel
| TOp_goto(label),_,_ ->
CG.EmitInstr cgbuf [] (I_br label);
// NOTE: discard sequel
| TOp_return,[e],_ ->
GenExpr cenv cgbuf eenv SPSuppress e Return
// NOTE: discard sequel
| TOp_return,[],_ ->
GenSequel cenv eenv.cloc cgbuf ReturnVoid
// NOTE: discard sequel
| TOp_label(label),_,_ ->
cgbuf.SetMarkToHere (Mark label)
GenUnitThenSequel cenv eenv.cloc cgbuf sequel
| _ -> error(InternalError("Unexpected operator node expression",range_of_expr expr))
end
| TExpr_static_optimization(constraints,e2,e3,m) ->
GenStaticOptimization cenv cgbuf eenv (constraints,e2,e3,m) sequel
| TExpr_obj(uniq,typ,_,_,[meth],[],m,_) when is_delegate_typ cenv.g typ ->
GenDelegateExpr cenv cgbuf eenv expr (meth,m) sequel
| TExpr_obj(uniq,typ,basev,basecall,overrides,interfaceImpls,m,_) ->
GenObjectExpr cenv cgbuf eenv expr (typ,basev,basecall,overrides,interfaceImpls,m) sequel
| TExpr_quote(ast,conv,m,ty) -> GenQuotation cenv cgbuf eenv (ast,conv,m,ty) sequel
| TExpr_link _ -> failwith "Unexpected reclink"
| TExpr_tchoose (_,_,m) -> error(InternalError("Unexpected TExpr_tchoose",m))
and GenExprs cenv cgbuf eenv es = List.iter (fun e -> GenExpr cenv cgbuf eenv SPSuppress e Continue) es
and CodeGenMethodForExpr cenv mgbuf (spReq,entryPointInfo,methodName,eenv,alreadyUsedArgs,alreadyUsedLocals,expr0,sequel0) =
let zapFirstSeqPointToStart = (spReq = SPAlways)
CodeGenMethod cenv mgbuf (zapFirstSeqPointToStart,entryPointInfo,methodName,eenv,alreadyUsedArgs,alreadyUsedLocals,
(fun cgbuf eenv -> GenExpr cenv cgbuf eenv spReq expr0 sequel0),
(range_of_expr expr0))
//--------------------------------------------------------------------------
// Generate sequels
//--------------------------------------------------------------------------
(* does the sequel discard its result, and if so what does it do next? *)
and sequelAfterDiscard sequel =
match sequel with
| DiscardThen sequel -> Some(sequel)
| EndLocalScope(sq,mark) -> sequelAfterDiscard sq |> Option.map (fun sq -> EndLocalScope(sq,mark))
| _ -> None
and sequel_ignoring_end_scopes_and_discard sequel =
let sequel = sequel_ignore_end_scopes sequel
match sequelAfterDiscard sequel with
| Some sq -> sq
| None -> sequel
and sequel_ignore_end_scopes sequel =
match sequel with
| EndLocalScope(sq,m) -> sequel_ignore_end_scopes sq
| sq -> sq
(* commit any 'EndLocalScope' nodes in the sequel and return the residue *)
and GenSequelEndScopes cgbuf sequel =
match sequel with
| EndLocalScope(sq,m) -> CG.SetMarkToHere cgbuf m; GenSequelEndScopes cgbuf sq
| sq -> ()
and StringOfSequel sequel =
match sequel with
| Continue -> "continue"
| DiscardThen sequel -> "discard; "^StringOfSequel sequel
| ReturnVoid -> "ReturnVoid"
| CmpThenBrOrContinue(pushpops,bri) -> "CmpThenBrOrContinue"
| Return -> "Return"
| EndLocalScope (sq,Mark k) -> "EndLocalScope("^StringOfSequel sq^","^string_of_code_label k^")"
| Br (Mark x) -> sprintf "Br L%s" (string_of_code_label x)
| LeaveHandler _ -> "LeaveHandler"
| EndFilter -> "EndFilter"
and GenSequel cenv cloc cgbuf sequel =
let sq = sequel_ignore_end_scopes sequel
if verbose then dprintn ("GenSequel:" ^ StringOfSequel sequel);
(match sq with
| Continue -> ()
| DiscardThen sq ->
CG.EmitInstr cgbuf [Pop] (i_pop);
GenSequel cenv cloc cgbuf sq
| ReturnVoid ->
CG.EmitInstr cgbuf [] I_ret
| CmpThenBrOrContinue(pushpops,bri) ->
CG.EmitInstr cgbuf pushpops bri
| Return ->
CG.EmitInstr cgbuf [Pop] I_ret
| EndLocalScope _ -> failwith "EndLocalScope unexpected"
| Br x ->
// Emit a NOP in debug code in case the branch instruction gets eliminated
// because it is a "branch to next instruction". This prevents two unrelated sequence points
// (the one before the branch and the one after) being coalesced together
if cgbuf.mgbuf.cenv.debug then
CG.EmitInstr cgbuf [] i_nop
CG.EmitInstr cgbuf [] (I_br(code_label_of_mark x))
| LeaveHandler (isFinally, whereToSaveResult,x) ->
if isFinally then
CG.EmitInstr cgbuf [Pop] (i_pop)
else
EmitSetLocal cgbuf whereToSaveResult;
CG.EmitInstr cgbuf [] (if isFinally then I_endfinally else I_leave(code_label_of_mark x))
| EndFilter ->
CG.EmitInstr cgbuf [Pop] I_endfilter
);
GenSequelEndScopes cgbuf sequel;
if verbose then dprintn ("GenSequel: done");
//--------------------------------------------------------------------------
// Generate constants
//--------------------------------------------------------------------------
and GenConstant cenv cgbuf eenv (c,m,ty) sequel =
let il_ty = GenType m cenv.g eenv.tyenv ty
(* Check if we need to generate the value at all! *)
begin match sequelAfterDiscard sequel with
| None ->
if verbose then dprintn ("GenConstant: generating ");
begin match TryEliminateDesugaredConstants cenv.g m c with
| Some e -> GenExpr cenv cgbuf eenv SPSuppress e Continue
| None ->
match c with
| TConst_bool b -> CG.EmitInstr cgbuf [Push cenv.g.ilg.typ_bool] (mk_ldc_i32 (if b then 1 else 0))
| TConst_sbyte(i) -> CG.EmitInstr cgbuf [Push il_ty] (mk_ldc_i32 (int32 i))
| TConst_int16(i) -> CG.EmitInstr cgbuf [Push il_ty] (mk_ldc_i32 (int32 i))
| TConst_int32(i) -> CG.EmitInstr cgbuf [Push il_ty] (mk_ldc_i32 i)
| TConst_int64(i) -> CG.EmitInstr cgbuf [Push il_ty] (mk_ldc_i64 i)
| TConst_nativeint(i) -> CG.EmitInstrs cgbuf [Push il_ty] [mk_ldc_i64 i; I_arith (AI_conv DT_I) ]
| TConst_byte(i) -> CG.EmitInstr cgbuf [Push il_ty] (mk_ldc_i32 (int32 i))
| TConst_uint16(i) -> CG.EmitInstr cgbuf [Push il_ty] (mk_ldc_i32 (int32 i))
| TConst_uint32(i) -> CG.EmitInstr cgbuf [Push il_ty] (mk_ldc_i32 (int32 i))
| TConst_uint64(i) -> CG.EmitInstr cgbuf [Push il_ty] (mk_ldc_i64 (int64 i))
| TConst_unativeint(i) -> CG.EmitInstrs cgbuf [Push il_ty] [mk_ldc_i64 (int64 i); I_arith (AI_conv DT_U) ]
| TConst_float(f) -> CG.EmitInstr cgbuf [Push il_ty] ( I_arith (AI_ldc (DT_R8,NUM_R8 f)) )
| TConst_float32(f) -> CG.EmitInstr cgbuf [Push il_ty] ( I_arith (AI_ldc (DT_R4,NUM_R4 f)) )
| TConst_char(c) -> CG.EmitInstr cgbuf [Push il_ty] ( mk_ldc_i32 (int c))
| TConst_string(s) -> GenString cenv cgbuf m s
| TConst_unit -> GenUnit cenv cgbuf
| TConst_zero -> GenDefaultValue cenv cgbuf eenv (ty,m)
| TConst_decimal _ -> failwith "unreachable"
end;
GenSequel cenv eenv.cloc cgbuf sequel
| Some sq ->
if verbose then dprintn ("GenConstant: skipping");
(* Even if we didn't need to generate the value then maybe we still have to branch or return *)
GenSequel cenv eenv.cloc cgbuf sq
end
and GenUnit cenv cgbuf = CG.EmitInstr cgbuf [Push cenv.g.ilg.typ_Object] i_ldnull
and GenUnitThenSequel cenv cloc cgbuf sequel =
if verbose then dprintn ("GenUnitThenSequel:");
match sequelAfterDiscard sequel with
| Some(sq) -> GenSequel cenv cloc cgbuf sq
| None -> GenUnit cenv cgbuf; GenSequel cenv cloc cgbuf sequel
//--------------------------------------------------------------------------
// Generate simple data-related constructs
//--------------------------------------------------------------------------
and GenAllocTuple cenv cgbuf eenv (args,argtys,m) sequel =
if verbose then dprintn ("GenAllocTuple:");
let tcref, tys, args, newm = compiled_mk_tuple cenv.g (argtys,args,m)
let typ = GenNamedTyApp newm cenv.g eenv.tyenv tcref tys
let ntyvars = if (tys.Length - 1) < goodTupleFields then (tys.Length - 1) else goodTupleFields
let tyvars = [0 .. ntyvars] |> List.map (fun n -> mk_tyvar_ty (uint16 n))
if verbose then dprintf "GenAllocTuple: #args = %d\n" (List.length args);
GenExprs cenv cgbuf eenv args;
(* generate a reference to the constructor *)
let tyenvinner = tyenv_for_tcref tcref
if verbose then dprintf "GenAllocTuple: call, #args = %d" (List.length args);
CG.EmitInstr cgbuf (List.replicate args.Length Pop @ [ Push typ; ])
(mk_normal_newobj
(mk_ctor_mspec_for_typ (typ,tyvars)));
GenSequel cenv eenv.cloc cgbuf sequel
and GenGetTupleField cenv cgbuf eenv (e,tys,n,m) sequel =
let rec compiled_get_system_tuple_item g (e,tys,n,m) =
let ar = List.length tys
if ar <= 0 then failwith "compiled_get_tuple_item"
elif ar < maxTuple then
let tcr' = compiled_tuple_tcref g tys
let typ = GenNamedTyApp m g eenv.tyenv tcr' tys
mk_call_Tuple_ItemN g m n typ e tys.[n]
else
let tysA,tysB = split_after (goodTupleFields) tys
let tyB = compiled_tuple_ty g tysB
let tys' = tysA@[tyB]
let tcr' = compiled_tuple_tcref g tys'
let typ' = GenNamedTyApp m g eenv.tyenv tcr' tys'
let n' = (min n goodTupleFields)
let elast = mk_call_Tuple_ItemN g m n' typ' e tys'.[n']
if n < goodTupleFields then
elast
else
compiled_get_system_tuple_item g (elast,tysB,n-goodTupleFields,m)
GenExpr cenv cgbuf eenv SPSuppress (compiled_get_system_tuple_item cenv.g (e,tys,n,m)) sequel
and GenAllocExn cenv cgbuf eenv (c,args,m) sequel =
GenExprs cenv cgbuf eenv args;
let typ = GenExnType m cenv.g eenv.tyenv c
let flds = rfields_of_ecref c
let argtys = flds |> List.map (fun rfld -> GenType m cenv.g eenv.tyenv rfld.FormalType)
let mspec = mk_ctor_mspec (typ.TypeRef, AsObject,argtys,[])
CG.EmitInstr cgbuf
(List.replicate args.Length Pop @ [ Push typ])
(mk_normal_newobj mspec) ;
GenSequel cenv eenv.cloc cgbuf sequel
and GenAllocUnionCase cenv cgbuf eenv (c,tyargs,args,m) sequel =
if verbose then dprintn ("GenAllocUnionCase");
GenExprs cenv cgbuf eenv args;
let cuspec,idx = GenUnionCaseSpec m cenv.g eenv.tyenv c tyargs
CG.EmitInstr cgbuf (List.replicate args.Length Pop @ [ Push (objtype_of_cuspec cuspec)]) (mk_IlxInstr (EI_newdata (cuspec,idx)));
GenSequel cenv eenv.cloc cgbuf sequel
and GenAllocRecd cenv cgbuf eenv ctorInfo (tcref,argtys,args,m) sequel =
let typ = GenNamedTyApp m cenv.g eenv.tyenv tcref argtys
(* Filter out fields with default initialization *)
let relevantFields =
tcref.AllInstanceFieldsAsList
|> List.filter (fun f -> not f.IsZeroInit)
match ctorInfo with
| RecdExprIsObjInit ->
if verbose then dprintn ("GenAllocRecd: class constructor");
(args,relevantFields) ||> List.iter2 (fun e f ->
CG.EmitInstr cgbuf [Push typ] ldarg_0;
GenExpr cenv cgbuf eenv SPSuppress e Continue;
GenFieldStore false cenv cgbuf eenv (mk_rfref tcref f.Name,argtys,m) discard)
(* Object construction doesn't generate a true value. *)
(* Object constructions will always just get thrown away so this is safe *)
GenSequel cenv eenv.cloc cgbuf sequel
| RecdExpr ->
if verbose then dprintf "GenAllocRecd: normal record, #args = %d\n" (List.length args);
GenExprs cenv cgbuf eenv args;
(* generate a reference to the record constructor *)
let tyenvinner = tyenv_for_tcref tcref
if verbose then dprintf "GenAllocRecd: call, #args = %d" (List.length args);
CG.EmitInstr cgbuf (List.replicate args.Length Pop @ [ Push typ; ])
(mk_normal_newobj
(mk_ctor_mspec_for_typ (typ,relevantFields |> List.map (fun f -> GenType m cenv.g tyenvinner f.FormalType) )));
GenSequel cenv eenv.cloc cgbuf sequel
and GenNewArraySimple cenv cgbuf eenv (elems,argty,m) sequel =
let argty' = GenType m cenv.g eenv.tyenv argty
let arrty = mk_array_ty_old (Rank1ArrayShape,argty')
CG.EmitInstrs cgbuf [Push arrty] [ I_arith (AI_ldc (DT_I4,NUM_I4 ((List.length elems)))); I_newarr (Rank1ArrayShape,argty') ];
List.iteri
(fun i e ->
CG.EmitInstrs cgbuf [Push arrty; Push cenv.g.ilg.typ_int32] [ i_dup; I_arith (AI_ldc (DT_I4,NUM_I4 (i))) ];
GenExpr cenv cgbuf eenv SPSuppress e Continue;
CG.EmitInstr cgbuf [Pop; Pop; Pop] (I_stelem_any (Rank1ArrayShape,argty')))
elems;
GenSequel cenv eenv.cloc cgbuf sequel
and GenNewArray cenv cgbuf eenv (elems: expr list,argty,m) sequel =
if elems.Length <= 5 || not cenv.emitConstantArraysUsingStaticDataBlobs then
GenNewArraySimple cenv cgbuf eenv (elems,argty,m) sequel
else
(* Try to emit a constant byte-blob array *)
let elems' = Array.of_list elems
let test,write =
match elems'.[0] with
| TExpr_const(TConst_bool _,_,_) -> (function TConst_bool _ -> true | _ -> false), (fun buf -> function TConst_bool b -> Bytes.Bytebuf.emit_bool_as_byte buf b | _ -> failwith "unreachable")
| TExpr_const(TConst_char _,_,_) -> (function TConst_char _ -> true | _ -> false), (fun buf -> function TConst_char b -> Bytes.Bytebuf.emit_i32_as_u16 buf (int b) | _ -> failwith "unreachable")
| TExpr_const(TConst_byte _,_,_) -> (function TConst_byte _ -> true | _ -> false), (fun buf -> function TConst_byte b -> Bytes.Bytebuf.emit_byte buf b | _ -> failwith "unreachable")
| TExpr_const(TConst_uint16 _,_,_) -> (function TConst_uint16 _ -> true | _ -> false), (fun buf -> function TConst_uint16 b -> Bytes.Bytebuf.emit_u16 buf b | _ -> failwith "unreachable")
| TExpr_const(TConst_uint32 _,_,_) -> (function TConst_uint32 _ -> true | _ -> false), (fun buf -> function TConst_uint32 b -> Bytes.Bytebuf.emit_i32 buf (int32 b) | _ -> failwith "unreachable")
| TExpr_const(TConst_uint64 _,_,_) -> (function TConst_uint64 _ -> true | _ -> false), (fun buf -> function TConst_uint64 b -> Bytes.Bytebuf.emit_i64 buf (int64 b) | _ -> failwith "unreachable")
| TExpr_const(TConst_sbyte _,_,_) -> (function TConst_sbyte _ -> true | _ -> false), (fun buf -> function TConst_sbyte b -> Bytes.Bytebuf.emit_byte buf (byte b) | _ -> failwith "unreachable")
| TExpr_const(TConst_int16 _,_,_) -> (function TConst_int16 _ -> true | _ -> false), (fun buf -> function TConst_int16 b -> Bytes.Bytebuf.emit_u16 buf (uint16 b) | _ -> failwith "unreachable")
| TExpr_const(TConst_int32 _,_,_) -> (function TConst_int32 _ -> true | _ -> false), (fun buf -> function TConst_int32 b -> Bytes.Bytebuf.emit_i32 buf b | _ -> failwith "unreachable")
| TExpr_const(TConst_int64 _,_,_) -> (function TConst_int64 _ -> true | _ -> false), (fun buf -> function TConst_int64 b -> Bytes.Bytebuf.emit_i64 buf b | _ -> failwith "unreachable")
| _ -> (function _ -> false), (fun _ _ -> failwith "unreachable")
if elems' |> Array.forall (function TExpr_const(c,_,_) -> test c | _ -> false) then
let argty' = GenType m cenv.g eenv.tyenv argty
GenConstArray cenv cgbuf eenv m argty' elems' (fun buf -> function TExpr_const(c,_,_) -> write buf c | _ -> failwith "unreachable");
GenSequel cenv eenv.cloc cgbuf sequel
else
GenNewArraySimple cenv cgbuf eenv (elems,argty,m) sequel
and GenCoerce cenv cgbuf eenv (e,tgty,m,srcty) sequel =
(* Is this an upcast? *)
if Typrelns.type_definitely_subsumes_type_no_coercion 0 cenv.g cenv.amap m tgty srcty &&
(* Do an extra check - should not be needed *)
Typrelns.type_feasibly_subsumes_type 0 cenv.g cenv.amap m tgty Typrelns.NoCoerce srcty then
begin
(* The .NET IL doesn't always support implict subsumption for interface types, e.g. at stack merge points *)
(* Hence be conservative here and always cast explicitly. *)
if (is_interface_typ cenv.g tgty) then (
GenExpr cenv cgbuf eenv SPSuppress e Continue;
let ilToTy = GenType m cenv.g eenv.tyenv tgty
CG.EmitInstrs cgbuf [Pop; Push ilToTy] [ I_unbox_any ilToTy; ];
GenSequel cenv eenv.cloc cgbuf sequel
) else (
GenExpr cenv cgbuf eenv SPSuppress e sequel;
)
end
else
GenExpr cenv cgbuf eenv SPSuppress e Continue;
if not (is_obj_typ cenv.g srcty) then
let ilFromTy = GenType m cenv.g eenv.tyenv srcty
CG.EmitInstrs cgbuf [Pop; Push cenv.g.ilg.typ_Object] [ I_box ilFromTy; ];
if not (is_obj_typ cenv.g tgty) then
let ilToTy = GenType m cenv.g eenv.tyenv tgty
CG.EmitInstrs cgbuf [Pop; Push ilToTy] [ I_unbox_any ilToTy; ];
GenSequel cenv eenv.cloc cgbuf sequel
and GenRethrow cenv cgbuf eenv (rtnty,m) sequel =
let ilReturnTy = GenType m cenv.g eenv.tyenv rtnty
CG.EmitInstrs cgbuf [] [I_rethrow];
// [See comment related to I_throw].
// Rethrow does not return. Required to push dummy value on the stack.
// This follows prior behaviour by prim-types rethrow<_>.
CG.EmitInstrs cgbuf [Push ilReturnTy] [i_ldnull; I_unbox_any ilReturnTy ];
GenSequel cenv eenv.cloc cgbuf sequel
and GenGetExnField cenv cgbuf eenv (e,ecref,fieldNum,m) sequel =
GenExpr cenv cgbuf eenv SPSuppress e Continue;
let exnc = strip_eqns_from_ecref ecref
let typ = GenExnType m cenv.g eenv.tyenv ecref
CG.EmitInstrs cgbuf [] [ I_castclass typ];
let fld = List.nth (exnc.TrueInstanceFieldsAsList) fieldNum
let ftyp = GenType m cenv.g eenv.tyenv fld.FormalType
let mspec = mk_nongeneric_instance_mspec_in_typ (typ,"get_"^fld.Name, [], ftyp)
CG.EmitInstr cgbuf ([Pop;Push ftyp]) (mk_normal_call mspec)
GenSequel cenv eenv.cloc cgbuf sequel
and GenSetExnField cenv cgbuf eenv (e,ecref,fieldNum,e2,m) sequel =
GenExpr cenv cgbuf eenv SPSuppress e Continue;
let exnc = strip_eqns_from_ecref ecref
let typ = GenExnType m cenv.g eenv.tyenv ecref
CG.EmitInstrs cgbuf [] [ I_castclass typ ];
let fld = List.nth (exnc.TrueInstanceFieldsAsList) fieldNum
let ftyp = GenType m cenv.g eenv.tyenv fld.FormalType
let fldName = GenFieldName exnc fld
GenExpr cenv cgbuf eenv SPSuppress e2 Continue;
CG.EmitInstr cgbuf [Pop; Pop] (mk_normal_stfld(mk_fspec_in_typ (typ,fldName,ftyp)));
GenUnitThenSequel cenv eenv.cloc cgbuf sequel
and GenUnionCaseProof cenv cgbuf eenv (e,constr,tyargs,m) sequel =
GenExpr cenv cgbuf eenv SPSuppress e Continue;
let cuspec,idx = GenUnionCaseSpec m cenv.g eenv.tyenv constr tyargs
let fty = EraseIlxClassunions.typ_of_alt cuspec idx
CG.EmitInstrs cgbuf [Pop; Push fty]
[ mk_IlxInstr (EI_castdata(false,cuspec,idx)); ];
GenSequel cenv eenv.cloc cgbuf sequel
and GenGetUnionCaseField cenv cgbuf eenv (e,constr,tyargs,n,m) sequel =
assert (is_proven_ucase_typ (type_of_expr cenv.g e));
GenExpr cenv cgbuf eenv SPSuppress e Continue;
let cuspec,idx = GenUnionCaseSpec m cenv.g eenv.tyenv constr tyargs
(* ANALYSIS: don't use castdata where we've already done a typetest *)
let fty = actual_typ_of_cuspec_field cuspec idx n
CG.EmitInstrs cgbuf [Pop; Push fty]
[ //mk_IlxInstr (EI_castdata(false,cuspec,idx));
mk_IlxInstr (EI_lddata(cuspec,idx,n)) ];
GenSequel cenv eenv.cloc cgbuf sequel
and GenGetUnionCaseTag cenv cgbuf eenv (e,tycon,tyargs,m) sequel =
GenExpr cenv cgbuf eenv SPSuppress e Continue;
let cuspec = GenUnionSpec m cenv.g eenv.tyenv tycon tyargs
CG.EmitInstrs cgbuf [Pop; Push cenv.g.ilg.typ_int32] [ mk_IlxInstr (EI_lddatatag(cuspec)) ];
GenSequel cenv eenv.cloc cgbuf sequel
and GenSetUnionCaseField cenv cgbuf eenv (e,constr,tyargs,n,e2,m) sequel =
GenExpr cenv cgbuf eenv SPSuppress e Continue;
let cuspec,idx = GenUnionCaseSpec m cenv.g eenv.tyenv constr tyargs
CG.EmitInstr cgbuf [Pop; Push (objtype_of_cuspec cuspec) ] (mk_IlxInstr (EI_castdata(false,cuspec,idx)));
GenExpr cenv cgbuf eenv SPSuppress e2 Continue;
CG.EmitInstr cgbuf [Pop; Pop] (mk_IlxInstr (EI_stdata(cuspec,idx,n)) );
GenUnitThenSequel cenv eenv.cloc cgbuf sequel
and GenGetRecdFieldAddr cenv cgbuf eenv (e,f,tyargs,m) sequel = (* follows GenGetAddrOfRefCellField code *)
GenExpr cenv cgbuf eenv SPSuppress e Continue;
let fref = GenRecdFieldRef m cenv eenv.tyenv f tyargs
CG.EmitInstrs cgbuf [Pop; Push (Type_byref (actual_typ_of_fspec fref))] [ I_ldflda fref ] ;
GenSequel cenv eenv.cloc cgbuf sequel
and GenGetStaticFieldAddr cenv cgbuf eenv (f,tyargs,m) sequel = (* follows GenGetAddrOfRefCellField code *)
let fspec = GenRecdFieldRef m cenv eenv.tyenv f tyargs
CG.EmitInstrs cgbuf [Push (Type_byref (actual_typ_of_fspec fspec))] [ I_ldsflda fspec ] ;
GenSequel cenv eenv.cloc cgbuf sequel
and GenGetRecdField cenv cgbuf eenv (e,f,tyargs,m) sequel =
if verbose then dprintn ("GenGetRecdField");
GenExpr cenv cgbuf eenv SPSuppress e Continue;
GenFieldGet false cenv cgbuf eenv (f,tyargs,m);
GenSequel cenv eenv.cloc cgbuf sequel
and GenSetRecdField cenv cgbuf eenv (e1,f,tyargs,e2,m) sequel =
GenExpr cenv cgbuf eenv SPSuppress e1 Continue;
GenExpr cenv cgbuf eenv SPSuppress e2 Continue;
GenFieldStore false cenv cgbuf eenv (f,tyargs,m) sequel
and GenGetStaticField cenv cgbuf eenv (f,tyargs,m) sequel =
GenFieldGet true cenv cgbuf eenv (f,tyargs,m);
GenSequel cenv eenv.cloc cgbuf sequel
and GenSetStaticField cenv cgbuf eenv (f,tyargs,e2,m) sequel =
GenExpr cenv cgbuf eenv SPSuppress e2 Continue;
GenFieldStore true cenv cgbuf eenv (f,tyargs,m) sequel
and mk_field_mspec isStatic =
(if isStatic then mk_static_nongeneric_mspec_in_typ else mk_nongeneric_instance_mspec_in_typ)
and mk_field_pops isStatic pops = if isStatic then pops else Pop::pops
and GenFieldGet isStatic cenv cgbuf eenv (rfref:RecdFieldRef,tyargs,m) =
let fspec = GenRecdFieldRef m cenv eenv.tyenv rfref tyargs
if use_genuine_field rfref.Tycon rfref.RecdField || tcref_in_this_assembly cenv.g.compilingFslib rfref.TyconRef then
CG.EmitInstrs cgbuf (mk_field_pops isStatic [ Push (actual_typ_of_fspec fspec)]) [ if isStatic then mk_normal_ldsfld fspec else mk_normal_ldfld fspec ]
else
let mspec = mk_field_mspec isStatic (fspec.EnclosingType,"get_"^rfref.RecdField.rfield_id.idText, [], fspec.FormalType)
CG.EmitInstr cgbuf (mk_field_pops isStatic [Push (actual_typ_of_fspec fspec)]) (mk_normal_call mspec)
and GenFieldStore isStatic cenv cgbuf eenv (rfref:RecdFieldRef,tyargs,m) sequel =
let fspec = GenRecdFieldRef m cenv eenv.tyenv rfref tyargs
let fld = rfref.RecdField
if fld.IsMutable && not (use_genuine_field rfref.Tycon fld) then
let mspec = mk_field_mspec isStatic (fspec.EnclosingType, "set_"^fld.rfield_id.idText, [fspec.FormalType],Type_void)
CG.EmitInstr cgbuf (mk_field_pops isStatic [Pop]) (mk_normal_call mspec)
else
(* Within assemblies we do generate some set-field operations *)
(* for immutable fields even when resolving recursive bindings. *)
(* However we do not generate "set" properties for these. *)
(* Hence we just set the field directly in this case. *)
CG.EmitInstr cgbuf (mk_field_pops isStatic [Pop]) (if isStatic then mk_normal_stsfld fspec else mk_normal_stfld fspec);
GenUnitThenSequel cenv eenv.cloc cgbuf sequel
//--------------------------------------------------------------------------
// Generate arguments to calls
//--------------------------------------------------------------------------
/// Generate arguments to a call, unless the argument is the single lone "unit" value
/// to a method or value compiled as a method taking no arguments
and GenUntupledArgsDiscardingLoneUnit cenv cgbuf eenv m numObjArgs curriedArgInfos args =
match curriedArgInfos ,args with
// Type.M()
// new C()
| [[]],[arg] when numObjArgs = 0 ->
assert is_unit_typ cenv.g (type_of_expr cenv.g arg)
GenExpr cenv cgbuf eenv SPSuppress arg discard
// obj.M()
| [[_];[]],[arg1;arg2] when numObjArgs = 1 ->
assert is_unit_typ cenv.g (type_of_expr cenv.g arg2)
GenExpr cenv cgbuf eenv SPSuppress arg1 Continue;
GenExpr cenv cgbuf eenv SPSuppress arg2 discard
| _ ->
(curriedArgInfos,args) ||> List.iter2 (fun argInfos x ->
GenUntupledArgExpr cenv cgbuf eenv m argInfos x Continue)
/// Codegen arguments
and GenUntupledArgExpr cenv cgbuf eenv m argInfos expr sequel =
let numRequiredExprs = List.length argInfos
assert (numRequiredExprs >= 1)
if numRequiredExprs = 1 then
GenExpr cenv cgbuf eenv SPSuppress expr sequel
elif is_tuple expr then
let es = try_dest_tuple expr
if es.Length <> numRequiredExprs then error(InternalError("GenUntupledArgExpr (2)",m));
es |> List.iter (fun x -> GenExpr cenv cgbuf eenv SPSuppress x Continue);
GenSequel cenv eenv.cloc cgbuf sequel
else
let ty = type_of_expr cenv.g expr
let locv,loce = mk_compgen_local m "arg" ty
let bind = mk_compgen_bind locv expr
LocalScope "untuple" cgbuf (fun scopeMarks ->
let eenvinner = AllocStorageForBind cenv cgbuf scopeMarks eenv bind
GenBind cenv cgbuf eenvinner bind;
if verbose then dprintf "expr = %s\nty = %s\narity = %d\n" (showL (ExprL expr)) ((DebugPrint.showType ty)) numRequiredExprs;
let tys = dest_tuple_typ cenv.g ty
assert (tys.Length = numRequiredExprs)
argInfos |> List.iteri (fun i fargty -> GenGetTupleField cenv cgbuf eenvinner (loce,tys,i,m) Continue);
GenSequel cenv eenv.cloc cgbuf sequel
)
//--------------------------------------------------------------------------
// Generate calls (try to detect direct calls)
//--------------------------------------------------------------------------
and GenApp cenv cgbuf eenv (f,fty,tyargs,args,m) sequel =
if verbose then dprintn ("GenApp:");
match (f,tyargs,args) with
(* Look for tailcall to turn into branch *)
| (TExpr_val(v,_,_),_,_) when
((ListAssoc.containsKey cenv.g.vref_eq v eenv.innerVals) &&
not v.IsConstructor &&
let (kind,_) = ListAssoc.find cenv.g.vref_eq v eenv.innerVals
(* when branch-calling methods we must have the right type parameters *)
begin match kind with
| BranchCallClosure _ -> true
| BranchCallMethod (_,_,tps,_,_) ->
(List.lengthsEqAndForall2 (fun ty tp -> type_equiv cenv.g ty (mk_typar_ty tp)) tyargs tps)
end &&
(* must be exact #args, ignoring tupling - we untuple if needed below *)
(let arityInfo =
match kind with
| BranchCallClosure arityInfo
| BranchCallMethod (arityInfo,_,_,_,_) -> arityInfo
arityInfo.Length = args.Length
) &&
(* no tailcall out of exception handler, etc. *)
(match sequel_ignoring_end_scopes_and_discard sequel with Return | ReturnVoid -> true | _ -> false))
->
let (kind,mark) = ListAssoc.find cenv.g.vref_eq v eenv.innerVals
let ntmargs =
match kind with
| BranchCallClosure arityInfo ->
let ntmargs = List.foldBack (+) arityInfo 0
GenExprs cenv cgbuf eenv args;
ntmargs
| BranchCallMethod (arityInfo,curriedArgInfos,tps,ntmargs,numObjArgs) ->
assert (curriedArgInfos.Length = arityInfo.Length )
assert (curriedArgInfos.Length = args.Length)
//assert (curriedArgInfos.Length = ntmargs )
GenUntupledArgsDiscardingLoneUnit cenv cgbuf eenv m numObjArgs curriedArgInfos args;
ntmargs
for i = ntmargs - 1 downto 0 do
CG.EmitInstrs cgbuf [Pop] [ I_starg (uint16 (i+cgbuf.PreallocatedArgCount)) ];
done;
CG.EmitInstrs cgbuf [] [ I_br (code_label_of_mark mark) ];
GenSequelEndScopes cgbuf sequel
// Similarly for PhysicalEquality becomes cheap reference equality for non-value-types
| (TExpr_val(v,_,_),[ty],[arg1;arg2]) when
(cenv.g.vref_eq v cenv.g.poly_eq_inner_vref)
&& (is_fsobjmodel_ref_typ cenv.g ty ||
(is_il_ref_typ cenv.g ty
&& not (type_equiv cenv.g ty cenv.g.system_Object_typ)
&& not (type_equiv cenv.g ty cenv.g.system_Value_typ)
&& not (type_equiv cenv.g ty cenv.g.system_Enum_typ)) or
is_delegate_typ cenv.g ty ||
is_union_typ cenv.g ty ||
is_recd_typ cenv.g ty ||
is_repr_hidden_typ cenv.g ty ||
is_tuple_typ cenv.g ty) ->
GenExpr cenv cgbuf eenv SPSuppress arg1 Continue;
GenExpr cenv cgbuf eenv SPSuppress arg2 Continue;
CG.EmitInstr cgbuf [ Pop; Pop; Push cenv.g.ilg.typ_bool ] (I_arith AI_ceq);
GenSequel cenv eenv.cloc cgbuf sequel
// Optimize calls to top methods when given "enough" arguments.
| (TExpr_val(vref,vFlags,_),_,_) when
(let storage = storage_for_vref m vref eenv
match storage with
| Method(topValInfo,vref,mspec,_,_,_) ->
(let tps,argtys,_,_ = GetTopValTypeInFSharpForm cenv.g topValInfo vref.Type m
tps.Length = tyargs.Length &&
argtys.Length <= args.Length)
| _ -> false) ->
let storage = storage_for_vref m vref eenv
begin match storage with
| Method (topValInfo,vref,mspec,_,_,_) ->
if verbose then dprintn ("GenApp: Method");
let nowArgs,laterArgs =
let _,curriedArgInfos,returnTy,retInfo = GetTopValTypeInFSharpForm cenv.g topValInfo vref.Type m
List.chop curriedArgInfos.Length args
let actualRetTy = apply_types cenv.g vref.Type (tyargs,nowArgs)
let _,curriedArgInfos,returnTy,retInfo = GetTopValTypeInCompiledForm cenv.g topValInfo vref.Type m
let ilTyArgs = GenTypeArgs m cenv.g eenv.tyenv tyargs
// For instance method calls chop off some type arguments, which are already
// carried by the class. Also work out if it's a virtual call.
let numEnclTypeArgs,virtualCall,newobj,isSuperInit,isSelfInit,_,_,_ = GetMemberCallInfo cenv.g (vref,vFlags) in
// numEnclTypeArgs will include unit-of-measure args, unfortunately. For now, just cut-and-paste code from GetMemberCallInfo
// @REVIEW: refactor this
let numEnclTypeArgs =
match vref.MemberInfo with
| Some(membInfo) when not (vref.IsExtensionMember) ->
List.length(vref.MemberApparentParent.TyparsNoRange |> DropErasedTypars)
| _ -> 0
let (ilClassArgTys,ilMethArgTys) =
if ilTyArgs.Length < numEnclTypeArgs then error(InternalError("length mismatch",m));
List.chop numEnclTypeArgs ilTyArgs
let boxity = boxity_of_typ mspec.EnclosingType
let mspec = mk_mspec (mspec.MethodRef, boxity,ilClassArgTys,ilMethArgTys)
// "Unit" return types on static methods become "void"
let mustGenerateUnitAfterCall = isNone returnTy
let isTailCall =
if isNil laterArgs && not isSelfInit then
let isDllImport = vref_isDllImport cenv.g vref
let hasByrefArg = nowArgs |> List.exists (type_of_expr cenv.g >> is_byref_typ cenv.g)
let makesNoCriticalTailcalls = vref.MakesNoCriticalTailcalls
CanTailcall(boxity,eenv.withinSEH,hasByrefArg,mustGenerateUnitAfterCall,isDllImport,isSelfInit,makesNoCriticalTailcalls,sequel)
else Normalcall
let callInstr =
if virtualCall then I_callvirt (isTailCall, mspec, None)
elif newobj then I_newobj (mspec, None)
else I_call (isTailCall, mspec, None)
// ok, now we're ready to generate
if isSuperInit || isSelfInit then
CG.EmitInstrs cgbuf [ Push mspec.EnclosingType ] [ ldarg_0 ] ;
//dprintfn "mspec.Name = %s, curriedArgInfos = %A, nowArgs = %A" mspec.Name (List.map List.length curriedArgInfos) (List.length nowArgs)
GenUntupledArgsDiscardingLoneUnit cenv cgbuf eenv m (GetNumObjArgsOfValRef vref) curriedArgInfos nowArgs;
let nargs = mspec.FormalArgTypes.Length
if verbose then dprintf "GenApp: call, nargs = %d, mspec.ILCallingConv.IsStatic = %b\n" nargs mspec.CallingConv.IsStatic;
CG.EmitInstr cgbuf (List.replicate (nargs + (if mspec.CallingConv.IsStatic || newobj then 0 else 1)) Pop @
(if mustGenerateUnitAfterCall || isSuperInit || isSelfInit then [] else [Push (GenType m cenv.g eenv.tyenv actualRetTy)])) callInstr;
if verbose then dprintn ("GenApp: after");
// For isSuperInit, load the 'this' pointer as the pretend 'result' of the operation. It will be popped agin in most cases
if isSuperInit then CG.EmitInstrs cgbuf [ Push mspec.EnclosingType ] [ ldarg_0 ] ;
// When generating deubg code, generate a 'nop' after a 'call' that returns 'void'
// This is what C# does, as it allows the call location to be maintained correctly in the stack frame
if cenv.debug && mustGenerateUnitAfterCall && (isTailCall = Normalcall) then
CG.EmitInstrs cgbuf [ ] [ i_nop ] ;
if isNil laterArgs then
(* Generate the "unit" value if necessary *)
CommitCallSequel cenv eenv.cloc cgbuf mustGenerateUnitAfterCall sequel
else
GenIndirectCall cenv cgbuf eenv (actualRetTy,[],laterArgs,m) sequel
if verbose then dprintn ("GenApp: Method Done");
| _ -> failwith "??"
end
// This case is for getting/calling a value, when we can't call it directly.
// However, we know the type instantiation for the value.
// In this case we can often generate a type-specific local expression for the value.
// This reduces the number of dynamic type applications.
| (TExpr_val(vref,_,_),_,_) ->
GenGetValRefAndSequel cenv cgbuf eenv m vref (Some (tyargs,args,m,sequel))
| _ ->
(* worst case: generate a first-class function value and call *)
GenExpr cenv cgbuf eenv SPSuppress f Continue;
GenIndirectCall cenv cgbuf eenv (fty,tyargs,args,m) sequel
and CanTailcall(boxity,withinSEH,hasByrefArg,mustGenerateUnitAfterCall,isDllImport,isSelfInit,makesNoCriticalTailcalls,sequel) =
if (boxity = AsObject) && not withinSEH && not hasByrefArg && not isDllImport && not isSelfInit && not makesNoCriticalTailcalls &&
// We can tailcall even if we need to generate "unit", as long as we're about to throw the value away anyway as par of the return.
// We can tailcall if we don't need to generate "unit", as long as we're about to return.
(match sequel_ignore_end_scopes sequel with
| ReturnVoid | Return -> not mustGenerateUnitAfterCall
| DiscardThen ReturnVoid -> mustGenerateUnitAfterCall
| _ -> false)
then Tailcall
else Normalcall
and GenNamedLocalTyFuncCall cenv (cgbuf: CodeGenBuffer) eenv typ cloinfo tyargs m =
if verbose then dprintn ("Compiling local type func call in "^cgbuf.MethodName);
let contract_tinst = cloinfo.ltyfunc_contract_ftyvs |> List.map mk_typar_ty |> GenTypeArgs m cenv.g eenv.tyenv
let ilTyArgs = tyargs |> GenTypeArgs m cenv.g eenv.tyenv
let _,contract_meth_il_gparams,(contract_clo_il_tspec:ILTypeSpec),contract_formal_il_rty = GenNamedLocalTypeFuncContractInfo cenv m cloinfo
let contract_il_tspec = mk_tspec(contract_clo_il_tspec.TypeRef,contract_tinst)
if not (List.length contract_meth_il_gparams = List.length tyargs) then errorR(Error("incorrect number of type arguments to local call",m));
let contract_il_ty = Type_boxed contract_il_tspec
// Local TyFunc are represented as a $contract type. they currently get stored in a value of type object
// Recover result (value or reference types) via unbox_any.
CG.EmitInstrs cgbuf [Pop;Push contract_il_ty] [I_unbox_any contract_il_ty];
let actual_rty = apply_types cenv.g typ (tyargs,[])
let il_mspec = mk_instance_mspec_in_boxed_tspec(contract_il_tspec, "DirectInvoke", [], contract_formal_il_rty, ilTyArgs)
let ilActualRetTy = GenType m cenv.g eenv.tyenv actual_rty
CountCallFuncInstructions();
CG.EmitInstr cgbuf [Pop;Push ilActualRetTy] (mk_normal_callvirt il_mspec);
if verbose then dprintn "Done local type func call..."
actual_rty
and GenIndirectCall cenv cgbuf eenv (functy,tyargs,args,m) sequel =
if verbose then dprintn ("Compiling call in "^cgbuf.MethodName);
GenExprs cenv cgbuf eenv args;
if verbose then dprintn "Compiling call instruction...";
(* Fold in the new types into the environment as we generate the formal types. *)
let apps =
let typars,formal_functy = try_dest_forall_typ cenv.g functy
if verbose then dprintf "length args = %d, formal_functy = %s\n" (List.length args) (showL(typeL formal_functy));
let feenv = add_typars eenv.tyenv typars
let mk_ty_apps = List.foldBack (fun tyarg apps -> Apps_tyapp(GenType m cenv.g eenv.tyenv tyarg,apps)) tyargs
let formal_rty,mk_tm_apps =
List.fold
(fun (formal_functy,sofar) _ ->
let formal_dty,formal_rty = dest_fun_typ cenv.g formal_functy
(formal_rty,(fun apps -> sofar (Apps_app(GenType m cenv.g feenv formal_dty,apps)))))
(formal_functy,(fun x -> x))
args
if verbose then dprintn "Compiling return type...";
let ret_apps = Apps_done (GenType m cenv.g feenv formal_rty)
mk_ty_apps (mk_tm_apps ret_apps)
let actual_rty = apply_types cenv.g functy (tyargs, args)
let ilActualRetTy = GenType m cenv.g eenv.tyenv actual_rty
let hasByrefArg =
let rec check x =
match x with
| Apps_tyapp(_,apps') -> check apps'
| Apps_app(arg,apps') -> is_byref arg || check apps'
| _ -> false
check apps
let isTailCall = CanTailcall(AsObject,eenv.withinSEH,hasByrefArg,false,false,false,false,sequel)
CountCallFuncInstructions();
CG.EmitInstr cgbuf (List.replicate (1+args.Length) Pop @ [Push ilActualRetTy]) (mk_IlxInstr (EI_callfunc(isTailCall,apps)));
if verbose then dprintn "Done compiling indirect call...";
GenSequel cenv eenv.cloc cgbuf sequel
//--------------------------------------------------------------------------
// Generate try expressions
//--------------------------------------------------------------------------
and GenTry cenv cgbuf eenv scopeMarks (e1,m,resty,spTry) =
let sp =
match spTry with
| SequencePointAtTry m -> CG.EmitSeqPoint cgbuf m; SPAlways
| NoSequencePointAtTry -> SPSuppress
let stack,eenvinner = EmitSaveStack cenv cgbuf eenv m scopeMarks
let start_try = CG.GenerateMark cgbuf "start_try"
let end_try = CG.EmitDelayMark cgbuf "end_try"
let after_handler = CG.EmitDelayMark cgbuf "after_handler"
let eenvinner = {eenvinner with withinSEH = true}
let il_resty = GenType m cenv.g eenvinner.tyenv resty
let where_to_save_expr,eenvinner = AllocLocal cenv cgbuf eenvinner true (ilxgenGlobalNng.FreshCompilerGeneratedName ("tryres",m),il_resty) (start_try,end_try)
// Generate the body of the try. In the normal case (SequencePointAtTry) we generate a sequence point
// both on the 'try' keyword and on the start of the expression in the 'try'. For inlined code and
// compiler generated 'try' blocks (i.e. NoSequencePointAtTry, used for the try/finally implicit
// in a 'use' or 'foreach'), we suppress the sequence point
GenExpr cenv cgbuf eenvinner sp e1 (LeaveHandler (false, where_to_save_expr,after_handler));
CG.SetMarkToHere cgbuf end_try;
let tryMarks = (code_label_of_mark start_try, code_label_of_mark end_try)
where_to_save_expr,eenvinner,stack,tryMarks,after_handler,il_resty
and GenTryCatch cenv cgbuf eenv (e1,vf:Val,ef,vh:Val,eh,m,resty,spTry,spWith) sequel =
if verbose then dprintn ("GenTry");
(* Save the stack - gross because IL flushes the stack at the exn. handler *)
(* note: eenvinner notes spill vars are live *)
LocalScope "trystack" cgbuf (fun scopeMarks ->
let where_to_save_expr,eenvinner,stack,tryMarks,after_handler,il_resty = GenTry cenv cgbuf eenv scopeMarks (e1,m,resty,spTry)
(* Now the List.filter and catch blocks *)
let seh =
if cenv.generateFilterBlocks then
let startOfFilter = CG.GenerateMark cgbuf "startOfFilter"
let afterFilter = CG.EmitDelayMark cgbuf "afterFilter"
let (sequelOnBranches,afterJoin,stackAfterJoin,sequelAfterJoin) = GenJoinPoint cenv cgbuf "filter" eenv cenv.g.int_ty m EndFilter
begin
// We emit the sequence point for the 'with' keyword span on the start of the List.filter
// block. However the targets of the List.filter block pattern matching should not get any
// sequence points (they will be 'true'/'false' values indicating if the exception has been
// caught or not).
//
// The targets of the handler block DO get sequence points. Thus the expected behaviour
// for a try/with with a complex pattern is that we hit the "with" before the List.filter is run
// and then jump to the handler for the successful catch (or continue with exception handling
// if the List.filter fails)
match spWith with
| SequencePointAtWith m -> CG.EmitSeqPoint cgbuf m
| NoSequencePointAtWith -> ()
CG.SetStack cgbuf [cenv.g.ilg.typ_Object];
let _,eenvinner = AllocLocalVal cenv cgbuf vf eenvinner None (startOfFilter,afterFilter)
CG.EmitInstr cgbuf [Pop; Push cenv.g.ilg.typ_Exception] (I_castclass cenv.g.ilg.typ_Exception);
GenStoreVal cenv cgbuf eenvinner vf.Range vf;
// Why SPSuppress? Because we do not emit a sequence point at the start of the List.filter - we've already put one on
// the 'with' keyword above
GenExpr cenv cgbuf eenvinner SPSuppress ef sequelOnBranches;
CG.SetMarkToHere cgbuf afterJoin;
CG.SetStack cgbuf stackAfterJoin;
GenSequel cenv eenv.cloc cgbuf sequelAfterJoin;
end;
let endOfFilter = CG.GenerateMark cgbuf "endOfFilter"
let filterMarks = (code_label_of_mark startOfFilter, code_label_of_mark endOfFilter)
CG.SetMarkToHere cgbuf afterFilter;
let startOfHandler = CG.GenerateMark cgbuf "startOfHandler"
begin
CG.SetStack cgbuf [cenv.g.ilg.typ_Object];
let _,eenvinner = AllocLocalVal cenv cgbuf vh eenvinner None (startOfHandler,after_handler)
CG.EmitInstr cgbuf [Pop; Push cenv.g.ilg.typ_Exception] (I_castclass cenv.g.ilg.typ_Exception);
GenStoreVal cenv cgbuf eenvinner vh.Range vh;
GenExpr cenv cgbuf eenvinner SPAlways eh (LeaveHandler (false, where_to_save_expr,after_handler));
end;
let endOfHandler = CG.GenerateMark cgbuf "endOfHandler"
let handlerMarks = (code_label_of_mark startOfHandler, code_label_of_mark endOfHandler)
SEH_filter_catch(filterMarks, handlerMarks)
else
let startOfHandler = CG.GenerateMark cgbuf "startOfHandler"
begin
match spWith with
| SequencePointAtWith m -> CG.EmitSeqPoint cgbuf m
| NoSequencePointAtWith -> ()
CG.SetStack cgbuf [cenv.g.ilg.typ_Object];
let _,eenvinner = AllocLocalVal cenv cgbuf vh eenvinner None (startOfHandler,after_handler)
CG.EmitInstr cgbuf [Pop; Push cenv.g.ilg.typ_Exception] (I_castclass cenv.g.ilg.typ_Exception);
GenStoreVal cenv cgbuf eenvinner m vh;
GenExpr cenv cgbuf eenvinner SPAlways eh (LeaveHandler (false, where_to_save_expr,after_handler));
end;
let endOfHandler = CG.GenerateMark cgbuf "endOfHandler"
let handlerMarks = (code_label_of_mark startOfHandler, code_label_of_mark endOfHandler)
SEH_type_catch(cenv.g.ilg.typ_Object, handlerMarks)
cgbuf.EmitExceptionClause
{ exnClauses = [ seh ];
exnRange= tryMarks } ;
CG.SetMarkToHere cgbuf after_handler;
CG.SetStack cgbuf [];
match spWith with
| SequencePointAtWith m -> CG.EmitSeqPoint cgbuf m
| NoSequencePointAtWith -> ()
(* Restore the stack and load the result *)
EmitRestoreStack cenv cgbuf stack; (* RESTORE *)
EmitGetLocal cgbuf il_resty where_to_save_expr;
GenSequel cenv eenv.cloc cgbuf sequel
)
and GenTryFinally cenv cgbuf eenv (e1,e2,m,resty,spTry,spFinally) sequel =
if verbose then dprintn ("GenTry");
(* Save the stack - gross because IL flushes the stack at the exn. handler *)
(* note: eenvinner notes spill vars are live *)
LocalScope "trystack" cgbuf (fun scopeMarks ->
let where_to_save_expr,eenvinner,stack,tryMarks,after_handler,il_resty = GenTry cenv cgbuf eenv scopeMarks (e1,m,resty,spTry)
(* Now the catch/finally block *)
let startOfHandler = CG.GenerateMark cgbuf "startOfHandler"
CG.SetStack cgbuf [];
let sp =
match spFinally with
| SequencePointAtFinally m -> CG.EmitSeqPoint cgbuf m; SPAlways
| NoSequencePointAtFinally -> SPSuppress
GenExpr cenv cgbuf eenvinner sp e2 (LeaveHandler (true, where_to_save_expr,after_handler));
let endOfHandler = CG.GenerateMark cgbuf "endOfHandler"
let handlerMarks = (code_label_of_mark startOfHandler, code_label_of_mark endOfHandler)
cgbuf.EmitExceptionClause
{ exnClauses = [ SEH_finally(handlerMarks) ];
exnRange = tryMarks } ;
CG.SetMarkToHere cgbuf after_handler;
CG.SetStack cgbuf [];
(* Restore the stack and load the result *)
EmitRestoreStack cenv cgbuf stack; (* RESTORE *)
EmitGetLocal cgbuf il_resty where_to_save_expr;
GenSequel cenv eenv.cloc cgbuf sequel
)
//--------------------------------------------------------------------------
// Generate for-loop
//--------------------------------------------------------------------------
and GenForLoop cenv cgbuf eenv (spFor,v,e1,dir,e2,loopBody,m) sequel =
// The JIT/NGen eliminate array-bounds checks for C# loops of form:
// for(int i=0; i < (#ldlen arr#); i++) { ... arr[i] ... }
// Here
// dir = BI_blt indicates an optimized for loop that fits C# form that evaluates its 'end' argument each time around
// dir = BI_ble indicates a normal F# for loop that evaluates its argument only once
//
// It is also important that we follow C# IL-layout exactly "prefix, jmp test, body, test, finish" for JIT/NGEN.
let start = CG.GenerateMark cgbuf "for_start"
let finish = CG.EmitDelayMark cgbuf "for_finish"
let inner = CG.EmitDelayMark cgbuf "for_inner"
let test = CG.EmitDelayMark cgbuf "for_test"
let stack,eenvinner = EmitSaveStack cenv cgbuf eenv m (start,finish)
let isUp = (match dir with | FSharpForLoopUp | CSharpForLoopUp -> true | FSharpForLoopDown -> false);
let isFSharpStyle = (match dir with FSharpForLoopUp | FSharpForLoopDown -> true | CSharpForLoopUp -> false);
let finishIdx,eenvinner =
if isFSharpStyle then
let v,eenvinner = AllocLocal cenv cgbuf eenvinner true (ilxgenGlobalNng.FreshCompilerGeneratedName ("endLoop",m), cenv.g.ilg.typ_int32) (start,finish)
v, eenvinner
else
-1,eenvinner
let _,eenvinner = AllocLocalVal cenv cgbuf v eenvinner None (start,finish) (* note: eenvStack noted stack spill vars are live *)
match spFor with
| SequencePointAtForLoop(spStart) -> CG.EmitSeqPoint cgbuf spStart;
| NoSequencePointAtForLoop -> ()
GenExpr cenv cgbuf eenv SPSuppress e1 Continue;
GenStoreVal cenv cgbuf eenvinner m v;
if isFSharpStyle then
GenExpr cenv cgbuf eenv SPSuppress e2 Continue;
EmitSetLocal cgbuf finishIdx
EmitGetLocal cgbuf cenv.g.ilg.typ_int32 finishIdx
GenGetLocalVal cenv cgbuf eenvinner (range_of_expr e2) v None;
CG.EmitInstr cgbuf [Pop;Pop] (I_brcmp ((if isUp then BI_blt else BI_bgt),code_label_of_mark finish,code_label_of_mark inner));
else
CG.EmitInstr cgbuf [] (I_br (code_label_of_mark test));
// .inner
CG.SetMarkToHere cgbuf inner;
// <loop body>
GenExpr cenv cgbuf eenvinner SPAlways loopBody discard;
// v++ or v--
GenGetLocalVal cenv cgbuf eenvinner (range_of_expr e2) v None;
CG.EmitInstr cgbuf [Push cenv.g.ilg.typ_int32] (mk_ldc_i32 (1));
CG.EmitInstr cgbuf [Pop] (I_arith (if isUp then AI_add else AI_sub));
GenStoreVal cenv cgbuf eenvinner m v;
// .text
CG.SetMarkToHere cgbuf test;
// FSharpForLoopUp: if v <> e2 + 1 then goto .inner
// FSharpForLoopDown: if v <> e2 - 1 then goto .inner
// CSharpStyle: if v < e2 then goto .inner
CG.EmitSeqPoint cgbuf (range_of_expr e2);
GenGetLocalVal cenv cgbuf eenvinner (range_of_expr e2) v None;
let cmp = match dir with FSharpForLoopUp | FSharpForLoopDown -> BI_bne_un | CSharpForLoopUp -> BI_blt
let e2Sequel = (CmpThenBrOrContinue ( [Pop; Pop], I_brcmp(cmp,code_label_of_mark inner,code_label_of_mark finish)));
if isFSharpStyle then
EmitGetLocal cgbuf cenv.g.ilg.typ_int32 finishIdx
CG.EmitInstr cgbuf [Push cenv.g.ilg.typ_int32] (mk_ldc_i32 1);
CG.EmitInstr cgbuf [Pop] (I_arith (if isUp then AI_add else AI_sub));
GenSequel cenv eenv.cloc cgbuf e2Sequel
else
GenExpr cenv cgbuf eenv SPSuppress e2 e2Sequel;
// .finish - loop-exit here
CG.SetMarkToHere cgbuf finish;
// Restore the stack and load the result
EmitRestoreStack cenv cgbuf stack;
GenUnitThenSequel cenv eenv.cloc cgbuf sequel
//--------------------------------------------------------------------------
// Generate while-loop
//--------------------------------------------------------------------------
and GenWhileLoop cenv cgbuf eenv (spWhile,e1,e2,m) sequel =
let finish = CG.EmitDelayMark cgbuf "while_finish"
let inner = CG.EmitDelayMark cgbuf "while_inner"
let start_test = CG.GenerateMark cgbuf "start_test"
match spWhile with
| SequencePointAtWhileLoop(spStart) -> CG.EmitSeqPoint cgbuf spStart;
| NoSequencePointAtWhileLoop -> ()
(* SEQUENCE POINTS: Emit a sequence point to cover all of 'while e do' *)
GenExpr cenv cgbuf eenv SPSuppress e1 (CmpThenBrOrContinue ([Pop],(I_brcmp(BI_brfalse,code_label_of_mark finish,code_label_of_mark inner))));
CG.SetMarkToHere cgbuf inner;
GenExpr cenv cgbuf eenv SPAlways e2 (DiscardThen (Br start_test));
CG.SetMarkToHere cgbuf finish;
(* SEQUENCE POINTS: Emit a sequence point to cover 'done' if present *)
GenUnitThenSequel cenv eenv.cloc cgbuf sequel
//--------------------------------------------------------------------------
// Generate seq
//--------------------------------------------------------------------------
and GenSequential cenv cgbuf eenv spIn (e1,e2,specialSeqFlag,spSeq,m) sequel =
// Compiler generated sequential executions result in suppressions of sequence points on both
// left and right of the sequence
let spAction,spExpr =
(match spSeq with
| SequencePointsAtSeq -> SPAlways,spIn
| SuppressSequencePointOnExprOfSequential -> SPSuppress,spIn
| SuppressSequencePointOnStmtOfSequential -> spIn,SPSuppress)
match specialSeqFlag with
| NormalSeq ->
if verbose then dprintf "GenSequential (normal), sequel = %s\n" (StringOfSequel sequel);
GenExpr cenv cgbuf eenv spAction e1 discard;
GenExpr cenv cgbuf eenv spExpr e2 sequel
| ThenDoSeq ->
GenExpr cenv cgbuf eenv spExpr e1 Continue;
GenExpr cenv cgbuf eenv spAction e2 discard;
GenSequel cenv eenv.cloc cgbuf sequel
//--------------------------------------------------------------------------
// Generate IL assembly code.
// Polymorphic IL/ILX instructions may be instantiated when polymorphic code is inlined.
// We must implement this for the few uses of polymorphic instructions
// in the standard libarary.
//--------------------------------------------------------------------------
and GenAsmCode cenv cgbuf eenv (il,tyargs,args,rtys,m) sequel =
if verbose then dprintf "GenAsmCode, #args = %d" (List.length args);
let ilTyArgs = GenTypesPermitVoid m cenv.g eenv.tyenv tyargs
let il_rtys = GenTypesPermitVoid m cenv.g eenv.tyenv rtys
let il_after_inst =
il |> List.filter (function I_arith AI_nop -> false | _ -> true)
|> List.map (fun i ->
let err s =
errorR(InternalError(sprintf "%s: bad instruction: %A" s i,m))
let mod_fspec fspec =
{fspec with fspecEnclosingType=
let ty = fspec.fspecEnclosingType
let tspec = ty.TypeSpec
mk_typ (boxity_of_typ ty) (ILTypeSpec.Create(tspec.TypeRef, ilTyArgs)) }
match i,ilTyArgs with
| I_unbox_any (Type_tyvar idx) ,[tyarg] -> I_unbox_any (tyarg)
| I_box (Type_tyvar idx) ,[tyarg] -> I_box (tyarg)
| I_isinst (Type_tyvar idx) ,[tyarg] -> I_isinst (tyarg)
| I_castclass (Type_tyvar idx) ,[tyarg] -> I_castclass (tyarg)
| I_newarr (shape,Type_tyvar idx) ,[tyarg] -> I_newarr (shape,tyarg)
| I_ldelem_any (shape,Type_tyvar idx) ,[tyarg] -> I_ldelem_any (shape,tyarg)
| I_ldelema (ro,shape,Type_tyvar idx) ,[tyarg] -> I_ldelema (ro,shape,tyarg)
| I_stelem_any (shape,Type_tyvar idx) ,[tyarg] -> I_stelem_any (shape,tyarg)
| I_ldobj (a,b,Type_tyvar idx) ,[tyarg] -> I_ldobj (a,b,tyarg)
| I_stobj (a,b,Type_tyvar idx) ,[tyarg] -> I_stobj (a,b,tyarg)
| I_ldtoken (Token_type (Type_tyvar idx)),[tyarg] -> I_ldtoken (Token_type (tyarg))
| I_sizeof (Type_tyvar idx) ,[tyarg] -> I_sizeof (tyarg)
| I_ldfld (al,vol,fspec) ,_ -> I_ldfld (al,vol,mod_fspec fspec)
| I_ldflda (fspec) ,_ -> I_ldflda (mod_fspec fspec)
| I_stfld (al,vol,fspec) ,_ -> I_stfld (al,vol,mod_fspec fspec)
| I_stsfld (vol,fspec) ,_ -> I_stsfld (vol,mod_fspec fspec)
| I_ldsfld (vol,fspec) ,_ -> I_ldsfld (vol,mod_fspec fspec)
| I_ldsflda (fspec) ,_ -> I_ldsflda (mod_fspec fspec)
| EI_ilzero(Type_tyvar idx) ,[tyarg] -> EI_ilzero(tyarg)
| I_other e,_ when is_ilx_ext_instr e ->
begin match (dest_ilx_ext_instr e),ilTyArgs with
| _ ->
if not (isNil tyargs) then err "Bad polymorphic ILX instruction";
i
end
| I_arith AI_nop,_ -> i
(* These are embedded in the IL for a an initonly ldfld, i.e. *)
(* here's the relevant comment from tc.ml *)
(* "Add an I_nop if this is an initonly field to make sure we never recognize it as an lvalue. See mk_expra_of_expr." *)
| _ ->
if not (isNil tyargs) then err "Bad polymorphic IL instruction";
i)
match il_after_inst,args,sequel,il_rtys with
// Strip off any ("ceq" x false) when the sequel is a comparison branch and change the BI_brfalse to a BI_brtrue
// This is the instruction sequence for "not"
// For these we can just generate the argument and change the test (from a brfalse to a brtrue and vice versa)
| ([ I_arith AI_ceq ],
[arg1; TExpr_const((TConst_bool false | TConst_sbyte 0y| TConst_int16 0s | TConst_int32 0 | TConst_int64 0L | TConst_byte 0uy| TConst_uint16 0us | TConst_uint32 0u | TConst_uint64 0UL),_,_) ],
CmpThenBrOrContinue([Pop],I_brcmp (((BI_brfalse | BI_brtrue) as bi) , label1,label2)),
_) ->
let bi = match bi with BI_brtrue -> BI_brfalse | _ -> BI_brtrue
GenExpr cenv cgbuf eenv SPSuppress arg1 (CmpThenBrOrContinue([Pop],I_brcmp (bi, label1,label2)))
// Query; when do we get a 'ret' in IL assembly code?
| [ I_ret ], [arg1],sequel,[ilRetTy] ->
GenExpr cenv cgbuf eenv SPSuppress arg1 Continue;
CG.EmitInstr cgbuf [Pop] I_ret;
GenSequelEndScopes cgbuf sequel
// Query; when do we get a 'ret' in IL assembly code?
| [ I_ret ], [],sequel,[ilRetTy] ->
CG.EmitInstr cgbuf [Pop] I_ret;
GenSequelEndScopes cgbuf sequel
// 'throw' instructions are a bit of a problem - e.g. let x = (throw ...) in ... expects a value *)
// to be left on the stack. But dead-code checking by some versions of the .NET verifier *)
// mean that we can't just have fake code after the throw to generate the fake value *)
// (nb. a fake value can always be generated by a "ldnull unbox.any ty" sequence *)
// So in the worst case we generate a fake (never-taken) branch to a piece of code to generate *)
// the fake value *)
| [ I_throw ], [arg1],sequel,[ilRetTy] ->
match sequel_ignore_end_scopes sequel with
| s when IsSequelImmediate s ->
if verbose then dprintf "GenAsmCode: throw: A\n";
(* In most cases we can avoid doing this... *)
GenExpr cenv cgbuf eenv SPSuppress arg1 Continue;
CG.EmitInstr cgbuf [Pop] I_throw;
GenSequelEndScopes cgbuf sequel
| _ ->
if verbose then dprintf "GenAsmCode: throw: B\n";
let after1 = CG.EmitDelayMark cgbuf ("fake_join")
let after2 = CG.EmitDelayMark cgbuf ("fake_join")
let after3 = CG.EmitDelayMark cgbuf ("fake_join")
CG.EmitInstrs cgbuf [] [mk_ldc_i32 0;
I_brcmp (BI_brfalse,code_label_of_mark after2,code_label_of_mark after1); ];
CG.SetMarkToHere cgbuf after1;
CG.EmitInstrs cgbuf [Push ilRetTy] [i_ldnull; I_unbox_any ilRetTy; I_br (code_label_of_mark after3) ];
CG.SetMarkToHere cgbuf after2;
GenExpr cenv cgbuf eenv SPSuppress arg1 Continue;
CG.EmitInstr cgbuf [Pop] I_throw;
CG.SetMarkToHere cgbuf after3;
GenSequel cenv eenv.cloc cgbuf sequel;
| _ ->
// float or float32 or float<_> or float32<_>
let g = cenv.g in
let anyfpType ty = type_equiv_aux EraseMeasures g g.float_ty ty || type_equiv_aux EraseMeasures g g.float32_ty ty
// Otherwise generate the arguments, and see if we can use a I_brcmp rather than a comparison followed by an I_brfalse/I_brtrue
GenExprs cenv cgbuf eenv args;
match il_after_inst,sequel with
(* NOTE: THESE ARE NOT VALID ON FLOATING POINT DUE TO NaN. Hence INLINE ASM ON FP. MUST BE CAREFULLY WRITTEN *)
| [ I_arith AI_clt ], CmpThenBrOrContinue([Pop],I_brcmp (BI_brfalse, label1,label2)) when not (anyfpType (type_of_expr g args.Head)) ->
CG.EmitInstr cgbuf [Pop; Pop] (I_brcmp(BI_bge,label1,label2));
| [ I_arith AI_cgt ], CmpThenBrOrContinue([Pop],I_brcmp (BI_brfalse, label1,label2)) when not (anyfpType (type_of_expr g args.Head)) ->
CG.EmitInstr cgbuf [Pop; Pop] (I_brcmp(BI_ble,label1, label2));
| [ I_arith AI_clt_un ], CmpThenBrOrContinue([Pop],I_brcmp (BI_brfalse, label1,label2)) when not (anyfpType (type_of_expr g args.Head)) ->
CG.EmitInstr cgbuf [Pop; Pop] (I_brcmp(BI_bge_un,label1,label2));
| [ I_arith AI_cgt_un ], CmpThenBrOrContinue([Pop],I_brcmp (BI_brfalse, label1,label2)) when not (anyfpType (type_of_expr g args.Head)) ->
CG.EmitInstr cgbuf [Pop; Pop] (I_brcmp(BI_ble_un,label1, label2));
| [ I_arith AI_ceq ], CmpThenBrOrContinue([Pop],I_brcmp (BI_brfalse, label1,label2)) when not (anyfpType (type_of_expr g args.Head)) ->
CG.EmitInstr cgbuf [Pop; Pop] (I_brcmp(BI_bne_un,label1, label2));
// THESE ARE VALID ON FP w.r.t. NaN
| [ I_arith AI_clt ], CmpThenBrOrContinue([Pop],I_brcmp (BI_brtrue, label1,label2)) ->
CG.EmitInstr cgbuf [Pop; Pop] (I_brcmp(BI_blt,label1, label2));
| [ I_arith AI_cgt ], CmpThenBrOrContinue([Pop],I_brcmp (BI_brtrue, label1,label2)) ->
CG.EmitInstr cgbuf [Pop; Pop] (I_brcmp(BI_bgt,label1, label2));
| [ I_arith AI_clt_un ], CmpThenBrOrContinue([Pop],I_brcmp (BI_brtrue, label1,label2)) ->
CG.EmitInstr cgbuf [Pop; Pop] (I_brcmp(BI_blt_un,label1, label2));
| [ I_arith AI_cgt_un ], CmpThenBrOrContinue([Pop],I_brcmp (BI_brtrue, label1,label2)) ->
CG.EmitInstr cgbuf [Pop; Pop] (I_brcmp(BI_bgt_un,label1, label2));
| [ I_arith AI_ceq ], CmpThenBrOrContinue([Pop],I_brcmp (BI_brtrue, label1,label2)) ->
CG.EmitInstr cgbuf [Pop; Pop] (I_brcmp(BI_beq,label1, label2));
| _ ->
// Failing that, generate the real IL leaving value(s) on the stack
CG.EmitInstrs cgbuf (List.replicate args.Length Pop @ List.map push il_rtys) il_after_inst;
// If no return values were specified generate a "unit"
if isNil rtys then
GenUnitThenSequel cenv eenv.cloc cgbuf sequel
else
GenSequel cenv eenv.cloc cgbuf sequel
//--------------------------------------------------------------------------
// Generate expression quotations
//--------------------------------------------------------------------------
and GenQuotation cenv cgbuf eenv (ast,conv,m,ety) sequel =
let argTypes,argExprs, astSpec =
match !conv with
| Some res -> res
| None ->
try
Creflect.ConvExprPublic (cenv.g, cenv.amap, cenv.viewCcu) Creflect.empty_env ast
with
Creflect.InvalidQuotedTerm e -> error(e)
let astPickledBytes = Sreflect.pickle astSpec
let mk_typeof_expr ilty =
mk_asm ([ mk_normal_call (mspec_Type_GetTypeFromHandle cenv.g.ilg) ], [],
[mk_asm ([ I_ldtoken (Token_type ilty) ], [],[],[cenv.g.system_RuntimeTypeHandle_typ],m)],
[cenv.g.system_Type_typ],m)
let someTypeInModuleExpr = mk_typeof_expr(Type_boxed eenv.someTspecInThisModule)
let rawTy = mk_raw_expr_ty cenv.g
let mk_list ty els = List.foldBack (mk_cons cenv.g ty) els (mk_nil cenv.g m ty)
let typeExprs = List.map (GenType m cenv.g eenv.tyenv >> mk_typeof_expr) argTypes
let typesExpr = mk_list cenv.g.system_Type_typ typeExprs
let argsExpr = mk_list rawTy argExprs
let bytesExpr = TExpr_op(TOp_bytes(astPickledBytes),[],[],m)
let unpickledExpr = mk_call_unpickle_quotation cenv.g m someTypeInModuleExpr typesExpr argsExpr bytesExpr
let afterCastExpr =
// Detect a typed quotation and insert the cast if needed. The cast should not fail but does
// unfortunately involve a "typeOf" computation over a quotation tree.
if tcref_eq cenv.g (tcref_of_stripped_typ cenv.g ety) cenv.g.expr_tcr then
mk_call_cast_quotation cenv.g m (List.hd (tinst_of_stripped_typ cenv.g ety)) unpickledExpr
else
unpickledExpr
GenExpr cenv cgbuf eenv SPSuppress afterCastExpr sequel
//--------------------------------------------------------------------------
// Generate calls to IL methods
//--------------------------------------------------------------------------
and GenIlCall cenv cgbuf eenv ((virt,protect,valu,newobj,vFlags,_,isDllImport,_,mref),enclTypeArgs,methTypeArgs,args,rtys,m) sequel =
if verbose then dprintn ("GenIlCall");
let hasByrefArg = mref.ArgTypes |> List.exists is_byref
let isSuperInit = (vFlags = CtorValUsedAsSuperInit)
let boxity = (if valu then AsValue else AsObject)
let mustGenerateUnitAfterCall = (isNil rtys)
let makesNoCriticalTailcalls = (newobj || not virt) // Don't tailcall for 'newobj', or 'call' to IL code
let tail = CanTailcall(boxity,eenv.withinSEH,hasByrefArg,mustGenerateUnitAfterCall,isDllImport,false,makesNoCriticalTailcalls,sequel)
let il_ttyargs = GenTypeArgs m cenv.g eenv.tyenv enclTypeArgs
let ilMethArgTys = GenTypeArgs m cenv.g eenv.tyenv methTypeArgs
let il_rtys = GenTypes m cenv.g eenv.tyenv rtys
let mspec = mk_mspec (mref,boxity,il_ttyargs,ilMethArgTys)
// Load the 'this' pointer to pass to the superclass constructor. This argument is not
// in the expression tree since it can't be treated like an ordinary value
if isSuperInit then CG.EmitInstrs cgbuf [ Push mspec.EnclosingType ] [ ldarg_0 ] ;
GenExprs cenv cgbuf eenv args;
let il =
if newobj then [ I_newobj(mspec,None) ]
elif virt then [ I_callvirt(tail,mspec,None) ]
else [ I_call(tail,mspec,None) ]
CG.EmitInstrs cgbuf (List.replicate (args.Length + (if isSuperInit then 1 else 0)) Pop @ (if isSuperInit then [] else List.map push il_rtys)) il;
// Load the 'this' pointer as the pretend 'result' of the isSuperInit operation.
// It will be immediately popped in most cases, but may also be used as the target of ome "property set" oeprations.
if isSuperInit then CG.EmitInstrs cgbuf [ Push mspec.EnclosingType ] [ ldarg_0 ] ;
CommitCallSequel cenv eenv.cloc cgbuf mustGenerateUnitAfterCall sequel
and CommitCallSequel cenv cloc cgbuf mustGenerateUnitAfterCall sequel =
if mustGenerateUnitAfterCall
then GenUnitThenSequel cenv cloc cgbuf sequel
else GenSequel cenv cloc cgbuf sequel
and GenTraitCall cenv cgbuf eenv (traitInfo, args, m) expr sequel =
let minfoOpt = CommitOperationResult (ConstraintSolver.CodegenWitnessThatTypSupportsTraitConstraint cenv.g cenv.amap m traitInfo)
match minfoOpt with
| None ->
let replacementExpr =
mk_throw m (type_of_expr cenv.g expr)
(mk_exnconstr(mk_MFCore_tcref cenv.g.fslibCcu "DynamicInvocationNotSupportedException",
[ mk_string cenv.g m traitInfo.MemberName],m))
GenExpr cenv cgbuf eenv SPSuppress replacementExpr sequel
| Some (minfo,methTypeArgs) ->
// Fix bug 1281: If we resolve to an instance method on a struct and we haven't yet taken
// the address of the object then go do that
if Infos.minfo_is_struct cenv.g minfo && minfo.IsInstance && (match args with [] -> false | h::t -> not (is_byref_typ cenv.g (type_of_expr cenv.g h))) then
let h,t = List.headAndTail args
let wrap,h' = mk_expra_of_expr cenv.g true PossiblyMutates h m
GenExpr cenv cgbuf eenv SPSuppress (wrap (TExpr_op(TOp_trait_call(traitInfo), [], (h' :: t), m))) sequel
else
let slotsig = Infos.SlotSigOfMethodInfo cenv.amap m minfo
let gty = minfo.EnclosingType
let (il_gty:ILType),ilParams,(ilReturn:ILReturnValue) = GenFormalSlotsig m cenv eenv slotsig
let ilArgTys = typs_of_params ilParams
let ilRetTy = ilReturn.Type
let mref = mk_mref(il_gty.TypeRef, (if minfo.IsInstance then ILCallingConv.Instance else ILCallingConv.Static), minfo.LogicalName, List.length (DropErasedTyargs methTypeArgs), ilArgTys, ilRetTy)
let tinst = snd(dest_stripped_tyapp_typ cenv.g gty)
let rtys = Option.to_list (actual_rty_of_slotsig tinst methTypeArgs slotsig)
GenIlCall cenv cgbuf eenv ((minfo.IsVirtual,
minfo.IsProtectedAccessiblity,
Infos.minfo_is_struct cenv.g minfo,false,NormalValUse,false,false,None,mref),
tinst,methTypeArgs,args,rtys,m) sequel
//--------------------------------------------------------------------------
// Generate byref-related operations
//--------------------------------------------------------------------------
and GenGetAddrOfRefCellField cenv cgbuf eenv (e,ty,m) sequel =
if verbose then dprintn ("GenGetAddrOfRefCellField");
GenExpr cenv cgbuf eenv SPSuppress e Continue;
let fref = GenRecdFieldRef m cenv eenv.tyenv (mk_refcell_contents_rfref cenv.g) [ty]
CG.EmitInstrs cgbuf [Pop; Push (Type_byref (actual_typ_of_fspec fref))] [ I_ldflda fref ] ;
GenSequel cenv eenv.cloc cgbuf sequel
and GenGetValAddr cenv cgbuf eenv (v,m) sequel =
if verbose then dprintn ("GenGetValAddr");
let vspec = deref_val v
let il_ty = GenTypeOfVal cenv eenv vspec
match storage_for_vref m v eenv with
| Local (idx,None) ->
CG.EmitInstrs cgbuf [ Push (Type_byref il_ty)] [ I_ldloca (uint16 idx) ] ;
| Arg idx ->
CG.EmitInstrs cgbuf [ Push (Type_byref il_ty)] [ I_ldarga (uint16 idx) ] ;
| StaticField (fspec,vref,hasLiteralAttr,ilTypeSpecForProperty,fieldName,_,il_ty,_,_,_) ->
if hasLiteralAttr then errorR(Error("Taking the address of a literal field is invalid",m));
EmitGetStaticFieldAddr cgbuf il_ty fspec
| Env (_,i,localCloInfo) ->
CG.EmitInstr cgbuf [Push (Type_byref il_ty)] (mk_IlxInstr (EI_ldenva i));
| Local (_,Some _) | Method _ | Env _ | Unrealized | Null ->
errorR(Error( "This operation involves taking the address of a value '"^v.DisplayName^"' represented using a local variable or other special representation. This is invalid",m));
CG.EmitInstrs cgbuf [Pop; Push (Type_byref il_ty)] [ I_ldarga (uint16 669 (* random value for post-hoc diagnostic analysis on generated tree *) ) ] ;
GenSequel cenv eenv.cloc cgbuf sequel
and GenGetByref cenv cgbuf eenv (v:ValRef,m) sequel =
if verbose then dprintn ("GenGetByref");
GenGetLocalVRef cenv cgbuf eenv m v None;
let ilty = GenType m cenv.g eenv.tyenv (dest_byref_typ cenv.g v.Type)
CG.EmitInstrs cgbuf [Pop; Push ilty] [ mk_normal_ldobj ilty ];
GenSequel cenv eenv.cloc cgbuf sequel
and GenSetByref cenv cgbuf eenv (v:ValRef,e,m) sequel =
if verbose then dprintn ("GenSetByref");
GenGetLocalVRef cenv cgbuf eenv m v None;
GenExpr cenv cgbuf eenv SPSuppress e Continue;
let ilty = GenType m cenv.g eenv.tyenv (dest_byref_typ cenv.g v.Type)
CG.EmitInstrs cgbuf [Pop; Pop] [ mk_normal_stobj ilty ];
GenUnitThenSequel cenv eenv.cloc cgbuf sequel
and GenDefaultValue cenv cgbuf eenv (ty,m) =
let il_ty = GenType m cenv.g eenv.tyenv ty
if is_ref_typ cenv.g ty then
CG.EmitInstr cgbuf [Push il_ty] i_ldnull
else
match try_tcref_of_stripped_typ cenv.g ty with
| Some tcref when (tcref_eq cenv.g cenv.g.system_SByte_tcref tcref ||
tcref_eq cenv.g cenv.g.system_Int16_tcref tcref ||
tcref_eq cenv.g cenv.g.system_Int32_tcref tcref ||
tcref_eq cenv.g cenv.g.system_Bool_tcref tcref ||
tcref_eq cenv.g cenv.g.system_Byte_tcref tcref ||
tcref_eq cenv.g cenv.g.system_Char_tcref tcref ||
tcref_eq cenv.g cenv.g.system_UInt16_tcref tcref ||
tcref_eq cenv.g cenv.g.system_UInt32_tcref tcref) ->
CG.EmitInstr cgbuf [Push il_ty] i_ldc_i32_0
| Some tcref when (tcref_eq cenv.g cenv.g.system_Int64_tcref tcref ||
tcref_eq cenv.g cenv.g.system_UInt64_tcref tcref) ->
CG.EmitInstr cgbuf [Push il_ty] (mk_ldc_i64 0L)
| Some tcref when (tcref_eq cenv.g cenv.g.system_Single_tcref tcref) ->
CG.EmitInstr cgbuf [Push il_ty] (mk_ldc_single 0.0f)
| Some tcref when (tcref_eq cenv.g cenv.g.system_Double_tcref tcref) ->
CG.EmitInstr cgbuf [Push il_ty] (mk_ldc_double 0.0)
| _ ->
let il_ty = GenType m cenv.g eenv.tyenv ty
LocalScope "ilzero" cgbuf (fun scopeMarks ->
let loc_idx,eenvinner = AllocLocal cenv cgbuf eenv true (ilxgenGlobalNng.FreshCompilerGeneratedName ("default",m), il_ty) scopeMarks
// "initobj" (Generated by EmitInitLocal) doesn't work on byref types
// But ilzero(&ty) only gets generated in the built-in get-address function so
// we can just rely on zeroinit of all IL locals.
match il_ty with
| Type_byref _ -> ()
| _ -> EmitInitLocal cgbuf il_ty loc_idx
EmitGetLocal cgbuf il_ty loc_idx;
)
//--------------------------------------------------------------------------
// Generate object expressions as ILX "closures"
//--------------------------------------------------------------------------
and GenSlotParam m cenv eenv isGeneric (TSlotParam(nm,ty,inFlag,outFlag,optionalFlag,attribs)) =
let inFlag2,outFlag2,optionalFlag2,paramMarshal2,attribs = GenParamAttribs cenv attribs
{ paramName=nm;
paramType= GenParamType m cenv.g eenv.tyenv false isGeneric ty;
paramDefault=None;
paramMarshal=paramMarshal2;
paramIn=inFlag || inFlag2;
paramOut=outFlag || outFlag2;
paramOptional=optionalFlag || optionalFlag2;
paramCustomAttrs= mk_custom_attrs (GenAttrs cenv eenv attribs) }
and GenFormalSlotsig m cenv eenv (TSlotSig(nm,typ,ctps,mtps,paraml,returnTy) as slotsig) =
let paraml = List.concat paraml
let ilTy = GenType m cenv.g eenv.tyenv typ
let eenv_for_slotsig = env_for_typars (ctps @ mtps) eenv
let isGeneric = nonNil (DropErasedTypars ctps) || nonNil (DropErasedTypars mtps)
let ilParams = paraml |> List.map (GenSlotParam m cenv eenv_for_slotsig isGeneric)
let ilRetTy = GenReturnType m cenv.g eenv_for_slotsig.tyenv false isGeneric returnTy
let ilReturn = mk_return ilRetTy
ilTy, ilParams,ilReturn
and inst_slotparam inst (TSlotParam(nm,ty,inFlag,fl2,fl3,attrs)) = TSlotParam(nm,InstType inst ty,inFlag,fl2,fl3,attrs)
and GenActualSlotsig m cenv eenv (TSlotSig(nm,typ,ctps,mtps,paraml,returnTy) as SlotSig) methTyparsOfOverridingMethod =
let paraml = List.concat paraml
let slotsig_inst = mk_typar_inst (ctps@mtps) (tinst_of_stripped_typ cenv.g typ @ generalize_typars methTyparsOfOverridingMethod)
let isGeneric = nonNil (DropErasedTypars ctps) || nonNil (DropErasedTypars mtps)
let ilParams = paraml |> List.map (inst_slotparam slotsig_inst >> GenSlotParam m cenv eenv isGeneric)
let ilRetTy = GenReturnType m cenv.g eenv.tyenv false isGeneric (Option.map (InstType slotsig_inst) returnTy)
let ilReturn = mk_return ilRetTy
ilParams,ilReturn
and GenMethodImpl cenv eenv (shouldUseMethodImpl,(TSlotSig(nameOfOverridenMethod,enclTypOfOverridenMethod,_,_,_,_) as slotsig)) m =
let ov_il_typ,ov_il_params,ov_il_ret = GenFormalSlotsig m cenv eenv slotsig
let reallyUseMethodImpl =
if shouldUseMethodImpl
&& cenv.workAroundReflectionEmitBugs
&& inst_of_typ ov_il_typ <> []
&& ov_il_typ.TypeRef.Scope = ScopeRef_local then
warning(Error("The implementation of a specified generic interface required a method implementation not fully supported by F# Interactive. In the unlikely event that the resulting class fails to load then compile the interface type into a statically-compiled DLL and reference it using '#r'",m));
false
else
shouldUseMethodImpl
let nameOfOverridingMethod = if reallyUseMethodImpl then qualified_mangled_name_of_tcref (tcref_of_stripped_typ cenv.g enclTypOfOverridenMethod) nameOfOverridenMethod else nameOfOverridenMethod
reallyUseMethodImpl,nameOfOverridingMethod,
(fun (ilTypeSpecForOverriding,methTyparsOfOverridingMethod) ->
let ov_tref = ov_il_typ.TypeRef
let ov_mref = mk_mref(ov_tref, ILCallingConv.Instance, nameOfOverridenMethod, List.length (DropErasedTypars methTyparsOfOverridingMethod), (typs_of_params ov_il_params), ov_il_ret.Type)
let eenv_for_ovby = AddTyparsToEnv methTyparsOfOverridingMethod eenv
let ilParamsOfOverridingMethod,ilReturnOfOverridingMethod = GenActualSlotsig m cenv eenv_for_ovby slotsig methTyparsOfOverridingMethod
let ovby_mgparams = GenGenericParams m cenv eenv_for_ovby.tyenv methTyparsOfOverridingMethod
let ovby_mgactuals = generalize_gparams ovby_mgparams
let ovby = mk_instance_mspec_in_boxed_tspec(ilTypeSpecForOverriding, nameOfOverridingMethod, typs_of_params ilParamsOfOverridingMethod, ilReturnOfOverridingMethod.Type, ovby_mgactuals)
{ mimplOverrides = OverridesSpec(ov_mref,ov_il_typ);
mimplOverrideBy = ovby })
and bindBaseVarOpt cenv eenv baseValOpt =
match baseValOpt with
| None -> eenv
| Some basev -> AddStorageForVal cenv.g (basev,notlazy (Arg 0)) eenv
and fixupVirtualSlotFlags mdef =
{mdef with
mdHideBySig=true;
mdKind = (match mdef.mdKind with
| MethodKind_virtual vinfo ->
MethodKind_virtual
{vinfo with
virtStrict=false }
| _ -> failwith "fixupVirtualSlotFlags") }
and renameMethodDef nameOfOverridingMethod mdef =
{mdef with mdName=nameOfOverridingMethod }
and fixupMethodImplFlags mdef =
{mdef with mdAccess=MemAccess_private;
mdHideBySig=true;
mdKind=(match mdef.mdKind with
| MethodKind_virtual vinfo ->
MethodKind_virtual
{vinfo with
virtStrict=false;
virtFinal=true;
virtNewslot=true; }
| _ -> failwith "fixupMethodImpl") }
and GenObjectMethod cenv eenvinner (cgbuf:CodeGenBuffer) shouldUseMethodImpl (TObjExprMethod((TSlotSig(nameOfOverridenMethod,enclTypOfOverridenMethod,_,_,_,_) as slotsig),methTyparsOfOverridingMethod,methodParams,methodBodyExpr,m)) =
let eenvUnderTypars = AddTyparsToEnv methTyparsOfOverridingMethod eenvinner
let ilParamsOfOverridingMethod,ilReturnOfOverridingMethod = GenActualSlotsig m cenv eenvUnderTypars slotsig methTyparsOfOverridingMethod
// Args are stored starting at #1
let methodParams = List.concat methodParams
let eenvForMeth = AddStorageForLocalVals cenv.g (methodParams |> List.mapi (fun i v -> (v,Arg i))) eenvUnderTypars
let ilMethodBody = CodeGenMethodForExpr cenv cgbuf.mgbuf (SPAlways,[],nameOfOverridenMethod,eenvForMeth,0,0,methodBodyExpr,(if slotsig_has_void_rty slotsig then discardAndReturnVoid else Return))
let reallyUseMethodImpl,nameOfOverridingMethod,methodImplGenerator = GenMethodImpl cenv eenvinner (shouldUseMethodImpl,slotsig) (range_of_expr methodBodyExpr)
let mdef =
mk_generic_virtual_mdef
(nameOfOverridingMethod,
ComputePublicMemberAccess false, (* false = may not change accessibility (override) *)
GenGenericParams m cenv eenvUnderTypars.tyenv methTyparsOfOverridingMethod,
ilParamsOfOverridingMethod,
ilReturnOfOverridingMethod,
MethodBody_il ilMethodBody)
// fixup attributes to generate a method impl
let mdef = if reallyUseMethodImpl then fixupMethodImplFlags mdef else mdef
let mdef = fixupVirtualSlotFlags mdef
(reallyUseMethodImpl,methodImplGenerator,methTyparsOfOverridingMethod),mdef
and GenObjectExpr cenv cgbuf eenvouter expr (baseType,baseValOpt,basecall,overrides,interfaceImpls,m) sequel =
if verbose then dprintn ("GenObjectExpr");
let cloinfo,body,eenvinner = GetIlxClosureInfo cenv m false None eenvouter expr
let cloAttribs = cloinfo.clo_attribs
let cloFreeVars = cloinfo.clo_freevars
let cloLambdas = cloinfo.clo_lambdas
let cloName = cloinfo.clo_name
let ilxCloSpec = cloinfo.clo_clospec
let ilCloFreeVars = cloinfo.clo_il_frees
let ilCloGenericFormals = cloinfo.clo_il_gparams
assert(isNil cloinfo.ltyfunc_direct_il_gparams);
let ilCloGenericActuals = inst_of_clospec cloinfo.clo_clospec
let ilCloRetTy = cloinfo.clo_formal_il_rty
let ilCloTypeRef = tref_of_clospec cloinfo.clo_clospec
let ilTypeSpecForOverriding = mk_tspec(ilCloTypeRef,ilCloGenericActuals)
let eenvinner = bindBaseVarOpt cenv eenvinner baseValOpt
let ilCtorBody = CodeGenMethodForExpr cenv cgbuf.mgbuf (SPAlways,[],cloName,eenvinner,1,0,basecall,discardAndReturnVoid)
let mdefs = overrides |> List.map (GenObjectMethod cenv eenvinner cgbuf false >> snd)
// Generate a method impl. This looks overly contorted and should likely be de-functionalized
let methodImplGenerator ((reallyUseMethodImpl,methodImplGeneratorFunction,methTyparsOfOverridingMethod),mdef) =
let mimpl = (if reallyUseMethodImpl then Some(methodImplGeneratorFunction (ilTypeSpecForOverriding,methTyparsOfOverridingMethod)) else None)
mimpl,mdef
let mimpls,interfaceImplMethodDefs = interfaceImpls |> List.collect (snd >> List.map (GenObjectMethod cenv eenvinner cgbuf true >> methodImplGenerator)) |> List.unzip
let mimpls = mimpls |> List.choose (fun x -> x)
let interfaceTys = interfaceImpls |> List.map (fst >> GenType m cenv.g eenvinner.tyenv)
let attrs = GenAttrs cenv eenvinner cloAttribs
let super = (if is_interface_typ cenv.g baseType then cenv.g.ilg.typ_Object else ilCloRetTy)
let interfaceTys = interfaceTys @ (if is_interface_typ cenv.g baseType then [ilCloRetTy] else [])
let cloTypeDef = GenClosureTypeDef cenv (ilCloTypeRef,cloFreeVars,ilCloGenericFormals,attrs,m,ilCloFreeVars,cloLambdas,ilCtorBody,(interfaceImplMethodDefs @ mdefs),mimpls,super,interfaceTys)
cgbuf.mgbuf.AddTypeDef(ilCloTypeRef,cloTypeDef);
CountClosure();
GenGetLocalVals cenv cgbuf eenvouter m cloFreeVars;
CG.EmitInstr cgbuf (List.replicate ilCloFreeVars.Length Pop@ [ Push (Pubclo.typ_of_lambdas cenv.g.ilxPubCloEnv cloLambdas)]) (mk_IlxInstr (EI_newclo ilxCloSpec));
GenSequel cenv eenvouter.cloc cgbuf sequel
and GenSequenceExpr cenv (cgbuf:CodeGenBuffer) eenvouter (nextEnumeratorValRef:ValRef,pcvref:ValRef,currvref:ValRef,stateVars,generateNextExpr,closeExpr,checkCloseExpr:expr,seqElemTy, m) sequel =
let stateVars = [ pcvref; currvref ] @ stateVars
let stateVarsSet = stateVars |> List.map deref_val |> Zset.of_list val_spec_order
if verbose then dprintn ("GenSequenceExpr");
// pretend that the state variables are bound
let eenvouter =
eenvouter |> AddStorageForLocalVals cenv.g (stateVars |> List.map (fun v -> v.Deref,Local(0,None)))
// Get the free variables. Make a lambda to pretend that the 'nextEnumeratorValRef' is bound (it is an argument to GenerateNext)
let (cloAttribs,_,_,cloFreeTyvars,cloFreeVars,ilCloTypeRef:ILTypeRef,eenvinner) =
GetIlxClosureFreeVars cenv m None eenvouter (mk_lambda m nextEnumeratorValRef.Deref (generateNextExpr, cenv.g.int32_ty))
let ilCloFreeVars = GetClosureILFreeVars cenv m [] eenvouter eenvinner cloFreeVars
let ilCloSeqElemTy = GenType m cenv.g eenvinner.tyenv seqElemTy
let cloRetTy = mk_seq_ty cenv.g seqElemTy
let ilCloRetTyInner = GenType m cenv.g eenvinner.tyenv cloRetTy
let ilCloRetTyOuter = GenType m cenv.g eenvouter.tyenv cloRetTy
let ilCloEnumeratorTy = GenType m cenv.g eenvinner.tyenv (mk_IEnumerator_ty cenv.g seqElemTy)
let ilCloEnumerableTy = GenType m cenv.g eenvinner.tyenv (mk_seq_ty cenv.g seqElemTy)
let ilCloBaseTy = GenType m cenv.g eenvinner.tyenv (mk_tyapp_ty cenv.g.seq_base_tcr [seqElemTy])
let ilCloGenericParams = GenGenericParams m cenv eenvinner.tyenv cloFreeTyvars
// Create a new closure class with a single "MoveNext" method that implements the iterator.
let ilCloTypeSpecInner = mk_tspec (ilCloTypeRef, generalize_gparams ilCloGenericParams)
let cloLambdas = Lambdas_return ilCloRetTyInner
let cloref = IlxClosureRef(ilCloTypeRef, cloLambdas, ilCloFreeVars)
let ilxCloSpec = IlxClosureSpec(cloref, GenGenericArgs m eenvouter.tyenv cloFreeTyvars)
let formalClospec = IlxClosureSpec(cloref, generalize_gparams ilCloGenericParams)
let getFreshMethod =
let mbody =
CodeGenMethod cenv cgbuf.mgbuf (true,[],"GetFreshEnumerator",eenvinner,1,0,
(fun cgbuf eenv ->
for fv in cloFreeVars do
(* TODO: Emit CompareExchange
if (System.Threading.Interlocked.CompareExchange(&__state, 1, 0) = 0) then
(x :> IEnumerator<'T>)
else
...
*)
/// State variables always get zero-initialized
if stateVarsSet.Contains fv then
GenDefaultValue cenv cgbuf eenv (fv.Type,m)
else
GenGetLocalVal cenv cgbuf eenv m fv None;
CG.EmitInstr cgbuf (List.replicate ilCloFreeVars.Length Pop@ [ Push ilCloRetTyInner ]) (mk_IlxInstr (EI_newclo formalClospec));
GenSequel cenv eenv.cloc cgbuf Return),
m)
mk_virtual_mdef("GetFreshEnumerator",MemAccess_public, [], mk_return ilCloEnumeratorTy, MethodBody_il mbody)
|> AddNonUserCompilerGeneratedAttribs cenv.g
let closeMethod =
// Note: We suppress the first sequence point in the body of this method since it is the initial state machine jump
let spReq = SPSuppress
mk_virtual_mdef("Close",MemAccess_public, [], mk_return Type_void, MethodBody_il (CodeGenMethodForExpr cenv cgbuf.mgbuf (spReq,[],"Close",eenvinner,1,0,closeExpr,discardAndReturnVoid)))
let checkCloseMethod =
// Note: We suppress the first sequence point in the body of this method since it is the initial state machine jump
let spReq = SPSuppress
mk_virtual_mdef("get_CheckClose",MemAccess_public, [], mk_return cenv.g.ilg.typ_Bool, MethodBody_il (CodeGenMethodForExpr cenv cgbuf.mgbuf (spReq,[],"get_CheckClose",eenvinner,1,0,checkCloseExpr,Return)))
let generateNextMethod =
// Note: We suppress the first sequence point in the body of this method since it is the initial state machine jump
let spReq = SPSuppress
// the 'next enumerator' byref arg is at arg position 1
let eenvinner = eenvinner |> AddStorageForLocalVals cenv.g [ (nextEnumeratorValRef.Deref, Arg 1) ]
mk_virtual_mdef("GenerateNext",MemAccess_public, [mk_named_param("next",Type_byref ilCloEnumerableTy)], mk_return cenv.g.ilg.typ_Int32, MethodBody_il (CodeGenMethodForExpr cenv cgbuf.mgbuf (spReq,[],"GenerateNext",eenvinner,2,0,generateNextExpr,Return)))
let lastGeneratedMethod =
mk_virtual_mdef("get_LastGenerated",MemAccess_public, [], mk_return ilCloSeqElemTy, MethodBody_il (CodeGenMethodForExpr cenv cgbuf.mgbuf (SPSuppress,[],"get_LastGenerated",eenvinner,1,0,expr_for_vref m currvref,Return)))
|> AddNonUserCompilerGeneratedAttribs cenv.g
let ilCtorBody = mk_simple_storage_ctor(None, Some ilCloBaseTy.TypeSpec, ilCloTypeSpecInner, [], MemAccess_assembly) |> ilmbody_of_mdef
let attrs = GenAttrs cenv eenvinner cloAttribs
let clo = GenClosureTypeDef cenv (ilCloTypeRef,cloFreeVars,ilCloGenericParams,attrs,m,ilCloFreeVars,cloLambdas,ilCtorBody,[generateNextMethod;closeMethod;checkCloseMethod;lastGeneratedMethod;getFreshMethod],[],ilCloBaseTy,[])
cgbuf.mgbuf.AddTypeDef(ilCloTypeRef,clo);
CountClosure();
for fv in cloFreeVars do
/// State variables always get zero-initialized
if stateVarsSet.Contains fv then
GenDefaultValue cenv cgbuf eenvouter (fv.Type,m)
else
GenGetLocalVal cenv cgbuf eenvouter m fv None;
CG.EmitInstr cgbuf (List.replicate ilCloFreeVars.Length Pop@ [ Push ilCloRetTyOuter ]) (mk_IlxInstr (EI_newclo ilxCloSpec));
GenSequel cenv eenvouter.cloc cgbuf sequel
/// Generate the class for a closure type definition
and GenClosureTypeDef cenv (tref:ILTypeRef, fvs:list<Val>, gparams, attrs, m, ilCloFreeVars, cloLambdas, ilCtorBody, mdefs, mimpls,ext, intfs) =
// Closure types should not be marked as Serializable if any of their free variable fields are non-serializable
let tdSerializable = not (fvs |> List.exists (fun fv -> is_definitely_not_serializable cenv.g fv.Type))
{ tdName = tref.Name;
tdLayout = TypeLayout_auto;
tdAccess = ComputeTypeAccess tref true;
tdGenericParams = gparams;
tdCustomAttrs = mk_custom_attrs(attrs @ [mk_CompilationMappingAttr cenv.g SourceLevelConstruct_Closure ]);
tdFieldDefs = mk_fdefs [];
tdInitSemantics=TypeInit_beforefield;
tdSealed=true;
tdAbstract=false;
tdKind=mk_IlxTypeDefKind (ETypeDef_closure { cloSource=None;
cloFreeVars=ilCloFreeVars;
cloStructure=cloLambdas;
cloCode=notlazy ilCtorBody });
tdEvents= mk_events [];
tdProperties = mk_properties [];
tdMethodDefs= mk_mdefs mdefs;
tdMethodImpls= mk_mimpls mimpls;
tdSerializable= tdSerializable;
tdComInterop=false;
tdSpecialName= true;
tdNested=mk_tdefs [];
tdEncoding= TypeEncoding_autochar;
tdImplements= intfs;
tdExtends= Some ext;
tdSecurityDecls= mk_security_decls [];
tdHasSecurity=false; }
and GenGenericParams m cenv tyenv tps = List.map (GenGenericParam m cenv.g tyenv) (DropErasedTypars tps)
and GenGenericArgs m eenv tps = List.map (fun c -> (mk_tyvar_ty (repr_of_typar m c eenv))) (DropErasedTypars tps)
/// Generate the closure class for a function
and GenLambdaClosure cenv (cgbuf:CodeGenBuffer) eenv isLocalTypeFunc selfv expr =
if verbose then dprintn ("GenLambdaClosure:");
match expr with
| TExpr_lambda (_,_,_,_,m,_,_)
| TExpr_tlambda(_,_,_,m,_,_) ->
let cloinfo,body,eenvinner = GetIlxClosureInfo cenv m isLocalTypeFunc selfv eenv expr
let entryPointInfo =
match selfv with
| Some v -> [(v, BranchCallClosure (cloinfo.clo_arity_info))]
| _ -> []
let clo_body = CodeGenMethodForExpr cenv cgbuf.mgbuf (SPAlways,entryPointInfo,cloinfo.clo_name,eenvinner,1,0,body,Return)
let clo_tref = tref_of_clospec cloinfo.clo_clospec
let clo =
if isLocalTypeFunc then
// Work out the contract type and generate a class with an abstract method for this type
let (ilContractGenericParams,contract_meth_il_gparams,contract_actual_tspec:ILTypeSpec,contract_formal_il_rty) = GenNamedLocalTypeFuncContractInfo cenv m cloinfo
let contract_tref = contract_actual_tspec.TypeRef
let contract_tspec = mk_tspec(contract_tref,generalize_gparams ilContractGenericParams)
let contract_ctor = mk_nongeneric_nothing_ctor None cenv.g.ilg.tref_Object []
let contract_meths = [contract_ctor; mk_generic_virtual_mdef("DirectInvoke",MemAccess_assembly,contract_meth_il_gparams,[],mk_return contract_formal_il_rty, MethodBody_abstract) ]
let contract_tdef =
{ tdName = contract_tref.Name;
tdLayout = TypeLayout_auto;
tdAccess = ComputeTypeAccess contract_tref true;
tdGenericParams = ilContractGenericParams;
tdCustomAttrs = mk_custom_attrs([mk_CompilationMappingAttr cenv.g SourceLevelConstruct_Closure ]);
tdFieldDefs = mk_fdefs [];
tdInitSemantics=TypeInit_beforefield;
tdSealed=false; // the contract type is an abstract type and not sealed
tdAbstract=true; // the contract type is an abstract type
tdKind=TypeDef_class;
tdEvents= mk_events [];
tdProperties = mk_properties [];
tdMethodDefs= mk_mdefs contract_meths;
tdMethodImpls= mk_mimpls [];
tdSerializable= true;
tdComInterop=false;
tdSpecialName= true;
tdNested=mk_tdefs [];
tdEncoding= TypeEncoding_autochar;
tdImplements= [];
tdExtends= Some cenv.g.ilg.typ_Object;
tdSecurityDecls= mk_security_decls [];
tdHasSecurity=false; }
cgbuf.mgbuf.AddTypeDef(contract_tref,contract_tdef);
let ilCtorBody = mk_ilmbody (true,[ ] ,8,nonbranching_instrs_to_code (mk_call_superclass_constructor([],contract_tspec)), None )
let cloMethods = [ mk_generic_virtual_mdef("DirectInvoke",MemAccess_assembly,cloinfo.ltyfunc_direct_il_gparams,[],mk_return (cloinfo.clo_formal_il_rty), MethodBody_il clo_body) ]
let cloTypeDef = GenClosureTypeDef cenv (clo_tref,cloinfo.clo_freevars,cloinfo.clo_il_gparams,[],m,cloinfo.clo_il_frees,cloinfo.clo_lambdas,ilCtorBody,cloMethods,[],Type_boxed contract_tspec,[])
cloTypeDef
else
GenClosureTypeDef cenv (clo_tref,cloinfo.clo_freevars,cloinfo.clo_il_gparams,[],m,cloinfo.clo_il_frees,cloinfo.clo_lambdas,clo_body,[],[],cenv.g.ilg.typ_Object,[])
CountClosure();
cgbuf.mgbuf.AddTypeDef(clo_tref,clo);
cloinfo,m
| _ -> failwith "GenLambda: not a lambda"
and GenLambdaVal cenv (cgbuf:CodeGenBuffer) eenv (cloinfo,m) =
if verbose then dprintn ("Loading environment for "^cloinfo.clo_name ^" in "^cgbuf.MethodName);
GenGetLocalVals cenv cgbuf eenv m cloinfo.clo_freevars;
if verbose then dprintn ("Compiling newclo for "^cloinfo.clo_name ^" in "^cgbuf.MethodName);
CG.EmitInstr cgbuf (List.replicate cloinfo.clo_il_frees.Length Pop@ [ Push (Pubclo.typ_of_lambdas cenv.g.ilxPubCloEnv cloinfo.clo_lambdas)]) (* REVIEW: more specific type when ILX supports them more explicitly *)
(mk_IlxInstr (EI_newclo cloinfo.clo_clospec))
and GenLambda cenv cgbuf eenv isLocalTypeFunc selfv expr sequel =
if verbose then dprintn ("GenLambda:");
let cloinfo,m = GenLambdaClosure cenv cgbuf eenv isLocalTypeFunc selfv expr
GenLambdaVal cenv cgbuf eenv (cloinfo,m);
if verbose then dprintn ("GenLambda: done val");
GenSequel cenv eenv.cloc cgbuf sequel
and GenTypeOfVal cenv eenv (v:Val) =
if verbose then dprintn ("GenTypeOfVal");
GenType v.Range cenv.g eenv.tyenv v.Type
and GenFreevar cenv m eenvouter eenvinner (fv:Val) =
match storage_for_val m fv eenvouter with
// Local type functions
| Local(_,Some _) | Env(_,_,Some _) -> cenv.g.ilg.typ_Object
#if DEBUG
// Check for things that should never make it into the free variable set. Only do this in debug for performance reasons
| (StaticField _ | Method _ | Unrealized | Null) -> error(InternalError("GenFreevar: compiler error: unexpected unrealized value",fv.Range))
#endif
| _ -> GenType m cenv.g eenvinner.tyenv fv.Type
and GetIlxClosureFreeVars cenv m selfv eenvouter expr =
// Choose a base name for the closure
let basename =
let boundv = eenvouter.letBoundVars |> List.tryfind (fun v -> not v.IsCompilerGenerated)
match boundv with
| Some v -> v.CompiledName
| None -> "clo"
// Get a unique stamp for the closure. This must be stable for things that can be part of a let rec.
let uniq =
match expr with
| TExpr_obj (uniq,_,_,_,_,_,m,_)
| TExpr_lambda (uniq,_,_,_,m,_,_)
| TExpr_tlambda(uniq,_,_,m,_,_) -> uniq
| _ -> new_uniq()
// Choose a name for the closure
let ilCloTypeRef =
// FSharp 1.0 bug 3404: System.Reflection doesn't like '.' and '`' in type names
let basenameSafeForUseAsTypename = basename.Replace('.', '$').Replace('`', '$')
let suffixmark = range_of_expr expr
let cloName = globalStableNameGenerator.GetUniqueCompilerGeneratedName(basenameSafeForUseAsTypename,suffixmark,uniq)
NestedTypeRefForCompLoc eenvouter.cloc cloName
// Collect the free variables of the closure
let cloFreeVarResults = free_in_expr CollectTyparsAndLocals expr
// Partition the free variables when some can be accessed from places besides the immediate environment
// Also filter out the current value being bound, if any, as it is available from the "this"
// pointer which gives the current closure itself. This is in the case e.g. let rec f = ... f ...
let cloFreeVars =
cloFreeVarResults.FreeLocals
|> Zset.elements
|> List.filter (fun fv ->
match storage_for_val m fv eenvouter with
| (StaticField _ | Method _ | Unrealized | Null) -> false
| _ ->
match selfv with
| Some v -> not (cenv.g.vref_eq (mk_local_vref fv) v)
| _ -> true)
// The general shape is:
// {LAM <tyfunc-typars>. expr }[free-typars] : overall-type[contract-typars]
// Then
// internal-typars = free-typars - contract-typars
//
// In other words, the free type variables get divided into two sets
// -- "contract" ones, which are part of the return type. We separate these to enable use to
// bake our own function base contracts for local type functions
//
// -- "internal" ones, which get used internally in the implementation
let cloContractFreeTyvarSet = (free_in_type CollectTypars (type_of_expr cenv.g expr)).FreeTypars
let cloInternalFreeTyvars = Zset.diff cloFreeVarResults.FreeTyvars.FreeTypars cloContractFreeTyvarSet |> Zset.elements
let cloContractFreeTyvars = cloContractFreeTyvarSet |> Zset.elements
let cloFreeTyvars = cloContractFreeTyvars @ cloInternalFreeTyvars
let cloAttribs = []
// If generating a named closure, add the closure itself as a var, available via "arg0" .
// The latter doesn't apply for the delegate implementation of closures.
let eenvinner = eenvouter |> env_for_typars cloFreeTyvars
let ilCloTypeSpecInner =
let ilCloGenericParams = GenGenericParams m cenv eenvinner.tyenv cloFreeTyvars
mk_tspec (ilCloTypeRef, generalize_gparams ilCloGenericParams)
// Build the environment that is active inside the closure itself
let eenvinner = { eenvinner with tyenv = { eenvinner.tyenv with tyenv_nativeptr_as_nativeint=true } }
let eenvinner = eenvinner |> AddStorageForLocalVals cenv.g (match selfv with | Some v -> [(deref_val v,Arg 0)] | _ -> [])
let eenvinner = eenvinner |> AddStorageForLocalVals cenv.g
(cloFreeVars |> List.mapi (fun i v ->
let localCloInfo =
match storage_for_val m v eenvouter with
| Local(_,localCloInfo)
| Env(_,_,localCloInfo) -> localCloInfo
| _ -> None
(v,Env(ilCloTypeSpecInner,i,localCloInfo))) )
// Return a various results
(cloAttribs,cloInternalFreeTyvars,cloContractFreeTyvars,cloFreeTyvars,cloFreeVars,ilCloTypeRef,eenvinner)
and GetClosureILFreeVars cenv m takenNames eenvouter eenvinner cloFreeVars =
let ilCloFreeVarNames = ChooseFreeVarNames takenNames (List.map name_of_val cloFreeVars)
let ilCloFreeVars = (cloFreeVars,ilCloFreeVarNames) ||> List.map2 (fun fv nm -> mk_freevar (nm,fv.IsCompilerGenerated, GenFreevar cenv m eenvouter eenvinner fv))
ilCloFreeVars
and GetIlxClosureInfo cenv m isLocalTypeFunc selfv eenvouter expr =
if verbose then dprintn ("GetIlxClosureInfo");
let (cloAttribs,cloInternalFreeTyvars,cloContractFreeTyvars,cloFreeTyvars,cloFreeVars,ilCloTypeRef,eenvinner) = GetIlxClosureFreeVars cenv m selfv eenvouter expr
if verbose then dprintn ("GetIlxClosureInfo: returnTy");
let returnTy =
match expr with
| TExpr_lambda (_,_,_,_,_,returnTy,_) | TExpr_tlambda(_,_,_,_,returnTy,_) -> returnTy
| TExpr_obj(_,typ,_,_,_,_,_,_) -> typ
| _ -> failwith "GetIlxClosureInfo: not a lambda expression"
if verbose then dprintn ("GetIlxClosureInfo: getClosureArgs");
let rec getClosureArgs eenv ntmargs takenNames (e,returnTy) =
match e with
| TExpr_lambda (_,_,vs,body,m,bty,_) when not isLocalTypeFunc ->
// Transform a lambda taking untupled arguments into one
// taking only a single tupled argument if necessary. REVIEW: do this earlier
let tupledv, body = multi_lambda_to_tupled_lambda vs body
let nm = tupledv.MangledName
let returnTy',l,arityInfo,takenNames,(body',bty'),eenv =
let eenv = AddStorageForVal cenv.g (tupledv,notlazy (Arg ntmargs)) eenv
getClosureArgs eenv (ntmargs + 1) (nm :: takenNames) (body,bty)
returnTy',Lambdas_lambda (mk_named_param(nm,GenTypeOfVal cenv eenv tupledv),l),1 :: arityInfo,takenNames,(body',bty'),eenv
| TExpr_tlambda(_,tvs,body,m,bty,_) ->
let returnTy',l,arityInfo,takenNames,body,eenv =
let eenv = AddTyparsToEnv tvs eenv
getClosureArgs eenv ntmargs takenNames (body,bty)
returnTy',List.foldBack (fun tv sofar ->
let gp = GenGenericParam m cenv.g eenv.tyenv tv
Lambdas_forall(gp,sofar)) tvs l,arityInfo, takenNames,body,eenv
| _ ->
let returnTy' = GenType m cenv.g eenv.tyenv returnTy
returnTy',Lambdas_return returnTy', [],takenNames,(e,returnTy),eenv
// start at arg number 1 as "this" pointer holds the current closure
let (ilReturnTy,cloLambdas,narginfo,takenNames,(body,_),eenvinner) = getClosureArgs eenvinner 1 [] (expr,returnTy)
// The general shape is:
// {LAM <tyfunc-typars>. expr }[free-typars] : overall-type[contract-typars]
// Then
// internal-typars = free-typars - contract-typars
//
// For a local type function closure, this becomes
// class Contract<contract-typars> {
// abstract DirectInvoke<tyfunc-typars> : overall-type
// }
//
// class ContractImplementation<contract-typars, internal-typars> : Contract<contract-typars> {
// override DirectInvoke<tyfunc-typars> : overall-type { expr }
// }
//
// For a non-local type function closure, this becomes
//
// class FunctionImplementation<contract-typars, internal-typars> : TypeFunc {
// override Specialize<tyfunc-typars> : overall-type { expr }
// }
//
// For a normal function closure, <tyfunc-typars> is empty, and this becomes
//
// class FunctionImplementation<contract-typars, internal-typars> : overall-type<contract-typars> {
// override Invoke(..) { expr }
// }
// In other words, the free type variables get divided into two sets
// -- "contract" ones, which are part of the return type. We separate these to enable use to
// bake our own function base contracts for local type functions
//
// -- "internal" ones, which get used internally in the implementation
//
// There are also "direct" and "indirect" type variables, which are part of the lambdas of the type function.
// Direct type variables are only used for local type functions, and indirect type variables only used for first class
// function values.
/// Compute the contract if it is a local type function
let ilContractGenericParams = GenGenericParams m cenv eenvinner.tyenv cloContractFreeTyvars
let ilContractGenericActuals = GenGenericArgs m eenvouter.tyenv cloContractFreeTyvars
let ilInternalGenericParams = GenGenericParams m cenv eenvinner.tyenv cloInternalFreeTyvars
let ilInternalGenericActuals = GenGenericArgs m eenvouter.tyenv cloInternalFreeTyvars
let ilCloGenericFormals = ilContractGenericParams @ ilInternalGenericParams
let ilCloGenericActuals = ilContractGenericActuals @ ilInternalGenericActuals
let ilCloFreeVars = GetClosureILFreeVars cenv m takenNames eenvouter eenvinner cloFreeVars
let ilDirectGenericParams,ilReturnTy,cloLambdas =
if isLocalTypeFunc then
let rec strip lambdas acc =
match lambdas with
| Lambdas_forall(gp,r) -> strip r (gp::acc)
| Lambdas_return returnTy -> List.rev acc,returnTy,lambdas
| _ -> failwith "AdjustNamedLocalTypeFuncIlxClosureInfo: local functions can currently only be type functions"
strip cloLambdas []
else
[],ilReturnTy,cloLambdas
let ilxCloSpec = IlxClosureSpec(IlxClosureRef(ilCloTypeRef, cloLambdas, ilCloFreeVars), ilCloGenericActuals)
let cloinfo =
{ clo_expr=expr;
clo_name=ilCloTypeRef.Name;
clo_arity_info =narginfo;
clo_lambdas=cloLambdas;
clo_il_frees = ilCloFreeVars;
clo_formal_il_rty=ilReturnTy;
clo_clospec = ilxCloSpec;
clo_il_gparams = ilCloGenericFormals;
clo_freevars=cloFreeVars;
clo_attribs=cloAttribs;
ltyfunc_contract_ftyvs = cloContractFreeTyvars;
ltyfunc_internal_ftyvs = cloInternalFreeTyvars;
ltyfunc_contract_il_gactuals = ilContractGenericActuals;
ltyfunc_direct_il_gparams=ilDirectGenericParams; }
if verbose then dprintn ("<-- GetIlxClosureInfo");
cloinfo,body,eenvinner
//--------------------------------------------------------------------------
// Named local type functions
//--------------------------------------------------------------------------
and IsNamedLocalTypeFuncVal g (v:Val) expr =
not v.IsCompiledAsTopLevel &&
is_forall_typ g v.Type &&
(let tps,_ = dest_forall_typ g v.Type in tps |> List.exists (fun tp -> not tp.Constraints.IsEmpty)) &&
(match strip_expr expr with TExpr_tlambda _ -> true | _ -> false)
/// Generate the information relecant to the contract portion of a named local type function
and GenNamedLocalTypeFuncContractInfo cenv m cloinfo =
let clo_tref = tref_of_clospec cloinfo.clo_clospec
let contract_tref = ILTypeRef.Create(scope=clo_tref.Scope,enclosing=clo_tref.Enclosing,name=clo_tref.Name^"$contract")
let contract_tyenv = tyenv_for_typars cloinfo.ltyfunc_contract_ftyvs
let ilContractGenericParams = GenGenericParams m cenv contract_tyenv cloinfo.ltyfunc_contract_ftyvs
let tvs,contract_rty =
match cloinfo.clo_expr with
| TExpr_tlambda(_,tvs,body,m,bty,_) -> tvs, bty
| e -> [], type_of_expr cenv.g e
let contract_tyenv = add_typars contract_tyenv tvs
let contract_meth_il_gparams = GenGenericParams m cenv contract_tyenv tvs
let contract_formal_il_rty = GenType m cenv.g contract_tyenv contract_rty
ilContractGenericParams,contract_meth_il_gparams,mk_tspec(contract_tref,cloinfo.ltyfunc_contract_il_gactuals),contract_formal_il_rty
/// Generate a new delegate construction including a clousre class if necessary. This is a lot like generating function closures
/// and object expression closures, and most of the code is shared.
and GenDelegateExpr cenv cgbuf eenvouter expr (TObjExprMethod((TSlotSig(_,delegateTy, _,_,_, _) as slotsig),methTyparsOfOverridingMethod,tmvs,body,implm),m) sequel =
if verbose then dprintn ("GenDelegateExpr");
// Get the instantiation of the delegate type
let ctxt_il_delty = GenType m cenv.g eenvouter.tyenv delegateTy
let tmvs = List.concat tmvs
// Yuck. TLBIMP.EXE generated APIs use UIntPtr for the delegate ctor.
let useUIntPtrForDelegateCtor =
try
if is_il_named_typ cenv.g delegateTy then
let tcref = tcref_of_stripped_typ cenv.g delegateTy
let _,_,tdef = tcref.ILTyconInfo
match find_mdefs_by_name ".ctor" tdef.tdMethodDefs with
| [ctorMDef] ->
match ctorMDef.mdParams with
| [_;p2] -> (p2.paramType.TypeSpec.Name = "System.UIntPtr")
| _ -> false
| _ -> false
else
false
with _ ->
false
// Work out the free type variables for the morphing thunk
let (cloAttribs,_,_,cloFreeTyvars,cloFreeVars,delegee_tref,eenvinner) = GetIlxClosureFreeVars cenv m None eenvouter expr
let takenNames = List.map name_of_val tmvs
let ilCloFreeVars = GetClosureILFreeVars cenv m takenNames eenvouter eenvinner cloFreeVars
let ilDelegeeGenericParams = GenGenericParams m cenv eenvinner.tyenv cloFreeTyvars
let ilDelegeeTypeName = delegee_tref.Name
let ilDelegeeGenericActualsInner = generalize_gparams ilDelegeeGenericParams
// Create a new closure class with a single "delegee" method that implements the delegate.
let delegeeMethName = "Invoke"
let ilDelegeeTypeSpecInner = mk_tspec (delegee_tref, ilDelegeeGenericActualsInner)
let delegee_eenv_under_typars = AddTyparsToEnv methTyparsOfOverridingMethod eenvinner
// The slot sig contains a formal instantiation. When creating delegates we're only
// interested in the actual instantiation since we don't have to emit a method impl.
let ilDelegeeParams,ilDelegeeRet = GenActualSlotsig m cenv delegee_eenv_under_typars slotsig methTyparsOfOverridingMethod
let numthis = 1
let delegee_meth_env = AddStorageForLocalVals cenv.g (List.mapi (fun i v -> (v,Arg (i+numthis))) tmvs) delegee_eenv_under_typars
let ilMethodBody = CodeGenMethodForExpr cenv cgbuf.mgbuf (SPAlways,[],delegeeMethName,delegee_meth_env,1,0,body,(if slotsig_has_void_rty slotsig then discardAndReturnVoid else Return))
let delegeeInvokeMeth =
mk_instance_mdef
(delegeeMethName,MemAccess_assembly,
ilDelegeeParams,
ilDelegeeRet,
MethodBody_il ilMethodBody)
let delegeeCtorMeth = mk_simple_storage_ctor(None, Some cenv.g.ilg.tspec_Object, ilDelegeeTypeSpecInner, [], MemAccess_assembly)
let ilCtorBody = ilmbody_of_mdef delegeeCtorMeth
let cloLambdas = Lambdas_return ctxt_il_delty
let ilAttribs = GenAttrs cenv eenvinner cloAttribs
let clo = GenClosureTypeDef cenv (delegee_tref,cloFreeVars,ilDelegeeGenericParams,ilAttribs,m,ilCloFreeVars,cloLambdas,ilCtorBody,[delegeeInvokeMeth],[],cenv.g.ilg.typ_Object,[])
cgbuf.mgbuf.AddTypeDef(delegee_tref,clo);
CountClosure();
let ctxt_gactuals_for_delegee = GenGenericArgs m eenvouter.tyenv cloFreeTyvars
let ilxCloSpec = IlxClosureSpec(IlxClosureRef(delegee_tref, cloLambdas, ilCloFreeVars), ctxt_gactuals_for_delegee)
GenGetLocalVals cenv cgbuf eenvouter m cloFreeVars;
CG.EmitInstr cgbuf (List.replicate ilCloFreeVars.Length Pop@ [ Push (Pubclo.typ_of_lambdas cenv.g.ilxPubCloEnv cloLambdas)]) (mk_IlxInstr (EI_newclo ilxCloSpec));
let ilDelegeeTypeSpecOuter = mk_tspec (delegee_tref,ctxt_gactuals_for_delegee)
let ilDelegeeInvokeMethOuter = mk_nongeneric_instance_mspec_in_boxed_tspec (ilDelegeeTypeSpecOuter,"Invoke",typs_of_params ilDelegeeParams, ilDelegeeRet.Type)
let ilDelegeeCtorMethOuter = mk_ctor_mspec_for_delegate cenv.g.ilg (ctxt_il_delty.TypeRef,IL.inst_of_typ ctxt_il_delty,useUIntPtrForDelegateCtor)
CG.EmitInstrs cgbuf
[Push cenv.g.ilg.typ_int32; Pop; Pop; Push ctxt_il_delty]
[ I_ldftn ilDelegeeInvokeMethOuter;
I_newobj(ilDelegeeCtorMethOuter,None) ];
GenSequel cenv eenvouter.cloc cgbuf sequel
//-------------------------------------------------------------------------
// Generate statically-resolved conditionals used for type-directed optimizations.
//-------------------------------------------------------------------------
and GenStaticOptimization cenv cgbuf eenv (constraints,e2,e3,m) sequel =
let e =
if DecideStaticOptimizations cenv.g constraints = 1 then e2
else e3
GenExpr cenv cgbuf eenv SPSuppress e sequel
//-------------------------------------------------------------------------
// Generate discrimination trees
//-------------------------------------------------------------------------
and IsSequelImmediate sequel =
match sequel with
(* All of these can be done at the end of each branch - we don't need a real join point *)
| Return | ReturnVoid | Br _ | LeaveHandler _ -> true
| DiscardThen sequel -> IsSequelImmediate sequel
| _ -> false
and GenJoinPoint cenv cgbuf pos eenv ty m sequel =
if verbose then dprintn ("GenJoinPoint");
match sequel with
// All of these can be done at the end of each branch - we don't need a real join point
| _ when IsSequelImmediate sequel ->
let stackAfterJoin = cgbuf.GetCurrentStack()
let afterJoin = CG.EmitDelayMark cgbuf (pos^"_join")
sequel,afterJoin,stackAfterJoin,Continue
// We end scopes at the join point, if any
| EndLocalScope(sq,mark) ->
let sequel_now,afterJoin,stackAfterJoin,sequelAfterJoin = GenJoinPoint cenv cgbuf pos eenv ty m sq
sequel_now,afterJoin,stackAfterJoin,EndLocalScope(sequelAfterJoin,mark)
// If something non-trivial happens after a discard then generate a join point, but first discard the value (often this means we won't generate it at all)
| DiscardThen sequel ->
let stackAfterJoin = cgbuf.GetCurrentStack()
let afterJoin = CG.EmitDelayMark cgbuf (pos^"_join")
DiscardThen (Br afterJoin),afterJoin,stackAfterJoin,sequel
// The others (e.g. Continue, LeaveFilter and CmpThenBrOrContinue) can't be done at the end of each branch. We must create a join point.
| _ ->
let pushed = GenType m cenv.g eenv.tyenv ty
let stackAfterJoin = (pushed :: (cgbuf.GetCurrentStack()))
let afterJoin = CG.EmitDelayMark cgbuf (pos^"_join")
// go to the join point
Br afterJoin, afterJoin,stackAfterJoin,sequel
and GenMatch cenv cgbuf eenv (spBind,exprm,tree,targets,m,ty) sequel =
if verbose then dprintf "GenMatch, dtree = %s\n" (showL (DecisionTreeL tree));
match spBind with
| SequencePointAtBinding m -> CG.EmitSeqPoint cgbuf m
| NoSequencePointAtDoBinding
| NoSequencePointAtLetBinding
| NoSequencePointAtInvisibleBinding
| NoSequencePointAtStickyBinding -> ()
// The target of branch needs a sequence point.
// If we don't give it one it will get entirely the wrong sequence point depending on earlier codegen
// Note we're not interested in having pattern matching and decision trees reveal their inner working.
// Hence at each branch target we 'reassert' the overall sequence point that was active as we came into the match.
//
// NOTE: sadly this causes multiple sequence points to appear for the "initial" location of an if/then/else or match.
let activeSP = cgbuf.GetLastSequencePoint()
let repeatSP() =
match activeSP with
| None -> ()
| Some src ->
if activeSP <> cgbuf.GetLastSequencePoint() then
CG.EmitSeqPoint cgbuf src
// First try the common cases where we don't need a join point.
match tree with
| TDSuccess(es,n) ->
failwith "internal error: matches that immediately succeed should have been normalized using mk_and_optimize_match"
| _ ->
// Create a join point
let stackAtTargets = cgbuf.GetCurrentStack() in (* the stack at the r.h.s. of each clause *)
let (sequelOnBranches,afterJoin,stackAfterJoin,sequelAfterJoin) = GenJoinPoint cenv cgbuf "match" eenv ty m sequel
// Stack: "stackAtTargets" is "stack prior to any match-testing" and also "stack at the start of each branch-RHS".
// match-testing (dtrees) should not contribute to the stack.
// Each branch-RHS (targets) may contribute to the stack, leaving it in the "stackAfterJoin" state, for the join point.
// Since code is branching and joining, the cgbuf stack is maintained manually.
GenDecisionTreeAndTargets cenv cgbuf stackAtTargets eenv tree targets repeatSP sequelOnBranches;
CG.SetMarkToHere cgbuf afterJoin;
//assert(cgbuf.GetCurrentStack() = stackAfterJoin); // REVIEW: Since gen_dtree* now sets stack, stack should be stackAfterJoin at this point...
CG.SetStack cgbuf stackAfterJoin;
GenSequel cenv eenv.cloc cgbuf sequelAfterJoin
// Accumulate the decision graph as we go
and GenDecisionTreeAndTargets cenv cgbuf stackAtTargets eenv tree targets repeatSP sequel =
ignore (GenDecisionTreeAndTargetsInner cenv cgbuf (CG.EmitDelayMark cgbuf "start_dtree") stackAtTargets eenv tree targets repeatSP (Imap.empty()) sequel)
and get_prev_target rgraph n = Imap.tryfind n rgraph
and GenDecisionTreeAndTargetsInner cenv cgbuf inplab stackAtTargets eenv tree targets repeatSP rgraph sequel =
if verbose then dprintf "GenDecisionTreeAndTargetsInner, dtree = %s\n" (showL (DecisionTreeL tree));
CG.SetStack cgbuf stackAtTargets; // Set the expected initial stack.
match tree with
| TDBind(bind,rest) ->
CG.SetMarkToHere cgbuf inplab;
let startScope,endScope as scopeMarks = StartDelayedLocalScope "dtree_bind" cgbuf
let eenv = AllocStorageForBind cenv cgbuf scopeMarks eenv bind
let sp = GenSequencePointForBind cenv cgbuf eenv bind
CG.SetMarkToHere cgbuf startScope;
GenBindAfterSequencePoint cenv cgbuf eenv sp bind;
// We don't get the scope marks quite right for dtree-bound variables. This is because
// we effectively lose an EndLocalScope for all dtrees that go to the same target
// So we just pretend that the variable goes out of scope here.
CG.SetMarkToHere cgbuf endScope;
let bodyLabel = CG.EmitDelayMark cgbuf "decisionTreeBindBody"
CG.EmitInstr cgbuf [] (I_br (code_label_of_mark bodyLabel));
GenDecisionTreeAndTargetsInner cenv cgbuf bodyLabel stackAtTargets eenv rest targets repeatSP rgraph sequel
| TDSuccess (es,n) ->
GenDecisionTreeSuccess cenv cgbuf inplab stackAtTargets eenv es n targets repeatSP rgraph sequel
| TDSwitch(e, cases, dflt,m) ->
GenDecisionTreeSwitch cenv cgbuf inplab stackAtTargets eenv e cases dflt m targets repeatSP rgraph sequel
and GetTarget targets n =
if n >= Array.length targets then failwith "GetTarget: target not found in decision tree";
targets.[n]
and GenDecisionTreeSuccess cenv cgbuf inplab stackAtTargets eenv es n targets repeatSP rgraph sequel =
if verbose then dprintn ("GenDecisionTreeSuccess");
let (TTarget(vs,successExpr,spTarget)) = GetTarget targets n
match get_prev_target rgraph n with
| Some (success,eenvrhs) ->
// If not binding anything we can go directly to the success point
// This is useful to avoid lots of branches e.g. in match A | B | C -> e
// In this case each case will just go straight to "e"
if FlatList.isEmpty vs then
CG.SetMark cgbuf inplab success;
rgraph
else
CG.SetMarkToHere cgbuf inplab;
repeatSP();
FlatList.iter2 (GenSetBindValue cenv cgbuf eenvrhs eenv ) vs es;
CG.EmitInstr cgbuf [] (I_br (code_label_of_mark success));
rgraph
| None ->
CG.SetMarkToHere cgbuf inplab;
// Repeat the sequence point to make sure each target branch has some sequence point (instead of inheriting
// a random sequence point from the previously generated IL code from the previous block. See comment on
// repeatSP() above.
//
// Only repeat the sequence point if we really have to, i.e. if the target expression doesn't start with a
// sequence point anyway
let spTarget = (match spTarget with SequencePointAtTarget -> SPAlways | SuppressSequencePointAtTarget _ -> SPSuppress)
if isNil vs && DoesGenExprStartWithSequencePoint spTarget successExpr then
()
else
repeatSP();
let binds = mk_invisible_FlatBindings vs es
let _,endScope as scopeMarks = StartLocalScope "matchrhs" cgbuf
let eenvrhs = AllocStorageForBinds cenv cgbuf scopeMarks eenv binds
GenBindings cenv cgbuf eenvrhs binds;
let success = CG.GenerateMark cgbuf "matching_rhs"
CG.SetStack cgbuf stackAtTargets;
GenExpr cenv cgbuf eenvrhs spTarget successExpr (EndLocalScope(sequel,endScope));
// add the generated rhs. to the graph
Imap.add n (success,eenvrhs) rgraph
and GenDecisionTreeSwitch cenv cgbuf inplab stackAtTargets eenv e cases dflt_opt switchm targets repeatSP rgraph sequel =
let m = range_of_expr e
CG.SetMarkToHere cgbuf inplab;
repeatSP();
match cases with
// optimize a test against a boolean value, i.e. the all-important if-then-else
| TCase(TTest_const(TConst_bool b), success_dtree) :: _ ->
let failure_dtree = (match dflt_opt with None -> dest_of_case (List.hd (List.tl cases)) | Some d -> d)
GenDecisionTreeTest cenv eenv.cloc cgbuf stackAtTargets e None eenv (if b then success_dtree else failure_dtree) (if b then failure_dtree else success_dtree) targets repeatSP rgraph sequel
// optimize a single test for a type constructor to an "isdata" test - much
// more efficient code, and this case occurs in the generated equality testers where perf is important
| TCase(TTest_unionconstr(c,tyargs), success_dtree) :: rest when List.length rest = (match dflt_opt with None -> 1 | Some x -> 0) ->
let failure_dtree = if dflt_opt = None then dest_of_case (List.hd (List.tl cases)) else the dflt_opt
let cuspec = GenUnionSpec m cenv.g eenv.tyenv c.TyconRef tyargs
let idx = ucref_index c
GenDecisionTreeTest cenv eenv.cloc cgbuf stackAtTargets e (Some ([Pop; Push cenv.g.ilg.typ_bool],(mk_IlxInstr (EI_isdata (cuspec, idx))))) eenv success_dtree failure_dtree targets repeatSP rgraph sequel
| _ ->
let caseLabels = List.map (fun _ -> CG.EmitDelayMark cgbuf "switch_case") cases
let dflt_label = match dflt_opt with None -> List.hd caseLabels | Some _ -> CG.EmitDelayMark cgbuf "switch_dflt"
let fst_discrim = discrim_of_case (List.hd cases)
match fst_discrim with
// Iterated tests, e.g. exception constructors, nulltests, typetests and active patterns.
// These should always have one positive and one negative branch
| TTest_isinst _
| TTest_array_length _
| TTest_isnull
| TTest_const(TConst_zero) ->
if List.length cases <> 1 || isNone dflt_opt then failwith "internal error: GenDecisionTreeSwitch: TTest_isinst/isnull/query";
let bi =
match fst_discrim with
| TTest_const(TConst_zero) ->
GenExpr cenv cgbuf eenv SPSuppress e Continue;
BI_brfalse
| TTest_isnull ->
GenExpr cenv cgbuf eenv SPSuppress e Continue;
let srcTy = type_of_expr cenv.g e
if is_typar_typ cenv.g srcTy then
let ilFromTy = GenType m cenv.g eenv.tyenv srcTy
CG.EmitInstr cgbuf [Pop; Push cenv.g.ilg.typ_Object] (I_box ilFromTy);
BI_brfalse
| TTest_isinst (srcty,tgty) ->
let e = mk_call_istype cenv.g m tgty e
GenExpr cenv cgbuf eenv SPSuppress e Continue;
BI_brtrue
| _ -> failwith "internal error: GenDecisionTreeSwitch"
CG.EmitInstr cgbuf [Pop] (I_brcmp (bi,code_label_of_mark (List.hd caseLabels),code_label_of_mark dflt_label));
GenDecisionTreeCases cenv cgbuf stackAtTargets eenv targets repeatSP rgraph caseLabels cases dflt_opt dflt_label sequel
| TTest_query _ -> error(Error("internal error in codegen: TTest_query",switchm))
| TTest_unionconstr (hdc,tyargs) ->
GenExpr cenv cgbuf eenv SPSuppress e Continue;
let cuspec = GenUnionSpec m cenv.g eenv.tyenv hdc.TyconRef tyargs
let dests =
if cases.Length <> caseLabels.Length then failwith "internal error: TTest_unionconstr";
(cases , caseLabels) ||> List.map2 (fun case label ->
match case with
| TCase(TTest_unionconstr (c,_),_) -> (ucref_index c, code_label_of_mark label)
| _ -> failwith "error: mixed constructor/const test?")
CG.EmitInstr cgbuf [Pop] (mk_IlxInstr (EI_datacase (false,cuspec,dests, code_label_of_mark dflt_label)));
GenDecisionTreeCases cenv cgbuf stackAtTargets eenv targets repeatSP rgraph caseLabels cases dflt_opt dflt_label sequel
| TTest_const c ->
GenExpr cenv cgbuf eenv SPSuppress e Continue;
match c with
| (TConst_bool b) -> failwith "should have been done earlier"
| (TConst_sbyte _)
| (TConst_int16 _)
| (TConst_int32 _)
| (TConst_byte _)
| (TConst_uint16 _)
| (TConst_uint32 _)
| (TConst_char _) ->
if List.length cases <> List.length caseLabels then failwith "internal error: ";
let dests =
(cases,caseLabels) ||> List.map2 (fun case label ->
let i =
match discrim_of_case case with
TTest_const c' ->
match c' with
| TConst_sbyte i -> int32 i
| TConst_int16 i -> int32 i
| TConst_int32 i -> i
| TConst_byte i -> int32 i
| TConst_uint16 i -> int32 i
| TConst_uint32 i -> int32 i
| TConst_char c -> int32 c
| _ -> failwith "internal error: badly formed const test"
| _ -> failwith "internal error: badly formed const test"
(i,code_label_of_mark label))
let mn = List.foldBack (fst >> Operators.min) dests (fst(List.hd dests))
let mx = List.foldBack (fst >> Operators.max) dests (fst(List.hd dests))
// Check if it's worth using a switch
// REVIEW: this is using switches even for single integer matches!
if mx - mn = (List.length dests - 1) then
let dest_labels = dests |> List.sortBy fst |> List.map snd
if mn <> 0 then
CG.EmitInstrs cgbuf [Push cenv.g.ilg.typ_int32; Pop] [ mk_ldc_i32 mn;I_arith AI_sub ];
CG.EmitInstr cgbuf [Pop] (I_switch (dest_labels, code_label_of_mark dflt_label));
else
error(InternalError("non-dense integer matches not implemented in codegen - these should have been removed by the pattern match compiler",switchm));
GenDecisionTreeCases cenv cgbuf stackAtTargets eenv targets repeatSP rgraph caseLabels cases dflt_opt dflt_label sequel
| _ -> error(InternalError("these matches should never be needed",switchm))
and GenDecisionTreeCases cenv cgbuf stackAtTargets eenv targets repeatSP rgraph caseLabels cases dflt_opt dflt_label sequel =
assert(cgbuf.GetCurrentStack() = stackAtTargets); // cgbuf stack should be unchanged over tests. [bug://1750].
let rgraph =
match dflt_opt with
| Some dflt_rhs -> GenDecisionTreeAndTargetsInner cenv cgbuf dflt_label stackAtTargets eenv dflt_rhs targets repeatSP rgraph sequel
| None -> rgraph
let rgraph =
List.fold_left2
(fun rgraph case_label (TCase(_,case_rhs)) ->
GenDecisionTreeAndTargetsInner cenv cgbuf case_label stackAtTargets eenv case_rhs targets repeatSP rgraph sequel)
rgraph
caseLabels
cases
rgraph
and (|BoolExpr|_|) = function TExpr_const(TConst_bool b1,_,_) -> Some(b1) | _ -> None
and GenDecisionTreeTest cenv cloc cgbuf stackAtTargets e tester eenv success_dtree failure_dtree targets repeatSP rgraph sequel =
match success_dtree,failure_dtree with
// Peephole: if generating a boolean value or its negation then just leave it on the stack
// This comes up in the generated equality functions. REVIEW: do this as a peephole optimization elsewhere
| TDSuccess(es1,n1),
TDSuccess(es2,n2) when
FlatList.isEmpty es1 && FlatList.isEmpty es2 &&
(match GetTarget targets n1, GetTarget targets n2 with
TTarget(_,BoolExpr(b1),_),TTarget(_,BoolExpr(b2),_) -> b1 = not b2
| _ -> false) ->
match GetTarget targets n1, GetTarget targets n2 with
| TTarget(_,BoolExpr(b1),_),_ ->
GenExpr cenv cgbuf eenv SPSuppress e Continue;
(match tester with Some (pushpop,i) -> CG.EmitInstr cgbuf pushpop i; | _ -> ());
if not b1 then
CG.EmitInstrs cgbuf [Push cenv.g.ilg.typ_bool; Pop] [mk_ldc_i32 (0); I_arith AI_ceq];
GenSequel cenv cloc cgbuf sequel;
rgraph
| _ -> failwith "internal error: GenDecisionTreeTest during bool elim"
| _ ->
let success = CG.EmitDelayMark cgbuf "test_success"
let failure = CG.EmitDelayMark cgbuf "test_failure"
(match tester with
| None ->
(* generate the expression, then test it for "false" *)
GenExpr cenv cgbuf eenv SPSuppress e (CmpThenBrOrContinue([Pop],I_brcmp (BI_brfalse, code_label_of_mark failure,code_label_of_mark success)));
(* Turn "EI_isdata" tests that branch into EI_brisdata tests *)
| Some (_,I_other i) when is_ilx_ext_instr i && (match dest_ilx_ext_instr i with EI_isdata _ -> true | _ -> false) ->
let (cuspec,idx) = match dest_ilx_ext_instr i with EI_isdata (cuspec,idx) -> (cuspec,idx) | _ -> failwith "??"
GenExpr cenv cgbuf eenv SPSuppress e (CmpThenBrOrContinue([Pop],mk_IlxInstr (EI_brisdata (cuspec, idx, code_label_of_mark success,code_label_of_mark failure))));
| Some (pushpop,i) ->
GenExpr cenv cgbuf eenv SPSuppress e Continue;
CG.EmitInstr cgbuf pushpop i;
CG.EmitInstr cgbuf [Pop] (I_brcmp (BI_brfalse, code_label_of_mark failure,code_label_of_mark success)));
let rgraph = GenDecisionTreeAndTargetsInner cenv cgbuf success stackAtTargets eenv success_dtree targets repeatSP rgraph sequel
GenDecisionTreeAndTargetsInner cenv cgbuf failure stackAtTargets eenv failure_dtree targets repeatSP rgraph sequel
//-------------------------------------------------------------------------
// Generate letrec bindings
//-------------------------------------------------------------------------
and GenLetRecFixup cenv cgbuf eenv (ilxCloSpec,e,n,e2,m) =
GenExpr cenv cgbuf eenv SPSuppress e Continue;
CG.EmitInstrs cgbuf [] [ mk_IlxInstr (EI_castclo ilxCloSpec) ];
GenExpr cenv cgbuf eenv SPSuppress e2 Continue;
CG.EmitInstrs cgbuf [Pop; Pop] [ mk_IlxInstr (EI_stclofld(ilxCloSpec, n)) ]
and GenLetRecBinds cenv cgbuf eenv (allBinds: Bindings,m) =
(* Fix up recursion for non-toplevel recursive bindings *)
let bindsPossiblyRequiringFixup =
allBinds |> FlatList.filter (fun b ->
match (storage_for_val m b.Var eenv) with
| Method _
| Unrealized
(* Note: Recursive data stored in static fields may require fixups e.g. let x = C(x) *)
(* | StaticField _ *)
| Null -> false
| _ -> true)
let computeFixupsForOneRecursiveVar boundv forwardReferenceSet fixups selfv access set e =
match e with
| TExpr_lambda _ | TExpr_tlambda _ | TExpr_obj _ ->
let isLocalTypeFunc = (isSome selfv && (IsNamedLocalTypeFuncVal cenv.g (the selfv) e))
let selfv = (match e with TExpr_obj _ -> None | _ when isLocalTypeFunc -> None | _ -> Option.map mk_local_vref selfv)
let clo,_,eenvclo = GetIlxClosureInfo cenv m isLocalTypeFunc selfv {eenv with letBoundVars=(mk_local_vref boundv)::eenv.letBoundVars} e
clo.clo_freevars |> List.iter (fun fv ->
if Zset.mem fv forwardReferenceSet then
match storage_for_val m fv eenvclo with
| Env (_,n,_) -> fixups := (boundv, fv, (fun () -> GenLetRecFixup cenv cgbuf eenv (clo.clo_clospec,access,n,expr_for_val m fv,m))) :: !fixups
| _ -> error (InternalError("GenLetRec: "^fv.MangledName^" was not in the environment",m)) )
| TExpr_val (vref,_,m) ->
let fv = deref_val vref
let needsFixup = Zset.mem fv forwardReferenceSet
if needsFixup then fixups := (boundv, fv,(fun () -> GenExpr cenv cgbuf eenv SPSuppress (set e) discard)) :: !fixups
| _ -> failwith "compute real fixup vars"
let fixups = ref []
let recursiveVars = Zset.addFlatList (bindsPossiblyRequiringFixup |> FlatList.map (fun v -> v.Var)) (Zset.empty val_spec_order)
FlatList.fold
(fun forwardReferenceSet (bind:Binding) ->
let valBeingDefined = bind.Var
// compute fixups
bind.Expr |> iter_letrec_fixups cenv.g (Some valBeingDefined) (computeFixupsForOneRecursiveVar valBeingDefined forwardReferenceSet fixups) (expr_for_val m valBeingDefined, (fun e -> failwith ("internal error: should never need to set non-delayed recursive val: " ^ valBeingDefined.MangledName)));
Zset.remove valBeingDefined forwardReferenceSet)
recursiveVars
bindsPossiblyRequiringFixup |> ignore;
FlatList.fold
(fun forwardReferenceSet (bind:Binding) ->
let valBeingDefined = bind.Var
if verbose then dprintn ("GenLetRec: generate binding for "^showL(valL valBeingDefined));
GenBind cenv cgbuf eenv bind;
// execute and discard any fixups that can now be committed
let forwardReferenceSet = Zset.remove valBeingDefined forwardReferenceSet
fixups := !fixups |> List.filter (fun (boundv, fv, action) -> if (Zset.mem boundv forwardReferenceSet or Zset.mem fv forwardReferenceSet) then true else (action(); false));
forwardReferenceSet)
recursiveVars
allBinds |> ignore;
and GenLetRec cenv cgbuf eenv (binds,body,m) sequel =
let _,endScope as scopeMarks = StartLocalScope "letrec" cgbuf
if verbose then dprintn ("GenLetRec");
let eenv = AllocStorageForBinds cenv cgbuf scopeMarks eenv binds
GenLetRecBinds cenv cgbuf eenv (binds,m);
if verbose then dprintn ("GenLetRec: body");
let sp = if FlatList.exists bindHasSeqPt binds || FlatList.forall bindIsInvisible binds then SPAlways else SPSuppress
GenExpr cenv cgbuf eenv sp body (EndLocalScope(sequel,endScope))
//-------------------------------------------------------------------------
// Generate simple bindings
//-------------------------------------------------------------------------
and GenSequencePointForBind cenv cgbuf eenv (TBind(vspec,e,spBind)) =
let emitSP() =
match spBind,e with
| (( NoSequencePointAtInvisibleBinding
| NoSequencePointAtStickyBinding),_) -> SPSuppress
| (NoSequencePointAtDoBinding,_) -> SPAlways
| (NoSequencePointAtLetBinding,_) -> SPSuppress
// Don't emit sequence points for lambdas.
// SEQUENCE POINT REVIEW: don't emit for lazy either, nor any builder expressions
| _, (TExpr_lambda _ | TExpr_tlambda _) -> SPSuppress
| SequencePointAtBinding m,_ ->
CG.EmitSeqPoint cgbuf m;
SPSuppress
let m = vspec.Range
match storage_for_val m vspec eenv with
| Unrealized -> SPSuppress
| Null -> emitSP()
| Method (topValInfo,_,mspec,_,paramInfos,retInfo) -> SPSuppress
| StaticField _ -> emitSP()
| _ -> emitSP()
and GenBind cenv cgbuf eenv bind =
let sp = GenSequencePointForBind cenv cgbuf eenv bind
GenBindAfterSequencePoint cenv cgbuf eenv sp bind
and GenBindAfterSequencePoint cenv cgbuf eenv sp (TBind(vspec,e,spBind)) =
if verbose then dprintn ("GenBind");
// Record the closed reflection definition if publishing
// There is no real reason we're doing this so late in the day
match vspec.PublicPath, vspec.ReflectedDefinition with
| Some p, Some e -> cgbuf.mgbuf.AddReflectedDefinition(vspec,e)
| _ -> ()
if verbose then dprintn ("GenBind: " ^ showL(vspecAtBindL vspec));
let eenv = {eenv with letBoundVars= (mk_local_vref vspec) :: eenv.letBoundVars}
//let access = ComputeMemberAccess (not vspec.IsOverride) (IsHiddenVal eenv.sigToImplRemapInfo vspec || not vspec.IsMemberOrModuleBinding)
let access =
let isHidden =
IsHiddenVal eenv.sigToImplRemapInfo vspec || // anything hiden by a signature gets assembly visibility
not vspec.IsMemberOrModuleBinding || // anything that's not a module or member binding gets assembly visibility
vspec.IsIncrClassGeneratedMember // compiler generated members for class function 'let' bindings get assembly visibility
ComputeMemberAccess (not vspec.IsOverride) isHidden
// Workaround for .NET and Visual Studio restriction w.r.t debugger type proxys
// Mark internal constructors in internal classes as public.
let access =
if access = MemAccess_assembly && vspec.IsConstructor && IsHiddenTycon eenv.sigToImplRemapInfo vspec.MemberApparentParent.Deref then
MemAccess_public
else
access
let m = vspec.Range
match storage_for_val m vspec eenv with
| Unrealized -> ()
| Null ->
GenExpr cenv cgbuf eenv SPSuppress e discard
| Method (topValInfo,_,mspec,_,paramInfos,retInfo) ->
let tps,baseValOpt,vsl,body',bodyty = IteratedAdjustArityOfLambda cenv.g cenv.amap topValInfo e
let methodVars = List.concat vsl
GenMethodForBinding cenv cgbuf eenv (vspec,mspec,access,paramInfos,retInfo) (topValInfo,baseValOpt,tps,methodVars, body', bodyty)
| StaticField (fspec,vref,hasLiteralAttr,ilTypeSpecForProperty,fieldName,propName,fty,ilGetterMethRef,ilSetterMethRef,optShadowLocal) ->
let mut = vspec.IsMutable
match mut,hasLiteralAttr,e with
| _,false,_ -> ()
| true,true,_ -> errorR(Error("Values marked with 'LiteralAttribute' may not be mutable",m))
| _,true,TExpr_const _ -> ()
| _,true,_ -> errorR(Error("Values marked with 'LiteralAttribute' must currently be simple integer, character, Boolean, string or floating point constants",m))
/// Generate a static field definition and the get/set properties to access it.
let ilAttribs = GenAttrs cenv eenv (vspec.Attribs)
let fieldDef =
let access = ComputeMemberAccess true (not hasLiteralAttr or IsHiddenVal eenv.sigToImplRemapInfo vspec)
let fieldDef = mk_static_fdef(fieldName,fty, None, None, access)
{ fieldDef with
fdCustomAttrs = mk_custom_attrs (ilAttribs @ [ mk_DebuggerBrowsableNeverAttribute cenv.g.ilg ]) }
let fieldDef =
match hasLiteralAttr,e with
| false,_ -> fieldDef
| true,TExpr_const(konst,m,_) -> { fieldDef with fdLiteral=true; fdInit= Some(GenFieldInit m konst) }
| true,_ -> fieldDef (* error given above *)
let ilTypeRefForProperty = ilTypeSpecForProperty.TypeRef
let fieldDef =
let isClassInitializer = (cgbuf.MethodName = ".cctor")
if mut || (not isClassInitializer) || hasLiteralAttr then
fieldDef
else
{fieldDef with fdInitOnly=true }
cgbuf.mgbuf.AddFieldDef(fspec.EnclosingTypeRef,fieldDef);
CountStaticFieldDef();
if not hasLiteralAttr then
let ilPropertyDef =
{ propName=propName;
propRTSpecialName=false;
propSpecialName=false;
propSet=if mut then Some(ilSetterMethRef) else None;
propGet=Some(ilGetterMethRef);
propCallconv=CC_static;
propType=fty;
propInit=None;
propArgs=[];
propCustomAttrs=mk_custom_attrs (ilAttribs @ [mk_CompilationMappingAttr cenv.g SourceLevelConstruct_Value]); }
cgbuf.mgbuf.AddOrMergePropertyDef(ilTypeRefForProperty,ilPropertyDef,m);
let getterMethod =
mk_static_mdef([],ilGetterMethRef.Name,access,[],mk_return fty,
MethodBody_il(mk_ilmbody(true,[],2,nonbranching_instrs_to_code([ mk_normal_ldsfld fspec ]),None)))
|> AddSpecialNameFlag
cgbuf.mgbuf.AddMethodDef(ilTypeRefForProperty,getterMethod) ;
if mut then
let setterMethod =
mk_static_mdef([],ilSetterMethRef.Name,access,[mk_named_param("value",fty)],mk_return Type_void,
MethodBody_il(mk_ilmbody(true,[],2,nonbranching_instrs_to_code([ ldarg_0;mk_normal_stsfld fspec]),None)))
|> AddSpecialNameFlag
cgbuf.mgbuf.AddMethodDef(ilTypeRefForProperty,setterMethod)
GenBindRhs cenv cgbuf eenv sp vspec e;
match optShadowLocal with
| NoShadowLocal -> EmitSetStaticField cgbuf fspec
| ShadowLocal storage->
CG.EmitInstr cgbuf [Push fty] i_dup
EmitSetStaticField cgbuf fspec
GenSetStorage cenv m cgbuf storage
| _ ->
GenSetBindValue cenv cgbuf eenv eenv vspec e
//-------------------------------------------------------------------------
// Generate method bindings
//-------------------------------------------------------------------------
/// Spectacularly gross table encoding P/Invoke and COM marshalling information
and GenMarshal cenv attribs =
let otherAttribs =
attribs
|> List.filter (IsMatchingAttrib cenv.g cenv.g.attrib_MarshalAsAttribute >> not)
match TryFindAttrib cenv.g cenv.g.attrib_MarshalAsAttribute attribs with
| Some (Attrib(_,_,[ AttribInt32Arg unmanagedType ],namedArgs,m)) ->
let decoder = AttributeDecoder namedArgs
let rec decodeUnmanagedType unmanagedType =
(* enumeration values for System.Runtime.InteropServices.UnmanagedType taken from mscorlib.il *)
match unmanagedType with
| 0x0 -> NativeType_empty
| 0x01 -> NativeType_void
| 0x02 -> NativeType_bool
| 0x03 -> NativeType_int8
| 0x04 -> NativeType_unsigned_int8
| 0x05 -> NativeType_int16
| 0x06 -> NativeType_unsigned_int16
| 0x07 -> NativeType_int32
| 0x08 -> NativeType_unsigned_int32
| 0x09 -> NativeType_int64
| 0x0A -> NativeType_unsigned_int64
| 0x0B -> NativeType_float32
| 0x0C -> NativeType_float64
| 0x0F -> NativeType_currency
| 0x13 -> NativeType_bstr
| 0x14 -> NativeType_lpstr
| 0x15 -> NativeType_lpwstr
| 0x16 -> NativeType_lptstr
| 0x17 -> NativeType_fixed_sysstring (decoder.FindInt32 "SizeConst" 0x0)
| 0x19 -> NativeType_iunknown
| 0x1A -> NativeType_idsipatch
| 0x1B -> NativeType_struct
| 0x1C -> NativeType_interface
| 0x1D ->
let safeArraySubType =
match decoder.FindInt32 "SafeArraySubType" 0x0 with
(* enumeration values for System.Runtime.InteropServices.VarType taken from mscorlib.il *)
| 0x0 -> VariantType_empty
| 0x1 -> VariantType_null
| 0x02 -> VariantType_int16
| 0x03 -> VariantType_int32
| 0x0C -> VariantType_variant
| 0x04 -> VariantType_float32
| 0x05 -> VariantType_float64
| 0x06 -> VariantType_currency
| 0x07 -> VariantType_date
| 0x08 -> VariantType_bstr
| 0x09 -> VariantType_idispatch
| 0x0a -> VariantType_error
| 0x0b -> VariantType_bool
| 0x0d -> VariantType_iunknown
| 0x0e -> VariantType_decimal
| 0x10 -> VariantType_int8
| 0x11 -> VariantType_unsigned_int8
| 0x12 -> VariantType_unsigned_int16
| 0x13 -> VariantType_unsigned_int32
| 0x15 -> VariantType_unsigned_int64
| 0x16 -> VariantType_int
| 0x17 -> VariantType_unsigned_int
| 0x18 -> VariantType_void
| 0x19 -> VariantType_hresult
| 0x1a -> VariantType_ptr
| 0x1c -> VariantType_carray
| 0x1d -> VariantType_userdefined
| 0x1e -> VariantType_lpstr
| 0x1B -> VariantType_safearray
| 0x1f -> VariantType_lpwstr
| 0x24 -> VariantType_record
| 0x40 -> VariantType_filetime
| 0x41 -> VariantType_blob
| 0x42 -> VariantType_stream
| 0x43 -> VariantType_storage
| 0x44 -> VariantType_streamed_object
| 0x45 -> VariantType_stored_object
| 0x46 -> VariantType_blob_object
| 0x47 -> VariantType_cf
| 0x48 -> VariantType_clsid
| 0x14 -> VariantType_int64
| _ -> VariantType_empty
let safeArrayUserDefinedSubType =
// the argument is a System.Type obj, but it's written to MD as a UTF8 string
match decoder.FindTypeName "SafeArrayUserDefinedSubType" "" with
| "" -> None
| res -> if (safeArraySubType = VariantType_idispatch) || (safeArraySubType = VariantType_iunknown) then Some(res) else None
NativeType_safe_array(safeArraySubType,safeArrayUserDefinedSubType)
| 0x1E -> NativeType_fixed_array (decoder.FindInt32 "SizeConst" 0x0)
| 0x1F -> NativeType_int
| 0x20 -> NativeType_unsigned_int
| 0x22 -> NativeType_byvalstr
| 0x23 -> NativeType_ansi_bstr
| 0x24 -> NativeType_tbstr
| 0x25 -> NativeType_variant_bool
| 0x26 -> NativeType_method
| 0x28 -> NativeType_as_any
| 0x2A ->
let sizeParamIndex =
match decoder.FindInt16 "SizeParamIndex" -1s with
| -1s -> None
| res -> Some ((int)res,None)
let arraySubType =
match decoder.FindInt32 "ArraySubType" -1 with
| -1 -> None
| res -> Some (decodeUnmanagedType res)
NativeType_array(arraySubType,sizeParamIndex)
| 0x2B -> NativeType_lpstruct
| 0x2C ->
error(Error("Custom marshallers may not be specified in F# code. Consider using a C# helper function",m))
(* NativeType_custom of bytes * string * string * bytes (* GUID,nativeTypeName,custMarshallerName,cookieString *) *)
//NativeType_error
| 0x2D -> NativeType_error
| _ -> NativeType_empty
Some(decodeUnmanagedType unmanagedType), otherAttribs
| Some (Attrib(_,_,_,_,m)) ->
errorR(Error("The MarshalAs attribute could not be decoded",m));
None, attribs
| _ ->
// No MarshalAs detected
None, attribs
and GenParamAttribs cenv attribs =
let inFlag = HasAttrib cenv.g cenv.g.attrib_InAttribute attribs
let outFlag = HasAttrib cenv.g cenv.g.attrib_OutAttribute attribs
let optionalFlag = HasAttrib cenv.g cenv.g.attrib_OptionalAttribute attribs
// Return the filtered attributes. Do not generate In, Out or Optional attributes
// as custom attributes in the code - they are implicit from the IL bits for these
let attribs =
attribs
|> List.filter (IsMatchingAttrib cenv.g cenv.g.attrib_InAttribute >> not)
|> List.filter (IsMatchingAttrib cenv.g cenv.g.attrib_OutAttribute >> not)
|> List.filter (IsMatchingAttrib cenv.g cenv.g.attrib_OptionalAttribute >> not)
let paramMarshal,attribs = GenMarshal cenv attribs
inFlag,outFlag,optionalFlag,paramMarshal,attribs
and GenParams cenv eenv (mspec:ILMethodSpec) (attribs:TopArgInfo list) (implValsOpt: Val list option) =
let ilArgTys = mspec.FormalArgTypes
let argInfosAndTypes =
if attribs.Length = ilArgTys.Length then List.zip ilArgTys attribs
else ilArgTys |> List.map (fun ilArgTy -> ilArgTy,TopValInfo.unnamedTopArg1)
let argInfosAndTypes =
match implValsOpt with
| Some(implVals) when (implVals.Length = ilArgTys.Length) ->
List.map2 (fun x y -> x,Some y) argInfosAndTypes implVals
| _ ->
List.map (fun x -> x,None) argInfosAndTypes
(Set.empty,argInfosAndTypes)
||> List.mapfold (fun takenNames ((ilArgTy,TopArgInfo(attribs,isOpt)),implValOpt) ->
let inFlag,outFlag,optionalFlag,paramMarshal,attribs = GenParamAttribs cenv attribs
let idOpt = (match isOpt with
| Some v -> Some v
| None -> match implValOpt with
| Some v -> Some v.Id
| None -> None)
let nmOpt,takenNames =
match idOpt with
| Some id ->
let nm = if takenNames.Contains(id.idText) then globalNng.FreshCompilerGeneratedName (id.idText, id.idRange) else id.idText
Some nm, takenNames.Add(nm)
| None ->
None, takenNames
let param =
{ paramName=nmOpt;
paramType= ilArgTy;
paramDefault=None; (* REVIEW: support "default" attributes *)
paramMarshal=paramMarshal;
paramIn=inFlag;
paramOut=outFlag;
paramOptional=optionalFlag;
paramCustomAttrs= mk_custom_attrs (GenAttrs cenv eenv attribs) }
param, takenNames)
|> fst
and GenReturnInfo cenv eenv ilRetTy (TopArgInfo(attrs,_)) =
let marshal,attrs = GenMarshal cenv attrs
{ returnType=ilRetTy;
returnMarshal=marshal;
returnCustomAttrs= mk_custom_attrs (GenAttrs cenv eenv attrs) }
and GenPropertyForMethodDef g compileAsInstance tref mdef (memberInfo:ValMemberInfo) ilArgTys ilPropTy ilAttrs =
let name = memberInfo.PropertyName in (* chop "get_" *)
if verbose then dprintf "GenPropertyForMethodDef %s\n" name;
{ propName=name;
propRTSpecialName=false;
propSpecialName=false;
propSet=(if memberInfo.MemberFlags.MemberKind= MemberKindPropertySet then Some(mk_mref_to_mdef(tref,mdef)) else None);
propGet=(if memberInfo.MemberFlags.MemberKind= MemberKindPropertyGet then Some(mk_mref_to_mdef(tref,mdef)) else None);
propCallconv=(if compileAsInstance then CC_instance else CC_static);
propType=ilPropTy;
propInit=None;
propArgs= ilArgTys;
propCustomAttrs=ilAttrs; }
and GenEventForProperty cenv eenvForMeth (mspec:ILMethodSpec) (memberInfo:ValMemberInfo) ilAttrsThatGoOnPrimaryItem m returnTy =
let evname = memberInfo.PropertyName
let delegateTy = Infos.FindDelegateTypeOfPropertyEvent cenv.g cenv.amap evname m returnTy
let ilDelegateTy = GenType m cenv.g eenvForMeth.tyenv delegateTy
let ilThisTy = mspec.EnclosingType
let addMethRef = mk_mref (ilThisTy.TypeRef,mspec.CallingConv,"add_" ^evname,0,[ilDelegateTy],Type_void)
let removeMethRef = mk_mref (ilThisTy.TypeRef,mspec.CallingConv,"remove_"^evname,0,[ilDelegateTy],Type_void)
{ eventType = Some(ilDelegateTy);
eventName= evname;
eventRTSpecialName=false;
eventSpecialName=false;
eventAddOn = addMethRef;
eventRemoveOn = removeMethRef;
eventFire= None;
eventOther= [];
eventCustomAttrs = mk_custom_attrs ilAttrsThatGoOnPrimaryItem; }
and ComputeFlagFixupsForMemberBinding cenv eenv (v:Val,memberInfo:ValMemberInfo) =
if isNil memberInfo.ImplementedSlotSigs then
[fixupVirtualSlotFlags]
else
memberInfo.ImplementedSlotSigs |> List.map (fun slotsig ->
let oty = slotsig.ImplementedType
let tcref = v.MemberApparentParent
let tcaug = tcref.TypeContents
let shouldUseMethodImpl =
// TODO: it would be good to get rid of this special casing of Compare and GetHashCode during code generation
let isCompare =
(isSome tcaug.tcaug_compare && type_equiv cenv.g oty cenv.g.mk_IComparable_ty)
let isStructural =
(isSome tcaug.tcaug_compare_withc && type_equiv cenv.g oty cenv.g.mk_IStructuralComparable_ty) ||
(isSome tcaug.tcaug_hash_and_equals_withc && type_equiv cenv.g oty cenv.g.mk_IStructuralEquatable_ty)
is_interface_typ cenv.g oty && not isCompare && not isStructural
let memberParentTypars =
match PartitionValTypars cenv.g v with
| Some(_,memberParentTypars,_,_,_) -> memberParentTypars
| None -> errorR(InternalError("PartitionValTypars",v.Range)); []
let eenvUnderTypars = env_for_typars memberParentTypars eenv
let reallyUseMethodImpl,nameOfOverridingMethod, _ =
GenMethodImpl cenv eenvUnderTypars (shouldUseMethodImpl,slotsig) v.Range
(if reallyUseMethodImpl then fixupMethodImplFlags >> renameMethodDef nameOfOverridingMethod
else fixupVirtualSlotFlags >> renameMethodDef nameOfOverridingMethod))
and GenMethodForBinding
cenv cgbuf eenv
(v:Val,mspec,access,paramInfos,retInfo)
(topValInfo,baseValOpt,tps,methodVars, body, returnTy) =
let m = v.Range
let selfMethodVars,nonSelfMethodVars,compileAsInstance =
match v.MemberInfo with
| Some(memberInfo) when ValSpecIsCompiledAsInstance cenv.g v ->
match methodVars with
| [] -> error(Error("Internal error: empty argument list for instance method",v.Range))
| h::t -> [h],t,true
| _ -> [],methodVars,false
let nonUnitNonSelfMethodVars,body = BindUnitVars cenv.g (nonSelfMethodVars,paramInfos,body)
let nonUnitMethodVars = selfMethodVars@nonUnitNonSelfMethodVars
let cmtps,curriedArgInfos,_,_ = GetTopValTypeInCompiledForm cenv.g topValInfo v.Type v.Range
let eenv = bindBaseVarOpt cenv eenv baseValOpt
// The type parameters of the method's type are different to the type parameters
// for the big lambda ("tlambda") of the implementation of the method.
let eenv_under_meth_tlambda_typars = env_for_typars tps eenv
let eenv_under_meth_type_typars = env_for_typars cmtps eenv
// Add the arguments to the environment. We add an implicit 'this' argument to constructors
let isCtor = v.IsConstructor
let eenvForMeth =
let eenvForMeth = eenv_under_meth_tlambda_typars
let numImplicitArgs = if isCtor then 1 else 0
let eenvForMeth = AddStorageForLocalVals cenv.g (List.mapi (fun i v -> (v,Arg (numImplicitArgs+i))) nonUnitMethodVars) eenvForMeth
eenvForMeth
let tailCallInfo = [(mk_local_vref v,BranchCallMethod (topValInfo.AritiesOfArgs,curriedArgInfos,tps,nonUnitMethodVars.Length,GetNumObjArgsOfVal v))]
// Discard the result on a 'void' return type. For a constructor just return 'void'
let sequel =
if is_unit_typ cenv.g returnTy then discardAndReturnVoid
elif isCtor then ReturnVoid
else Return
// Now generate the code.
let ilMethodBody,preservesig =
match TryFindAttrib cenv.g cenv.g.attrib_DllImportAttribute v.Attribs with
| Some (Attrib(_,_,[ AttribStringArg(dll) ],namedArgs,m)) ->
if tps <> [] then error(Error("The signature for this external function contains type parameters. Constrain the argument and return types to indicate the types of the corresponding C function",m));
GenPInvokeMethod (v.CompiledName,dll,namedArgs), true
| Some (Attrib(_,_,_,_,m)) ->
error(Error("The DllImport attribute could not be decoded",m));
| _ ->
// Replace the body of PseudoValue "must inline" methods with a 'throw'
// However still generate the code for reflection etc.
let bodyExpr =
if HasAttrib cenv.g cenv.g.attrib_NoDynamicInvocationAttribute v.Attribs then
mk_throw m returnTy
(mk_exnconstr(mk_MFCore_tcref cenv.g.fslibCcu "DynamicInvocationNotSupportedException",
[ mk_string cenv.g m v.CompiledName],m))
else
body
// This is the main code generation for most methods
MethodBody_il(CodeGenMethodForExpr cenv cgbuf.mgbuf (SPAlways,tailCallInfo, mspec.Name, eenvForMeth, 0, 0, bodyExpr, sequel)),
false
// Do not generate DllImport attributes into the code - they are implicit from the P/Invoke
let attrs = v.Attribs |> List.filter (IsMatchingAttrib cenv.g cenv.g.attrib_DllImportAttribute >> not)
// Do not push the attributes to the method for events and properties
// However OverloadIDAttribute does get pushed to the methods as this is
// required by the F# quotation/reflection implementation.
let attrsThatMustBeOnMethod, attrsThatGoOnPropertyIfItExists =
attrs |> List.partition (IsMatchingAttrib cenv.g cenv.g.attrib_OverloadIDAttribute)
let ilAttrsCompilerGenerated = if v.IsCompilerGenerated then [ mk_CompilerGeneratedAttribute cenv.g.ilg ] else []
let ilAttrsThatGoOnMethod = GenAttrs cenv eenv attrsThatMustBeOnMethod
let ilAttrsThatGoOnPrimaryItem = GenAttrs cenv eenv attrsThatGoOnPropertyIfItExists
let ilTypars = GenGenericParams m cenv eenv_under_meth_tlambda_typars.tyenv tps
let ilParams = GenParams cenv eenv mspec paramInfos (Some(nonUnitNonSelfMethodVars))
let ilReturn = GenReturnInfo cenv eenv mspec.FormalReturnType retInfo
let methName = mspec.Name
let tref = mspec.MethodRef.EnclosingTypeRef
let EmitTheMethodDef mdef =
// Does the function have an explicit [<EntryPoint>] attribute?
let isExplicitEntryPoint = HasAttrib cenv.g cenv.g.attrib_EntryPointAttribute attrs
let mdef = {mdef with
mdPreserveSig=preservesig;
mdEntrypoint = isExplicitEntryPoint; }
let mdef =
if // operator names
mdef.mdName.StartsWith("op_",System.StringComparison.Ordinal) ||
// active pattern names
mdef.mdName.StartsWith("|",System.StringComparison.Ordinal) then
{mdef with mdSpecialName=true}
else
mdef
CountMethodDef();
cgbuf.mgbuf.AddMethodDef(tref,mdef)
match v.MemberInfo with
// don't generate unimplemented abstracts
| Some(memberInfo) when memberInfo.MemberFlags.MemberIsDispatchSlot && not memberInfo.IsImplemented ->
// skipping unimplemented abstract method
()
| Some(memberInfo) when not v.IsExtensionMember ->
let _,ilMethTypars = ilTypars |> List.chop (inst_of_typ mspec.EnclosingType).Length
if memberInfo.MemberFlags.MemberKind = MemberKindConstructor then
assert (isNil ilMethTypars)
// Constructors in abstract classes become protected
let access =
if HasAttrib cenv.g cenv.g.attrib_AbstractClassAttribute v.MemberApparentParent.Attribs then
MemAccess_family
else
access
let mdef = mk_ctor (access,ilParams,ilMethodBody)
let mdef = { mdef with mdCustomAttrs= mk_custom_attrs (ilAttrsThatGoOnPrimaryItem @ ilAttrsThatGoOnMethod @ ilAttrsCompilerGenerated) };
EmitTheMethodDef mdef
elif memberInfo.MemberFlags.MemberKind = MemberKindClassConstructor then
assert (isNil ilMethTypars)
let mdef = mk_cctor ilMethodBody
let mdef = { mdef with mdCustomAttrs= mk_custom_attrs (ilAttrsThatGoOnPrimaryItem @ ilAttrsThatGoOnMethod @ ilAttrsCompilerGenerated) };
EmitTheMethodDef mdef
// Generate virtual/override methods + method-impl information if needed
else
let mdef =
if not compileAsInstance then
mk_static_mdef (ilMethTypars,memberInfo.CompiledName,access,ilParams,ilReturn,ilMethodBody)
elif memberInfo.MemberFlags.MemberIsVirtual ||
(memberInfo.MemberFlags.MemberIsDispatchSlot && memberInfo.IsImplemented) ||
memberInfo.MemberFlags.MemberIsOverrideOrExplicitImpl then
// Virtual methods are used to implement interfaces and hence must currently be public
// REVIEW: use method impls to implement the interfaces
if access <> MemAccess_public && not v.IsCompilerGenerated then
warning(FullAbstraction("This method will be made public in the underlying IL because it may implement an interface or override a method",v.Range));
let flagFixups = ComputeFlagFixupsForMemberBinding cenv eenv (v,memberInfo)
let mdef = mk_generic_virtual_mdef (memberInfo.CompiledName,ComputePublicMemberAccess false,ilMethTypars,ilParams,ilReturn,ilMethodBody)
let mdef = List.fold (fun mdef f -> f mdef) mdef flagFixups
mdef
else
mk_generic_instance_mdef (memberInfo.CompiledName,access,ilMethTypars,ilParams,ilReturn,ilMethodBody)
let isAbstract =
memberInfo.MemberFlags.MemberIsDispatchSlot &&
let tcref = v.MemberApparentParent
not (deref_tycon tcref).IsFSharpDelegateTycon
let mdef =
{mdef with mdKind=match mdef.mdKind with
| MethodKind_virtual vinfo ->
MethodKind_virtual {vinfo with virtFinal=memberInfo.MemberFlags.MemberIsFinal;
virtAbstract=isAbstract; }
| k -> k }
match memberInfo.MemberFlags.MemberKind with
| (MemberKindPropertySet | MemberKindPropertyGet) as k ->
if nonNil ilMethTypars then
error(InternalError("A property may not be more generic than the enclosing type - constrain the polymorphism in the expression",v.Range));
// Check if we're compiling the property as a .NET event
if CompileAsEvent cenv.g v.Attribs then
// Emit the pseudo-property as an event, but not if its a private method impl
if mdef.mdAccess <> MemAccess_private then
let edef = GenEventForProperty cenv eenvForMeth mspec memberInfo ilAttrsThatGoOnPrimaryItem m returnTy
cgbuf.mgbuf.AddEventDef(tref,edef)
// The method def is dropped on the floor here
else
// Emit the property, but not if its a private method impl
if mdef.mdAccess <> MemAccess_private then
let vtyp = ReturnTypeOfPropertyVal cenv.g v
let ilPropTy = GenType m cenv.g eenv_under_meth_type_typars.tyenv vtyp
let ilArgTys = v |> ArgInfosOfPropertyVal cenv.g |> List.map fst |> GenTypes m cenv.g eenv_under_meth_type_typars.tyenv
let ilPropertyDef = GenPropertyForMethodDef cenv.g compileAsInstance tref mdef memberInfo ilArgTys ilPropTy (mk_custom_attrs ilAttrsThatGoOnPrimaryItem)
cgbuf.mgbuf.AddOrMergePropertyDef(tref,ilPropertyDef,m)
// Add the special name flag for all properties
let mdef = mdef |> AddSpecialNameFlag
// Do not push the attributes to the method for events and properties
// However OverloadIDAttribute does get pushed to the methods as this is
// required by the F# quotation/reflection implementation.
let mdef = { mdef with mdCustomAttrs= mk_custom_attrs (ilAttrsThatGoOnMethod @ ilAttrsCompilerGenerated) };
EmitTheMethodDef mdef
| _ ->
let mdef = { mdef with mdCustomAttrs= mk_custom_attrs (ilAttrsThatGoOnPrimaryItem @ ilAttrsThatGoOnMethod @ ilAttrsCompilerGenerated) };
EmitTheMethodDef mdef
| _ ->
let mdef = mk_static_mdef (ilTypars, methName, access,ilParams,ilReturn,ilMethodBody)
let mdef = { mdef with mdCustomAttrs= mk_custom_attrs (ilAttrsThatGoOnPrimaryItem @ ilAttrsThatGoOnMethod @ ilAttrsCompilerGenerated) }
EmitTheMethodDef mdef
and GenPInvokeMethod (nm,dll,namedArgs) =
let decoder = AttributeDecoder namedArgs
MethodBody_pinvoke
{ pinvokeWhere=mk_simple_modref dll;
pinvokeName=decoder.FindString "EntryPoint" nm;
pinvokeCallconv=
match decoder.FindInt32 "ILCallingConv" 0 with
| 1 -> PInvokeCallConvWinapi
| 2 -> PInvokeCallConvCdecl
| 3 -> PInvokeCallConvStdcall
| 4 -> PInvokeCallConvThiscall
| 5 -> PInvokeCallConvFastcall
| _ -> PInvokeCallConvWinapi;
PInvokeCharEncoding=
match decoder.FindInt32 "CharSet" 0 with
| 1 -> PInvokeEncodingNone
| 2 -> PInvokeEncodingAnsi
| 3 -> PInvokeEncodingUnicode
| 4 -> PInvokeEncodingAuto
| _ -> PInvokeEncodingNone;
pinvokeNoMangle= decoder.FindBool "ExactSpelling" false;
pinvokeLastErr= decoder.FindBool "SetLastError" false;
PInvokeThrowOnUnmappableChar= if (decoder.FindBool "ThrowOnUnmappableChar" false) then PInvokeThrowOnUnmappableCharEnabled else PInvokeThrowOnUnmappableCharUseAssem;
PInvokeCharBestFit=if (decoder.FindBool "BestFitMapping" false) then PInvokeBestFitEnabled else PInvokeBestFitUseAssem }
and GenBindings cenv cgbuf eenv binds = FlatList.iter (GenBind cenv cgbuf eenv) binds
//-------------------------------------------------------------------------
// Generate locals and other storage of values
//-------------------------------------------------------------------------
and GenSetVal cenv cgbuf eenv (vref,e,m) sequel =
if verbose then dprintn ("GenSetVal");
let storage = storage_for_vref m vref eenv
match storage with
| Env (ilCloTypeSpec,i,localCloInfo) ->
CG.EmitInstr cgbuf [Push (Type_boxed ilCloTypeSpec) ] ldarg_0;
| _ ->
()
GenExpr cenv cgbuf eenv SPSuppress e Continue;
GenSetStorage cenv vref.Range cgbuf storage
GenUnitThenSequel cenv eenv.cloc cgbuf sequel
and GenGetValRefAndSequel cenv cgbuf eenv m (v:ValRef) fetch_sequel =
let ty = v.Type
GenGetStorageAndSequel cenv cgbuf eenv m (ty, GenType m cenv.g eenv.tyenv ty) (storage_for_vref m v eenv) fetch_sequel
and GenGetVal cenv cgbuf eenv (v:ValRef,m) sequel =
GenGetValRefAndSequel cenv cgbuf eenv m v None;
GenSequel cenv eenv.cloc cgbuf sequel
and GenBindRhs cenv cgbuf eenv sp (vspec:Val) e =
match e with
| TExpr_tlambda _ | TExpr_lambda _ ->
let isLocalTypeFunc = IsNamedLocalTypeFuncVal cenv.g vspec e
let selfv = if isLocalTypeFunc then None else Some (mk_local_vref vspec)
GenLambda cenv cgbuf eenv isLocalTypeFunc selfv e Continue
| _ ->
GenExpr cenv cgbuf eenv sp e Continue;
and GenSetBindValue cenv cgbuf eenv eenv2 (vspec:Val) e =
GenBindRhs cenv cgbuf eenv2 SPSuppress vspec e;
GenStoreVal cenv cgbuf eenv vspec.Range vspec
and EmitInitLocal cgbuf typ idx = CG.EmitInstrs cgbuf [] [I_ldloca (uint16 idx); (I_initobj typ) ]
and EmitSetLocal cgbuf idx = CG.EmitInstr cgbuf [Pop] (I_stloc (uint16 idx))
and EmitGetLocal cgbuf typ idx = CG.EmitInstr cgbuf [Push typ] (I_ldloc (uint16 idx))
and EmitSetStaticField cgbuf fspec = CG.EmitInstr cgbuf [Pop] (mk_normal_stsfld fspec)
and EmitGetStaticFieldAddr cgbuf typ fspec = CG.EmitInstr cgbuf [Push typ] (I_ldsflda fspec)
and EmitGetStaticField cgbuf typ fspec = CG.EmitInstr cgbuf [Push typ] (mk_normal_ldsfld fspec)
and GenSetStorage cenv m cgbuf storage =
if verbose then dprintn ("GenSetStorage");
match storage with
| Local (idx,_) -> EmitSetLocal cgbuf idx
| StaticField (fspec,vref,hasLiteralAttr,tspec,_,_,_,_,ilSetterMethRef,optShadowLocal) ->
if hasLiteralAttr then errorR(Error("Literal fields may not be set",m));
CG.EmitInstr cgbuf [Pop] (I_call(Normalcall,mk_mref_mspec_in_typ(ilSetterMethRef,mk_typ AsObject tspec,[]),None))
| Method (_,_,mspec,m,_,_) ->
error(Error("GenSetStorage: "^mspec.Name^" was represented as a static method but was not an appropriate lambda expression",m))
| Null -> CG.EmitInstr cgbuf [Pop] (i_pop)
| Arg _ -> error(Error("mutable variables may not escape their method",m))
| Env (_,i,localCloInfo) ->
// Note: ldarg0 has already been emitted in GenSetVal
CG.EmitInstr cgbuf [Pop;Pop] (mk_IlxInstr (EI_stenv i));
| Unrealized -> error(Error("compiler error: unexpected unrealized value",m))
and CommitGetStorageSequel cenv cgbuf eenv m typ localCloInfo storeSequel =
if verbose then dprintn ("CommitGetStorageSequel");
match localCloInfo,storeSequel with
| Some {contents =NamedLocalIlxClosureInfoGenerator cloinfo},_ -> error(InternalError("Unexpected generator",m))
| Some {contents =NamedLocalIlxClosureInfoGenerated cloinfo},Some (tyargs,args,m,sequel) when nonNil tyargs ->
let actual_rty = GenNamedLocalTyFuncCall cenv cgbuf eenv typ cloinfo tyargs m;
CommitGetStorageSequel cenv cgbuf eenv m actual_rty None (Some ([],args,m,sequel))
| _, None ->
(if verbose then dprintn ("CommitGetStorageSequel: None");
())
| _,Some ([],[],m,sequel) ->
GenSequel cenv eenv.cloc cgbuf sequel
| _,Some (tyargs,args,m,sequel) ->
GenIndirectCall cenv cgbuf eenv (typ,tyargs,args,m) sequel
and GenGetStorageAndSequel cenv cgbuf eenv m (typ,ilTy) storage storeSequel =
if verbose then dprintn ("GenGetStorageAndSequel:");
match storage with
| Local (idx,localCloInfo) ->
if verbose then dprintn ("GenGetStorageAndSequel: Local...");
EmitGetLocal cgbuf ilTy idx;
CommitGetStorageSequel cenv cgbuf eenv m typ localCloInfo storeSequel
| StaticField (fspec,vref,hasLiteralAttr,ilTypeSpecForProperty,fieldName,_,_,ilGetterMethRef,_,_) ->
if verbose then dprintn ("GenGetStorageAndSequel: StaticField...");
// References to literals go directly to the field - no property is used
if hasLiteralAttr then
EmitGetStaticField cgbuf ilTy fspec
else
CG.EmitInstr cgbuf [Push ilTy] (I_call(Normalcall,mk_mref_mspec_in_typ(ilGetterMethRef,mk_typ AsObject ilTypeSpecForProperty,[]),None));
CommitGetStorageSequel cenv cgbuf eenv m typ None storeSequel
| Method (topValInfo,vref,mspec,_,_,_) ->
// Get a toplevel value as a first-class value.
// We generate a lambda expression and that simply calls
// the toplevel method. However we optimize the case where we are
// immediately applying the value anyway (to insufficient arguments).
// First build a lambda expression for the saturated use of the toplevel value...
// REVIEW: we should NOT be doing this in the backend...
if verbose then dprintn ("GenGetStorageAndSequel: Method...");
let expr,exprty = AdjustValForExpectedArity cenv.g m vref NormalValUse topValInfo
// Then reduce out any arguments (i.e. apply the sequel immediately if we can...)
match storeSequel with
| None -> GenLambda cenv cgbuf eenv false None expr Continue
| Some (tyargs',args,m,sequel) ->
let specialized_expr =
if verbose && tyargs' <> [] then dprintn ("creating type-specialized lambda at use of method "^mspec.Name);
if verbose && args <> [] then dprintf "creating term-specialized lambda at use of method %s\n--> expr = %s\n--> exprty = %s\n--> #args = %d\n" mspec.Name (showL (ExprL expr)) (showL (typeL exprty)) (List.length args);
if isNil args && isNil tyargs' then failwith ("non-lambda at use of method "^mspec.Name);
MakeApplicationAndBetaReduce cenv.g (expr,exprty,[tyargs'],args,m)
GenExpr cenv cgbuf eenv SPSuppress specialized_expr sequel
| Null ->
CG.EmitInstr cgbuf [Push ilTy] (i_ldnull);
CommitGetStorageSequel cenv cgbuf eenv m typ None storeSequel
| Unrealized ->
error(InternalError(sprintf "getting an unrealized value of type '%s'" (showL(typeL typ)),m));
| Arg i ->
CG.EmitInstr cgbuf [Push ilTy] (I_ldarg (uint16 i));
CommitGetStorageSequel cenv cgbuf eenv m typ None storeSequel
| Env (_,i,localCloInfo) ->
// Note: ldarg 0 is emitted in 'cu_erase' erasure of the ldenv instruction
CG.EmitInstr cgbuf [Push ilTy] (mk_IlxInstr (EI_ldenv i));
CommitGetStorageSequel cenv cgbuf eenv m typ localCloInfo storeSequel
and GenGetLocalVals cenv cgbuf eenvouter m fvs =
List.iter (fun v -> GenGetLocalVal cenv cgbuf eenvouter m v None) fvs;
and GenGetLocalVal cenv cgbuf eenv m (vspec:Val) fetch_sequel =
GenGetStorageAndSequel cenv cgbuf eenv m (vspec.Type, GenTypeOfVal cenv eenv vspec) (storage_for_val m vspec eenv) fetch_sequel
and GenGetLocalVRef cenv cgbuf eenv m (vref:ValRef) fetch_sequel =
GenGetStorageAndSequel cenv cgbuf eenv m (vref.Type, GenTypeOfVal cenv eenv (deref_val vref)) (storage_for_vref m vref eenv) fetch_sequel
and GenStoreVal cenv cgbuf eenv m (vspec:Val) =
GenSetStorage cenv vspec.Range cgbuf (storage_for_val m vspec eenv)
//and gen_begin_end
//--------------------------------------------------------------------------
// Allocate locals for values
//--------------------------------------------------------------------------
and AllocLocal cenv cgbuf eenv compgen (v,ty) (scopeMarks : mark * mark) =
// The debug range for the local
let ranges = if compgen then [] else [(v,scopeMarks)]
// Get an index for the local
let j =
if cenv.localOptimizationsAreOn
then cgbuf.ReallocLocal((fun i (_,ty') -> not (Imap.mem i eenv.liveLocals) && (ty = ty')),ranges,ty)
else cgbuf.AllocLocal(ranges,ty)
j, { eenv with liveLocals = Imap.add j () eenv.liveLocals }
and AllocLocalVal cenv cgbuf v eenv repr scopeMarks =
let repr,eenv =
let ty = v.Type
if is_unit_typ cenv.g ty && not v.IsMutable then Null,eenv
elif isSome repr && IsNamedLocalTypeFuncVal cenv.g v (the repr) then
(* known, named, non-escaping type functions *)
let cloinfoGenerate eenv =
let eenvinner =
{eenv with
letBoundVars=(mk_local_vref v)::eenv.letBoundVars}
let cloinfo,_,_ = GetIlxClosureInfo cenv v.Range true None eenvinner (the repr)
cloinfo
let idx,eenv = AllocLocal cenv cgbuf eenv v.IsCompilerGenerated (v.MangledName, cenv.g.ilg.typ_Object) scopeMarks
Local (idx,Some(ref (NamedLocalIlxClosureInfoGenerator cloinfoGenerate))),eenv
else
(* normal local *)
let idx,eenv = AllocLocal cenv cgbuf eenv v.IsCompilerGenerated (v.MangledName, GenTypeOfVal cenv eenv v) scopeMarks
Local (idx,None),eenv
Some repr,AddStorageForVal cenv.g (v,notlazy repr) eenv
and AllocStorageForBind cenv cgbuf scopeMarks eenv bind =
AllocStorageForBinds cenv cgbuf scopeMarks eenv (FlatList.one bind)
and AllocStorageForBinds cenv cgbuf scopeMarks eenv binds =
// phase 1 - decicde representations - most are very simple.
let reps, eenv = FlatList.mapfold (AllocValForBind cenv cgbuf scopeMarks) eenv binds
// Phase 2 - run the cloinfo generators for NamedLocalClosure values against the environment recording the
// representation choices.
reps |> FlatList.iter (fun reprOpt ->
match reprOpt with
| Some repr ->
match repr with
| Local(_,Some g)
| Env(_,_,Some g) ->
match !g with
| NamedLocalIlxClosureInfoGenerator f -> g := NamedLocalIlxClosureInfoGenerated (f eenv)
| NamedLocalIlxClosureInfoGenerated _ -> ()
| _ -> ()
| _ -> ());
eenv
and AllocValForBind cenv cgbuf (scopeMarks:mark*mark) eenv (TBind(v,repr,_)) =
match v.TopValInfo with
| None ->
AllocLocalVal cenv cgbuf v eenv (Some repr) scopeMarks
| Some _ ->
None,AllocTopValWithinExpr cenv cgbuf eenv.cloc scopeMarks v eenv
and AllocTopValWithinExpr cenv cgbuf cloc scopeMarks v eenv =
// decide whether to use a shadow local or not
let useShadowLocal =
cenv.debug &&
not cenv.localOptimizationsAreOn &&
not v.IsCompilerGenerated &&
not v.IsMutable &&
IsCompiledAsStaticValue cenv.g v
let optShadowLocal,eenv =
if useShadowLocal then
let storageOpt, eenv = AllocLocalVal cenv cgbuf v eenv None scopeMarks
match storageOpt with
| None -> NoShadowLocal,eenv
| Some storage -> ShadowLocal storage,eenv
else
NoShadowLocal,eenv
ComputeAndAddStorageForLocalTopVal cenv.g cloc optShadowLocal v eenv
//--------------------------------------------------------------------------
// Generate stack save/restore and assertions - pulled into letrec by alloc*
//--------------------------------------------------------------------------
/// Save the stack
/// - [gross] because IL flushes the stack at the exn. handler
/// - and because IL requires empty stack following a forward br (jump).
and EmitSaveStack cenv cgbuf eenv m scopeMarks =
if verbose then dprintn ("gen_save_stack");
let stack_saved = (cgbuf.GetCurrentStack())
let where_stack_saved,eenvinner = List.mapfold (fun eenv ty -> AllocLocal cenv cgbuf eenv true (ilxgenGlobalNng.FreshCompilerGeneratedName ("spill",m), ty) scopeMarks) eenv stack_saved
List.iter (EmitSetLocal cgbuf) where_stack_saved;
cgbuf.AssertEmptyStack();
(stack_saved,where_stack_saved),eenvinner (* need to return, it marks locals "live" *)
/// Restore the stack and load the result
and EmitRestoreStack cenv cgbuf (stack_saved,where_stack_saved) =
cgbuf.AssertEmptyStack();
List.iter2 (EmitGetLocal cgbuf) (List.rev stack_saved) (List.rev where_stack_saved)
//-------------------------------------------------------------------------
//GenAttr: custom attribute generation
//-------------------------------------------------------------------------
and GenAttribArg cenv eenv x (ilArgTy:ILType) =
match x,ilArgTy with
(* Detect standard constants *)
| TExpr_const(c,m,_),_ ->
let tynm = ilArgTy.TypeSpec.Name
let isobj = (tynm = "System.Object")
match c with
| TConst_bool b -> CustomElem_bool b
| TConst_int32 i when isobj || tynm = "System.Int32" -> CustomElem_int32 ( i)
| TConst_int32 i when tynm = "System.SByte" -> CustomElem_int8 (sbyte i)
| TConst_int32 i when tynm = "System.Int16" -> CustomElem_int16 (int16 i)
| TConst_int32 i when tynm = "System.Byte" -> CustomElem_uint8 (byte i)
| TConst_int32 i when tynm = "System.UInt16" ->CustomElem_uint16 (uint16 i)
| TConst_int32 i when tynm = "System.UInt32" ->CustomElem_uint32 (uint32 i)
| TConst_int32 i when tynm = "System.UInt64" ->CustomElem_uint64 (uint64 (int64 i))
| TConst_sbyte i -> CustomElem_int8 i
| TConst_int16 i -> CustomElem_int16 i
| TConst_int32 i -> CustomElem_int32 i
| TConst_int64 i -> CustomElem_int64 i
| TConst_byte i -> CustomElem_uint8 i
| TConst_uint16 i -> CustomElem_uint16 i
| TConst_uint32 i -> CustomElem_uint32 i
| TConst_uint64 i -> CustomElem_uint64 i
| TConst_float i -> CustomElem_float64 i
| TConst_float32 i -> CustomElem_float32 i
| TConst_char i -> CustomElem_char i
| TConst_zero when tynm = "System.String" -> CustomElem_string None
| TConst_string i when isobj || tynm = "System.String" -> CustomElem_string (Some(i))
| _ -> error (InternalError ( "The type '"^tynm^"' may not be used as a custom attribute value",m))
// Detect '[| ... |]' nodes
| TExpr_op(TOp_array,[elemTy],args,m),Type_array _ ->
let ilElemTy = GenType m cenv.g eenv.tyenv elemTy
CustomElem_array (List.map (fun arg -> GenAttribArg cenv eenv arg ilElemTy) args)
// Detect 'typeof<ty>' calls
| TExpr_app(TExpr_val(vref,_,_),_,[ty],[],m),_ when is_typeof_vref cenv.g vref ->
CustomElem_type (GenType m cenv.g eenv.tyenv ty)
// Detect 'typedefof<ty>' calls
| TExpr_app(TExpr_val(vref,_,_),_,[ty],[],m),_ when is_typedefof_vref cenv.g vref ->
CustomElem_tref ((GenType m cenv.g eenv.tyenv ty).TypeRef)
// Ignore upcasts
| TExpr_op(TOp_coerce,_,[arg2],_),_ ->
GenAttribArg cenv eenv arg2 ilArgTy
// Detect explicit enum values
| TExpr_app(TExpr_val(vref,_,_),_,_,[arg1],_),_ when cenv.g.vref_eq vref cenv.g.enum_vref ->
GenAttribArg cenv eenv arg1 ilArgTy
// Detect bitwise or of attribute flags: one case of constant folding (a more general treatment is needed)
| BitwiseOr cenv.g (arg1,arg2),_ ->
let v1 = GenAttribArg cenv eenv arg1 ilArgTy
let v2 = GenAttribArg cenv eenv arg2 ilArgTy
match v1,v2 with
| CustomElem_int8 i1, CustomElem_int8 i2 -> CustomElem_int8 (i1 ||| i2)
| CustomElem_int16 i1, CustomElem_int16 i2-> CustomElem_int16 (i1 ||| i2)
| CustomElem_int32 i1, CustomElem_int32 i2-> CustomElem_int32 (i1 ||| i2)
| CustomElem_int64 i1, CustomElem_int64 i2-> CustomElem_int64 (i1 ||| i2)
| CustomElem_uint8 i1, CustomElem_uint8 i2-> CustomElem_uint8 (i1 ||| i2)
| CustomElem_uint16 i1, CustomElem_uint16 i2-> CustomElem_uint16 (i1 ||| i2)
| CustomElem_uint32 i1, CustomElem_uint32 i2-> CustomElem_uint32 (i1 ||| i2)
| CustomElem_uint64 i1, CustomElem_uint64 i2-> CustomElem_uint64 (i1 ||| i2)
| _ -> error (InternalError ("invalid custom attribute value (not a valid constant): "^showL (ExprL x),range_of_expr x))
// Other expressions are not valid custom attribute values
| _ ->
error (InternalError ("invalid custom attribute value (not a constant): "^showL (ExprL x),range_of_expr x))
and GenAttr cenv eenv (Attrib(_,k,args,props,m)) =
let props =
props |> List.map (fun (AttribNamedArg(s,ty,fld,AttribExpr(_,expr))) ->
let m = (range_of_expr expr)
let il_ty = GenType m cenv.g eenv.tyenv ty
let cval = GenAttribArg cenv eenv expr il_ty
(s,il_ty,fld,cval))
let mspec =
match k with
| ILAttrib(mref) -> mk_mspec(mref,AsObject,[],[])
| FSAttrib(vref) ->
assert(vref.IsMember);
let mspec,_,_,_,_ = GetMethodSpecForMemberVal cenv.g (the vref.MemberInfo) vref
mspec
let ilArgs = List.map2 (fun (AttribExpr(_,vexpr)) ty -> GenAttribArg cenv eenv vexpr ty) args mspec.FormalArgTypes
mk_custom_attribute_mref cenv.g.ilg (mspec,ilArgs, props)
and GenAttrs cenv eenv attrs = List.map (GenAttr cenv eenv) attrs
//--------------------------------------------------------------------------
// Generate the set of modules for an assembly, and the declarations in each module
//--------------------------------------------------------------------------
and GenTypeDefForCompLoc cenv eenv (mgbuf: AssemblyBuilder) cloc hidden attribs =
let tref = TypeRefForCompLoc cloc
let tdef =
mk_simple_tdef cenv.g.ilg
(tref.Name,
ComputeTypeAccess tref hidden,
mk_mdefs [],
mk_fdefs [],
mk_properties [],
mk_events [],
mk_custom_attrs
(GenAttrs cenv eenv attribs @
(if List.mem tref.Name [TypeNameForStatupCode cloc; TypeNameForPrivateImplementationDetails cloc]
then [ (* mk_CompilerGeneratedAttribute *) ]
else [mk_CompilationMappingAttr cenv.g SourceLevelConstruct_Module])))
let tdef = { tdef with tdSealed=true; tdAbstract=true }
mgbuf.AddTypeDef(tref,tdef)
and GenModuleExpr cenv cgbuf qname lazyInitInfo eenv cloc x =
let (TMTyped(mty,def,m)) = x
// REVIEW: the scopeMarks are used for any shadow locals we create for the module bindings
// We use one scope for all the bindings in the module, which makes them all appear with their "default" values
// rather than incrementally as we step through the initializations in the module. This is a little unfortunate
// but stems from the way we add module values all at once before we generate the module itself.
LocalScope "module" cgbuf (fun scopeMarks ->
let sigToImplRemapInfo = mk_mdef_to_mtyp_remapping def mty
let eenv = AddSignatureRemapInfo "defs" sigToImplRemapInfo eenv
let eenv =
// Allocate all the values, including any shadow locals for static fields
let allocVal cloc v = AllocTopValWithinExpr cenv cgbuf cloc scopeMarks v
AddBindingsForModuleDef allocVal eenv.cloc eenv def
GenModuleDef cenv cgbuf qname lazyInitInfo eenv def)
and GenModuleDefs cenv cgbuf qname lazyInitInfo eenv mdefs =
List.iter (GenModuleDef cenv cgbuf qname lazyInitInfo eenv) mdefs
and GenModuleDef cenv (cgbuf:CodeGenBuffer) qname lazyInitInfo eenv x =
if verbose then dprintf "GenModuleDef, tspec(cloc) = %A\n" (TypeSpecForCompLoc eenv.cloc);
match x with
| TMDefRec(tycons,binds,mbinds,m) ->
tycons |> List.iter (fun tc ->
if tc.IsExceptionDecl
then GenExnDef cenv cgbuf.mgbuf eenv m tc
else GenTypeDef cenv cgbuf.mgbuf lazyInitInfo eenv m tc) ;
GenLetRecBinds cenv cgbuf eenv (binds,m);
mbinds |> List.iter (GenModuleBinding cenv cgbuf qname lazyInitInfo eenv)
| TMDefLet(bind,m) ->
GenBindings cenv cgbuf eenv (FlatList.one bind)
| TMDefDo(e,m) ->
GenExpr cenv cgbuf eenv SPAlways e discard;
| TMAbstract(mexpr) ->
GenModuleExpr cenv cgbuf qname lazyInitInfo eenv eenv.cloc mexpr
| TMDefs(mdefs) ->
GenModuleDefs cenv cgbuf qname lazyInitInfo eenv mdefs
// Generate a module binding
and GenModuleBinding cenv (cgbuf:CodeGenBuffer) (qname:QualifiedNameOfFile) lazyInitInfo eenv (TMBind(mspec, mdef)) =
let hidden = IsHiddenTycon eenv.sigToImplRemapInfo mspec
let eenvinner =
if mspec.IsNamespace then eenv else
{eenv with cloc = CompLocForFixedModule cenv.fragName qname.Text mspec }
// Create the class to hold the contents of this module. No class needed if
// we're compiling it as a namespace
if not mspec.IsNamespace then
GenTypeDefForCompLoc cenv eenvinner cgbuf.mgbuf eenvinner.cloc hidden mspec.Attribs;
GenModuleDef cenv cgbuf qname lazyInitInfo eenvinner mdef;
// Generate the declarations in the module and its initialization code
// Most module fields go into the startup code. If there are no fields we don't need a .cctor.
// However modules with mutable values contain public mutable
// static fields. In this case we need to ensure that if those fields are "touched" then the outer constructor
// is forced. The outer constructor will fill ni the value of the field.
if not mspec.IsNamespace && (cgbuf.mgbuf.GetCurrentFields(TypeRefForCompLoc eenvinner.cloc) |> Seq.is_empty |> not) then
GenForceOuterInitializationAsPartOfCCtor cenv cgbuf.mgbuf lazyInitInfo (TypeRefForCompLoc eenvinner.cloc) mspec.Range;
// Generate an entire file
and GenTopImpl cenv mgbuf mainInfo eenv (TImplFile(qname,_,mexpr) as impl) =
let fragName = qname.Text
if verbose then dprintf "-----------------------------------------------------------------------------\ngen_top_impl %s\n" fragName;
let eenv = {eenv with cloc = { eenv.cloc with clocTopImplQualifiedName = qname.Text } }
// This is used to point the inner classes back to the startup module for initialization purposes
let clocStartup = CompLocForStartupCode eenv.cloc
let startupTspec = mk_nongeneric_tspec (TypeRefForCompLoc clocStartup)
// Create the class to hold the initialization code and static fields for this file.
GenTypeDefForCompLoc cenv eenv mgbuf clocStartup true [];
let eenv = {eenv with cloc = clocStartup;
someTspecInThisModule=mk_nongeneric_tspec (TypeRefForCompLoc clocStartup) }
let createStaticInitializerFieldInStartupClass() =
let initFieldName = CompilerGeneratedName "init"
let fieldDef =
mk_static_fdef (initFieldName,cenv.g.ilg.typ_Int32, None, None, ComputeMemberAccess true true)
|> add_fdef_never_attrs cenv.g.ilg
|> add_fdef_generated_attrs cenv.g.ilg
let fspec = mk_fspec_in_boxed_tspec (startupTspec, initFieldName, cenv. g.ilg.typ_Int32)
CountStaticFieldDef();
mgbuf.AddFieldDef(startupTspec.TypeRef,fieldDef);
fspec
let lazyInitInfo =
match mainInfo with
| Some _ -> None
| None ->
// We keep an accumulator of the fragments needed to force the initialization semantics through the compiled code.
// These fragments only get executed/committed if we actually end up producing some code for the .cctor.
// NOTE: The existence of .cctors adds costs to execution so this is a half-sensible attempt to avoid adding them when possible.
let initSemanticsAcc = ref []
let fspec = createStaticInitializerFieldInStartupClass()
(*initSemanticsAcc := addCCtor :: !initSemanticsAcc; *)
Some(fspec,initSemanticsAcc)
if verbose then dprintn ("gen_top_impl_expr: codegen .cctor/main for outer module");
let m = qname.Range
let clocCcu = (CompLocForCcu cenv.viewCcu)
let methodName = match mainInfo with None -> ".cctor" | _ -> mainMethName
let topCode = CodeGenMethod cenv mgbuf (true,[],methodName,eenv,0,0,(fun cgbuf eenv ->
GenModuleExpr cenv cgbuf qname lazyInitInfo eenv clocCcu mexpr;
CG.EmitInstr cgbuf [] I_ret),m)
// Make a .cctor method to run the top level bindings. This initializes all modules.
if verbose then dprintn ("Creating .cctor/main for outer module");
let initmeths =
match mainInfo, lazyInitInfo with
| Some (main_attrs), None ->
// Generate an explicit main method. If necessary, make a class constructor as
// well for the bindings earlier in the file containing the entrypoint.
match mgbuf.GetExplicitEntryPointInfo() with
| Some(tref) ->
if (CheckCodeDoesSomething topCode.ilCode) then
let fspec = createStaticInitializerFieldInStartupClass()
mgbuf.AddExplicitInitToSpecificMethodDef((fun md -> md.mdEntrypoint),tref,fspec,GenPossibleILSourceMarker cenv m);
[ mk_cctor (MethodBody_il topCode) ]
else
[]
// Generate an implicit main method
| None ->
let ilAttrs = mk_custom_attrs (GenAttrs cenv eenv main_attrs)
if not cenv.emptyProgramOk && not (CheckCodeDoesSomething topCode.ilCode) then
let errorM = mk_range (file_of_range m) (end_of_range m) (end_of_range m)
warning (Error("Main module of program is empty: nothing will happen when it is run", errorM));
let mdef = mk_static_nongeneric_mdef(mainMethName,ComputePublicMemberAccess true,[],mk_return Type_void, MethodBody_il topCode)
[ {mdef with mdEntrypoint= true; mdCustomAttrs = ilAttrs } ]
// Generate an on-demand .cctor for the file
| None, Some(fspec, initSemanticsAcc) ->
if (CheckCodeDoesSomething topCode.ilCode) then
// Run the imperative (yuck!) actions that force the generation
// of references to the cctor for nested modules etc.
(List.rev !initSemanticsAcc) |> List.iter (fun f -> f());
// Return the generated cctor
[ mk_cctor (MethodBody_il topCode) ]
else
[]
| _ -> failwith "unreachable"
initmeths |> List.iter (fun mdef -> mgbuf.AddMethodDef(TypeRefForCompLoc clocStartup,mdef)) ;
// Compute the ilxgenEnv after the generation of the module, i.e. the residue need to generate anything that
// uses the constructs exported from this module.
// We add the module type all over again. Note no shadow locals for static fields needed here since they are only relevant to the main/.cctor
let eenvafter =
let allocVal cloc v = ComputeAndAddStorageForLocalTopVal cenv.g cloc NoShadowLocal v
AddBindingsForLocalModuleType allocVal clocCcu eenv (mtyp_of_mexpr mexpr)
eenvafter
and GenForceOuterInitializationAsPartOfCCtor cenv (mgbuf:AssemblyBuilder) lazyInitInfo tref m =
// Authoring a .cctor with effects forces the cctor for the 'initialization' module by doing a dummy store & load of a field
// Doing both a store and load keeps FxCop happier because it thinks the field is useful
match lazyInitInfo with
| Some (fspec,initSemanticsAcc) ->
initSemanticsAcc := (fun () -> mgbuf.AddExplicitInitToSpecificMethodDef((fun md -> md.mdName = ".cctor"),tref,fspec,GenPossibleILSourceMarker cenv m)) :: !initSemanticsAcc
| None -> ()
/// Generate an Equals method.
and GenEqualsOverrideCallingIComparable cenv mgbuf eenv m (this_tcref,this_ilty,that_ilty) =
let mspec = mk_nongeneric_instance_mspec_in_typ (cenv.g.ilg.typ_IComparable, "CompareTo", [cenv.g.ilg.typ_Object], cenv.g.ilg.typ_int32)
mk_virtual_mdef
("Equals",MemAccess_public,
[mk_named_param ("obj",cenv.g.ilg.typ_Object)],
mk_return cenv.g.ilg.typ_bool,
MethodBody_il
(mk_ilmbody(true,[],2,
nonbranching_instrs_to_code
[ yield ldarg_0;
yield I_ldarg 1us;
if is_struct_tcref this_tcref then
yield I_callconstraint ( Normalcall, this_ilty,mspec,None)
else
yield I_callvirt ( Normalcall, mspec,None)
yield mk_ldc_i32 (0)
yield I_arith AI_ceq ],
None)))
|> AddNonUserCompilerGeneratedAttribs cenv.g
/// Generate a GetHashCode method.
and GenHashOverride cenv mgbuf eenv m this_tcref this_ilty =
let icomparer_iltref = (cenv.g.tcref_System_Collections_IComparer).CompiledRepresentationForTyrepNamed
let icomparer_ilt = mk_boxed_typ (icomparer_iltref) []
let iequalitycomparer_iltref = (cenv.g.tcref_System_Collections_IEqualityComparer).CompiledRepresentationForTyrepNamed
let iequalitycomparer_ilt = mk_boxed_typ (iequalitycomparer_iltref) []
let langprim_iltref = (cenv.g.tcref_LanguagePrimitives).CompiledRepresentationForTyrepNamed
let langprim_ilt = mk_boxed_typ (langprim_iltref) []
let mspec_getComparer = mk_static_nongeneric_mspec_in_typ (langprim_ilt,"FSharpEqualityComparer",[],iequalitycomparer_ilt)
let mspec_IStructuralEquatable_GetHashCode = mk_nongeneric_instance_mspec_in_typ(this_ilty,"GetHashCode",[iequalitycomparer_ilt],cenv.g.ilg.typ_Int32)
mk_virtual_mdef
("GetHashCode",MemAccess_public,[],
mk_return cenv.g.ilg.typ_int32,
MethodBody_il
(mk_ilmbody(true,[],2,
nonbranching_instrs_to_code
[ yield ldarg_0
yield I_call (Normalcall, mspec_getComparer, None)
yield I_call (Normalcall, mspec_IStructuralEquatable_GetHashCode, None) ],
None)))
|> AddNonUserCompilerGeneratedAttribs cenv.g
and GenFieldInit m c =
match c with
| TConst_sbyte n -> FieldInit_int8 n
| TConst_int16 n -> FieldInit_int16 n
| TConst_int32 n -> FieldInit_int32 n
| TConst_int64 n -> FieldInit_int64 n
| TConst_byte n -> FieldInit_uint8 n
| TConst_uint16 n -> FieldInit_uint16 n
| TConst_uint32 n -> FieldInit_uint32 n
| TConst_uint64 n -> FieldInit_uint64 n
| TConst_bool n -> FieldInit_bool n
| TConst_char n -> FieldInit_char (uint16 n)
| TConst_float32 n -> FieldInit_single n
| TConst_float n -> FieldInit_double n
| TConst_string s -> FieldInit_string s
| TConst_zero -> FieldInit_ref
| _ -> error(Error("This type may not be used for a literal field",m))
and GenAbstractBinding cenv eenv mgbuf tref (vref:ValRef) =
assert(vref.IsMember);
let m = vref.Range
let memberInfo = (the (vref.MemberInfo))
let attribs = vref.Attribs
if memberInfo.MemberFlags.MemberIsDispatchSlot && not memberInfo.IsImplemented then
let ilAttrs = GenAttrs cenv eenv attribs
let mspec,ctps,mtps,argInfos,retInfo = GetMethodSpecForMemberVal cenv.g memberInfo vref
let eenvForMeth = env_for_typars (ctps@mtps) eenv
let ilMethTypars = GenGenericParams m cenv eenvForMeth.tyenv mtps
let ilReturn = GenReturnInfo cenv eenvForMeth mspec.FormalReturnType retInfo
let ilParams = GenParams cenv eenvForMeth mspec argInfos None
let compileAsInstance = ValRefIsCompiledAsInstanceMember cenv.g vref
let mdef = mk_generic_virtual_mdef (memberInfo.CompiledName,ComputePublicMemberAccess false,ilMethTypars,ilParams,ilReturn,MethodBody_abstract)
let mdef = fixupVirtualSlotFlags mdef
let mdef =
{mdef with
mdKind=match mdef.mdKind with
| MethodKind_virtual vinfo ->
MethodKind_virtual {vinfo with virtFinal=memberInfo.MemberFlags.MemberIsFinal;
virtAbstract=memberInfo.MemberFlags.MemberIsDispatchSlot; }
| k -> k }
match memberInfo.MemberFlags.MemberKind with
| MemberKindClassConstructor
| MemberKindConstructor
| MemberKindMember ->
let mdef = {mdef with mdCustomAttrs= mk_custom_attrs ilAttrs }
[mdef], [], []
| MemberKindPropertyGetSet -> error(Error("Unexpected GetSet annotation on a property",m));
| MemberKindPropertySet | MemberKindPropertyGet ->
let v = deref_val vref
let vtyp = ReturnTypeOfPropertyVal cenv.g v
if CompileAsEvent cenv.g attribs then
let edef = GenEventForProperty cenv eenvForMeth mspec memberInfo ilAttrs m vtyp
[],[],[edef]
else
let ilPropertyDef =
let ilPropTy = GenType m cenv.g eenvForMeth.tyenv vtyp
let ilArgTys = v |> ArgInfosOfPropertyVal cenv.g |> List.map fst |> GenTypes m cenv.g eenvForMeth.tyenv
GenPropertyForMethodDef cenv.g compileAsInstance tref mdef memberInfo ilArgTys ilPropTy (mk_custom_attrs ilAttrs)
let mdef = mdef |> AddSpecialNameFlag
[mdef], [ilPropertyDef],[]
else
[],[],[]
and GenTypeDef cenv mgbuf lazyInitInfo eenv m (tycon:Tycon) =
let tcref = mk_local_tcref tycon
if tycon.IsTypeAbbrev then () else
match tycon.TypeReprInfo with
| None -> ()
| Some (TAsmRepr _ | TILObjModelRepr _ | TMeasureableRepr _) -> ()
| Some (TFsObjModelRepr _ | TRecdRepr _ | TFiniteUnionRepr _) ->
let eenvinner = replace_tyenv (tyenv_for_tycon tycon) eenv
let _,thisTy = generalize_tcref tcref
let ilty = GenType m cenv.g eenvinner.tyenv thisTy
let tref = ilty.TypeRef
let tname = tref.Name
let hidden = IsHiddenTycon eenv.sigToImplRemapInfo tycon
let hiddenRepr = hidden or IsHiddenTyconRepr eenv.sigToImplRemapInfo tycon
let access = ComputeTypeAccess tref hidden
let gparams = GenGenericParams m cenv eenvinner.tyenv tycon.TyparsNoRange
let aug = tycon.TypeContents
let intfs = List.map (p13 >> GenType m cenv.g eenvinner.tyenv) aug.tcaug_implements
let tcaug = tycon.TypeContents
let augmentOverrideMethodDefs =
// The implicit augmentation doesn't actually create CompareTo(object) or Object.Equals
// So we do it here.
let specialCompareMethod =
// Note you only have to implement 'System.IComparable' to customize structural comparison AND equality on F# types
// See also FinalTypeDefinitionChecksAtEndOfInferenceScope in tc.ml
// Generate an Equals method implemented via IComparable if the type EXPLICITLY implements IComparable.
// HOWEVER, if the type doesn't override Object.Equals already.
(if isNone tcaug.tcaug_compare &&
tcaug_has_interface cenv.g tcaug cenv.g.mk_IComparable_ty &&
not (tcaug_has_override cenv.g tcaug "Equals" [cenv.g.obj_ty]) &&
not tycon.IsFSharpInterfaceTycon
then
[ GenEqualsOverrideCallingIComparable cenv mgbuf eenv m (tcref,ilty,ilty) ]
else [])
// The implicit augmentation doesn't actually create GetHashCode
let hashMethodDefs =
(if isSome tycon.TypeContents.tcaug_hash_and_equals_withc (* && not (tcref_alloc_observable tcref) *) && not tycon.TypeContents.tcaug_hasObjectGetHashCode
then [ GenHashOverride cenv mgbuf eenv m tcref ilty ]
else [])
(specialCompareMethod @ hashMethodDefs)
// We can't reduce the accessibility because these implement virtual slots
(* |> List.map (fun md -> { md with mdAccess=memberAccess }) *)
// Generate the interface slots and abstract slots.
let abstractMethodDefs,abstractPropDefs, abstractEventDefs =
if tycon.IsFSharpDelegateTycon then
[],[],[]
else
// sort by order of declaration
tycon.TypeContents.tcaug_adhoc
|> NameMultiMap.range
|> List.sortWith (fun v1 v2 -> range_ord v1.DefinitionRange v2.DefinitionRange)
|> List.map (GenAbstractBinding cenv eenv mgbuf tref)
|> List.unzip3
|> triple_map List.concat List.concat List.concat
let abstractPropDefs = abstractPropDefs |> merge_pdef_list m
let isAbstract = is_partially_implemented_tycon tycon
// Generate all the method impls showing how various abstract slots and interface slots get implemented
// REVIEW: no method impl generated for IStructuralHash or ICompare
let methodImpls =
[ for vref in tycon.TypeContents.tcaug_adhoc |> NameMultiMap.range do
assert(vref.IsMember);
let memberInfo = vref.MemberInfo.Value
if memberInfo.MemberFlags.MemberIsOverrideOrExplicitImpl && not (CompileAsEvent cenv.g vref.Attribs) then
for slotsig in memberInfo.ImplementedSlotSigs do
if is_interface_typ cenv.g slotsig.ImplementedType then
match vref.TopValInfo with
| Some arities ->
let memberParentTypars,memberMethodTypars =
match PartitionValRefTypars cenv.g vref with
| Some(_,memberParentTypars,memberMethodTypars,_,_) -> memberParentTypars,memberMethodTypars
| None -> [],[]
let shouldUseMethodImpl = true
let eenvUnderTypars = env_for_typars memberParentTypars eenv
let reallyUseMethodImpl,_,methodImplGenerator = GenMethodImpl cenv eenvUnderTypars (shouldUseMethodImpl,slotsig) m
if reallyUseMethodImpl then
yield methodImplGenerator (ilty.TypeSpec,memberMethodTypars)
| _ -> () ]
let defaultMemberAttrs =
tycon.TypeContents.tcaug_adhoc
|> NameMultiMap.range
|> List.tryPick (fun vref ->
let name = vref.DisplayName
match vref.MemberInfo with
| None -> None
| Some memberInfo ->
match name, memberInfo.MemberFlags.MemberKind with
| ("Item" | "op_IndexedLookup"), (MemberKindPropertyGet | MemberKindPropertySet) when nonNil (ArgInfosOfPropertyVal cenv.g (deref_val vref)) ->
Some( mk_custom_attribute cenv.g.ilg (mk_tref (cenv.g.ilg.mscorlib_scoref,"System.Reflection.DefaultMemberAttribute"),[cenv.g.ilg.typ_String],[CustomElem_string(Some(name))],[]) )
| _ -> None)
|> Option.to_list
let tyconRepr = tycon.TypeReprInfo
// DebugDisplayAttribute gets copied to the subtypes generated as part of DU compilation
let debugDisplayAttrs,normalAttrs = tycon.Attribs |> List.partition (IsMatchingAttrib cenv.g cenv.g.attrib_DebuggerDisplayAttribute)
let generateDebugDisplayAttribute = not cenv.g.compilingFslib && tycon.IsUnionTycon && isNil debugDisplayAttrs
let generateDebugProxies = (not (tcref_eq cenv.g tcref cenv.g.unit_tcr_canon) &&
not (HasAttrib cenv.g cenv.g.attrib_DebuggerTypeProxyAttribute tycon.Attribs))
let ilDebugDisplayAttributes =
[ yield! GenAttrs cenv eenv debugDisplayAttrs
if generateDebugDisplayAttribute then
yield mk_DebuggerDisplayAttribute cenv.g.ilg ("{"^debugDisplayMethodName^"()}") ]
let tdCustomAttrs =
[ yield! defaultMemberAttrs
yield! normalAttrs
|> List.filter (IsMatchingAttrib cenv.g cenv.g.attrib_StructLayoutAttribute >> not)
|> GenAttrs cenv eenv
yield! ilDebugDisplayAttributes ]
let reprAccess = ComputeMemberAccess true hiddenRepr
let tdKind =
match tyconRepr with
| Some (TFsObjModelRepr o) ->
match o.fsobjmodel_kind with
| TTyconClass -> TypeDef_class
| TTyconStruct -> TypeDef_valuetype
| TTyconInterface -> TypeDef_interface
| TTyconEnum -> TypeDef_enum
| TTyconDelegate _ -> TypeDef_delegate
| _ -> TypeDef_class
let isEmptyStruct =
(match tdKind with TypeDef_valuetype -> true | _ -> false) &&
// All structs are sequential by default
// Structs with no instance fields get size 1, pack 0
tycon.AllFieldsAsList |> List.exists (fun f -> not f.IsStatic)
let requiresExtraField =
isEmptyStruct && cenv.workAroundReflectionEmitBugs && not tycon.TyparsNoRange.IsEmpty
// Compute a bunch of useful thnigs for each field
let fieldSummaries =
[ for fspec in tycon.AllFieldsAsList do
let useGenuineField = use_genuine_field tycon fspec
// The property (or genuine IL field) is hidden in these circumstances:
// - secret fields apart from "__value" fields for enums
// - the representation of the type is hidden
// - the F# field is hidden by a signature or private declaration
let propHidden =
((fspec.IsCompilerGenerated && not (is_enum_tycon tycon)) ||
hiddenRepr ||
IsHiddenRecdField eenv.sigToImplRemapInfo (rfref_of_rfield tcref fspec))
let propType = GenType m cenv.g eenvinner.tyenv fspec.FormalType
let fldName = GenFieldName tycon fspec
yield (useGenuineField,fldName,fspec.IsMutable, fspec.IsStatic, fspec.PropertyAttribs,propType,propHidden,fspec) ]
// Generate the IL fields
let fieldDefs =
[ for (useGenuineField,fldName,mut,stat,_,propType,propHidden,fspec) in fieldSummaries do
let literalValue = fspec.LiteralValue
let fdOffset =
match TryFindAttrib cenv.g cenv.g.attrib_FieldOffsetAttribute fspec.FieldAttribs with
| Some (Attrib(_,_,[ AttribInt32Arg(fieldOffset) ],_,m)) ->
Some fieldOffset
| Some (Attrib(_,_,_,_,m)) ->
errorR(Error("The FieldOffset attribute could not be decoded",m));
None
| _ ->
None
let attribs =
[ // If using a field then all the attributes go on the field
// See also FSharp 1.0 Bug 4727: once we start compiling them as real mutable fields, you should not be able to target both "property" for "val mutable" fields in classes
if useGenuineField then yield! fspec.PropertyAttribs
yield! fspec.FieldAttribs ]
let fattribs =
attribs
|> List.filter (IsMatchingAttrib cenv.g cenv.g.attrib_FieldOffsetAttribute >> not)
let fieldMarshal, fattribs = GenMarshal cenv fattribs
let fdNotSerialized = HasAttrib cenv.g cenv.g.attrib_NonSerializedAttribute fattribs
// The IL field is hidden if the property/field is hidden OR we're using a property AND the field is not mutable (because we can take the address of a mutable field). *)
// Otherwise fields are always accessed via their property getters/setters *)
let fdHidden = propHidden || (not useGenuineField && not mut)
let extraAttribs =
match tyconRepr with
| Some (TRecdRepr _) when not useGenuineField -> [ mk_DebuggerBrowsableNeverAttribute cenv.g.ilg ] // hide fields in records in debug display
| _ -> [] // don't hide fields in classes in debug display
yield
{ fdName=fldName;
fdType=propType;
fdStatic=stat;
fdAccess=ComputeMemberAccess true fdHidden;
fdData=None; // REVIEW
fdInit= Option.map (GenFieldInit m) literalValue;
fdOffset=fdOffset;
fdSpecialName = (fldName="value__" && is_enum_tycon tycon);
fdMarshal=fieldMarshal
fdNotSerialized=fdNotSerialized;
fdInitOnly = false; // REVIEW
fdLiteral =isSome(literalValue);
fdCustomAttrs=mk_custom_attrs (GenAttrs cenv eenv fattribs @ extraAttribs) }
if requiresExtraField then
yield mk_instance_fdef("__dummy",cenv.g.ilg.typ_int32,None,MemAccess_assembly) ]
// Generate property definitions for the fields compiled as properties
let propertyDefs =
[ for (i, (useGenuineField,fldName,mut,stat,propAttribs,propType,propHidden,fspec)) in markup fieldSummaries do
if not useGenuineField then
let cc = if stat then ILCallingConv.Static else ILCallingConv.Instance
let propName = fspec.Name
let fattrs = GenAttrs cenv eenv propAttribs @ [mk_CompilationMappingAttrWithSeqNum cenv.g SourceLevelConstruct_Field i]
yield
{ propName=propName;
propRTSpecialName=false;
propSpecialName=false;
propSet=(if mut then Some(mk_mref(tref,cc,"set_"^propName,0,[propType],Type_void)) else None);
propGet=Some(mk_mref(tref,cc,"get_"^propName,0,[],propType));
propCallconv=(if stat then CC_static else CC_instance);
propType=propType;
propInit=None;
propArgs=[];
propCustomAttrs=mk_custom_attrs fattrs; } ]
let methodDefs =
[ // Generate property getter methods for those fields that have properties
for (useGenuineField,fldName,_,stat,_,propType,propHidden,fspec) in fieldSummaries do
if not useGenuineField then
let propName = fspec.Name
let methnm = "get_"^propName
let access = ComputeMemberAccess true propHidden
yield mk_ldfld_method_def (methnm,access,stat,ilty,fldName,propType)
// Generate property setter methods for the mutable fields
for (useGenuineField,fldName,mut,stat,_,propType,propHidden,fspec) in fieldSummaries do
if mut && not useGenuineField then
let propName = fspec.Name
let il_fspec = mk_fspec_in_typ(ilty,fldName,propType)
let methnm = "set_"^propName
let parms = [mk_named_param("value",propType)]
let ret = mk_return Type_void
let access = ComputeMemberAccess true propHidden
let mdef =
if stat then
mk_static_nongeneric_mdef
(methnm,access,parms,ret,MethodBody_il
(mk_ilmbody(true,[],2,nonbranching_instrs_to_code ([ ldarg_0;mk_normal_stsfld il_fspec]),None)))
else
mk_instance_mdef
(methnm,access,parms,ret,MethodBody_il
(mk_ilmbody(true,[],2,nonbranching_instrs_to_code ([ ldarg_0;I_ldarg 1us;mk_normal_stfld il_fspec]),None)))
yield mdef |> AddSpecialNameFlag
if generateDebugDisplayAttribute then
let (|Lazy|) (x:Lazy<_>) = x.Force()
match (vspec_map_tryfind cenv.g.sprintf_vref.Deref eenv.valsInScope,
vspec_map_tryfind cenv.g.new_format_vref.Deref eenv.valsInScope) with
| Some(Lazy(Method(_,_,sprintf_mspec,_,_,_))), Some(Lazy(Method(_,_,new_format_mspec,_,_,_))) ->
// The type returned by the 'sprintf' call
let funcTy = Pubclo.typ_Func1 cenv.g.ilxPubCloEnv ilty cenv.g.ilg.typ_String
// Give the instantiation of the printf format object, i.e. a Format`5 object compatible with StringFormat<ilty>
let new_format_mspec = mk_mspec(new_format_mspec.MethodRef,AsObject,
[// 'T -> string'
funcTy;
// rest follow from 'StringFormat<T>'
GenType m cenv.g eenv.tyenv cenv.g.unit_ty;
cenv.g.ilg.typ_String;
cenv.g.ilg.typ_String;
cenv.g.ilg.typ_String],[])
// Instantiate with our own type
let sprintf_mspec = mk_mspec(sprintf_mspec.MethodRef,AsObject,[],[funcTy])
// Here's the body of the method. Call printf, then invoke the function it returns
let mdef = mk_instance_mdef (debugDisplayMethodName,MemAccess_assembly,[],
mk_return cenv.g.ilg.typ_Object,
MethodBody_il
(mk_ilmbody
(true,[],2,
nonbranching_instrs_to_code
[ // load the hardwired format string
I_ldstr "%+0.8A";
// make the printf format object
mk_normal_newobj new_format_mspec;
// call sprintf
mk_normal_call sprintf_mspec;
// call the function returned by sprintf
ldarg_0;
mk_IlxInstr (EI_callfunc(Normalcall,Apps_app(ilty, Apps_done cenv.g.ilg.typ_String)));
mk_normal_newobj (mspec_StringBuilder_string cenv.g.ilg) ],
None)))
yield mdef |> AddSpecialNameFlag
| None,_ ->
printfn "sprintf not found"
()
| _,None ->
printfn "new formatnot found"
()
| _ ->
printfn "neither found, or non-method"
()
// Build record constructors and the funky methods that go with records and delegate types.
// Constructors and delegate methods have the same access as the representation
match tyconRepr with
| Some (TRecdRepr _) when not (is_enum_tycon tycon) ->
// No constructor for enum types
// Otherwise find all the non-static, non zero-init fields and build a constructor
let relevantFields =
fieldSummaries
|> List.filter (fun (_,_,_,stat,_,_,_,fspec) -> not stat && not fspec.IsZeroInit)
let takenFieldNames =
relevantFields
|> List.map (fun (_,fldName,_,_,_,propType,_,fspec) -> fldName)
|> Set.of_list
let fieldNamesAndTypes =
relevantFields
|> List.map (fun (_,fldName,_,_,_,propType,_,fspec) -> (fspec.Name,fldName,propType))
let mdef = mk_simple_storage_ctor_with_param_names(None, Some cenv.g.ilg.tspec_Object, ilty.TypeSpec, ChooseParamNames fieldNamesAndTypes, reprAccess)
yield mdef
// FSharp 1.0 bug 1988: Explicitly setting the ComVisible(true) attribute on an F# type causes an F# record to be emitted in a way that enables mutation for COM interop scenarios
if TryFindBoolAttrib cenv.g cenv.g.attrib_ComVisibleAttribute tycon.Attribs = Some(true) then
yield mk_simple_storage_ctor(None, Some cenv.g.ilg.tspec_Object, ilty.TypeSpec, [], reprAccess)
| Some (TFsObjModelRepr r) when tycon.IsFSharpDelegateTycon ->
// Build all the methods that go with a delegate type
match r.fsobjmodel_kind with
| TTyconDelegate ss ->
let p,r =
// When "type delagateTy = delegate of unit -> returnTy",
// suppress the unit arg from delagate .Invoke vslot.
let (TSlotSig(nm,typ,ctps,mtps,paraml,returnTy)) = ss
let paraml =
match paraml with
| [[tsp]] when is_unit_typ cenv.g tsp.Type -> [] (* suppress unit arg *)
| paraml -> paraml
GenActualSlotsig m cenv eenvinner (TSlotSig(nm,typ,ctps,mtps,paraml,returnTy)) []
for mdef in mk_delegate_mdefs cenv.g.ilg (p,r) do
yield { mdef with mdAccess=reprAccess }
| _ ->
()
| _ -> () ]
let methods = methodDefs @ augmentOverrideMethodDefs @ abstractMethodDefs
let properties = mk_properties (propertyDefs @ abstractPropDefs)
let events = mk_events abstractEventDefs
let fields = mk_fdefs fieldDefs
let tdef =
let tdSerializable = (TryFindBoolAttrib cenv.g cenv.g.attrib_AutoSerializableAttribute tycon.Attribs <> Some(false))
match tycon.TypeReprInfo with
| Some (TILObjModelRepr (_,_,td)) ->
{td with tdAccess = access;
tdCustomAttrs = mk_custom_attrs tdCustomAttrs;
tdGenericParams = gparams; }
| Some (TRecdRepr _ | TFsObjModelRepr _ as tyconRepr) ->
let super = super_of_tycon cenv.g tycon
let super_il = GenType m cenv.g eenvinner.tyenv super
// Build a basic type definition
let isObjectType = (match tyconRepr with TFsObjModelRepr _ -> true | _ -> false)
let attrs =
tdCustomAttrs @
[mk_CompilationMappingAttr cenv.g
(if isObjectType
then SourceLevelConstruct_ObjectType
elif hiddenRepr then SourceLevelConstruct_RecordType ||| SourceLevelConstruct_PrivateRepresentation
else SourceLevelConstruct_RecordType )]
let tdef = mk_generic_class(tname,access,gparams,super_il, intfs,mk_mdefs methods,fields,properties,events,mk_custom_attrs attrs)
// Set some the extra entries in the definition
let tcref_is_the_sealed_attribute tcref = tcref_eq cenv.g tcref cenv.g.attrib_SealedAttribute.TyconRef
let tdef = { tdef with tdSealed = is_sealed_typ cenv.g thisTy || tcref_is_the_sealed_attribute tcref;
tdSerializable = tdSerializable;
tdMethodImpls=mk_mimpls methodImpls;
tdAbstract=isAbstract;
tdComInterop=IsComInteropType cenv.g thisTy }
let tdLayout,tdEncoding =
match TryFindAttrib cenv.g cenv.g.attrib_StructLayoutAttribute tycon.Attribs with
| Some (Attrib(_,_,[ AttribInt32Arg(layoutKind) ],namedArgs,m)) ->
let decoder = AttributeDecoder namedArgs
let typePack = decoder.FindInt32 "Pack" 0x0
let typeSize = decoder.FindInt32 "Size" 0x0
let tdEncoding =
match (decoder.FindInt32 "CharSet" 0x0) with
(* enumeration values for System.Runtime.InteropServices.CharSet taken from mscorlib.il *)
| 0x03 -> TypeEncoding_unicode
| 0x04 -> TypeEncoding_autochar
| _ -> TypeEncoding_ansi
let layoutInfo =
if typePack = 0x0 && typeSize = 0x0
then { typeSize=None; typePack=None }
else { typeSize = Some typeSize; typePack = Some (uint16 typePack) }
let tdLayout =
match layoutKind with
(* enumeration values for System.Runtime.InteropServices.LayoutKind taken from mscorlib.il *)
| 0x0 -> TypeLayout_sequential layoutInfo
| 0x2 -> TypeLayout_explicit layoutInfo
| _ -> TypeLayout_auto
tdLayout,tdEncoding
| Some (Attrib(_,_,_,_,m)) ->
errorR(Error("The StructLayout attribute could not be decoded",m));
TypeLayout_auto, TypeEncoding_ansi
| _ when (match tdKind with TypeDef_valuetype -> true | _ -> false) ->
// All structs are sequential by default
// Structs with no instance fields get size 1, pack 0
if tycon.AllFieldsAsList |> List.exists (fun f -> not f.IsStatic) ||
// Reflection emit doesn't let us emit 'pack' and 'size' for generic structs.
// In that case we generate a dummy field instead
(cenv.workAroundReflectionEmitBugs && not tycon.TyparsNoRange.IsEmpty)
then
TypeLayout_sequential { typeSize=None; typePack=None }, TypeEncoding_ansi
else
TypeLayout_sequential { typeSize=Some 1; typePack=Some 0us }, TypeEncoding_ansi
| _ ->
TypeLayout_auto, TypeEncoding_ansi
let tdef = { tdef with tdKind = tdKind; tdLayout=tdLayout; tdEncoding=tdEncoding }
let tdef = match tdKind with TypeDef_interface -> { tdef with tdExtends = None; tdAbstract=true } | _ -> tdef
tdef
| Some (TFiniteUnionRepr _) ->
let alternatives =
tycon.UnionCasesArray |> Array.mapi (fun i ucspec ->
{ altName=ucspec.ucase_il_name;
altFields=GenUnionCaseRef m cenv.g eenvinner.tyenv i ucspec.RecdFieldsArray;
altCustomAttrs= mk_custom_attrs (GenAttrs cenv eenv ucspec.ucase_attribs @ [mk_CompilationMappingAttrWithSeqNum cenv.g SourceLevelConstruct_Alternative i]) })
{ tdName = tname;
tdLayout = TypeLayout_auto;
tdAccess = access;
tdGenericParams = gparams;
tdCustomAttrs =
mk_custom_attrs (tdCustomAttrs @
[mk_CompilationMappingAttr cenv.g
(if hiddenRepr
then SourceLevelConstruct_SumType ||| SourceLevelConstruct_PrivateRepresentation
else SourceLevelConstruct_SumType )]);
tdInitSemantics=TypeInit_beforefield;
tdSealed=true;
tdAbstract=false;
tdKind=
mk_IlxTypeDefKind
(ETypeDef_classunion
{ cudReprAccess=reprAccess;
cudNullPermitted=IsUnionTypeWithNullAsTrueValue cenv.g tycon;
cudHelpersAccess=reprAccess;
cudHelpers=
(not (tcref_eq cenv.g tcref cenv.g.unit_tcr_canon) &&
match TryFindAttrib cenv.g cenv.g.attrib_DefaultAugmentationAttribute tycon.Attribs with
| Some(Attrib(_,_,[ AttribBoolArg (b) ],_,_)) -> b
| Some (Attrib(_,_,_,_,m)) ->
errorR(Error("The DefaultAugmentation attribute could not be decoded",m));
true
| _ ->
true) (* not hiddenRepr *)
cudDebugProxies= generateDebugProxies;
cudDebugDisplayAttributes= ilDebugDisplayAttributes;
cudAlternatives= alternatives;
cudWhere = None});
tdFieldDefs = fields;
tdEvents= events;
tdProperties = properties;
tdMethodDefs= mk_mdefs methods;
tdMethodImpls= mk_mimpls methodImpls;
tdComInterop=false;
tdSerializable= tdSerializable;
tdSpecialName= false;
tdNested=mk_tdefs [];
tdEncoding= TypeEncoding_autochar;
tdImplements= intfs;
tdExtends= Some cenv.g.ilg.typ_Object;
tdSecurityDecls= mk_security_decls [];
tdHasSecurity=false; }
| _ -> failwith "??"
mgbuf.AddTypeDef(tref,tdef);
// If a type has a .cctor, then the outer .cctor must be run before the inner .cctor
if methods |> List.exists (fun md -> md.Name = ".cctor" &&
Option.for_all CheckCodeDoesSomething md.Code) then
GenForceOuterInitializationAsPartOfCCtor cenv mgbuf lazyInitInfo tref m
/// Generate the type for an F# exception declaration.
and GenExnDef cenv mgbuf eenv m (exnc:Tycon) =
let exncref = mk_local_ecref exnc
match exnc.ExceptionInfo with
| TExnAbbrevRepr _ | TExnAsmRepr _ | TExnNone -> ()
| TExnFresh _ ->
let ilty = GenExnType m cenv.g eenv.tyenv exncref
let tref = ilty.TypeRef
let reprAccess = ComputeMemberAccess true (IsHiddenTyconRepr eenv.sigToImplRemapInfo exnc)
let isHidden = IsHiddenTycon eenv.sigToImplRemapInfo exnc
let access = ComputeTypeAccess tref isHidden
let reprAccess = ComputeMemberAccess true isHidden
let fspecs = exnc.TrueInstanceFieldsAsList
let propMethodDefs,fieldDefs,propertyDefs,fieldNamesAndTypes =
[ for i,fld in markup fspecs do
let propName = fld.Name
let propType = GenType m cenv.g eenv.tyenv fld.FormalType
let methnm = "get_"^fld.Name
let fldName = GenFieldName exnc fld
let mdef = mk_ldfld_method_def (methnm,reprAccess,false,ilty,fldName,propType)
let fieldDef = IL.mk_instance_fdef(fldName,propType, None, MemAccess_assembly)
let ilPropertyDef =
let cc = ILCallingConv.Instance
{ propName=propName;
propRTSpecialName=false;
propSpecialName=false;
propSet=None;
propGet=Some(mk_mref(tref,cc,methnm,0,[],propType));
propCallconv=CC_instance;
propType=propType;
propInit=None;
propArgs=[];
propCustomAttrs=mk_custom_attrs (GenAttrs cenv eenv fld.PropertyAttribs @ [mk_CompilationMappingAttrWithSeqNum cenv.g SourceLevelConstruct_Field i]); }
yield (mdef,fieldDef,ilPropertyDef,(propName,fldName,propType)) ]
|> List.unzip4
let ctorMethodDef =
mk_simple_storage_ctor_with_param_names(None, Some cenv.g.ilg.tspec_Exception, ilty.TypeSpec, ChooseParamNames fieldNamesAndTypes, reprAccess)
// In compiled code, all exception types get a parameterless constructor for use with XML serialization
// This does default-initialization of all fields
let ctorMethodDefNoArgs =
if nonNil fieldNamesAndTypes then
[ mk_simple_storage_ctor(None, Some cenv.g.ilg.tspec_Exception, ilty.TypeSpec, [], reprAccess) ]
else
[]
let ctorMethodDefForSerialization =
mk_ctor(MemAccess_family,
[mk_named_param("info",cenv.g.ilg.typ_SerializationInfo);mk_named_param("context",cenv.g.ilg.typ_StreamingContext)],
mk_impl
(false,[],8,
nonbranching_instrs_to_code
[ ldarg_0;
I_ldarg 1us;
I_ldarg 2us;
mk_normal_call (mk_ctor_mspec_for_boxed_tspec (cenv.g.ilg.tspec_Exception,[cenv.g.ilg.typ_SerializationInfo;cenv.g.ilg.typ_StreamingContext])) ]
(*
preblock @
begin
List.concat (List.mapi (fun n (pnm,nm,ty) ->
[ ldarg_0;
I_ldarg (uint16 (n+1));
mk_normal_stfld (mk_fspec_in_boxed_tspec (tspec,nm,ty));
]) flds)
end
*)
,None))
let getObjectDataMethodForSerialization =
let mdef =
mk_virtual_mdef
("GetObjectData",MemAccess_public,
[mk_named_param ("info",cenv.g.ilg.typ_SerializationInfo);mk_named_param("context",cenv.g.ilg.typ_StreamingContext)],
mk_return Type_void,
(let code =
nonbranching_instrs_to_code
[ ldarg_0;
I_ldarg 1us;
I_ldarg 2us;
mk_normal_call (mk_nongeneric_instance_mspec_in_typ (cenv.g.ilg.typ_Exception, "GetObjectData", [cenv.g.ilg.typ_SerializationInfo;cenv.g.ilg.typ_StreamingContext], Type_void))
]
MethodBody_il(mk_ilmbody(true,[],8,code, None))))
// Here we must encode: [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
// In ILDASM this is: .permissionset demand = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SerializationFormatter' = bool(true)}}
{ mdef with mdSecurityDecls=mk_security_decls [ mk_permission_set cenv.g.ilg (SecAction_demand,[(cenv.g.ilg.tref_SecurityPermissionAttribute,[("SerializationFormatter",cenv.g.ilg.typ_Bool, CustomElem_bool(true))])])];
mdHasSecurity=true }
let tname = tref.Name
let compareMethodDefs =
(*if isSome exnc.TypeContents.tcaug_compare
then [ GenCompareOverride cenv mgbuf eenv m cenv.g.exn_ty (ilty,cenv.g.ilg.typ_Exception) ]
else*)
[]
let hashMethodDefs =
if isSome exnc.TypeContents.tcaug_hash_and_equals_withc && not exnc.TypeContents.tcaug_hasObjectGetHashCode
then [ GenHashOverride cenv mgbuf eenv m exncref ilty ]
else []
let interfaces = List.map (p13 >> GenType m cenv.g eenv.tyenv) exnc.TypeContents.tcaug_implements
let tdef =
mk_generic_class
(tname,access,[],cenv.g.ilg.typ_Exception,
interfaces,
#if BE_SECURITY_TRANSPARENT
mk_mdefs ([ctorMethodDef] @ compareMethodDefs @ ctorMethodDefNoArgs @ [ ctorMethodDefForSerialization ] @ propMethodDefs @ hashMethodDefs),
#else
mk_mdefs ([ctorMethodDef] @ compareMethodDefs @ ctorMethodDefNoArgs @ [ getObjectDataMethodForSerialization; ctorMethodDefForSerialization ] @ propMethodDefs @ hashMethodDefs),
#endif
mk_fdefs fieldDefs,
mk_properties propertyDefs,
mk_events [],
mk_custom_attrs [mk_CompilationMappingAttr cenv.g SourceLevelConstruct_Exception])
let tdef = { tdef with tdSerializable = true }
if verbose then dprintf "GenExnDef: writing results\n";
mgbuf.AddTypeDef(tref,tdef)
and CodegenAssembly cenv eenv mgbuf fileImpls =
if List.length fileImpls > 0 then
let a,b = List.frontAndBack fileImpls
let eenv = List.fold (GenTopImpl cenv mgbuf None) eenv a
let eenv = GenTopImpl cenv mgbuf cenv.mainMethodInfo eenv b
()
//-------------------------------------------------------------------------
// When generating a module we just write into mutable
// structures representing the contents of the module.
//-------------------------------------------------------------------------
let GetEmptyIlxGenEnv ccu =
let thisCompLoc = CompLocForCcu ccu
{ tyenv=empty_tyenv;
cloc = thisCompLoc;
valsInScope=vspec_map_empty();
someTspecInThisModule=ecmaILGlobals.tspec_Object; (* dummy value *)
letBoundVars=[];
liveLocals=Imap.empty();
innerVals = [];
sigToImplRemapInfo = []; (* "module remap info" *)
withinSEH = false;}
type CodegenResults =
{ ilTypeDefs: ILTypeDef list;
ilAssemAttrs : ILAttribute list;
ilNetModuleAttrs: ILAttribute list;
quotationResourceBytes: byte[] list }
let GenerateCode cenv eenv (TAssembly fileImpls) (assemA,moduleA) =
(* Generate the implementations into the mgbuf *)
let mgbuf= new AssemblyBuilder(cenv)
let eenv = { eenv with cloc = CompLocForFragment cenv.fragName cenv.viewCcu }
GenTypeDefForCompLoc cenv eenv mgbuf (CompLocForPrivateImplementationDetails eenv.cloc) true [];
CodegenAssembly cenv eenv mgbuf fileImpls;
let ilAssemAttrs = GenAttrs cenv eenv assemA
let tdefs,reflectedDefinitions = mgbuf.Close()
let quotationResourceBytes =
match reflectedDefinitions with
| [] -> []
| _ ->
if verbose then dprintf "creating quotation resource";
let defnsResource =
reflectedDefinitions |> List.choose (fun (v,e) ->
try
let ety = type_of_expr cenv.g e
let tps,taue,tauty =
match e with
| TExpr_tlambda (_,tps,b,_,_,_) -> tps,b,reduce_forall_typ cenv.g ety (List.map mk_typar_ty tps)
| _ -> [],e,ety
let env = Creflect.BindTypars Creflect.empty_env tps
let freeTypes,argExprs, astExpr = Creflect.ConvExprPublic (cenv.g,cenv.amap,cenv.viewCcu) env taue
let m = range_of_expr e
if nonNil(freeTypes) then error(InternalError("A free type variable was detected in a reflected definition",m));
if nonNil(argExprs) then error(Error("Reflected definitions may not contain uses of the prefix splice operator '%'",m));
let crenv = Creflect.mk_cenv (cenv.g,cenv.amap,cenv.viewCcu)
let mbaseR = Creflect.ConvMethodBase crenv env v
Some(mbaseR,astExpr)
with
| Creflect.InvalidQuotedTerm e -> warning(e); None)
|> Sreflect.PickleDefns
[ defnsResource ]
let ilNetModuleAttrs = GenAttrs cenv eenv moduleA
if verbose then dprintf "codegen complete";
{ ilTypeDefs= tdefs;
ilAssemAttrs = ilAssemAttrs;
ilNetModuleAttrs = ilNetModuleAttrs;
quotationResourceBytes = quotationResourceBytes }
//-------------------------------------------------------------------------
// For printing values in fsi we want to lookup the value of given vrefs.
// The storage in the eenv says if the vref is stored in a static field.
// If we know how/where the field was generated, then we can lookup via reflection.
//-------------------------------------------------------------------------
open System
open System.Reflection
let lookupGeneratedValue ((lookupFieldRef : ILFieldRef -> FieldInfo),
(lookupILMethodRef : ILMethodRef -> MethodInfo),
(lookupTypeRef : ILTypeRef -> Type),
(lookupType : ILType -> Type)) g eenv (v:Val) =
// Top-level val bindings are stored (for example) in static fields.
// In the FSI case, these fields are be created and initialised, so we can recover the object.
// Ilxgen knows how v was stored, and then ilreflect knows how this storage was generated.
// Ilxgen converts (v:Tast.Val) to AbsIL datatstructures.
// Ilreflect converts from AbsIL datatstructures to emitted Type, FieldInfo, MethodInfo etc.
//------
// The lookup* functions are the conversions available from ilreflect.
try
// Convert the v.Type into a System.Type according to ilxgen and ilreflect.
let objTyp =
let il_ty = GenType range0 g empty_tyenv v.Type (* empty_tyenv ok, not expecting typars *)
lookupType il_ty
// Lookup the compiled v value (as an object).
// ASIDE: this code like an "immediate" form of GenGetStorageAndSequel
let storage = storage_for_val range0 v eenv
match storage with
| StaticField (fspec,vref,hasLiteralAttr,ilTypeSpecForProperty,fieldName,_,_,ilGetterMethRef,_,_) ->
let obj =
if hasLiteralAttr then
let staticTyp = lookupTypeRef fspec.EnclosingTypeRef
// Checked: This FieldInfo (FieldBuilder) supports GetValue().
staticTyp.GetField(fieldName).GetValue(null:obj)
else
let staticTyp = lookupTypeRef ilTypeSpecForProperty.TypeRef
// Unfortunately we can not call .Invoke on the ILMethodRef's MethodInfo,
// because it is the MethodBuilder and that does not support .Invoke...
// Rather, we look for the getter MethodInfo from the built type and .Invoke on that.
if ilGetterMethRef.ArgCount <> 0 then
failwith "Expected ilGetterMethRef to have no arguments" (* immediately caught below! *)
let methInfo = staticTyp.GetMethod(ilGetterMethRef.Name,[||])
methInfo.Invoke((null:obj),(null:obj[]))
Some (obj,objTyp)
| Null ->
Some (null,objTyp)
| Local _ -> None
| Method _ -> None
| Unrealized -> None
| Arg _ -> None
| Env _ -> None
with
e ->
#if DEBUG
printf "ilxGen.lookupGeneratedValue for v=%s caught exception:\n%A\n\n" v.MangledName e
#endif
None
let lookupGeneratedInfo ((lookupFieldRef : ILFieldRef -> FieldInfo),
(lookupILMethodRef : ILMethodRef -> MethodInfo),
(lookupTypeRef : ILTypeRef -> Type),
(lookupType : ILType -> Type)) g eenv (v:Val) =
try
// Convert the v.Type into a System.Type according to ilxgen and ilreflect.
let objTyp =
let il_ty = GenType range0 g empty_tyenv v.Type (* empty_tyenv ok, not expecting typars *)
lookupType il_ty
// Lookup the compiled v value (as an object).
let storage = storage_for_val range0 v eenv
match storage with
| StaticField (fspec,vref,hasLiteralAttr,ilTypeSpecForProperty,fieldName,_,_,ilGetterMethRef,_,_) ->
let staticTyp = lookupTypeRef ilTypeSpecForProperty.TypeRef
if hasLiteralAttr then
Some (staticTyp.GetField(fieldName) :> MemberInfo)
else
Some (staticTyp.GetMethod(ilGetterMethRef.Name,[||]) :> MemberInfo)
| Null -> None
| Local _ -> None
| Method _ -> None
| Unrealized -> None
| Arg _ -> None
| Env _ -> None
with
e ->
#if DEBUG
printf "ilxGen.lookupGenertedInfo for v=%s caught exception:\n%A\n\n" v.MangledName e
#endif
None