Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize integer for loop code gen #13573

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Compiler/Checking/PostInferenceChecks.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1060,7 +1060,7 @@ and TryCheckResumableCodeConstructs cenv env expr : bool =
true

// Integer for-loops are allowed but their bodies are not currently resumable
| IntegerForLoopExpr (_sp1, _sp2, _style, e1, e2, v, e3, _m) ->
| IntegerForLoopExpr (_sp1, _sp2, _style, e1, e2, v, e3, _, _m) ->
CheckExprNoByrefs cenv { env with resumableCode = Resumable.None } e1
CheckExprNoByrefs cenv { env with resumableCode = Resumable.None } e2
BindVal cenv env v
Expand Down
161 changes: 127 additions & 34 deletions src/Compiler/CodeGen/IlxGen.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3014,7 +3014,13 @@ and GenExprAux (cenv: cenv) (cgbuf: CodeGenBuffer) eenv expr (sequel: sequel) =
GenWhileLoop cenv cgbuf eenv (sp, e1, e2, m) sequel
| TOp.IntegerForLoop (spFor, spTo, dir),
[ Expr.Lambda (_, _, _, [ _ ], e1, _, _); Expr.Lambda (_, _, _, [ _ ], e2, _, _); Expr.Lambda (_, _, _, [ v ], e3, _, _) ],
[] -> GenIntegerForLoop cenv cgbuf eenv (spFor, spTo, v, e1, dir, e2, e3, m) sequel
[] -> GenIntegerForLoop cenv cgbuf eenv (spFor, spTo, v, e1, dir, e2, e3, None, m) sequel
| TOp.IntegerForLoop (spFor, spTo, dir),
[ Expr.Lambda (_, _, _, [ _ ], e1, _, _)
Expr.Lambda (_, _, _, [ _ ], e2, _, _)
Expr.Lambda (_, _, _, [ v ], e3, _, _)
Expr.Lambda (_, _, _, [ _ ], e4, _, _) ],
[] -> GenIntegerForLoop cenv cgbuf eenv (spFor, spTo, v, e1, dir, e2, e3, Some e4, m) sequel
| TOp.TryFinally (spTry, spFinally),
[ Expr.Lambda (_, _, _, [ _ ], e1, _, _); Expr.Lambda (_, _, _, [ _ ], e2, _, _) ],
[ resTy ] -> GenTryFinally cenv cgbuf eenv (e1, e2, m, resTy, spTry, spFinally) sequel
Expand Down Expand Up @@ -4889,7 +4895,7 @@ and GenTryFinally cenv cgbuf eenv (bodyExpr, handlerExpr, m, resTy, spTry, spFin
// Generate for-loop
//--------------------------------------------------------------------------

and GenIntegerForLoop cenv cgbuf eenv (spFor, spTo, v, e1, dir, e2, loopBody, m) sequel =
and GenIntegerForLoop cenv cgbuf eenv (spFor, spTo, v, e1, dir, e2, loopBody, stepExpr: Expr option, m) sequel =
let eenv = SetIsInLoop true eenv
let g = cenv.g

Expand All @@ -4906,19 +4912,19 @@ and GenIntegerForLoop cenv cgbuf eenv (spFor, spTo, v, e1, dir, e2, loopBody, m)
let test = CG.GenerateDelayMark 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, isUp =
match stepExpr, dir with
| Some _, _ -> true, true
| None, FSharpForLoopUp -> true, true
| None, FSharpForLoopDown -> true, false
| None, CSharpForLoopUp -> false, true

let isFSharpStyle =
(match dir with
| FSharpForLoopUp
| FSharpForLoopDown -> true
| CSharpForLoopUp -> false)
let stepConst =
match stepExpr with
| Some (Expr.Const (Const.Int32 i, _, _)) when i <> 0 -> Some i
| _ -> None

let finishIdx, eenvinner =
let finishIdx, stepIdx, eenvinner =
if isFSharpStyle then
// Ensure that we have an g.CompilerGlobalState
assert (g.CompilerGlobalState |> Option.isSome)
Expand All @@ -4929,9 +4935,15 @@ and GenIntegerForLoop cenv cgbuf eenv (spFor, spTo, v, e1, dir, e2, loopBody, m)
let v, _realloc, eenvinner =
AllocLocal cenv cgbuf eenvinner true (vName, g.ilg.typ_Int32, false) (start, finish)

v, eenvinner
if stepExpr.IsSome && stepConst.IsNone then
let v2, _realloc, eenvinner =
AllocLocal cenv cgbuf eenvinner true (vName, g.ilg.typ_Int32, false) (start, finish)

v, v2, eenvinner
else
v, -1, eenvinner
else
-1, eenvinner
-1, -1, eenvinner

let _, eenvinner = AllocLocalVal cenv cgbuf v eenvinner None (start, finish)

Expand All @@ -4942,15 +4954,39 @@ and GenIntegerForLoop cenv cgbuf eenv (spFor, spTo, v, e1, dir, e2, loopBody, m)
GenExpr cenv cgbuf eenv e1 Continue
GenStoreVal cgbuf eenvinner m v

match stepExpr, stepConst with
| Some step, None ->
// Throw invalidarg at runtime if step is 0.
// Emulates behavior of the RangeInt32 enumerator that this replaces.
GenExpr cenv cgbuf eenvinner step Continue
EmitSetLocal cgbuf stepIdx
EmitGetLocal cgbuf g.ilg.typ_Int32 stepIdx

let notZero = CG.GenerateDelayMark cgbuf "notZero"
CG.EmitInstr cgbuf (pop 1) Push0 (I_brcmp(BI_brtrue, notZero.CodeLabel))

let arg1 = mkString g step.Range (SR.GetString "StepCannotBeZero")
let arg2 = mkString g step.Range "step"
let invalidArgExpr = MakeArgumentExnExpr cenv eenv (arg1, arg2, step.Range)
GenExpr cenv cgbuf eenvinner invalidArgExpr Continue
CG.EmitInstr cgbuf (pop 1) Push0 I_throw

CG.SetMarkToHere cgbuf notZero
| _ -> ()

if isFSharpStyle then
GenExpr cenv cgbuf eenvinner e2 Continue
EmitSetLocal cgbuf finishIdx
EmitGetLocal cgbuf g.ilg.typ_Int32 finishIdx
GenGetLocalVal cenv cgbuf eenvinner e2.Range v None
CG.EmitInstr cgbuf (pop 2) Push0 (I_brcmp((if isUp then BI_blt else BI_bgt), finish.CodeLabel))

else
CG.EmitInstr cgbuf (pop 0) Push0 (I_br test.CodeLabel)
if stepExpr.IsNone then
EmitGetLocal cgbuf g.ilg.typ_Int32 finishIdx
GenGetLocalVal cenv cgbuf eenvinner e2.Range v None

match stepExpr, dir with
| Some _, _
| None, CSharpForLoopUp -> CG.EmitInstr cgbuf (pop 0) Push0 (I_br test.CodeLabel)
| None, FSharpForLoopUp -> CG.EmitInstr cgbuf (pop 2) Push0 (I_brcmp(BI_blt, finish.CodeLabel))
| None, FSharpForLoopDown -> CG.EmitInstr cgbuf (pop 2) Push0 (I_brcmp(BI_bgt, finish.CodeLabel))

cgbuf.EmitStartOfHiddenCode()

Expand All @@ -4963,37 +4999,84 @@ and GenIntegerForLoop cenv cgbuf eenv (spFor, spTo, v, e1, dir, e2, loopBody, m)
// v++ or v--
GenGetLocalVal cenv cgbuf eenvinner e2.Range v None

CG.EmitInstr cgbuf (pop 0) (Push [ g.ilg.typ_Int32 ]) (mkLdcInt32 1)
CG.EmitInstr cgbuf (pop 1) Push0 (if isUp then AI_add else AI_sub)
match stepExpr with
| None ->
match dir with
| FSharpForLoopUp
| FSharpForLoopDown
| CSharpForLoopUp ->
CG.EmitInstr cgbuf (pop 0) (Push [ g.ilg.typ_Int32 ]) (mkLdcInt32 1)
CG.EmitInstr cgbuf (pop 1) Push0 (if isUp then AI_add else AI_sub)
| Some _ ->
match stepConst with
| Some sc ->
let pos = sc > 0
CG.EmitInstr cgbuf (pop 0) (Push [ g.ilg.typ_Int32 ]) (mkLdcInt32 (if pos then sc else -sc))
CG.EmitInstr cgbuf (pop 1) Push0 (if pos then AI_add else AI_sub)
| None ->
EmitGetLocal cgbuf g.ilg.typ_Int32 stepIdx
CG.EmitInstr cgbuf (pop 1) Push0 AI_add

GenStoreVal cgbuf eenvinner m v

// .text
// .test
CG.SetMarkToHere cgbuf test

// FSharpForLoopUp: if v <> e2 + 1 then goto .inner
// FSharpForLoopDown: if v <> e2 - 1 then goto .inner
// FSharpForLoopWithStep: if (step > 0 && v <= e2) || (step < 0 && v >= e2) then goto .inner (variable step)
// FSharpForLoopWithStep: if v <= e2 && v >= e1 then goto .inner (constant step > 0)
// FSharpForLoopWithStep: if v >= e2 && v <= e1 then goto .inner (constant step < 0)
// CSharpStyle: if v < e2 then goto .inner
match spTo with
| DebugPointAtInOrTo.Yes spStart -> CG.EmitDebugPoint cgbuf spStart
| DebugPointAtInOrTo.No -> ()

GenGetLocalVal cenv cgbuf eenvinner e2.Range v None

let cmp =
match stepExpr with
| None ->
match dir with
| FSharpForLoopUp
| FSharpForLoopDown -> BI_bne_un
| CSharpForLoopUp -> BI_blt
| FSharpForLoopDown ->
EmitGetLocal cgbuf g.ilg.typ_Int32 finishIdx
CG.EmitInstr cgbuf (pop 0) (Push [ g.ilg.typ_Int32 ]) (mkLdcInt32 1)
CG.EmitInstr cgbuf (pop 1) Push0 (if isUp then AI_add else AI_sub)

CmpThenBrOrContinue(pop 2, [ I_brcmp(BI_bne_un, inner.CodeLabel) ])
|> GenSequel cenv eenv.cloc cgbuf
| CSharpForLoopUp ->
CmpThenBrOrContinue(pop 2, [ I_brcmp(BI_blt, inner.CodeLabel) ])
|> GenExpr cenv cgbuf eenv e2
| Some _ ->
match stepConst with
| Some sc ->
let pos = sc > 0

GenExpr cenv cgbuf eenv e1 Continue
CG.EmitInstr cgbuf (pop 2) Push0 (I_brcmp((if pos then BI_blt else BI_bgt), finish.CodeLabel))

GenGetLocalVal cenv cgbuf eenvinner e2.Range v None
EmitGetLocal cgbuf g.ilg.typ_Int32 finishIdx
CmpThenBrOrContinue(pop 2, [ I_brcmp((if pos then BI_ble else BI_bge), inner.CodeLabel) ])
| None ->
EmitGetLocal cgbuf g.ilg.typ_Int32 finishIdx
let testPassed = CG.GenerateDelayMark cgbuf "testPassed"
CG.EmitInstr cgbuf (pop 2) Push0 (I_brcmp(BI_ble, testPassed.CodeLabel))

let e2Sequel = (CmpThenBrOrContinue(pop 2, [ I_brcmp(cmp, inner.CodeLabel) ]))
EmitGetLocal cgbuf g.ilg.typ_Int32 stepIdx
CG.EmitInstr cgbuf (pop 0) (Push [ g.ilg.typ_Int32 ]) (mkLdcInt32 0)
CG.EmitInstr cgbuf (pop 2) Push0 (I_brcmp(BI_bgt, finish.CodeLabel))

if isFSharpStyle then
EmitGetLocal cgbuf g.ilg.typ_Int32 finishIdx
CG.EmitInstr cgbuf (pop 0) (Push [ g.ilg.typ_Int32 ]) (mkLdcInt32 1)
CG.EmitInstr cgbuf (pop 1) Push0 (if isUp then AI_add else AI_sub)
GenSequel cenv eenv.cloc cgbuf e2Sequel
else
GenExpr cenv cgbuf eenv e2 e2Sequel
CG.SetMarkToHere cgbuf testPassed
GenGetLocalVal cenv cgbuf eenvinner e2.Range v None
EmitGetLocal cgbuf g.ilg.typ_Int32 finishIdx
CG.EmitInstr cgbuf (pop 2) Push0 (I_brcmp(BI_bge, inner.CodeLabel))

EmitGetLocal cgbuf g.ilg.typ_Int32 stepIdx
CG.EmitInstr cgbuf (pop 0) (Push [ g.ilg.typ_Int32 ]) (mkLdcInt32 0)
CmpThenBrOrContinue(pop 2, [ I_brcmp(BI_bge, inner.CodeLabel) ])
|> GenSequel cenv eenv.cloc cgbuf

// .finish - loop-exit here
CG.SetMarkToHere cgbuf finish
Expand Down Expand Up @@ -5407,6 +5490,16 @@ and MakeNotSupportedExnExpr cenv eenv (argExpr, m) =
let mref = mkILCtorMethSpecForTy(ilTy, [ g.ilg.typ_String ]).MethodRef
Expr.Op(TOp.ILCall(false, false, false, true, NormalValUse, false, false, mref, [], [], [ ety ]), [], [ argExpr ], m)

and MakeArgumentExnExpr cenv eenv (messageExpr, argNameExpr, m) =
let g = cenv.g
let ety = mkAppTy (g.FindSysTyconRef [ "System" ] "ArgumentException") []
let ilTy = GenType cenv m eenv.tyenv ety

let mref =
mkILCtorMethSpecForTy(ilTy, [ g.ilg.typ_String; g.ilg.typ_String ]).MethodRef

Expr.Op(TOp.ILCall(false, false, false, true, NormalValUse, false, false, mref, [], [], [ ety ]), [], [ messageExpr; argNameExpr ], m)

and GenTraitCall (cenv: cenv) cgbuf eenv (traitInfo: TraitConstraintInfo, argExprs, m) expr sequel =
let g = cenv.g
let generateWitnesses = ComputeGenerateWitnesses g eenv
Expand Down
3 changes: 3 additions & 0 deletions src/Compiler/FSStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1110,4 +1110,7 @@
<data name="NotUpperCaseConstructorWithoutRQA" xml:space="preserve">
<value>Lowercase discriminated union cases are only allowed when using RequireQualifiedAccess attribute</value>
</data>
<data name="StepCannotBeZero" xml:space="preserve">
<value>The step of a range cannot be zero.</value>
</data>
</root>
40 changes: 34 additions & 6 deletions src/Compiler/Optimize/LowerStateMachines.fs
Original file line number Diff line number Diff line change
Expand Up @@ -481,8 +481,8 @@ type LowerStateMachine(g: TcGlobals) =

// The expanded code for state machines may use for loops, however the
// body must be synchronous.
| IntegerForLoopExpr (sp1, sp2, style, e1, e2, v, e3, m) ->
ConvertResumableIntegerForLoop env pcValInfo (sp1, sp2, style, e1, e2, v, e3, m)
| IntegerForLoopExpr (sp1, sp2, style, e1, e2, v, e3, step, m) ->
ConvertResumableIntegerForLoop env pcValInfo (sp1, sp2, style, e1, e2, v, e3, step, m)

// The expanded code for state machines may use try/with....
| TryWithExpr (spTry, spWith, resTy, bodyExpr, filterVar, filterExpr, handlerVar, handlerExpr, m) ->
Expand Down Expand Up @@ -673,13 +673,14 @@ type LowerStateMachine(g: TcGlobals) =
|> Result.Ok
| Result.Error err, _ | _, Result.Error err -> Result.Error err

and ConvertResumableIntegerForLoop env pcValInfo (spFor, spTo, style, e1, e2, v, e3, m) =
and ConvertResumableIntegerForLoop env pcValInfo (spFor, spTo, style, e1, e2, v, e3, e4, m) =
if sm_verbose then printfn "IntegerForLoopExpr"
let res1 = ConvertResumableCode env pcValInfo e1
let res2 = ConvertResumableCode env pcValInfo e2
let res3 = ConvertResumableCode env pcValInfo e3
match res1, res2, res3 with
| Result.Ok res1, Result.Ok res2, Result.Ok res3 ->
let res4 = Option.map (ConvertResumableCode env pcValInfo) e4
match res1, res2, res3, res4 with
| Result.Ok res1, Result.Ok res2, Result.Ok res3, None ->
let eps = res1.entryPoints @ res2.entryPoints @ res3.entryPoints
if eps.Length > 0 then
Result.Error(FSComp.SR.reprResumableCodeContainsFastIntegerForLoop())
Expand All @@ -705,7 +706,34 @@ type LowerStateMachine(g: TcGlobals) =
thisVars = res1.thisVars @ res2.thisVars @ res3.thisVars
resumableVars = emptyFreeVars (* eps is empty, hence synchronous, no capture *) }
|> Result.Ok
| Result.Error err, _, _ | _, Result.Error err, _ | _, _, Result.Error err -> Result.Error err
| Result.Ok res1, Result.Ok res2, Result.Ok res3, Some(Result.Ok res4) ->
let eps = res1.entryPoints @ res2.entryPoints @ res3.entryPoints @ res4.entryPoints
if eps.Length > 0 then
Result.Error(FSComp.SR.reprResumableCodeContainsFastIntegerForLoop())
else
{ phase1 = mkIntegerForLoopWithStep g (spFor, spTo, v, res1.phase1, res4.phase1, res2.phase1, res3.phase1, m)
phase2 = (fun ctxt ->
let e1R = res1.phase2 ctxt
let e2R = res2.phase2 ctxt
let e3R = res3.phase2 ctxt
let e4R = res4.phase2 ctxt

// Clear the pcVal on backward branch, causing jump tables at entry to nested try-blocks to not activate
let e3R2 =
match pcValInfo with
| None -> e3R
| Some ((pcVal, _), _) ->
mkCompGenThenDoSequential m
e3R
(mkValSet m (mkLocalValRef pcVal) (mkZero g m))

mkIntegerForLoopWithStep g (spFor, spTo, v, e1R, e4R, e2R, e3R2, m))
entryPoints= eps
stateVars = res1.stateVars @ res2.stateVars @ res3.stateVars @res4.stateVars
thisVars = res1.thisVars @ res2.thisVars @ res3.thisVars @res3.thisVars
resumableVars = emptyFreeVars (* eps is empty, hence synchronous, no capture *) }
|> Result.Ok
| Result.Error err, _, _, _ | _, Result.Error err, _, _ | _, _, Result.Error err, _ | _, _, _, Some(Result.Error err) -> Result.Error err

and ConvertResumableTryWith env pcValInfo (spTry, spWith, resTy, bodyExpr, filterVar, filterExpr, handlerVar, handlerExpr, m) =
if sm_verbose then printfn "TryWithExpr"
Expand Down
26 changes: 20 additions & 6 deletions src/Compiler/Optimize/Optimizer.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2544,7 +2544,10 @@ and OptimizeExprOp cenv env (op, tyargs, args, m) =
OptimizeWhileLoop cenv { env with disableMethodSplitting=true } (spWhile, marker, e1, e2, m)

| TOp.IntegerForLoop (spFor, spTo, dir), _, [Expr.Lambda (_, _, _, [_], e1, _, _);Expr.Lambda (_, _, _, [_], e2, _, _);Expr.Lambda (_, _, _, [v], e3, _, _)] ->
OptimizeFastIntegerForLoop cenv { env with disableMethodSplitting=true } (spFor, spTo, v, e1, dir, e2, e3, m)
OptimizeFastIntegerForLoop cenv { env with disableMethodSplitting=true } (spFor, spTo, v, e1, dir, e2, e3, None, m)

| TOp.IntegerForLoop (spFor, spTo, dir), _, [Expr.Lambda (_, _, _, [_], e1, _, _);Expr.Lambda (_, _, _, [_], e2, _, _);Expr.Lambda (_, _, _, [v], e3, _, _);Expr.Lambda (_, _, _, [_], e4, _, _)] ->
OptimizeFastIntegerForLoop cenv { env with disableMethodSplitting=true } (spFor, spTo, v, e1, dir, e2, e3, Some e4, m)

| TOp.TryFinally (spTry, spFinally), [resty], [Expr.Lambda (_, _, _, [_], e1, _, _); Expr.Lambda (_, _, _, [_], e2, _, _)] ->
OptimizeTryFinally cenv env (spTry, spFinally, e1, e2, m, resty)
Expand Down Expand Up @@ -2737,11 +2740,17 @@ and TryOptimizeUnionCaseGet cenv _env (e1info, cspec, _tys, n, m) =
| _ -> None

/// Optimize/analyze a for-loop
and OptimizeFastIntegerForLoop cenv env (spFor, spTo, v, e1, dir, e2, e3, m) =
and OptimizeFastIntegerForLoop cenv env (spFor, spTo, v, e1, dir, e2, e3, stepExpr, m) =
let g = cenv.g

let e1R, e1info = OptimizeExpr cenv env e1
let e2R, e2info = OptimizeExpr cenv env e2
let e4R, e4info =
match stepExpr with
| Some step ->
let e4R, e4info = OptimizeExpr cenv env step
Some e4R, Some e4info
| None -> None, None
let env = BindInternalValToUnknown cenv v env
let e3R, e3info = OptimizeExpr cenv env e3
// Try to replace F#-style loops with C# style loops that recompute their bounds but which are compiled more efficiently by the JITs, e.g.
Expand All @@ -2751,12 +2760,12 @@ and OptimizeFastIntegerForLoop cenv env (spFor, spTo, v, e1, dir, e2, e3, m) =
match dir, e2R with
// detect upwards for loops with bounds of the form "arr.Length - 1" and convert them to a C#-style for loop
| FSharpForLoopUp, Expr.Op (TOp.ILAsm ([ (AI_sub | AI_sub_ovf)], _), _, [Expr.Op (TOp.ILAsm ([ I_ldlen; (AI_conv DT_I4)], _), _, [arre], _); Expr.Const (Const.Int32 1, _, _)], _)
when not (snd(OptimizeExpr cenv env arre)).HasEffect ->
when not (snd(OptimizeExpr cenv env arre)).HasEffect ->

mkLdlen g e2R.Range arre, CSharpForLoopUp

| FSharpForLoopUp, Expr.Op (TOp.ILAsm ([ (AI_sub | AI_sub_ovf)], _), _, [Expr.Op (TOp.ILCall(_,_,_,_,_,_,_, mth, _,_,_), _, [arre], _) as lenOp; Expr.Const (Const.Int32 1, _, _)], _)
when
when
mth.Name = "get_Length" && (mth.DeclaringTypeRef.FullName = "System.Span`1" || mth.DeclaringTypeRef.FullName = "System.ReadOnlySpan`1")
&& not (snd(OptimizeExpr cenv env arre)).HasEffect ->

Expand All @@ -2771,13 +2780,18 @@ and OptimizeFastIntegerForLoop cenv env (spFor, spTo, v, e1, dir, e2, e3, m) =
| _ ->
e2R, dir

let einfos = [e1info;e2info;e3info]
let einfos = [e1info;e2info;e3info; match e4info with Some e4i -> e4i | None -> ()]
let eff = OrEffects einfos
(* neither bounds nor body has an effect, and loops always terminate, hence eliminate the loop *)
if cenv.settings.EliminateForLoop && not eff then
mkUnit g m, { TotalSize=0; FunctionSize=0; HasEffect=false; MightMakeCriticalTailcall=false; Info=UnknownValue }
else
let exprR = mkIntegerForLoop g (spFor, spTo, v, e1R, dir, e2R, e3R, m)
let exprR =
match e4R with
| Some e4R ->
mkIntegerForLoopWithStep g (spFor, spTo, v, e1R, e4R, e2R, e3R, m)
| None ->
mkIntegerForLoop g (spFor, spTo, v, e1R, dir, e2R, e3R, m)
exprR, { TotalSize=AddTotalSizes einfos + forAndWhileLoopSize
FunctionSize=AddFunctionSizes einfos + forAndWhileLoopSize
HasEffect=eff
Expand Down
Loading