dotnet / dotnet/fsharp

Unnecessary static "init" field checks for some simple cases

Open
#6,454 2 comments 2 reactions 0 assignees View on GitHub
Area-Compiler-Optimization Feature Improvement
Dominant language
F#
Stars
4.3k
Forks
876
Avg merge
4d 22h
Merged PRs (30d)
144

Description

The F# compiler emits "init" field checks to prevent access of bindings before initialization is complete. This is for soundness.

However, this impacts performance for a normal implementation of some immutable types where these safety checks are not needed.

Thanks to @auduchinok for reporting this.

#### Repro steps

Consider this code:
```
[]
type S<'T>(x: 'T[]) =
static let empty = S<'T>(null)
static member Empty = empty
```
The compiled form of this code has an inserted static `init` field which is checked on each access to `Empty`, see below. The same applies for non-generic and non-struct types.

The F# compiler does this for good reasons - without it it is possible to read uninitialized bindings too easily and without any warning, e.g. consider the following small variation:
```
[]
type S2<'T>(x: 'T[]) =
static let empty = S2<'T>.M()
static member M() = empty
```
Ideally, the F# compiler would automatically detect the most important cases where safety checks are not required.

Here is the ILDASM for the original:
```
.method private specialname rtspecialname static
void .cctor() cil managed
{
// Code size 20 (0x14)
.maxstack 8
IL_0000: ldnull
IL_0001: newobj instance void valuetype A/S`1::.ctor(!0[])
IL_0006: stsfld valuetype A/S`1 valuetype A/S`1::empty
IL_000b: ldc.i4.1
IL_000c: volatile.
IL_000e: stsfld int32 valuetype A/S`1::init@4
IL_0013: ret
} // end of method S`1::.cctor

```
and
```
.method public specialname static valuetype A/S`1
get_Empty() cil managed
{
// Code size 23 (0x17)
.maxstack 8
IL_0000: volatile.
IL_0002: ldsfld int32 valuetype A/S`1::init@4
IL_0007: ldc.i4.1
IL_0008: bge.s IL_0011
IL_000a: call void [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions::FailStaticInit()
IL_000f: br.s IL_0011
IL_0011: ldsfld valuetype A/S`1 valuetype A/S`1::empty
IL_0016: ret
} // end of method S`1::get_Empty

```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.