Skip to content

Commit 6a04e7b

Browse files
committed
Optimizer: don't inline named functions in debug builds
1 parent 1775f42 commit 6a04e7b

File tree

13 files changed

+516
-12
lines changed

13 files changed

+516
-12
lines changed

src/Compiler/CodeGen/IlxGen.fs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ type cenv =
312312
/// Guard the stack and move to a new one if necessary
313313
mutable stackGuard: StackGuard
314314

315+
emittedSpecializedInlineVals: HashSet<Stamp>
315316
}
316317

317318
member cenv.options =
@@ -8567,6 +8568,8 @@ and GenBindingAfterDebugPoint cenv cgbuf eenv bind isStateVar startMarkOpt =
85678568
GenExpr cenv cgbuf eenv cctorBody discard
85688569

85698570
| Method(valReprInfo, _, mspec, mspecW, _, ctps, mtps, curriedArgInfos, paramInfos, witnessInfos, argTys, retInfo) when not isStateVar ->
8571+
if vspec.InlineInfo = ValInline.InlinedDefinition && not (cenv.emittedSpecializedInlineVals.Add(vspec.Stamp)) then
8572+
CommitStartScope cgbuf startMarkOpt else
85708573

85718574
let methLambdaTypars, methLambdaCtorThisValOpt, methLambdaBaseValOpt, methLambdaCurriedVars, methLambdaBody, methLambdaBodyTy =
85728575
IteratedAdjustLambdaToMatchValReprInfo g cenv.amap valReprInfo rhsExpr
@@ -12486,6 +12489,7 @@ type IlxAssemblyGenerator(amap: ImportMap, g: TcGlobals, tcVal: ConstraintSolver
1248612489
optimizeDuringCodeGen = (fun _flag expr -> expr)
1248712490
stackGuard = getEmptyStackGuard ()
1248812491
delayedGenMethods = Queue()
12492+
emittedSpecializedInlineVals = HashSet()
1248912493
}
1249012494

1249112495
/// Register a set of referenced assemblies with the ILX code generator

src/Compiler/Driver/CompilerConfig.fs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,8 @@ type TcConfigBuilder =
600600

601601
mutable strictIndentation: bool option
602602

603+
mutable inlineNamedFunctions: bool option
604+
603605
mutable exename: string option
604606

605607
// If true - the compiler will copy FSharp.Core.dll along the produced binaries
@@ -853,6 +855,7 @@ type TcConfigBuilder =
853855
dumpSignatureData = false
854856
realsig = false
855857
strictIndentation = None
858+
inlineNamedFunctions = None
856859
compilationMode = TcGlobals.CompilationMode.Unset
857860
}
858861

@@ -1253,6 +1256,7 @@ type TcConfig private (data: TcConfigBuilder, validate: bool) =
12531256
member _.fsiMultiAssemblyEmit = data.fsiMultiAssemblyEmit
12541257
member _.FxResolver = data.FxResolver
12551258
member _.strictIndentation = data.strictIndentation
1259+
member _.inlineNamedFunctions = data.inlineNamedFunctions
12561260
member _.primaryAssembly = data.primaryAssembly
12571261
member _.noFeedback = data.noFeedback
12581262
member _.stackReserveSize = data.stackReserveSize

src/Compiler/Driver/CompilerConfig.fsi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,8 @@ type TcConfigBuilder =
472472

473473
mutable strictIndentation: bool option
474474

475+
mutable inlineNamedFunctions: bool option
476+
475477
mutable exename: string option
476478

477479
mutable copyFSharpCore: CopyFSharpCoreFlag
@@ -814,6 +816,8 @@ type TcConfig =
814816

815817
member strictIndentation: bool option
816818

819+
member inlineNamedFunctions: bool option
820+
817821
member GetTargetFrameworkDirectories: unit -> string list
818822

819823
/// Get the loaded sources that exist and issue a warning for the ones that don't

src/Compiler/Driver/CompilerOptions.fs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,6 +1207,14 @@ let languageFlags tcConfigB =
12071207
None,
12081208
Some(FSComp.SR.optsStrictIndentation (formatOptionSwitch (Option.defaultValue false tcConfigB.strictIndentation)))
12091209
)
1210+
1211+
CompilerOption(
1212+
"inline-named-functions",
1213+
tagNone,
1214+
OptionSwitch(fun switch -> tcConfigB.inlineNamedFunctions <- Some(switch = OptionSwitch.On)),
1215+
None,
1216+
Some(FSComp.SR.optsInlineNamedFunctions ())
1217+
)
12101218
]
12111219

12121220
// OptionBlock: Advanced user options

src/Compiler/Driver/OptimizeInputs.fs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,13 +327,17 @@ let ApplyAllOptimizations
327327
// Only do abstractBigTargets in the first phase, and only when TLR is on.
328328
abstractBigTargets = tcConfig.doTLR
329329
reportingPhase = true
330+
inlineNamedFunctions =
331+
tcConfig.inlineNamedFunctions
332+
|> Option.defaultValue (not tcConfig.debuginfo || tcConfig.optSettings.LocalOptimizationsEnabled)
330333
}
331334

332335
// Only do these two steps in the first phase.
333336
let extraAndFinalLoopSettings =
334337
{ firstLoopSettings with
335338
abstractBigTargets = false
336339
reportingPhase = false
340+
inlineNamedFunctions = false
337341
}
338342

339343
let addPhaseDiagnostics (f: PhaseFunc) (info: Phase) =

src/Compiler/FSComp.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1560,6 +1560,7 @@ optsSetLangVersion,"Specify language version such as 'latest' or 'preview'."
15601560
optsDisableLanguageFeature,"Disable a specific language feature by name."
15611561
optsSupportedLangVersions,"Supported language versions:"
15621562
optsStrictIndentation,"Override indentation rules implied by the language version (%s by default)"
1563+
optsInlineNamedFunctions,"Inline named 'inline' functions"
15631564
nativeResourceFormatError,"Stream does not begin with a null resource and is not in '.RES' format."
15641565
nativeResourceHeaderMalformed,"Resource header beginning at offset %s is malformed."
15651566
formatDashItem," - %s"

src/Compiler/Optimize/Optimizer.fs

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ open FSharp.Compiler.Text.LayoutRender
2323
open FSharp.Compiler.Text.TaggedText
2424
open FSharp.Compiler.TypedTree
2525
open FSharp.Compiler.TypedTreeBasics
26+
open FSharp.Compiler.Xml
2627
open FSharp.Compiler.TypedTreeOps
2728
open FSharp.Compiler.TypedTreeOps.DebugPrint
2829
open FSharp.Compiler.TypedTreePickle
@@ -327,6 +328,8 @@ type OptimizationSettings =
327328
reportTotalSizes : bool
328329

329330
processingMode : OptimizationProcessingMode
331+
332+
inlineNamedFunctions: bool
330333
}
331334

332335
static member Defaults =
@@ -344,6 +347,7 @@ type OptimizationSettings =
344347
reportHasEffect = false
345348
reportTotalSizes = false
346349
processingMode = OptimizationProcessingMode.Parallel
350+
inlineNamedFunctions = false
347351
}
348352

349353
/// Determines if JIT optimizations are enabled
@@ -432,6 +436,8 @@ type cenv =
432436
stackGuard: StackGuard
433437

434438
realsig: bool
439+
440+
specializedInlineVals: HashMultiMap<Stamp, TType * Val * Expr>
435441
}
436442

437443
override x.ToString() = "<cenv>"
@@ -1692,6 +1698,7 @@ let TryEliminateBinding cenv _env bind e2 _m =
16921698
not vspec1.IsCompilerGenerated then
16931699
None
16941700
elif vspec1.IsFixed then None
1701+
elif vspec1.InlineInfo = ValInline.InlinedDefinition then None
16951702
elif vspec1.LogicalName.StartsWithOrdinal stackVarPrefix ||
16961703
vspec1.LogicalName.Contains suffixForVariablesThatMayNotBeEliminated then None
16971704
else
@@ -3421,8 +3428,60 @@ and TryDevirtualizeApplication cenv env (f, tyargs, args, m) =
34213428
| _ -> None
34223429

34233430
/// Attempt to inline an application of a known value at callsites
3424-
and TryInlineApplication cenv env finfo (tyargs: TType list, args: Expr list, m) =
3431+
and TryInlineApplication cenv env finfo (valExpr: Expr) (tyargs: TType list, args: Expr list, m) =
34253432
let g = cenv.g
3433+
3434+
match cenv.settings.inlineNamedFunctions, stripExpr valExpr with
3435+
| false, Expr.Val(vref, _, _) when vref.ShouldInline ->
3436+
let origFinfo = GetInfoForValWithCheck cenv env m vref
3437+
match stripValue origFinfo.ValExprInfo with
3438+
| CurriedLambdaValue(origLambdaId, _, _, origLambda, origLambdaTy) when not (Zset.contains origLambdaId env.dontInline) ->
3439+
let argsR = args |> List.map (OptimizeExpr cenv env >> fst)
3440+
let info = { TotalSize = 1; FunctionSize = 1; HasEffect = true; MightMakeCriticalTailcall = false; Info = UnknownValue }
3441+
3442+
let canCallDirectly =
3443+
let hasNoTraits =
3444+
match vref.ValReprInfo with
3445+
| Some reprInfo ->
3446+
let tps, _, _, _ = GetValReprTypeInFSharpForm g reprInfo vref.Type m
3447+
GetTraitWitnessInfosOfTypars g 0 tps |> List.isEmpty
3448+
| None -> false
3449+
3450+
let hasNoFreeTyargs =
3451+
tyargs |> List.forall (fun t -> (freeInType CollectTyparsNoCaching t).FreeTypars.IsEmpty) |> not
3452+
3453+
hasNoTraits || hasNoFreeTyargs
3454+
3455+
if canCallDirectly then
3456+
Some(mkApps g ((exprForValRef m vref, vref.Type), [tyargs], argsR, m), info)
3457+
else
3458+
let f2R = CopyExprForInlining cenv true origLambda m
3459+
let specLambda = MakeApplicationAndBetaReduce g (f2R, origLambdaTy, [tyargs], [], m)
3460+
let specLambdaTy = tyOfExpr g specLambda
3461+
3462+
let debugVal, specLambdaR =
3463+
match cenv.specializedInlineVals.FindAll(origLambdaId) |> List.tryFind (fun (ty, _, _) -> typeEquiv g ty specLambdaTy) with
3464+
| Some (_, v, body) -> v, body
3465+
| None ->
3466+
3467+
let specLambdaR, _ = OptimizeExpr cenv { env with dontInline = Zset.add origLambdaId env.dontInline } specLambda
3468+
let debugVal =
3469+
let name = $"<{vref.LogicalName}>__debug"
3470+
let valReprInfo = Some(InferValReprInfoOfExpr g AllowTypeDirectedDetupling.No specLambdaTy [] [] specLambdaR)
3471+
3472+
Construct.NewVal(name, m, None, specLambdaTy, Immutable, true, valReprInfo, taccessPublic, ValNotInRecScope, None,
3473+
NormalVal, [], ValInline.InlinedDefinition, XmlDoc.Empty, false, false, false, false, false, false, None,
3474+
ParentNone)
3475+
3476+
cenv.specializedInlineVals.Add(origLambdaId, (specLambdaTy, debugVal, specLambdaR))
3477+
debugVal, specLambdaR
3478+
3479+
let callExpr = mkApps g ((exprForVal m debugVal, specLambdaTy), [], argsR, m)
3480+
Some(mkCompGenLet m debugVal specLambdaR callExpr, info)
3481+
3482+
| _ -> None
3483+
| _ ->
3484+
34263485
// Considering inlining app
34273486
match finfo.Info with
34283487
| StripLambdaValue (lambdaId, arities, size, f2, f2ty) when
@@ -3621,7 +3680,7 @@ and OptimizeApplication cenv env (f0, f0ty, tyargs, args, m) =
36213680
OptimizeExpr cenv env remade
36223681
| Choice2Of2 (newf0, remake) ->
36233682

3624-
match TryInlineApplication cenv env finfo (tyargs, args, m) with
3683+
match TryInlineApplication cenv env finfo f0 (tyargs, args, m) with
36253684
| Some (res, info) ->
36263685
// inlined
36273686
(res |> remake), info
@@ -3869,6 +3928,10 @@ and OptimizeLambdas (vspec: Val option) cenv env valReprInfo expr exprTy =
38693928

38703929
// can't inline any values with semi-recursive object references to self or base
38713930
let value_ =
3931+
match vspec with
3932+
| Some v when v.InlineInfo = ValInline.InlinedDefinition -> UnknownValue
3933+
| _ ->
3934+
38723935
match baseValOpt with
38733936
| None -> CurriedLambdaValue (lambdaId, arities, bsize, exprR, exprTy)
38743937
| Some baseVal ->
@@ -4403,6 +4466,7 @@ let OptimizeImplFile (settings, ccu, tcGlobals, tcVal, importMap, optEnv, isIncr
44034466
casApplied=Dictionary<Stamp, bool>()
44044467
stackGuard = StackGuard("OptimizerStackGuardDepth")
44054468
realsig = tcGlobals.realsig
4469+
specializedInlineVals = HashMultiMap(HashIdentity.Structural, true)
44064470
}
44074471

44084472
let env, _, _, _ as results = OptimizeImplFileInternal cenv optEnv isIncrementalFragment hidden mimpls

src/Compiler/Optimize/Optimizer.fsi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ type OptimizationSettings =
5151
reportTotalSizes: bool
5252

5353
processingMode: OptimizationProcessingMode
54+
55+
inlineNamedFunctions: bool
5456
}
5557

5658
member JitOptimizationsEnabled: bool

src/Compiler/Symbols/Symbols.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1857,7 +1857,7 @@ type FSharpMemberOrFunctionOrValue(cenv, d:FSharpMemberOrValData, item) =
18571857
match v.InlineInfo with
18581858
| ValInline.Always -> FSharpInlineAnnotation.AlwaysInline
18591859
| ValInline.Optional -> FSharpInlineAnnotation.OptionalInline
1860-
| ValInline.Never -> FSharpInlineAnnotation.NeverInline
1860+
| ValInline.Never | ValInline.InlinedDefinition -> FSharpInlineAnnotation.NeverInline
18611861

18621862
member _.IsMutable =
18631863
if isUnresolved() then false else

src/Compiler/TypedTree/TypedTree.fs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,21 +41,15 @@ type StampMap<'T> = Map<Stamp, 'T>
4141

4242
[<RequireQualifiedAccess>]
4343
type ValInline =
44-
45-
/// Indicates the value is inlined but the .NET IL code for the function still exists, e.g. to satisfy interfaces on objects, but that it is also always inlined
4644
| Always
47-
48-
/// Indicates the value may optionally be inlined by the optimizer
4945
| Optional
50-
51-
/// Indicates the value must never be inlined by the optimizer
5246
| Never
47+
| InlinedDefinition
5348

54-
/// Returns true if the implementation of a value should be inlined
5549
member x.ShouldInline =
5650
match x with
5751
| ValInline.Always -> true
58-
| ValInline.Optional | ValInline.Never -> false
52+
| ValInline.Optional | ValInline.Never | ValInline.InlinedDefinition -> false
5953

6054
/// A flag associated with values that indicates whether the recursive scope of the value is currently being processed, and
6155
/// if the value has been generalized or not as yet.
@@ -110,6 +104,7 @@ type ValFlags(flags: int64) =
110104
(if isCompGen then 0b00000000000000001000L
111105
else 0b000000000000000000000L) |||
112106
(match inlineInfo with
107+
| ValInline.InlinedDefinition -> 0b00000000000000000000L
113108
| ValInline.Always -> 0b00000000000000010000L
114109
| ValInline.Optional -> 0b00000000000000100000L
115110
| ValInline.Never -> 0b00000000000000110000L) |||
@@ -166,7 +161,7 @@ type ValFlags(flags: int64) =
166161

167162
member x.InlineInfo =
168163
match (flags &&& 0b00000000000000110000L) with
169-
| 0b00000000000000000000L
164+
| 0b00000000000000000000L -> ValInline.InlinedDefinition
170165
| 0b00000000000000010000L -> ValInline.Always
171166
| 0b00000000000000100000L -> ValInline.Optional
172167
| 0b00000000000000110000L -> ValInline.Never

0 commit comments

Comments
 (0)