Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Changed internal value to readonly for primitive types #18645

Merged
merged 3 commits into from
Jun 27, 2018

Conversation

tgiphil
Copy link

@tgiphil tgiphil commented Jun 26, 2018

See PR #18138 for more information.

@@ -226,7 +226,7 @@ public bool Equals(double obj)
[MethodImpl(MethodImplOptions.AggressiveInlining)] // 64-bit constants make the IL unusually large that makes the inliner to reject the method
public override int GetHashCode()
{
var bits = Unsafe.As<double, long>(ref m_value);
var bits = BitConverter.DoubleToInt64Bits(m_value);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will make the GetHashCode method significantly slower, I think.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really? The BitConverter.Int64BitsToDouble() method is simply:

return *((long*)&value);

Which seems more straightforward than Unsafe.As(). Any way to review the generated assembly?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which seems more straightforward than Unsafe.As().

It's not as Int64BitsToDouble will result in the double value being loaded as double, stored to a temporary and then loaded again as long. The Unsafe.As variant simply converts this (which is basically a ref in the case of structs) from ref double to ref long, that is a no-op.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mikedn Can you suggest how to get Unsafe.AsRef to work? I couldn't figure it out.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side questions: Can the compiler optimize away the cast by promoting the temporary into a register? And is there a X86/X64 instruction that can bit-copy from a floating point XMM register to a general purpose register?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jkotas I made the change to Unsafe.cs; however, I'm not exactly sure what to do with jitinterface.cpp and mscorlib.h.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you go

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll need to check if this approach doesn't impact code gen as well. Sometimes the JIT has problems with multiple calls - even if they are inlined it may still spill trees and introduce new local variables.

I'll try to take a look later today.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works fine, the JIT generates identical IR for both old and new implementations.

And it turns out that the original As call does result in a temporary variable being introduced. Fortunately it has no impact on the generated code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mikedn Thanks for taking a look!

@mikedn
Copy link

mikedn commented Jun 27, 2018

@jkotas Should perhaps someone from Roslyn take a look as well? I vaguely remember a comment indicating that Roslyn treats primitive types as readonly anyway.

@jkotas
Copy link
Member

jkotas commented Jun 27, 2018

It does in some cases, but not all cases. #18138 (comment) is an example where this makes a difference.

@jkotas
Copy link
Member

jkotas commented Jun 27, 2018

@jaredpar Do you see any Roslyn issues with marking primitive types as readonly?

@tgiphil tgiphil changed the title Changed internal value to readonly to all the primitive types Changed internal value to readonly for all the primitive types Jun 27, 2018
@tgiphil tgiphil changed the title Changed internal value to readonly for all the primitive types Changed internal value to readonly for primitive types Jun 27, 2018
@jaredpar
Copy link
Member

@jkotas

Do you see any Roslyn issues with marking primitive types as readonly?

No. We already consider such types to be readonly.

CC @OmarTawfik

[Intrinsic]
[NonVersionable]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T AsRef<T>(in T source)
Copy link
Member

@jaredpar jaredpar Jun 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand this is in the Unsafe class hence it's unsafe. I want to emphasize though that this operation is not just about violating readonly semantics. It actually can cause some pretty severe safety issues. Consider the following code as an example:

void Example(in object o) { 
  Unsafe.AsRef(in o) = new object(); // really dangerous
}

void M(string[] array) { 
  object[] objArray = array;
  Example(in objArray[0]);
}

Hence this operator allows writing objects that violate our basic type safety into locations. It's not just about violating readonly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. S.R.CS.Unsafe is a very sharp knife that lets introduce pretty severe safety issue if used incorrectly. This particular API shipped already. This change is just adding internal clone of the public API for internal CoreLib consumption.

@jkotas jkotas merged commit eeb9e89 into dotnet:master Jun 27, 2018
@jkotas
Copy link
Member

jkotas commented Jun 27, 2018

@tgiphil Could you please create a matching PR in corefx repo to update the reference assembly https://github.com/dotnet/corefx/tree/master/src/System.Runtime/ref ?

(It will not build successfully until this change propagated to CoreFX - it is fine for the PR to just sit there until that happens.)

@tgiphil
Copy link
Author

tgiphil commented Jun 28, 2018

PR at CoreFX: dotnet/corefx#30717

Please review.

MichalStrehovsky added a commit to dotnet/corert that referenced this pull request Jun 28, 2018
MichalStrehovsky added a commit to dotnet/corert that referenced this pull request Jun 29, 2018
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
Changed internal value to readonly for primitive types

Commit migrated from dotnet/coreclr@eeb9e89
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants