Skip to content

Commit 858eddf

Browse files
authored
Merge pull request #1270 from dsyme/fixed
F# RFC FS-1015 - Support for "fixed"
2 parents 4e562f3 + 8aefe6c commit 858eddf

File tree

26 files changed

+1461
-178
lines changed

26 files changed

+1461
-178
lines changed

src/absil/ilwrite.fs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,9 @@ and GetTypeAsTypeDefOrRef cenv env (ty:ILType) =
849849

850850
and GetTypeAsBytes cenv env ty = emitBytesViaBuffer (fun bb -> EmitType cenv env bb ty)
851851

852+
and GetTypeOfLocalAsBytes cenv env (l: ILLocal) =
853+
emitBytesViaBuffer (fun bb -> EmitLocalInfo cenv env bb l)
854+
852855
and GetTypeAsBlobIdx cenv env (ty:ILType) =
853856
GetBytesAsBlobIdx cenv (GetTypeAsBytes cenv env ty)
854857

@@ -912,6 +915,11 @@ and EmitType cenv env bb ty =
912915
EmitType cenv env bb ty
913916
| _ -> failwith "EmitType"
914917

918+
and EmitLocalInfo cenv env (bb:ByteBuffer) (l:ILLocal) =
919+
if l.IsPinned then
920+
bb.EmitByte et_PINNED
921+
EmitType cenv env bb l.Type
922+
915923
and EmitCallsig cenv env bb (callconv,args:ILTypes,ret,varargs:ILVarArgs,genarity) =
916924
bb.EmitByte (callconvToByte genarity callconv)
917925
if genarity > 0 then bb.EmitZ32 genarity
@@ -1494,7 +1502,7 @@ let GetCallsigAsStandAloneSigIdx cenv env info =
14941502
let EmitLocalSig cenv env (bb: ByteBuffer) (locals: ILLocals) =
14951503
bb.EmitByte e_IMAGE_CEE_CS_CALLCONV_LOCAL_SIG
14961504
bb.EmitZ32 locals.Length
1497-
locals |> ILList.iter (fun l -> EmitType cenv env bb l.Type)
1505+
locals |> ILList.iter (EmitLocalInfo cenv env bb)
14981506

14991507
let GetLocalSigAsBlobHeapIdx cenv env locals =
15001508
GetBytesAsBlobIdx cenv (emitBytesViaBuffer (fun bb -> EmitLocalSig cenv env bb locals))
@@ -2237,7 +2245,7 @@ let GenILMethodBody mname cenv env (il: ILMethodBody) =
22372245
// Write a fake entry for the local signature headed by e_IMAGE_CEE_CS_CALLCONV_FIELD. This is referenced by the PDB file
22382246
ignore (FindOrAddSharedRow cenv TableNames.StandAloneSig (SharedRow [| Blob (GetFieldDefTypeAsBlobIdx cenv env l.Type) |]))
22392247
// Now write the type
2240-
GetTypeAsBytes cenv env l.Type)
2248+
GetTypeOfLocalAsBytes cenv env l)
22412249
else
22422250
[| |]
22432251

src/fsharp/FSComp.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,3 +1313,5 @@ estApplyStaticArgumentsForMethodNotImplemented,"A type provider implemented GetS
13131313
3204,tcStructUnionMultiCase,"A union type which is a struct must have only one case."
13141314
3205,tcUseMayNotBeMutable,"This feature is deprecated. A 'use' binding may not be marked 'mutable'."
13151315
3206,CallerMemberNameIsOverriden,"The CallerMemberNameAttribute applied to parameter '%s' will have no effect. It is overridden by the CallerFilePathAttribute."
1316+
3207,tcFixedNotAllowed,"Invalid use of 'fixed'. 'fixed' may only be used in a declaration of the form 'use x = fixed expr' where the expression is an array, the address of a field, the address of an array element or a string'"
1317+
3208,tcCouldNotFindOffsetToStringData,"Could not find method System.Runtime.CompilerServices.OffsetToStringData in references when building 'fixed' expression."

src/fsharp/IlxGen.fs

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1278,7 +1278,7 @@ type CodeGenBuffer(m:range,
12781278
alreadyUsedLocals:int,
12791279
zapFirstSeqPointToStart:bool) =
12801280

1281-
let locals = new ResizeArray<((string * (Mark * Mark)) list * ILType)>(10)
1281+
let locals = new ResizeArray<((string * (Mark * Mark)) list * ILType * bool)>(10)
12821282
let codebuf = new ResizeArray<ILInstr>(200)
12831283
let exnSpecs = new ResizeArray<ILExceptionSpec>(10)
12841284

@@ -1417,20 +1417,20 @@ type CodeGenBuffer(m:range,
14171417
member cgbuf.MethodName = methodName
14181418
member cgbuf.PreallocatedArgCount = alreadyUsedArgs
14191419

1420-
member cgbuf.AllocLocal(ranges,ty) =
1420+
member cgbuf.AllocLocal(ranges,ty,isFixed) =
14211421
let j = locals.Count
1422-
locals.Add((ranges,ty));
1422+
locals.Add((ranges,ty,isFixed));
14231423
j
14241424

1425-
member cgbuf.ReallocLocal(cond,ranges,ty) =
1425+
member cgbuf.ReallocLocal(cond,ranges,ty,isFixed) =
14261426
let j =
14271427
match ResizeArray.tryFindIndexi cond locals with
14281428
| Some j ->
1429-
let (prevRanges,_) = locals.[j]
1430-
locals.[j] <- ((ranges@prevRanges),ty);
1429+
let (prevRanges,_,isFixed) = locals.[j]
1430+
locals.[j] <- ((ranges@prevRanges),ty,isFixed);
14311431
j
14321432
| None ->
1433-
cgbuf.AllocLocal(ranges,ty)
1433+
cgbuf.AllocLocal(ranges,ty,isFixed)
14341434
let j = j + alreadyUsedLocals
14351435
j
14361436

@@ -1562,15 +1562,16 @@ let CodeGenThen cenv mgbuf (zapFirstSeqPointToStart,entryPointInfo,methodName,ee
15621562

15631563
let localDebugSpecs : ILLocalDebugInfo list =
15641564
locals
1565-
|> List.mapi (fun i (nms,_) -> List.map (fun nm -> (i,nm)) nms)
1565+
|> List.mapi (fun i (nms,_,_isFixed) -> List.map (fun nm -> (i,nm)) nms)
15661566
|> List.concat
15671567
|> List.map (fun (i,(nm,(start,finish))) ->
15681568
{ Range=(start.CodeLabel, finish.CodeLabel);
15691569
DebugMappings= [{ LocalIndex=i; LocalName=nm }] })
15701570

15711571
let ilLocals =
15721572
locals
1573-
|> List.map (fun (infos, ty) ->
1573+
|> List.map (fun (infos, ty, isFixed) ->
1574+
let loc =
15741575
// in interactive environment, attach name and range info to locals to improve debug experience
15751576
if cenv.opts.isInteractive && cenv.opts.generateDebugSymbols then
15761577
match infos with
@@ -1580,7 +1581,8 @@ let CodeGenThen cenv mgbuf (zapFirstSeqPointToStart,entryPointInfo,methodName,ee
15801581
| [] -> mkILLocal ty None
15811582
// if not interactive, don't bother adding this info
15821583
else
1583-
mkILLocal ty None)
1584+
mkILLocal ty None
1585+
if isFixed then { loc with IsPinned=true } else loc)
15841586

15851587
(ilLocals,
15861588
maxStack,
@@ -2178,7 +2180,7 @@ and UnionCodeGen (cgbuf: CodeGenBuffer) =
21782180
{ new EraseUnions.ICodeGen<Mark> with
21792181
member __.CodeLabel(m) = m.CodeLabel
21802182
member __.GenerateDelayMark() = CG.GenerateDelayMark cgbuf "unionCodeGenMark"
2181-
member __.GenLocal(ilty) = cgbuf.AllocLocal([],ilty) |> uint16
2183+
member __.GenLocal(ilty) = cgbuf.AllocLocal([],ilty,false) |> uint16
21822184
member __.SetMarkToHere(m) = CG.SetMarkToHere cgbuf m
21832185
member __.EmitInstr x = CG.EmitInstr cgbuf (pop 0) (Push []) x
21842186
member __.EmitInstrs xs = CG.EmitInstrs cgbuf (pop 0) (Push []) xs }
@@ -2519,7 +2521,7 @@ and GenApp cenv cgbuf eenv (f,fty,tyargs,args,m) sequel =
25192521
// Only save arguments that have effects
25202522
if Optimizer.ExprHasEffect cenv.g laterArg then
25212523
let ilTy = laterArg |> tyOfExpr cenv.g |> GenType cenv.amap m cenv.g eenv.tyenv
2522-
let loc,eenv = AllocLocal cenv cgbuf eenv true (ilxgenGlobalNng.FreshCompilerGeneratedName ("arg",m), ilTy) scopeMarks
2524+
let loc,eenv = AllocLocal cenv cgbuf eenv true (ilxgenGlobalNng.FreshCompilerGeneratedName ("arg",m), ilTy, false) scopeMarks
25232525
GenExpr cenv cgbuf eenv SPSuppress laterArg Continue
25242526
EmitSetLocal cgbuf loc
25252527
Choice1Of2 (ilTy,loc),eenv
@@ -2652,7 +2654,7 @@ and GenIndirectCall cenv cgbuf eenv (functy,tyargs,args,m) sequel =
26522654
CountCallFuncInstructions();
26532655

26542656
// Generate the code code an ILX callfunc operation
2655-
let instrs = EraseClosures.mkCallFunc cenv.g.ilxPubCloEnv (fun ty -> cgbuf.AllocLocal([], ty) |> uint16) eenv.tyenv.Count isTailCall ilxClosureApps
2657+
let instrs = EraseClosures.mkCallFunc cenv.g.ilxPubCloEnv (fun ty -> cgbuf.AllocLocal([], ty,false) |> uint16) eenv.tyenv.Count isTailCall ilxClosureApps
26562658
CG.EmitInstrs cgbuf (pop (1+args.Length)) (Push [ilActualRetTy]) instrs;
26572659

26582660
// Done compiling indirect call...
@@ -2675,7 +2677,7 @@ and GenTry cenv cgbuf eenv scopeMarks (e1,m,resty,spTry) =
26752677
let afterHandler = CG.GenerateDelayMark cgbuf "afterHandler"
26762678
let eenvinner = {eenvinner with withinSEH = true}
26772679
let ilResultTy = GenType cenv.amap m cenv.g eenvinner.tyenv resty
2678-
let whereToSave,eenvinner = AllocLocal cenv cgbuf eenvinner true (ilxgenGlobalNng.FreshCompilerGeneratedName ("tryres",m),ilResultTy) (startTryMark,endTryMark)
2680+
let whereToSave,eenvinner = AllocLocal cenv cgbuf eenvinner true (ilxgenGlobalNng.FreshCompilerGeneratedName ("tryres",m),ilResultTy, false) (startTryMark,endTryMark)
26792681

26802682
// Generate the body of the try. In the normal case (SequencePointAtTry) we generate a sequence point
26812683
// both on the 'try' keyword and on the start of the expression in the 'try'. For inlined code and
@@ -2835,7 +2837,7 @@ and GenForLoop cenv cgbuf eenv (spFor,v,e1,dir,e2,loopBody,m) sequel =
28352837

28362838
let finishIdx,eenvinner =
28372839
if isFSharpStyle then
2838-
let v,eenvinner = AllocLocal cenv cgbuf eenvinner true (ilxgenGlobalNng.FreshCompilerGeneratedName ("endLoop",m), cenv.g.ilg.typ_int32) (start,finish)
2840+
let v,eenvinner = AllocLocal cenv cgbuf eenvinner true (ilxgenGlobalNng.FreshCompilerGeneratedName ("endLoop",m), cenv.g.ilg.typ_int32, false) (start,finish)
28392841
v, eenvinner
28402842
else
28412843
-1,eenvinner
@@ -3268,7 +3270,7 @@ and GenDefaultValue cenv cgbuf eenv (ty,m) =
32683270
| _ ->
32693271
let ilTy = GenType cenv.amap m cenv.g eenv.tyenv ty
32703272
LocalScope "ilzero" cgbuf (fun scopeMarks ->
3271-
let locIdx, _ = AllocLocal cenv cgbuf eenv true (ilxgenGlobalNng.FreshCompilerGeneratedName ("default",m), ilTy) scopeMarks
3273+
let locIdx, _ = AllocLocal cenv cgbuf eenv true (ilxgenGlobalNng.FreshCompilerGeneratedName ("default",m), ilTy, false) scopeMarks
32723274
// "initobj" (Generated by EmitInitLocal) doesn't work on byref types
32733275
// But ilzero(&ty) only gets generated in the built-in get-address function so
32743276
// we can just rely on zeroinit of all IL locals.
@@ -5411,14 +5413,14 @@ and GenStoreVal cgbuf eenv m (vspec:Val) =
54115413
// Allocate locals for values
54125414
//--------------------------------------------------------------------------
54135415

5414-
and AllocLocal cenv cgbuf eenv compgen (v,ty) (scopeMarks: Mark * Mark) =
5416+
and AllocLocal cenv cgbuf eenv compgen (v,ty,isFixed) (scopeMarks: Mark * Mark) =
54155417
// The debug range for the local
54165418
let ranges = if compgen then [] else [(v,scopeMarks)]
54175419
// Get an index for the local
54185420
let j =
54195421
if cenv.opts.localOptimizationsAreOn
5420-
then cgbuf.ReallocLocal((fun i (_,ty') -> not (IntMap.mem i eenv.liveLocals) && (ty = ty')),ranges,ty)
5421-
else cgbuf.AllocLocal(ranges,ty)
5422+
then cgbuf.ReallocLocal((fun i (_,ty',isFixed') -> not isFixed' && not isFixed && not (IntMap.mem i eenv.liveLocals) && (ty = ty')),ranges,ty,isFixed)
5423+
else cgbuf.AllocLocal(ranges,ty,isFixed)
54225424
j, { eenv with liveLocals = IntMap.add j () eenv.liveLocals }
54235425

54245426
and AllocLocalVal cenv cgbuf v eenv repr scopeMarks =
@@ -5434,11 +5436,11 @@ and AllocLocalVal cenv cgbuf v eenv repr scopeMarks =
54345436
let cloinfo,_,_ = GetIlxClosureInfo cenv v.Range true None eenvinner (Option.get repr)
54355437
cloinfo
54365438

5437-
let idx,eenv = AllocLocal cenv cgbuf eenv v.IsCompilerGenerated (v.CompiledName, cenv.g.ilg.typ_Object) scopeMarks
5439+
let idx,eenv = AllocLocal cenv cgbuf eenv v.IsCompilerGenerated (v.CompiledName, cenv.g.ilg.typ_Object, false) scopeMarks
54385440
Local (idx,Some(ref (NamedLocalIlxClosureInfoGenerator cloinfoGenerate))),eenv
54395441
else
54405442
(* normal local *)
5441-
let idx,eenv = AllocLocal cenv cgbuf eenv v.IsCompilerGenerated (v.CompiledName, GenTypeOfVal cenv eenv v) scopeMarks
5443+
let idx,eenv = AllocLocal cenv cgbuf eenv v.IsCompilerGenerated (v.CompiledName, GenTypeOfVal cenv eenv v, v.IsFixed) scopeMarks
54425444
Local (idx,None),eenv
54435445
let eenv = AddStorageForVal cenv.g (v,notlazy repr) eenv
54445446
Some repr, eenv
@@ -5508,7 +5510,7 @@ and AllocTopValWithinExpr cenv cgbuf cloc scopeMarks v eenv =
55085510
/// - and because IL requires empty stack following a forward br (jump).
55095511
and EmitSaveStack cenv cgbuf eenv m scopeMarks =
55105512
let savedStack = (cgbuf.GetCurrentStack())
5511-
let savedStackLocals,eenvinner = List.mapFold (fun eenv ty -> AllocLocal cenv cgbuf eenv true (ilxgenGlobalNng.FreshCompilerGeneratedName ("spill",m), ty) scopeMarks) eenv savedStack
5513+
let savedStackLocals,eenvinner = List.mapFold (fun eenv ty -> AllocLocal cenv cgbuf eenv true (ilxgenGlobalNng.FreshCompilerGeneratedName ("spill",m), ty, false) scopeMarks) eenv savedStack
55125514
List.iter (EmitSetLocal cgbuf) savedStackLocals;
55135515
cgbuf.AssertEmptyStack();
55145516
(savedStack,savedStackLocals),eenvinner (* need to return, it marks locals "live" *)

src/fsharp/Optimizer.fs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,7 @@ let BindExternalLocalVal cenv (v:Val) vval env =
431431
CheckInlineValueIsComplete v vval;
432432
#endif
433433

434-
if verboseOptimizationInfo then dprintn ("*** Binding "^v.LogicalName);
434+
if verboseOptimizationInfo then dprintn ("*** Binding "+v.LogicalName);
435435
let vval = if v.IsMutable then {vval with ValExprInfo=UnknownValue } else vval
436436
let env =
437437
#if CHECKED
@@ -534,7 +534,7 @@ let TryGetInfoForEntity sv n =
534534
| Some info -> Some (info.Force())
535535
| None ->
536536
if verboseOptimizationInfo then
537-
dprintn ("\n\n*** Optimization info for submodule "^n^" not found in parent module which contains submodules: "^String.concat "," (NameMap.domainL sv.ModuleOrNamespaceInfos));
537+
dprintn ("\n\n*** Optimization info for submodule "+n+" not found in parent module which contains submodules: "+String.concat "," (NameMap.domainL sv.ModuleOrNamespaceInfos));
538538
None
539539

540540
let rec TryGetInfoForPath sv (p:_[]) i =
@@ -558,7 +558,7 @@ let GetInfoForNonLocalVal cenv env (vref:ValRef) =
558558
match structInfo.ValInfos.TryFind(vref) with
559559
| Some ninfo -> snd ninfo
560560
| None ->
561-
//dprintn ("\n\n*** Optimization info for value "^n^" from module "^(full_name_of_nlpath smv)^" not found, module contains values: "^String.concat "," (NameMap.domainL structInfo.ValInfos));
561+
//dprintn ("\n\n*** Optimization info for value "+n+" from module "+(full_name_of_nlpath smv)+" not found, module contains values: "+String.concat "," (NameMap.domainL structInfo.ValInfos));
562562
//System.Diagnostics.Debug.Assert(false,sprintf "Break for module %s, value %s" (full_name_of_nlpath smv) n)
563563
if cenv.g.compilingFslib then
564564
match structInfo.ValInfos.TryFindForFslib(vref) with
@@ -1254,6 +1254,7 @@ let ValueIsUsedOrHasEffect cenv fvs (b:Binding,binfo) =
12541254
not (cenv.settings.EliminateUnusedBindings()) ||
12551255
isSome v.MemberInfo ||
12561256
binfo.HasEffect ||
1257+
v.IsFixed ||
12571258
Zset.contains v (fvs())
12581259

12591260
let rec SplitValuesByIsUsedOrHasEffect cenv fvs x =
@@ -1345,6 +1346,7 @@ let TryEliminateBinding cenv _env (TBind(vspec1,e1,spBind)) e2 _m =
13451346
if not (cenv.optimizing && cenv.settings.EliminateImmediatelyConsumedLocals()) &&
13461347
not vspec1.IsCompilerGenerated then
13471348
None
1349+
elif vspec1.IsFixed then None
13481350
else
13491351
// Peephole on immediate consumption of single bindings, e.g. "let x = e in x" --> "e"
13501352
// REVIEW: enhance this by general elimination of bindings to
@@ -1454,7 +1456,7 @@ let ExpandStructuralBindingRaw cenv expr =
14541456
else
14551457
let argTys = destTupleTy cenv.g v.Type
14561458
let argBind i (arg:Expr) argTy =
1457-
let name = v.LogicalName ^ "_" ^ string i
1459+
let name = v.LogicalName + "_" + string i
14581460
let v,ve = mkCompGenLocal arg.Range name argTy
14591461
ve,mkCompGenBind v arg
14601462

@@ -2834,7 +2836,8 @@ and ComputeSplitToMethodCondition flag threshold cenv env (e,einfo) =
28342836
// None of them should be local polymorphic constrained values
28352837
not (IsGenericValWithGenericContraints cenv.g v) &&
28362838
// None of them should be mutable
2837-
not v.IsMutable))))
2839+
not v.IsMutable)))) &&
2840+
not (isByrefLikeTy cenv.g (tyOfExpr cenv.g e))
28382841

28392842
and ConsiderSplitToMethod flag threshold cenv env (e,einfo) =
28402843
if ComputeSplitToMethodCondition flag threshold cenv env (e,einfo) then
@@ -2843,7 +2846,7 @@ and ConsiderSplitToMethod flag threshold cenv env (e,einfo) =
28432846
let ty = tyOfExpr cenv.g e
28442847
let nm =
28452848
match env.latestBoundId with
2846-
| Some id -> id.idText^suffixForVariablesThatMayNotBeEliminated
2849+
| Some id -> id.idText+suffixForVariablesThatMayNotBeEliminated
28472850
| None -> suffixForVariablesThatMayNotBeEliminated
28482851
let fv,fe = mkCompGenLocal m nm (cenv.g.unit_ty --> ty)
28492852
mkInvisibleLet m fv (mkLambda m uv (e,ty))
@@ -3063,7 +3066,7 @@ and OptimizeBinding cenv isRec env (TBind(v,e,spBind)) =
30633066
then {einfo with Info=UnknownValue}
30643067
else einfo
30653068
if v.MustInline && IsPartialExprVal einfo.Info then
3066-
errorR(InternalError("the mustinline value '"^v.LogicalName^"' was not inferred to have a known value",v.Range));
3069+
errorR(InternalError("the mustinline value '"+v.LogicalName+"' was not inferred to have a known value",v.Range));
30673070
#if DEBUG
30683071
if verboseOptimizations then dprintf "val %s gets opt info %s\n" (showL(valL v)) (showL(exprValueInfoL cenv.g einfo.Info));
30693072
#endif

0 commit comments

Comments
 (0)