Skip to content

Commit

Permalink
Merge branch 'main' into investigatebuild
Browse files Browse the repository at this point in the history
  • Loading branch information
psfinaki committed Apr 26, 2024
2 parents 9bb7bf2 + 353560e commit 1db1063
Show file tree
Hide file tree
Showing 228 changed files with 33,767 additions and 17,937 deletions.
44 changes: 2 additions & 42 deletions docs/optimizations-equality.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,51 +209,11 @@ let f (x: float32) (y: float32) = (x = y)

### F# struct type (records, tuples - with compiler-generated structural equality)

* Semantics: User expects field-by-field structural equality with no boxing
* Semantics: User expects field-by-field structural equality
* Perf expected: no boxing
* Compilation today: `GenericEqualityIntrinsic<SomeStructType>`
* Perf today: always boxes (Problem3 ❌)
* Perf today: good ✅
* [sharplab](https://sharplab.io/#v2:DYLgZgzgNALiCWwA+BYAUAbQDwGUYCcBXAYxgD4BddGATwAcBTAAhwHsBbBvI0gCgDcQTeADsYUJoSGiYASiYBedExVNO7AEYN8TAPoA6AGqKm/ZavVadBgKonC6dMAYwmYJrwAeQtp24k5JhoTLxMaWXQgA)
* Note: the optimization path is a bit strange here, see the reductions below

<details>

<summary>Details</summary>

```fsharp
(x = y)
--inline-->
GenericEquality x y
--inline-->
GenericEqualityFast x y
--inline-->
GenericEqualityIntrinsic x y
--devirtualize-->
x.Equals(box y, LanguagePrimitives.GenericEqualityComparer);
```

The struct type has these generated methods:
```csharp
override bool Equals(object y)
override bool Equals(SomeStruct obj)
override bool Equals(object obj, IEqualityComparer comp) //with EqualsVal
```

These call each other in sequence, boxing then unboxing then boxing. We do NOT generate this method, we probably should:

```csharp
override bool Equals(SomeStruct obj, IEqualityComparer comp) //with EqualsValUnboxed
```

If we did, the devirtualizing optimization should reduce to this directly, which would result in no boxing.

</details>

Expand Down
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/8.0.300.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
* Obsolete attribute is ignored in constructor property assignment ([PR #16900](https://github.com/dotnet/fsharp/pull/16900))
* Completion: fix completion in empty dot lambda prefix ([#16829](https://github.com/dotnet/fsharp/pull/16829))
* Fix StackOverflow when checking non-recursive bindings in module or namespace in `fscAnyCpu`/`fsiAnyCpu`. ([PR #16908](https://github.com/dotnet/fsharp/pull/16908))
* Removes signature file adjacency check in Transparent Compiler [Issue #17082](https://github.com/dotnet/fsharp/issues/17082) [PR #17085](https://github.com/dotnet/fsharp/pull/17085)

### Added

Expand Down
4 changes: 4 additions & 0 deletions docs/release-notes/.FSharp.Compiler.Service/8.0.400.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@
* Files passed with -embed:relative/path/to/file are not embedded. ([Issue #16768](https://github.com/dotnet/fsharp/pull/17068))
* Fix bug in optimization of for-loops over integral ranges with steps and units of measure. ([Issue #17025](https://github.com/dotnet/fsharp/issues/17025), [PR #17040](https://github.com/dotnet/fsharp/pull/17040), [PR #17048](https://github.com/dotnet/fsharp/pull/17048))
* Fix calling an overridden virtual static method via the interface ([PR #17013](https://github.com/dotnet/fsharp/pull/17013))
* Fix state machines compilation, when big decision trees are involved, by removing code split when resumable code is detected ([PR #17076](https://github.com/dotnet/fsharp/pull/17076))

### Added

* Generate new `Equals` overload to avoid boxing for structural comparison ([PR #16857](https://github.com/dotnet/fsharp/pull/16857))
2 changes: 2 additions & 0 deletions eng/Build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,11 @@ function Process-Arguments() {

if ($buildnorealsig) {
$script:buildnorealsig = $True;
$env:FSHARP_REALSIG="false"
}
else {
$script:buildnorealsig = $False;
$env:FSHARP_REALSIG="true"
}
if ($verifypackageshipstatus) {
$script:verifypackageshipstatus = $True;
Expand Down
115 changes: 101 additions & 14 deletions src/Compiler/Checking/AugmentWithHashCompare.fs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ let mkEqualsTy g ty =
let mkEqualsWithComparerTy g ty =
mkFunTy g (mkThisTy g ty) (mkFunTy g (mkRefTupledTy g [ g.obj_ty; g.IEqualityComparer_ty ]) g.bool_ty)

let mkEqualsWithComparerTyExact g ty =
mkFunTy g (mkThisTy g ty) (mkFunTy g (mkRefTupledTy g [ ty; g.IEqualityComparer_ty ]) g.bool_ty)

let mkHashTy g ty =
mkFunTy g (mkThisTy g ty) (mkFunTy g g.unit_ty g.int_ty)

Expand Down Expand Up @@ -361,7 +364,7 @@ let mkRecdEquality g tcref (tycon: Tycon) =
thisv, thatv, expr

/// Build the equality implementation for a record type when parameterized by a comparer
let mkRecdEqualityWithComparer g tcref (tycon: Tycon) (_thisv, thise) thatobje (thatv, thate) compe =
let mkRecdEqualityWithComparer g tcref (tycon: Tycon) thise thatobje (thatv, thate) compe isexact =
let m = tycon.Range
let fields = tycon.AllInstanceFieldsAsList
let tinst, ty = mkMinimalTy g tcref
Expand All @@ -382,14 +385,21 @@ let mkRecdEqualityWithComparer g tcref (tycon: Tycon) (_thisv, thise) thatobje (
let expr = mkEqualsTestConjuncts g m (List.map mkTest fields)

let expr = mkBindThatAddr g m ty thataddrv thatv thate expr
// will be optimized away if not necessary
let expr = mkIsInstConditional g m ty thatobje thatv expr (mkFalse g m)

let expr =
if isexact then
expr
else
mkIsInstConditional g m ty thatobje thatv expr (mkFalse g m)

let expr =
if tycon.IsStructOrEnumTycon then
expr
else
mkBindThisNullEquals g m thise thatobje expr
if isexact then
mkBindThatNullEquals g m thise thate expr
else
mkBindThisNullEquals g m thise thatobje expr

expr

Expand Down Expand Up @@ -425,7 +435,7 @@ let mkExnEquality (g: TcGlobals) exnref (exnc: Tycon) =
thisv, thatv, expr

/// Build the equality implementation for an exception definition when parameterized by a comparer
let mkExnEqualityWithComparer g exnref (exnc: Tycon) (_thisv, thise) thatobje (thatv, thate) compe =
let mkExnEqualityWithComparer g exnref (exnc: Tycon) thise thatobje (thatv, thate) compe isexact =
let m = exnc.Range
let thataddrv, thataddre = mkThatAddrLocal g m g.exn_ty

Expand Down Expand Up @@ -453,13 +463,21 @@ let mkExnEqualityWithComparer g exnref (exnc: Tycon) (_thisv, thise) thatobje (t
mbuilder.Close(dtree, m, g.bool_ty)

let expr = mkBindThatAddr g m g.exn_ty thataddrv thatv thate expr
let expr = mkIsInstConditional g m g.exn_ty thatobje thatv expr (mkFalse g m)

let expr =
if isexact then
expr
else
mkIsInstConditional g m g.exn_ty thatobje thatv expr (mkFalse g m)

let expr =
if exnc.IsStructOrEnumTycon then
expr
else
mkBindThisNullEquals g m thise thatobje expr
if isexact then
mkBindThatNullEquals g m thise thate expr
else
mkBindThisNullEquals g m thise thatobje expr

expr

Expand Down Expand Up @@ -758,7 +776,7 @@ let mkUnionEquality g tcref (tycon: Tycon) =
thisv, thatv, expr

/// Build the equality implementation for a union type when parameterized by a comparer
let mkUnionEqualityWithComparer g tcref (tycon: Tycon) (_thisv, thise) thatobje (thatv, thate) compe =
let mkUnionEqualityWithComparer g tcref (tycon: Tycon) thise thatobje (thatv, thate) compe isexact =
let m = tycon.Range
let ucases = tycon.UnionCasesAsList
let tinst, ty = mkMinimalTy g tcref
Expand Down Expand Up @@ -846,13 +864,21 @@ let mkUnionEqualityWithComparer g tcref (tycon: Tycon) (_thisv, thise) thatobje
(mkCompGenLet m thattagv (mkUnionCaseTagGetViaExprAddr (thataddre, tcref, tinst, m)) tagsEqTested)

let expr = mkBindThatAddr g m ty thataddrv thatv thate expr
let expr = mkIsInstConditional g m ty thatobje thatv expr (mkFalse g m)

let expr =
if isexact then
expr
else
mkIsInstConditional g m ty thatobje thatv expr (mkFalse g m)

let expr =
if tycon.IsStructOrEnumTycon then
expr
else
mkBindThisNullEquals g m thise thatobje expr
if isexact then
mkBindThatNullEquals g m thise thate expr
else
mkBindThisNullEquals g m thise thatobje expr

expr

Expand Down Expand Up @@ -1014,6 +1040,15 @@ let getAugmentationAttribs g (tycon: Tycon) =
TryFindFSharpBoolAttribute g g.attrib_CustomComparisonAttribute tycon.Attribs,
TryFindFSharpBoolAttribute g g.attrib_StructuralComparisonAttribute tycon.Attribs

[<NoEquality; NoComparison; StructuredFormatDisplay("{DebugText}")>]
type EqualityWithComparerAugmentation =
{
GetHashCode: Val
GetHashCodeWithComparer: Val
EqualsWithComparer: Val
EqualsExactWithComparer: Val
}

let CheckAugmentationAttribs isImplementation g amap (tycon: Tycon) =
let m = tycon.Range
let attribs = getAugmentationAttribs g tycon
Expand Down Expand Up @@ -1333,7 +1368,25 @@ let MakeValsForEqualityWithComparerAugmentation g (tcref: TyconRef) =
let withcEqualsVal =
mkValSpec g tcref ty vis (Some(mkIStructuralEquatableEqualsSlotSig g)) "Equals" (tps +-> (mkEqualsWithComparerTy g ty)) tupArg false

objGetHashCodeVal, withcGetHashCodeVal, withcEqualsVal
let withcEqualsValExact =
mkValSpec
g
tcref
ty
vis
// This doesn't implement any interface.
None
"Equals"
(tps +-> (mkEqualsWithComparerTyExact g ty))
tupArg
false

{
GetHashCode = objGetHashCodeVal
GetHashCodeWithComparer = withcGetHashCodeVal
EqualsWithComparer = withcEqualsVal
EqualsExactWithComparer = withcEqualsValExact
}

let MakeBindingsForCompareAugmentation g (tycon: Tycon) =
let tcref = mkLocalTyconRef tycon
Expand Down Expand Up @@ -1419,7 +1472,7 @@ let MakeBindingsForEqualityWithComparerAugmentation (g: TcGlobals) (tycon: Tycon
let mkStructuralEquatable hashf equalsf =
match tycon.GeneratedHashAndEqualsWithComparerValues with
| None -> []
| Some(objGetHashCodeVal, withcGetHashCodeVal, withcEqualsVal) ->
| Some(objGetHashCodeVal, withcGetHashCodeVal, withcEqualsVal, withcEqualsExactValOption) ->

// build the hash rhs
let withcGetHashCodeExpr =
Expand Down Expand Up @@ -1451,12 +1504,33 @@ let MakeBindingsForEqualityWithComparerAugmentation (g: TcGlobals) (tycon: Tycon

// build the equals rhs
let withcEqualsExpr =
let _tinst, ty = mkMinimalTy g tcref
let tinst, ty = mkMinimalTy g tcref
let thisv, thise = mkThisVar g m ty
let thatobjv, thatobje = mkCompGenLocal m "obj" g.obj_ty
let thatv, thate = mkCompGenLocal m "that" ty
let compv, compe = mkCompGenLocal m "comp" g.IEqualityComparer_ty
let equalse = equalsf g tcref tycon (thisv, thise) thatobje (thatv, thate) compe

// if the new overload is available, use it
// otherwise, generate the whole equals thing
let equalse =
match withcEqualsExactValOption with
| Some withcEqualsExactVal ->
mkIsInstConditional
g
m
ty
thatobje
thatv
(mkApps
g
((exprForValRef m withcEqualsExactVal, withcEqualsExactVal.Type),
(if isNil tinst then [] else [ tinst ]),
[ thise; mkRefTupled g m [ thate; compe ] [ty; g.IEqualityComparer_ty ] ],
m))
(mkFalse g m)
| None ->
equalsf g tcref tycon thise thatobje (thatv, thate) compe false

mkMultiLambdas g m tps [ [ thisv ]; [ thatobjv; compv ] ] (equalse, g.bool_ty)

let objGetHashCodeExpr =
Expand All @@ -1481,9 +1555,22 @@ let MakeBindingsForEqualityWithComparerAugmentation (g: TcGlobals) (tycon: Tycon

mkLambdas g m tps [ thisv; unitv ] (hashe, g.int_ty)

let withcEqualsExactExpr =
let _tinst, ty = mkMinimalTy g tcref
let thisv, thise = mkThisVar g m ty
let thatv, thate = mkCompGenLocal m "obj" ty
let compv, compe = mkCompGenLocal m "comp" g.IEqualityComparer_ty

let equalse = equalsf g tcref tycon thise thate (thatv, thate) compe true

mkMultiLambdas g m tps [ [ thisv ]; [ thatv; compv ] ] (equalse, g.bool_ty)

[
(mkCompGenBind withcGetHashCodeVal.Deref withcGetHashCodeExpr)
(mkCompGenBind objGetHashCodeVal.Deref objGetHashCodeExpr)
match withcEqualsExactValOption with
| Some withcEqualsExactVal -> mkCompGenBind withcEqualsExactVal.Deref withcEqualsExactExpr
| None -> ()
(mkCompGenBind withcEqualsVal.Deref withcEqualsExpr)
]

Expand Down
9 changes: 8 additions & 1 deletion src/Compiler/Checking/AugmentWithHashCompare.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ open FSharp.Compiler
open FSharp.Compiler.TypedTree
open FSharp.Compiler.TcGlobals

[<NoEquality; NoComparison; StructuredFormatDisplay("{DebugText}")>]
type EqualityWithComparerAugmentation =
{ GetHashCode: Val
GetHashCodeWithComparer: Val
EqualsWithComparer: Val
EqualsExactWithComparer: Val }

val CheckAugmentationAttribs: bool -> TcGlobals -> Import.ImportMap -> Tycon -> unit

val TyconIsCandidateForAugmentationWithCompare: TcGlobals -> Tycon -> bool
Expand All @@ -21,7 +28,7 @@ val MakeValsForCompareWithComparerAugmentation: TcGlobals -> TyconRef -> Val

val MakeValsForEqualsAugmentation: TcGlobals -> TyconRef -> Val * Val

val MakeValsForEqualityWithComparerAugmentation: TcGlobals -> TyconRef -> Val * Val * Val
val MakeValsForEqualityWithComparerAugmentation: TcGlobals -> TyconRef -> EqualityWithComparerAugmentation

val MakeBindingsForCompareAugmentation: TcGlobals -> Tycon -> Binding list

Expand Down

0 comments on commit 1db1063

Please sign in to comment.