Skip to content

Commit

Permalink
Adding Int128 and UInt128 with a base software implementation (#69204)
Browse files Browse the repository at this point in the history
* Adding barebones Int128 and UInt128 structs

* Special case Int128 and UInt128 alignment on x64 Unix and Arm64

* Implementing Int128 and UInt128

* Adding tests for Int128 and UInt128

* Updating Int128/UInt128 to respect the System V ABI ordering

* Fixing an issue with UInt128->BigInteger setting the wrong sign

* Don't use Unsafe.As in the Int128/UInt128 hex parsing logic

* Adding Int128 P/Invoke tests and ensure R2R correctly sets the packing

* Fixing some issues with the Int128 interop test for non-Windows

* Ensure that floating-point conversions exist for Int128 and UInt128

* Fixing the casing of a couple fields

* Revert "Don't use Unsafe.As in the Int128/UInt128 hex parsing logic"

This reverts commit 09e8bfc.

* Adjusting the Int128/UInt128 generic math tests to have consistent ordering

* Responding to PR feedback

* Ensure that pNativeLayoutInfo alignment is initialized for Int128/UInt128

* Don't use Unsafe.As in the Int128/UInt128 hex parsing logic

* Skip the Interop/PInvoke/Int128 tests on Mono
  • Loading branch information
tannergooding authored May 19, 2022
1 parent d8fe9cd commit 7f9ed8f
Show file tree
Hide file tree
Showing 54 changed files with 11,141 additions and 239 deletions.
28 changes: 27 additions & 1 deletion THIRD-PARTY-NOTICES.TXT
Original file line number Diff line number Diff line change
Expand Up @@ -1072,4 +1072,30 @@ Copyright (c) Microsoft Corporation.
Licensed under the MIT License.

Available at
https://github.com/microsoft/msquic/blob/main/LICENSE
https://github.com/microsoft/msquic/blob/main/LICENSE

License notice for m-ou-se/floatconv
-------------------------------

Copyright (c) 2020 Mara Bos <m-ou.se@m-ou.se>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
79 changes: 79 additions & 0 deletions src/coreclr/tools/Common/Compiler/Int128FieldLayoutAlgorithm.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Internal.TypeSystem;

using Debug = System.Diagnostics.Debug;

namespace ILCompiler
{
/// <summary>
/// Represents an algorithm that computes field layout for intrinsic integer types (Int128/UInt128).
/// </summary>
public class Int128FieldLayoutAlgorithm : FieldLayoutAlgorithm
{
private readonly FieldLayoutAlgorithm _fallbackAlgorithm;

public Int128FieldLayoutAlgorithm(FieldLayoutAlgorithm fallbackAlgorithm)
{
_fallbackAlgorithm = fallbackAlgorithm;
}

public override ComputedInstanceFieldLayout ComputeInstanceLayout(DefType defType, InstanceLayoutKind layoutKind)
{
Debug.Assert(IsIntegerType(defType));

string name = defType.Name;
Debug.Assert((name == "Int128") || (name == "UInt128"));

ComputedInstanceFieldLayout layoutFromMetadata = _fallbackAlgorithm.ComputeInstanceLayout(defType, layoutKind);

if (defType.Context.Target.IsWindows || (defType.Context.Target.PointerSize == 4))
{
return layoutFromMetadata;
}

// 64-bit Unix systems follow the System V ABI and have a 16-byte packing requirement for Int128/UInt128

return new ComputedInstanceFieldLayout
{
ByteCountUnaligned = layoutFromMetadata.ByteCountUnaligned,
ByteCountAlignment = layoutFromMetadata.ByteCountAlignment,
FieldAlignment = new LayoutInt(16),
FieldSize = layoutFromMetadata.FieldSize,
Offsets = layoutFromMetadata.Offsets,
LayoutAbiStable = true
};
}

public override ComputedStaticFieldLayout ComputeStaticFieldLayout(DefType defType, StaticLayoutKind layoutKind)
{
return _fallbackAlgorithm.ComputeStaticFieldLayout(defType, layoutKind);
}

public override bool ComputeContainsGCPointers(DefType type)
{
Debug.Assert(!_fallbackAlgorithm.ComputeContainsGCPointers(type));
return false;
}

public override bool ComputeIsUnsafeValueType(DefType type)
{
Debug.Assert(!_fallbackAlgorithm.ComputeIsUnsafeValueType(type));
return false;
}

public override ValueTypeShapeCharacteristics ComputeValueTypeShapeCharacteristics(DefType type)
{
Debug.Assert(_fallbackAlgorithm.ComputeValueTypeShapeCharacteristics(type) == ValueTypeShapeCharacteristics.None);
return ValueTypeShapeCharacteristics.None;
}

public static bool IsIntegerType(DefType type)
{
return type.IsIntrinsic
&& type.Namespace == "System."
&& ((type.Name == "Int128") || (type.Name == "UInt128"));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ internal SystemVStructRegisterPassingHelper(int totalStructSize)
FieldClassifications = new SystemVClassificationType[SYSTEMV_MAX_NUM_FIELDS_IN_REGISTER_PASSED_STRUCT];
FieldSizes = new int[SYSTEMV_MAX_NUM_FIELDS_IN_REGISTER_PASSED_STRUCT];
FieldOffsets = new int[SYSTEMV_MAX_NUM_FIELDS_IN_REGISTER_PASSED_STRUCT];

for (int i = 0; i < CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS; i++)
{
EightByteClassifications[i] = SystemVClassificationTypeNoClass;
Expand Down Expand Up @@ -94,7 +94,7 @@ public static void GetSystemVAmd64PassStructInRegisterDescriptor(TypeDesc typeDe
{
structPassInRegDescPtr = default;
structPassInRegDescPtr.passedInRegisters = false;

int typeSize = typeDesc.GetElementSize().AsInt;
if (typeDesc.IsValueType && (typeSize <= CLR_SYSTEMV_MAX_STRUCT_BYTES_TO_PASS_IN_REGISTERS))
{
Expand All @@ -114,7 +114,7 @@ public static void GetSystemVAmd64PassStructInRegisterDescriptor(TypeDesc typeDe
structPassInRegDescPtr.eightByteClassifications0 = helper.EightByteClassifications[0];
structPassInRegDescPtr.eightByteSizes0 = (byte)helper.EightByteSizes[0];
structPassInRegDescPtr.eightByteOffsets0 = (byte)helper.EightByteOffsets[0];

structPassInRegDescPtr.eightByteClassifications1 = helper.EightByteClassifications[1];
structPassInRegDescPtr.eightByteSizes1 = (byte)helper.EightByteSizes[1];
structPassInRegDescPtr.eightByteOffsets1 = (byte)helper.EightByteOffsets[1];
Expand Down Expand Up @@ -216,7 +216,7 @@ static SystemVClassificationType ReClassifyField(SystemVClassificationType origi
/// <summary>
/// Returns 'true' if the struct is passed in registers, 'false' otherwise.
/// </summary>
private static bool ClassifyEightBytes(TypeDesc typeDesc,
private static bool ClassifyEightBytes(TypeDesc typeDesc,
ref SystemVStructRegisterPassingHelper helper,
int startOffsetOfStruct)
{
Expand All @@ -239,14 +239,15 @@ private static bool ClassifyEightBytes(TypeDesc typeDesc,
return false;
}

// The SIMD Intrinsic types are meant to be handled specially and should not be passed as struct registers
// The SIMD and Int128 Intrinsic types are meant to be handled specially and should not be passed as struct registers
if (typeDesc.IsIntrinsic)
{
InstantiatedType instantiatedType = typeDesc as InstantiatedType;
if (instantiatedType != null)
{
if (VectorFieldLayoutAlgorithm.IsVectorType(instantiatedType) ||
VectorOfTFieldLayoutAlgorithm.IsVectorOfTType(instantiatedType))
VectorOfTFieldLayoutAlgorithm.IsVectorOfTType(instantiatedType) ||
Int128FieldLayoutAlgorithm.IsIntegerType(instantiatedType))
{
return false;
}
Expand Down Expand Up @@ -316,7 +317,7 @@ private static bool ClassifyEightBytes(TypeDesc typeDesc,

bool structRet = false;
structRet = ClassifyEightBytes(field.FieldType, ref helper, normalizedFieldOffset);

helper.InEmbeddedStruct = inEmbeddedStructPrev;

if (!structRet)
Expand Down Expand Up @@ -482,7 +483,7 @@ private static void AssignClassifiedEightByteTypes(ref SystemVStructRegisterPass
else if ((helper.EightByteClassifications[currentFieldEightByte] == SystemVClassificationTypeInteger) ||
(fieldClassificationType == SystemVClassificationTypeInteger))
{
Debug.Assert((fieldClassificationType != SystemVClassificationTypeIntegerReference) &&
Debug.Assert((fieldClassificationType != SystemVClassificationTypeIntegerReference) &&
(fieldClassificationType != SystemVClassificationTypeIntegerByRef));

helper.EightByteClassifications[currentFieldEightByte] = SystemVClassificationTypeInteger;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public SharedGenericsConfiguration GenericsConfig
private readonly RuntimeDeterminedFieldLayoutAlgorithm _runtimeDeterminedFieldLayoutAlgorithm = new RuntimeDeterminedFieldLayoutAlgorithm();
private readonly VectorOfTFieldLayoutAlgorithm _vectorOfTFieldLayoutAlgorithm;
private readonly VectorFieldLayoutAlgorithm _vectorFieldLayoutAlgorithm;
private readonly Int128FieldLayoutAlgorithm _int128FieldLayoutAlgorithm;

private TypeDesc[] _arrayOfTInterfaces;
private ArrayOfTRuntimeInterfacesAlgorithm _arrayOfTRuntimeInterfacesAlgorithm;
Expand All @@ -45,6 +46,7 @@ public CompilerTypeSystemContext(TargetDetails details, SharedGenericsMode gener

_vectorOfTFieldLayoutAlgorithm = new VectorOfTFieldLayoutAlgorithm(_metadataFieldLayoutAlgorithm);
_vectorFieldLayoutAlgorithm = new VectorFieldLayoutAlgorithm(_metadataFieldLayoutAlgorithm);
_int128FieldLayoutAlgorithm = new Int128FieldLayoutAlgorithm(_metadataFieldLayoutAlgorithm);

_delegateInfoHashtable = new DelegateInfoHashtable(delegateFeatures);

Expand Down Expand Up @@ -72,6 +74,8 @@ public override FieldLayoutAlgorithm GetLayoutAlgorithmForType(DefType type)
return _vectorOfTFieldLayoutAlgorithm;
else if (VectorFieldLayoutAlgorithm.IsVectorType(type))
return _vectorFieldLayoutAlgorithm;
else if (Int128FieldLayoutAlgorithm.IsIntegerType(type))
return _int128FieldLayoutAlgorithm;
else
return _metadataFieldLayoutAlgorithm;
}
Expand Down Expand Up @@ -220,8 +224,8 @@ public class SharedGenericsConfiguration
// method table.
public long UniversalCanonReflectionMethodRootHeuristic_InstantiationCount { get; }

// To avoid infinite generic recursion issues during debug type record generation, attempt to
// use canonical form for types with high generic complexity.
// To avoid infinite generic recursion issues during debug type record generation, attempt to
// use canonical form for types with high generic complexity.
public long MaxGenericDepthOfDebugRecord { get; }

public SharedGenericsConfiguration()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@
<Compile Include="..\..\Common\Compiler\HardwareIntrinsicHelpers.cs" Link="Compiler\HardwareIntrinsicHelpers.cs" />
<Compile Include="..\..\Common\Compiler\ICompilationRootProvider.cs" Link="Compiler\ICompilationRootProvider.cs" />
<Compile Include="..\..\Common\Compiler\InstructionSetSupport.cs" Link="Compiler\InstructionSetSupport.cs" />
<Compile Include="..\..\Common\Compiler\Int128FieldLayoutAlgorithm.cs" Link="Compiler\Int128FieldLayoutAlgorithm.cs" />
<Compile Include="..\..\Common\Compiler\InternalCompilerErrorException.cs" Link="Compiler\InternalCompilerErrorException.cs" />
<Compile Include="..\..\Common\Compiler\NameMangler.cs" Link="Compiler\NameMangler.cs" />
<Compile Include="..\..\Common\Compiler\SingleMethodRootProvider.cs" Link="Compiler\SingleMethodRootProvider.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public partial class ReadyToRunCompilerContext : CompilerTypeSystemContext
private SystemObjectFieldLayoutAlgorithm _systemObjectFieldLayoutAlgorithm;
private VectorOfTFieldLayoutAlgorithm _vectorOfTFieldLayoutAlgorithm;
private VectorFieldLayoutAlgorithm _vectorFieldLayoutAlgorithm;
private Int128FieldLayoutAlgorithm _int128FieldLayoutAlgorithm;

public ReadyToRunCompilerContext(TargetDetails details, SharedGenericsMode genericsMode, bool bubbleIncludesCorelib, CompilerTypeSystemContext oldTypeSystemContext = null)
: base(details, genericsMode)
Expand All @@ -55,6 +56,9 @@ public ReadyToRunCompilerContext(TargetDetails details, SharedGenericsMode gener
// No architecture has completely stable handling of Vector<T> in the abi (Arm64 may change to SVE)
_vectorOfTFieldLayoutAlgorithm = new VectorOfTFieldLayoutAlgorithm(_r2rFieldLayoutAlgorithm, _vectorFieldLayoutAlgorithm, matchingVectorType, bubbleIncludesCorelib);

// Int128 and UInt128 should be ABI stable on all currently supported platforms
_int128FieldLayoutAlgorithm = new Int128FieldLayoutAlgorithm(_r2rFieldLayoutAlgorithm);

if (oldTypeSystemContext != null)
{
InheritOpenModules(oldTypeSystemContext);
Expand All @@ -77,6 +81,10 @@ public override FieldLayoutAlgorithm GetLayoutAlgorithmForType(DefType type)
{
return _vectorFieldLayoutAlgorithm;
}
else if (Int128FieldLayoutAlgorithm.IsIntegerType(type))
{
return _int128FieldLayoutAlgorithm;
}
else
{
Debug.Assert(_r2rFieldLayoutAlgorithm != null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
<Compile Include="..\..\Common\Compiler\HardwareIntrinsicHelpers.cs" Link="Compiler\HardwareIntrinsicHelpers.cs" />
<Compile Include="..\..\Common\Compiler\ICompilationRootProvider.cs" Link="Compiler\ICompilationRootProvider.cs" />
<Compile Include="..\..\Common\Compiler\InstructionSetSupport.cs" Link="Compiler\InstructionSetSupport.cs" />
<Compile Include="..\..\Common\Compiler\Int128FieldLayoutAlgorithm.cs" Link="Compiler\Int128FieldLayoutAlgorithm.cs" />
<Compile Include="..\..\Common\Compiler\InternalCompilerErrorException.cs" Link="Compiler\InternalCompilerErrorException.cs" />
<Compile Include="..\..\Common\Compiler\NameMangler.cs" Link="Compiler\NameMangler.cs" />
<Compile Include="..\..\Common\Compiler\SingleMethodRootProvider.cs" Link="Compiler\SingleMethodRootProvider.cs" />
Expand Down
4 changes: 3 additions & 1 deletion src/coreclr/vm/classlayoutinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -955,7 +955,9 @@ EEClassNativeLayoutInfo* EEClassNativeLayoutInfo::CollectNativeLayoutFieldMetada
pNativeLayoutInfo->m_alignmentRequirement = pEEClassLayoutInfo->m_ManagedLargestAlignmentRequirementOfAllMembers;
}
else
if (pMT->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__VECTOR64T)) ||
if (pMT->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__INT128)) ||
pMT->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__UINT128)) ||
pMT->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__VECTOR64T)) ||
pMT->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__VECTOR128T)) ||
pMT->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__VECTOR256T)))
{
Expand Down
6 changes: 6 additions & 0 deletions src/coreclr/vm/classnames.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@
#define g_DateTimeOffsetClassName "System.DateTimeOffset"
#define g_DecimalClassName "System.Decimal"

#define g_Int128ClassName "System.Int128"
#define g_Int128Name "Int128"

#define g_UInt128ClassName "System.UInt128"
#define g_UInt128Name "UInt128"

#define g_Vector64ClassName "System.Runtime.Intrinsics.Vector64`1"
#define g_Vector64Name "Vector64`1"

Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/vm/corelib.h
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,9 @@ DEFINE_FIELD(DELEGATE, METHOD_PTR_AUX, _methodPtrAux)
DEFINE_METHOD(DELEGATE, CONSTRUCT_DELEGATE, DelegateConstruct, IM_Obj_IntPtr_RetVoid)
DEFINE_METHOD(DELEGATE, GET_INVOKE_METHOD, GetInvokeMethod, IM_RetIntPtr)

DEFINE_CLASS(INT128, System, Int128)
DEFINE_CLASS(UINT128, System, UInt128)

DEFINE_CLASS(DYNAMICMETHOD, ReflectionEmit, DynamicMethod)

DEFINE_CLASS(DYNAMICRESOLVER, ReflectionEmit, DynamicResolver)
Expand Down
18 changes: 16 additions & 2 deletions src/coreclr/vm/methodtablebuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9844,7 +9844,7 @@ void MethodTableBuilder::CheckForSystemTypes()

if (strcmp(nameSpace, g_IntrinsicsNS) == 0)
{
EEClassLayoutInfo * pLayout = pClass->GetLayoutInfo();
EEClassLayoutInfo* pLayout = pClass->GetLayoutInfo();

// The SIMD Hardware Intrinsic types correspond to fundamental data types in the underlying ABIs:
// * Vector64<T>: __m64
Expand All @@ -9854,7 +9854,6 @@ void MethodTableBuilder::CheckForSystemTypes()
// These __m128 and __m256 types, among other requirements, are special in that they must always
// be aligned properly.


if (strcmp(name, g_Vector64Name) == 0)
{
// The System V ABI for i386 defaults to 8-byte alignment for __m64, except for parameter passing,
Expand Down Expand Up @@ -9898,6 +9897,21 @@ void MethodTableBuilder::CheckForSystemTypes()

return;
}
#if defined(UNIX_AMD64_ABI) || defined(TARGET_ARM64)
else if (strcmp(nameSpace, g_SystemNS) == 0)
{
EEClassLayoutInfo* pLayout = pClass->GetLayoutInfo();

// These types correspond to fundamental data types in the underlying ABIs:
// * Int128: __int128
// * UInt128: unsigned __int128

if ((strcmp(name, g_Int128Name) == 0) || (strcmp(name, g_UInt128Name) == 0))
{
pLayout->m_ManagedLargestAlignmentRequirementOfAllMembers = 16; // sizeof(__int128)
}
}
#endif // UNIX_AMD64_ABI || TARGET_ARM64
}

if (g_pNullableClass != NULL)
Expand Down
12 changes: 12 additions & 0 deletions src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,9 @@
<data name="Arg_MustBeInt64" xml:space="preserve">
<value>Object must be of type Int64.</value>
</data>
<data name="Arg_MustBeInt128" xml:space="preserve">
<value>Object must be of type Int128.</value>
</data>
<data name="Arg_MustBeIntPtr" xml:space="preserve">
<value>Object must be of type IntPtr.</value>
</data>
Expand Down Expand Up @@ -567,6 +570,9 @@
<data name="Arg_MustBeUInt64" xml:space="preserve">
<value>Object must be of type UInt64.</value>
</data>
<data name="Arg_MustBeUInt128" xml:space="preserve">
<value>Object must be of type UInt128.</value>
</data>
<data name="Arg_MustBeUIntPtr" xml:space="preserve">
<value>Object must be of type UIntPtr.</value>
</data>
Expand Down Expand Up @@ -3022,6 +3028,9 @@
<data name="Overflow_Int64" xml:space="preserve">
<value>Value was either too large or too small for an Int64.</value>
</data>
<data name="Overflow_Int128" xml:space="preserve">
<value>Value was either too large or too small for an Int128.</value>
</data>
<data name="Overflow_MutexReacquireCount" xml:space="preserve">
<value>The current thread attempted to reacquire a mutex that has reached its maximum acquire count.</value>
</data>
Expand Down Expand Up @@ -3049,6 +3058,9 @@
<data name="Overflow_UInt64" xml:space="preserve">
<value>Value was either too large or too small for a UInt64.</value>
</data>
<data name="Overflow_UInt128" xml:space="preserve">
<value>Value was either too large or too small for a UInt128.</value>
</data>
<data name="PlatformNotSupported_ArgIterator" xml:space="preserve">
<value>ArgIterator is not supported on this platform.</value>
</data>
Expand Down
Loading

0 comments on commit 7f9ed8f

Please sign in to comment.