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

Select better auto-prop backing field type to help with devirtualization #30797

Closed
stephentoub opened this issue Oct 26, 2018 · 4 comments
Closed

Comments

@stephentoub
Copy link
Member

stephentoub commented Oct 26, 2018

Given code like:

public class C
{
    public static A Shared { get; } = new B();
}
public abstract class A {  public virtual void Foo(); }
public sealed class B : A { public override void Foo(); }

the compiler currently produces an equivalent for the Shared property like:

    private static readonly A <Shared>k__BackingField = new B();
    public static A Shared => <Shared>k__BackingField;

If the backing field were instead created as type B instead of as A, e.g.

    private static readonly B <Shared>k__BackingField = new B();
    public static A Shared => <Shared>k__BackingField;

it would be easier for the JIT to devirtualize calls to C.Shared.Foo(); It seems like the C# compiler could do this fairly easily, using the type of the RHS expression rather than the type of the property, as long as there isn't a conversion/boxing/etc. involved.

@benaadams
Copy link
Member

As seen in dotnet/coreclr#20637 (comment)

@jaredpar
Copy link
Member

jaredpar commented Nov 9, 2018

Spurred by @AustinWise proposing #30992 as a fix for this I followed up internally with our language design team to see if they were okay with the change. It's a border line spec'd behavior as the spec does discuss the type of emitted fields for auto-implemented properties. The feedback was that LDM does not oppose this change. It's an optimization and unobservable.

However in the discussion we found that implementing this is a lot more complex than we originally realized. Particularly because the property can be initialized in two different places: inline and in the constructor body.

public class E { 
  public object Value { get;  } = "example";
  public E() { 
    Value = new object();
  }
}

This is problematic because it creates a fundamental ordering problem in the compiler. In order to bind the constructor properly you need to have the property / field types set. In order to set the property / field types you need to bind the constructor.

We discussed this a bit and thought about all of the following options as ways to still make this doable:

Delay setting the field type

Essentially try and avoid setting the type of the backing field until after we've bound all of the constructors. That would let us make the decision with all of the right information in hand.

This isn't really feasible. From the API perspective it is possible for a consumer to inspect the properties, and their backing field, before we bind the constructor. This means we'd actually have to change the logic which calculates the field type to eagerly bind the constructors in the background. That is feasible ... but it's also a lot of work. Probably significantly more than justifies the wins that we would get.

Third type

Give auto-implemented properties three types: property, field which always matches property and the emit field which has this optimization. The emit field type would be calculated during binding and possibly not even exposed publicly.

This was rejected because the feeling was that it meant the symbol API was effectively lying to the customer at this point. That goes against our design goals.

Casting

Rather than emitting the field as the derived type, emit it as the declared type and insert a cast into the property getter for the derived type.

    private static readonly A <Shared>k__BackingField = new B();
    public static A Shared => (B)<Shared>k__BackingField;

This would be pretty doable as there is no need to publicly expose this cast type, it's an implementation detail. At the same time though it would add type checks to every single get and hence was rejected.

Overall we didn't really see a clean and simple way to do this. If there are other ideas I'd love to hear and consider them. The ones we found just aren't palatable.

Really do appreciate the effort at trying to get this done. But after digging into the design it seems like we should probably close this out.

Couple other items we came across when thinking through this:

  • Generic type parameters are tricky. Imagine a T which is constrained to class, IEnumerable and is always initialized with List<int>. Emitting a backing field as List<int> means that in order to implement the property getter we essentially have to do return (T)(object)_backingField. Messy. Probably wouldn't want to optimize this scenario.
  • Expression type of assignment. When initializing an auto-implemented property in the constructor the assignment goes directly to the backing field. The type of this assignment though needs to remain the declared type of the property, not the optimized type of the backing field.
  • The discussion also did come up as to whether this could be done for VB or not. Given that VB directly exposes the backing field it definitely cannot be done: clear back compat change.

@stephentoub
Copy link
Member Author

Thanks for investigating.

@AustinWise
Copy link
Contributor

Oh, that's a bummer. I had no idea you could assign a readonly auto property in the constructor.

I already had to add delaying binding the field type, as binding the initializer sometimes requires binding the property, causing infinite recursion. This delayed binding part of my PR was the most questionable part. Trying to also poke around constructors to find assignments seems overly complex compared to the benefit of this optimization.

stephentoub added a commit to dotnet/runtime that referenced this issue Nov 9, 2020
tqiu8 pushed a commit to tqiu8/runtime that referenced this issue Nov 9, 2020
author Stephen Toub <stoub@microsoft.com> 1604601164 -0500
committer Tammy Qiu <tammy.qiu@yahoo.com> 1604960878 -0500

Add stream conformance tests for TranscodingStream (dotnet#44248)

* Add stream conformance tests for TranscodingStream

* Special-case 0-length input buffers to TranscodingStream.Write{Async}

The base implementation of Encoder.Convert doesn't like empty inputs.  Regardless, if the input is empty, we can avoid a whole bunch of unnecessary work.

JIT: minor inliner refactoring (dotnet#44215)

Extract out the budget check logic so it can vary by inlining policy.
Use this to exempt the FullPolicy from budget checking.

Fix inline xml to dump the proper (full name) hash for inlinees.

Update range dumper to dump ranges in hex.

Remove unused QCall for WinRTSupported (dotnet#44278)

ConcurrentQueueSegment allows spinning threads to sleep. (dotnet#44265)

* Allow threads to sleep when ConcurrentQueue has many enqueuers/dequeuers.

* Update src/libraries/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentQueueSegment.cs

Co-authored-by: Stephen Toub <stoub@microsoft.com>

* Apply suggestions from code review

Co-authored-by: Stephen Toub <stoub@microsoft.com>

Co-authored-by: AMD DAYTONA EPYC <amd@amd-DAYTONA-X0.com>
Co-authored-by: Stephen Toub <stoub@microsoft.com>

File.Exists() is not null when true (dotnet#44310)

* File.Exists() is not null when true

* Fix compile

* Fix compile 2

[master][watchOS] Add simwatch64 support (dotnet#44303)

Xcode 12.2 removed 32 bits support for watchOS simulators, this PR helps to fix xamarin/xamarin-macios#9949, we have tested the new binaries and they are working as expected

![unknown](https://user-images.githubusercontent.com/204671/98253709-64413200-1f49-11eb-9774-8c5aa416fc57.png)

Co-authored-by: dalexsoto <dalexsoto@users.noreply.github.com>

Implementing support to Debugger::Break. (dotnet#44305)

Set fgOptimizedFinally flag correctly (dotnet#44268)

- Initialize to 0 at compiler startup
- Set flag when finally cloning optimization kicks in

Fixes non-deterministic generation of nop opcodes into ARM32 code

Forbid `- byref cnst` -> `+ (byref -cnst)` transformation. (dotnet#44266)

* Add a repro test.

* Forbid the transformation for byrefs.

* Update src/coreclr/src/jit/morph.cpp

Co-authored-by: Andy Ayers <andya@microsoft.com>

* Update src/coreclr/src/jit/morph.cpp

* Fix the test return value.

WriteLine is just to make sure we don't delete the value.

* improve the test.

avoid a possible overflow and don't waste time on printing.

Co-authored-by: Andy Ayers <andya@microsoft.com>

Pick libmonosgen-2.0.so from cmake install directory instead of .libs (dotnet#44291)

This aligns Linux with what we already do for all the other platforms.

Update SharedPerformanceCounter assert (dotnet#44333)

Remove silly ToString in GetCLRInstanceString (dotnet#44335)

Use targetPlatformMoniker for net5.0 and newer tfms (dotnet#43965)

* Use targetPlatformMoniker for net5.0 and newer tfms

* disabling analyzer, update version to 0.0, and use new format.

* update the targetFramework.sdk

* removing supportedOS assembly level attribute

* fix linker errors and addressing feedback

* making _TargetFrameworkWithoutPlatform as private

[sgen] Add Ward annotations to sgen_get_total_allocated_bytes (dotnet#43833)

Attempt to fix https://jenkins.mono-project.com/job/test-mono-mainline-staticanalysis/

Co-authored-by: lambdageek <lambdageek@users.noreply.github.com>

[tests] Re-enable tests fixed by dotnet#44081 (dotnet#44212)

Fixes
mono/mono#15030 and
fixes mono/mono#15031 and
fixes mono/mono#15032

Add an implicit argument coercion check. (dotnet#43386)

* Add `impCheckImplicitArgumentCoercion`.

* Fix tests with type mismatch.

* Try to fix VM signature.

* Allow to pass byref as native int.

* another fix.

* Fix another IL test.

[mono] Change CMakelists.txt "python" -> Python3_EXECUTABLE (dotnet#44340)

Debian doesn't install a "python" binary for python3.

Tweak StreamConformanceTests for cancellation (dotnet#44342)

- Avoid unnecessary timers
- Separate tests for precancellation, ReadAsync(byte[], ...) cancellation, and ReadAsync(Memory, ...) cancellation

Use Dictionary for underlying cache of ResourceSet (dotnet#44104)

Simplify catch-rethrow logic in NetworkStream (dotnet#44246)

A follow-up on dotnet#40772 (comment), simplifies and harmonizes the way we wrap exceptions into IOException. Having one catch block working with System.Exception seems to be enough here, no need for specific handling of SocketException.

Simple GT_NEG optimization for dotnet#13837 (dotnet#43921)

* Simple arithmetic optimization with GT_NEG

* Skip GT_NEG optimization when an operand is constant. Revert bitwise rotation pattern

* Fixed Value Numbering assert

* Cleaned up code and comments for simple GT_NEG optimization

* Formatting

Co-authored-by: Julie Lee <jeonlee@microsoft.com>

[master] Update dependencies from mono/linker (dotnet#44322)

* Update dependencies from https://github.com/mono/linker build 20201105.1

Microsoft.NET.ILLink.Tasks
 From Version 6.0.0-alpha.1.20527.2 -> To Version 6.0.0-alpha.1.20555.1

* Update dependencies from https://github.com/mono/linker build 20201105.2

Microsoft.NET.ILLink.Tasks
 From Version 6.0.0-alpha.1.20527.2 -> To Version 6.0.0-alpha.1.20555.2

* Disable new optimization for libraries mode (it cannot work in this mode)

Co-authored-by: dotnet-maestro[bot] <dotnet-maestro[bot]@users.noreply.github.com>
Co-authored-by: Marek Safar <marek.safar@gmail.com>

Tighten argument validation in StreamConformanceTests (dotnet#44326)

Add threshold on number of files / partition in SPMI collection (dotnet#44180)

* Add check for files count

* Fix the OS check

* decrese file limit to 1500:

* misc fix

* Do not upload to azure if mch files are zero size

Fix ELT profiler tests (dotnet#44285)

[master] Update dependencies from dotnet/arcade dotnet/llvm-project dotnet/icu (dotnet#44336)

[master] Update dependencies from dotnet/arcade dotnet/llvm-project dotnet/icu

 - Merge branch 'master' into darc-master-2211df94-2a02-4c3c-abe1-e3534e896267

Fix Send_TimeoutResponseContent_Throws (dotnet#44356)

If the client times out too quickly, the server may never have a connection to accept and will hang forever.

Match CoreCLR behaviour on thread start failure (dotnet#44124)

Co-authored-by: Aleksey Kliger (λgeek) <akliger@gmail.com>

Add slash in Windows SoD tool build (dotnet#44359)

* Add slash in Windows SoD tool build

* Update SoD search path to match output dir

* Fixup dotnet version

* Remove merge commit headers

* Disable PRs

Co-authored-by: Drew Scoggins <andrew.g.scoggins@gmail>

Reflect test path changes in .gitattributes; remove nonexistent files (dotnet#44371)

Bootstrapping a test for R2RDump (dotnet#42150)

Improve performance of Enum's generic IsDefined / GetName / GetNames (dotnet#44355)

Eliminates the boxing in IsDefined/GetName/GetValues, and in GetNames avoids having to go through RuntimeType's GetEnumNames override.

clarify http version test (dotnet#44379)

Co-authored-by: Geoffrey Kizer <geoffrek@windows.microsoft.com>

Update dependencies from https://github.com/mono/linker build 20201106.1 (dotnet#44367)

Microsoft.NET.ILLink.Tasks
 From Version 6.0.0-alpha.1.20555.2 -> To Version 6.0.0-alpha.1.20556.1

Co-authored-by: dotnet-maestro[bot] <dotnet-maestro[bot]@users.noreply.github.com>

Disable RunThreadLocalTest8_Values on Mono (dotnet#44357)

* Disable RunThreadLocalTest8_Values on Mono

It's failing on SLES

* fix typo

LongProcessNamesAreSupported: make test work on distros where sleep is a symlink/script (dotnet#44299)

* LongProcessNamesAreSupported: make test work on distros where sleep is a symlink/script

* PR feedback

Co-authored-by: Stephen Toub <stoub@microsoft.com>

* fix compilation

Co-authored-by: Stephen Toub <stoub@microsoft.com>

add missing constructor overloads (dotnet#44380)

Co-authored-by: Geoffrey Kizer <geoffrek@windows.microsoft.com>

change using in ConnectCallback_UseUnixDomainSocket_Success (dotnet#44366)

Clean up the samples (dotnet#44293)

Update dotnet/roslyn issue link

Delete stale comment about dotnet/roslyn#30797

Fix/remove TODO-NULLABLEs (dotnet#44300)

* Fix/remove TODO-NULLABLEs

* remove redundant !

* apply Jozkee's feedback

* address feedback

Update glossary (dotnet#44274)

Co-authored-by: Juan Hoyos <juan.hoyos@microsoft.com>
Co-authored-by: Stephen Toub <stoub@microsoft.com>
Co-authored-by: Günther Foidl <gue@korporal.at>

Add files need for wasm executable relinking/aot to the wasm runtime pack. (dotnet#43785)

Co-authored-by: Alexander Köplinger <alex.koeplinger@outlook.com>

Move some more UnmanagedCallersOnly tests to IL now that they're invalid C# (dotnet#43366)

Fix C++ build for mono/metadata/threads.c (dotnet#44413)

`throw` is a reserved keyword in C++.

Disable a failing test. (dotnet#44404)

Change async void System.Text.Json test to be async Task (dotnet#44418)

Improve crossgen2 comparison jobs (dotnet#44119)

- Fix compilation on unix platforms
  - Wrap use of wildcard in quotes
- Print better display name into log
- Fix X86 constant comparison handling
- Add ability to compile specific overload via single method switches

Remove some unnecessary GetTypeInfo usage (dotnet#44414)

Fix MarshalTypedArrayByte and re-enable it. Re-enable TestFunctionApply
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants