Changed internal value to readonly for primitive types#18645
Changed internal value to readonly for primitive types#18645jkotas merged 3 commits intodotnet:masterfrom
Conversation
| public override int GetHashCode() | ||
| { | ||
| var bits = Unsafe.As<double, long>(ref m_value); | ||
| var bits = BitConverter.DoubleToInt64Bits(m_value); |
There was a problem hiding this comment.
This will make the GetHashCode method significantly slower, I think.
There was a problem hiding this comment.
Really? The BitConverter.Int64BitsToDouble() method is simply:
Which seems more straightforward than Unsafe.As(). Any way to review the generated assembly?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
@mikedn Can you suggest how to get Unsafe.AsRef to work? I couldn't figure it out.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
@jkotas I made the change to Unsafe.cs; however, I'm not exactly sure what to do with jitinterface.cpp and mscorlib.h.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
|
@jkotas Should perhaps someone from Roslyn take a look as well? I vaguely remember a comment indicating that Roslyn treats primitive types as |
|
It does in some cases, but not all cases. #18138 (comment) is an example where this makes a difference. |
|
@jaredpar Do you see any Roslyn issues with marking primitive types as readonly? |
No. We already consider such types to be CC @OmarTawfik |
| [Intrinsic] | ||
| [NonVersionable] | ||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
| public static ref T AsRef<T>(in T source) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
|
@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.) |
|
PR at CoreFX: dotnet/corefx#30717 Please review. |
Changed internal value to readonly for primitive types Commit migrated from dotnet/coreclr@eeb9e89
See PR #18138 for more information.