Skip to content

Commit

Permalink
Remove decimal alignment quirk from the type loader
Browse files Browse the repository at this point in the history
System.Decimal fields did not match Win32 DECIMAL type for historic reasons. It required type loader to have a quirk to artifically inflate System.Decimal alignment to match the alignment of Win32 DECIMAL type to make interop work well.

This change is fixing the System.Decimal fields to match Win32 DECIMAL type and removing the quirk from the type loader since it does not belong there.

The downsides are:
- Slightly lower code quality on 32-bit platforms. 32-bit platforms are not our high performance targets anymore.
- Explicit implementation of ISerializable is required on System.Decimal for binary serialization compatibility.

Fixes #38390
  • Loading branch information
jkotas committed Jun 30, 2020
1 parent 92785d6 commit f3a2d46
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 128 deletions.
17 changes: 0 additions & 17 deletions src/coreclr/src/vm/methodtablebuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9724,23 +9724,6 @@ void MethodTableBuilder::CheckForSystemTypes()
pMT->SetInternalCorElementType (ELEMENT_TYPE_I);
}
#endif
#if defined(ALIGN_ACCESS) || defined(FEATURE_64BIT_ALIGNMENT)
else if (strcmp(name, g_DecimalName) == 0)
{
// This is required because native layout of System.Decimal causes it to be aligned
// differently to the layout of the native DECIMAL structure, which will cause
// data misalignent exceptions if Decimal is embedded in another type.

EEClassLayoutInfo* pLayout = pClass->GetLayoutInfo();
pLayout->m_ManagedLargestAlignmentRequirementOfAllMembers = sizeof(ULONGLONG);

#ifdef FEATURE_64BIT_ALIGNMENT
// Also need to mark the type so it will be allocated on a 64-bit boundary for
// platforms that won't do this naturally.
SetAlign8Candidate();
#endif
}
#endif // ALIGN_ACCESS || FEATURE_64BIT_ALIGNMENT
}
else
{
Expand Down
58 changes: 28 additions & 30 deletions src/libraries/System.Private.CoreLib/src/System/Decimal.DecCalc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,15 @@ namespace System
public partial struct Decimal
{
// Low level accessors used by a DecCalc and formatting
internal uint High => (uint)hi;
internal uint Low => (uint)lo;
internal uint Mid => (uint)mid;
internal uint High => _hi32;
internal uint Low => (uint)_lo64;
internal uint Mid => (uint)(_lo64 >> 32);

internal bool IsNegative => flags < 0;
internal bool IsNegative => _flags < 0;

internal int Scale => (byte)(flags >> ScaleShift);
internal int Scale => (byte)(_flags >> ScaleShift);

#if BIGENDIAN
private ulong Low64 => ((ulong)Mid << 32) | Low;
#else
private ulong Low64 => Unsafe.As<int, ulong>(ref Unsafe.AsRef(in lo));
#endif
private ulong Low64 => _lo64;

private static ref DecCalc AsMutable(ref decimal d) => ref Unsafe.As<decimal, DecCalc>(ref d);

Expand All @@ -50,16 +46,23 @@ private struct DecCalc
private uint uflags;
[FieldOffset(4)]
private uint uhi;
#if BIGENDIAN
[FieldOffset(8)]
private uint umid;
[FieldOffset(12)]
private uint ulo;
#else
[FieldOffset(8)]
private uint ulo;
[FieldOffset(12)]
private uint umid;
#endif

/// <summary>
/// The low and mid fields combined in little-endian order
/// The low and mid fields combined
/// </summary>
[FieldOffset(8)]
private ulong ulomidLE;
private ulong ulomid;

private uint High
{
Expand All @@ -85,13 +88,8 @@ private uint Mid

private ulong Low64
{
#if BIGENDIAN
get { return ((ulong)umid << 32) | ulo; }
set { umid = (uint)(value >> 32); ulo = (uint)value; }
#else
get => ulomidLE;
set => ulomidLE = value;
#endif
get => ulomid;
set => ulomid = value;
}

private const uint SignMask = 0x80000000;
Expand Down Expand Up @@ -163,7 +161,7 @@ private static unsafe void DebugPoison<T>(ref T s) where T : unmanaged
MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref s, 1)).Fill(0xCD);
}

#region Decimal Math Helpers
#region Decimal Math Helpers

private static unsafe uint GetExponent(float f)
{
Expand Down Expand Up @@ -1249,26 +1247,26 @@ internal static long VarCyFromDec(ref DecCalc pdecIn)
/// </summary>
internal static int VarDecCmp(in decimal d1, in decimal d2)
{
if ((d2.Low | d2.Mid | d2.High) == 0)
if ((d2.Low64 | d2.High) == 0)
{
if ((d1.Low | d1.Mid | d1.High) == 0)
if ((d1.Low64 | d1.High) == 0)
return 0;
return (d1.flags >> 31) | 1;
return (d1._flags >> 31) | 1;
}
if ((d1.Low | d1.Mid | d1.High) == 0)
return -((d2.flags >> 31) | 1);
if ((d1.Low64 | d1.High) == 0)
return -((d2._flags >> 31) | 1);

int sign = (d1.flags >> 31) - (d2.flags >> 31);
int sign = (d1._flags >> 31) - (d2._flags >> 31);
if (sign != 0)
return sign;
return VarDecCmpSub(in d1, in d2);
}

private static int VarDecCmpSub(in decimal d1, in decimal d2)
{
int flags = d2.flags;
int flags = d2._flags;
int sign = (flags >> 31) | 1;
int scale = flags - d1.flags;
int scale = flags - d1._flags;

ulong low64 = d1.Low64;
uint high = d1.High;
Expand Down Expand Up @@ -1899,10 +1897,10 @@ internal static double VarR8FromDec(in decimal value)

internal static int GetHashCode(in decimal d)
{
if ((d.Low | d.Mid | d.High) == 0)
if ((d.Low64 | d.High) == 0)
return 0;

uint flags = (uint)d.flags;
uint flags = (uint)d._flags;
if ((flags & ScaleMask) == 0 || (d.Low & 1) != 0)
return (int)(flags ^ d.High ^ d.Mid ^ d.Low);

Expand Down
Loading

0 comments on commit f3a2d46

Please sign in to comment.