Skip to content

Commit 283ad75

Browse files
Go branchless
* Use branchless `max` in call to `Array.init`/`List.init`. Getting the sequel to be appended to each branch correctly in all cases looked like a nontrivial undertaking. * Lower the size thresholds for const ranges (temporarily?).
1 parent 0136a96 commit 283ad75

File tree

4 files changed

+120
-101
lines changed

4 files changed

+120
-101
lines changed

src/Compiler/CodeGen/IlxGen.fs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2867,13 +2867,9 @@ and GenExprPreSteps (cenv: cenv) (cgbuf: CodeGenBuffer) eenv expr sequel =
28672867
None
28682868

28692869
match lowering with
2870-
| Some(LowerComputedCollectionExpressions.ComputedCollectionExprLowering.Expr initExpr) ->
2870+
| Some initExpr ->
28712871
GenExpr cenv cgbuf eenv initExpr sequel
28722872
true
2873-
| Some(LowerComputedCollectionExpressions.ComputedCollectionExprLowering.Either(branch1, branch2)) ->
2874-
GenExpr cenv cgbuf eenv branch1 (sequelIgnoreEndScopes sequel)
2875-
GenExpr cenv cgbuf eenv branch2 sequel
2876-
true
28772873
| None ->
28782874

28792875
let lowering =

src/Compiler/Optimize/LowerComputedCollections.fs

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -269,23 +269,18 @@ let (|Int32Range|_|) g expr =
269269
| ValApp g g.range_int32_op_vref ([], [start; Expr.Const (value = Const.Int32 1); finish], _), _ -> ValueSome (start, finish)
270270
| _ -> ValueNone
271271

272-
[<RequireQualifiedAccess>]
273-
type ComputedCollectionExprLowering =
274-
| Expr of initExpr: Expr
275-
| Either of branch1: Expr * branch2: Expr
276-
277272
let LowerComputedListOrArrayExpr tcVal (g: TcGlobals) amap overallExpr =
278273
// If ListCollector is in FSharp.Core then this optimization kicks in
279274
if g.ListCollector_tcr.CanDeref then
280-
let constListSizeThreshold = 100
281-
let constArrayBytesThreshold = 1024
275+
let constListSizeThreshold = 10
276+
let constArrayBytesThreshold = 40
282277

283278
match overallExpr with
284279
// [5..1] → []
285280
| SeqToList g (OptionalCoerce (OptionalSeq g amap (ConstInt32Range g (start, finish))), m) when
286281
start > finish
287282
->
288-
Some (ComputedCollectionExprLowering.Expr (mkUnionCaseExpr (g.nil_ucref, [g.int32_ty], [], m)))
283+
Some (mkUnionCaseExpr (g.nil_ucref, [g.int32_ty], [], m))
289284

290285
// [1..5] → [1; 2; 3; 4; 5] ≡ 1 :: 2 :: 3 :: 4 :: 5 :: []
291286
| SeqToList g (OptionalCoerce (OptionalSeq g amap (ConstInt32Range g (start, finish))), _) when
@@ -296,59 +291,57 @@ let LowerComputedListOrArrayExpr tcVal (g: TcGlobals) amap overallExpr =
296291
if n < start then acc
297292
else conses (mkCons g g.int32_ty (Expr.Const (Const.Int32 n, Text.Range.range0, g.int32_ty)) acc) (n - 1)
298293

299-
Some (ComputedCollectionExprLowering.Expr (conses (mkNil g Text.Range.range0 g.int32_ty) finish))
294+
Some (conses (mkNil g Text.Range.range0 g.int32_ty) finish)
300295

301296
// [start..finish] → if start <= finish then List.init (finish - start + 1) ((+) start) else []
302297
| SeqToList g (OptionalCoerce (OptionalSeq g amap (Int32Range g (start, finish))), m) ->
303298
let diff = mkAsmExpr ([AI_sub], [], [finish; start], [g.int32_ty], Text.Range.range0)
304299
let range = mkAsmExpr ([AI_add], [], [diff; mkOne g Text.Range.range0], [g.int32_ty], Text.Range.range0)
300+
let zero = mkZero g Text.Range.range0
301+
let negRangeLtZero = mkAsmExpr ([AI_neg], [], [mkAsmExpr ([AI_clt], [], [range; zero], [g.int32_ty], Text.Range.range0)], [g.int32_ty], Text.Range.range0)
302+
let anded = mkAsmExpr ([AI_and], [], [range; negRangeLtZero], [g.int32_ty], Text.Range.range0)
303+
// range ^ (range & -(range < 0))
304+
// https://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
305+
let range = mkAsmExpr ([AI_xor], [], [range; anded], [g.int32_ty], Text.Range.range0)
305306
let v, e = mkCompGenLocal Text.Range.range0 "i" g.int32_ty
306307
let body = mkAsmExpr ([AI_add], [], [start; e], [g.int32_ty], Text.Range.range0)
307308
let initializer = mkLambda Text.Range.range0 v (body, g.int32_ty)
308-
let init = mkCallListInit g Text.Range.range0 g.int32_ty range initializer
309-
310-
let emptyLabel = generateCodeLabel ()
311-
let empty = mkLabelled Text.Range.range0 emptyLabel (mkNil g Text.Range.range0 g.int32_ty)
312-
let breakToEmptyIfStartGtFinish = mkAsmExpr ([I_brcmp (BI_bgt, emptyLabel)], [], [start; finish], [mkListTy g g.int32_ty], Text.Range.range0)
313-
314-
Some (ComputedCollectionExprLowering.Either (mkAsmExpr ([], [], [breakToEmptyIfStartGtFinish; init], [mkListTy g g.int32_ty], m), empty))
309+
Some (mkCallListInit g m g.int32_ty range initializer)
315310

316311
| SeqToList g (OptionalCoerce (OptionalSeq g amap (overallSeqExpr, overallElemTy)), m) ->
317312
let collectorTy = g.mk_ListCollector_ty overallElemTy
318313
LowerComputedListOrArraySeqExpr tcVal g amap m collectorTy overallSeqExpr
319-
|> Option.map ComputedCollectionExprLowering.Expr
320314

321315
// [|5..1|] → [||]
322316
| SeqToArray g (OptionalCoerce (OptionalSeq g amap (ConstInt32Range g (start, finish))), m) when
323317
start > finish
324318
->
325-
Some (ComputedCollectionExprLowering.Expr (mkArray (g.int32_ty, [], m)))
319+
Some (mkArray (g.int32_ty, [], m))
326320

327321
// [|1..5|] → [|1; 2; 3; 4; 5|]
328322
| SeqToArray g (OptionalCoerce (OptionalSeq g amap (ConstInt32Range g (start, finish))), m) when
329323
(finish - start) * sizeof<int32> < constArrayBytesThreshold
330324
->
331-
Some (ComputedCollectionExprLowering.Expr (mkArray (g.int32_ty, [for n in start..finish -> Expr.Const (Const.Int32 n, Text.Range.range0, g.int32_ty)], m)))
325+
Some (mkArray (g.int32_ty, [for n in start..finish -> Expr.Const (Const.Int32 n, Text.Range.range0, g.int32_ty)], m))
332326

333327
// [|start..finish|] → if start <= finish then Array.init (finish - start + 1) ((+) start) else [||]
334328
| SeqToArray g (OptionalCoerce (OptionalSeq g amap (Int32Range g (start, finish))), m) ->
335329
let diff = mkAsmExpr ([AI_sub], [], [finish; start], [g.int32_ty], Text.Range.range0)
336330
let range = mkAsmExpr ([AI_add], [], [diff; mkOne g Text.Range.range0], [g.int32_ty], Text.Range.range0)
331+
let zero = mkZero g Text.Range.range0
332+
let negRangeLtZero = mkAsmExpr ([AI_neg], [], [mkAsmExpr ([AI_clt], [], [range; zero], [g.int32_ty], Text.Range.range0)], [g.int32_ty], Text.Range.range0)
333+
let anded = mkAsmExpr ([AI_and], [], [range; negRangeLtZero], [g.int32_ty], Text.Range.range0)
334+
// range ^ (range & -(range < 0))
335+
// https://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
336+
let range = mkAsmExpr ([AI_xor], [], [range; anded], [g.int32_ty], Text.Range.range0)
337337
let v, e = mkCompGenLocal Text.Range.range0 "i" g.int32_ty
338338
let body = mkAsmExpr ([AI_add], [], [start; e], [g.int32_ty], Text.Range.range0)
339339
let initializer = mkLambda Text.Range.range0 v (body, g.int32_ty)
340-
let init = mkCallArrayInit g Text.Range.range0 g.int32_ty range initializer
341-
342-
let emptyLabel = generateCodeLabel ()
343-
let empty = mkLabelled Text.Range.range0 emptyLabel (mkArray (g.int32_ty, [], Text.Range.range0))
344-
let breakToEmptyIfStartGtFinish = mkAsmExpr ([I_brcmp (BI_bgt, emptyLabel)], [], [start; finish], [mkArrayType g g.int32_ty], Text.Range.range0)
345-
346-
Some (ComputedCollectionExprLowering.Either (mkAsmExpr ([], [], [breakToEmptyIfStartGtFinish; init], [mkArrayType g g.int32_ty], m), empty))
340+
Some (mkCallArrayInit g m g.int32_ty range initializer)
347341

348342
| SeqToArray g (OptionalCoerce (OptionalSeq g amap (overallSeqExpr, overallElemTy)), m) ->
349343
let collectorTy = g.mk_ArrayCollector_ty overallElemTy
350344
LowerComputedListOrArraySeqExpr tcVal g amap m collectorTy overallSeqExpr
351-
|> Option.map ComputedCollectionExprLowering.Expr
352345

353346
| _ -> None
354347
else

src/Compiler/Optimize/LowerComputedCollections.fsi

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,5 @@ open FSharp.Compiler.Import
66
open FSharp.Compiler.TcGlobals
77
open FSharp.Compiler.TypedTree
88

9-
[<RequireQualifiedAccess>]
10-
type ComputedCollectionExprLowering =
11-
| Expr of initExpr: Expr
12-
| Either of branch1: Expr * branch2: Expr
13-
149
val LowerComputedListOrArrayExpr:
15-
tcVal: ConstraintSolver.TcValF -> g: TcGlobals -> amap: ImportMap -> Expr -> ComputedCollectionExprLowering option
10+
tcVal: ConstraintSolver.TcValF -> g: TcGlobals -> amap: ImportMap -> Expr -> Expr option

tests/fsharp/Compiler/Language/ComputedCollectionLoweringTests.fs

Lines changed: 98 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -250,22 +250,31 @@ module ComputedCollectionLoweringTests =
250250
{
251251
252252
.maxstack 8
253-
IL_0000: ldc.i4.1
254-
IL_0001: ldc.i4 0x101
255-
IL_0006: bgt.s IL_001c
256-
257-
IL_0008: ldc.i4 0x101
258-
IL_000d: ldc.i4.1
259-
IL_000e: sub
260-
IL_000f: ldc.i4.1
261-
IL_0010: add
262-
IL_0011: ldsfld class Test/test@1 Test/test@1::@_instance
263-
IL_0016: call !!0[] [FSharp.Core]Microsoft.FSharp.Collections.ArrayModule::Initialize<int32>(int32,
253+
IL_0000: ldc.i4 0x101
254+
IL_0005: ldc.i4.1
255+
IL_0006: sub
256+
IL_0007: ldc.i4.1
257+
IL_0008: add
258+
IL_0009: ldc.i4 0x101
259+
IL_000e: ldc.i4.1
260+
IL_000f: sub
261+
IL_0010: ldc.i4.1
262+
IL_0011: add
263+
IL_0012: ldc.i4 0x101
264+
IL_0017: ldc.i4.1
265+
IL_0018: sub
266+
IL_0019: ldc.i4.1
267+
IL_001a: add
268+
IL_001b: ldc.i4.0
269+
IL_001c: clt
270+
IL_001e: neg
271+
IL_001f: and
272+
IL_0020: xor
273+
IL_0021: ldsfld class Test/test@1 Test/test@1::@_instance
274+
IL_0026: tail.
275+
IL_0028: call !!0[] [FSharp.Core]Microsoft.FSharp.Collections.ArrayModule::Initialize<int32>(int32,
264276
class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<int32,!!0>)
265-
IL_001b: ret
266-
267-
IL_001c: call !!0[] [runtime]System.Array::Empty<int32>()
268-
IL_0021: ret
277+
IL_002d: ret
269278
}
270279
271280
}
@@ -362,23 +371,32 @@ module ComputedCollectionLoweringTests =
362371
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationArgumentCountsAttribute::.ctor(int32[]) = ( 01 00 02 00 00 00 01 00 00 00 01 00 00 00 00 00 )
363372
364373
.maxstack 8
365-
IL_0000: ldarg.0
366-
IL_0001: ldarg.1
367-
IL_0002: bgt.s IL_0015
368-
369-
IL_0004: ldarg.1
370-
IL_0005: ldarg.0
371-
IL_0006: sub
372-
IL_0007: ldc.i4.1
373-
IL_0008: add
374-
IL_0009: ldarg.0
375-
IL_000a: newobj instance void Test/test@1::.ctor(int32)
376-
IL_000f: call !!0[] [FSharp.Core]Microsoft.FSharp.Collections.ArrayModule::Initialize<int32>(int32,
374+
IL_0000: ldarg.1
375+
IL_0001: ldarg.0
376+
IL_0002: sub
377+
IL_0003: ldc.i4.1
378+
IL_0004: add
379+
IL_0005: ldarg.1
380+
IL_0006: ldarg.0
381+
IL_0007: sub
382+
IL_0008: ldc.i4.1
383+
IL_0009: add
384+
IL_000a: ldarg.1
385+
IL_000b: ldarg.0
386+
IL_000c: sub
387+
IL_000d: ldc.i4.1
388+
IL_000e: add
389+
IL_000f: ldc.i4.0
390+
IL_0010: clt
391+
IL_0012: neg
392+
IL_0013: and
393+
IL_0014: xor
394+
IL_0015: ldarg.0
395+
IL_0016: newobj instance void Test/test@1::.ctor(int32)
396+
IL_001b: tail.
397+
IL_001d: call !!0[] [FSharp.Core]Microsoft.FSharp.Collections.ArrayModule::Initialize<int32>(int32,
377398
class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<int32,!!0>)
378-
IL_0014: ret
379-
380-
IL_0015: call !!0[] [runtime]System.Array::Empty<int32>()
381-
IL_001a: ret
399+
IL_0022: ret
382400
}
383401
384402
}
@@ -639,22 +657,31 @@ module ComputedCollectionLoweringTests =
639657
{
640658
641659
.maxstack 8
642-
IL_0000: ldc.i4.1
643-
IL_0001: ldc.i4.s 101
644-
IL_0003: bgt.s IL_0016
645-
646-
IL_0005: ldc.i4.s 101
647-
IL_0007: ldc.i4.1
648-
IL_0008: sub
649-
IL_0009: ldc.i4.1
650-
IL_000a: add
651-
IL_000b: ldsfld class Test/test@1 Test/test@1::@_instance
652-
IL_0010: call class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<!!0> [FSharp.Core]Microsoft.FSharp.Collections.ListModule::Initialize<int32>(int32,
660+
IL_0000: ldc.i4.s 101
661+
IL_0002: ldc.i4.1
662+
IL_0003: sub
663+
IL_0004: ldc.i4.1
664+
IL_0005: add
665+
IL_0006: ldc.i4.s 101
666+
IL_0008: ldc.i4.1
667+
IL_0009: sub
668+
IL_000a: ldc.i4.1
669+
IL_000b: add
670+
IL_000c: ldc.i4.s 101
671+
IL_000e: ldc.i4.1
672+
IL_000f: sub
673+
IL_0010: ldc.i4.1
674+
IL_0011: add
675+
IL_0012: ldc.i4.0
676+
IL_0013: clt
677+
IL_0015: neg
678+
IL_0016: and
679+
IL_0017: xor
680+
IL_0018: ldsfld class Test/test@1 Test/test@1::@_instance
681+
IL_001d: tail.
682+
IL_001f: call class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<!!0> [FSharp.Core]Microsoft.FSharp.Collections.ListModule::Initialize<int32>(int32,
653683
class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<int32,!!0>)
654-
IL_0015: ret
655-
656-
IL_0016: call class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<!0> class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<int32>::get_Empty()
657-
IL_001b: ret
684+
IL_0024: ret
658685
}
659686
660687
}
@@ -752,23 +779,32 @@ module ComputedCollectionLoweringTests =
752779
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationArgumentCountsAttribute::.ctor(int32[]) = ( 01 00 02 00 00 00 01 00 00 00 01 00 00 00 00 00 )
753780
754781
.maxstack 8
755-
IL_0000: ldarg.0
756-
IL_0001: ldarg.1
757-
IL_0002: bgt.s IL_0015
758-
759-
IL_0004: ldarg.1
760-
IL_0005: ldarg.0
761-
IL_0006: sub
762-
IL_0007: ldc.i4.1
763-
IL_0008: add
764-
IL_0009: ldarg.0
765-
IL_000a: newobj instance void Test/test@1::.ctor(int32)
766-
IL_000f: call class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<!!0> [FSharp.Core]Microsoft.FSharp.Collections.ListModule::Initialize<int32>(int32,
782+
IL_0000: ldarg.1
783+
IL_0001: ldarg.0
784+
IL_0002: sub
785+
IL_0003: ldc.i4.1
786+
IL_0004: add
787+
IL_0005: ldarg.1
788+
IL_0006: ldarg.0
789+
IL_0007: sub
790+
IL_0008: ldc.i4.1
791+
IL_0009: add
792+
IL_000a: ldarg.1
793+
IL_000b: ldarg.0
794+
IL_000c: sub
795+
IL_000d: ldc.i4.1
796+
IL_000e: add
797+
IL_000f: ldc.i4.0
798+
IL_0010: clt
799+
IL_0012: neg
800+
IL_0013: and
801+
IL_0014: xor
802+
IL_0015: ldarg.0
803+
IL_0016: newobj instance void Test/test@1::.ctor(int32)
804+
IL_001b: tail.
805+
IL_001d: call class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<!!0> [FSharp.Core]Microsoft.FSharp.Collections.ListModule::Initialize<int32>(int32,
767806
class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<int32,!!0>)
768-
IL_0014: ret
769-
770-
IL_0015: call class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<!0> class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<int32>::get_Empty()
771-
IL_001a: ret
807+
IL_0022: ret
772808
}
773809
774810
}
@@ -779,4 +815,3 @@ module ComputedCollectionLoweringTests =
779815
}
780816
"""
781817
]))
782-

0 commit comments

Comments
 (0)