diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/CompilerServices/InlineArrayAttribute.cs b/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/CompilerServices/InlineArrayAttribute.cs new file mode 100644 index 00000000000000..f7d47791c7e310 --- /dev/null +++ b/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/CompilerServices/InlineArrayAttribute.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Struct, AllowMultiple = false)] + public sealed class InlineArrayAttribute : Attribute + { + public InlineArrayAttribute(int length) + { + Length = length; + } + + public int Length { get; } + } +} diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj b/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj index ec2e93c06ad04e..fc0d6ab6198c7f 100644 --- a/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj +++ b/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj @@ -218,6 +218,7 @@ + diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 5038d92ad386e9..2276c5cdc9a7cc 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -6,6 +6,7 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Text; using Internal.Pgo; namespace Internal.JitInterface @@ -1507,11 +1508,22 @@ private struct SwiftLoweredTypes public CorInfoType type; } + [InlineArray(4)] + private struct LoweredOffsets + { + public uint offset; + } + private SwiftLoweredTypes _loweredElements; [UnscopedRef] public Span LoweredElements => _loweredElements; + private LoweredOffsets _offsets; + + [UnscopedRef] + public Span Offsets => _offsets; + public nint numLoweredElements; // Override for a better unit test experience @@ -1522,7 +1534,20 @@ public override string ToString() return "byReference"; } - return string.Join(", ", LoweredElements[0..(int)numLoweredElements].ToArray()); + var stringBuilder = new StringBuilder(); + stringBuilder.Append('{'); + for (int i = 0; i < numLoweredElements; i++) + { + if (i != 0) + { + stringBuilder.Append(", "); + } + stringBuilder.Append(LoweredElements[i]); + stringBuilder.Append(": "); + stringBuilder.Append(Offsets[i]); + } + stringBuilder.Append('}'); + return stringBuilder.ToString(); } } } diff --git a/src/coreclr/tools/Common/JitInterface/SwiftPhysicalLowering.cs b/src/coreclr/tools/Common/JitInterface/SwiftPhysicalLowering.cs index 3dacc86f44340b..1a47536f67f9a5 100644 --- a/src/coreclr/tools/Common/JitInterface/SwiftPhysicalLowering.cs +++ b/src/coreclr/tools/Common/JitInterface/SwiftPhysicalLowering.cs @@ -1,11 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; -using System.Linq; using System.Runtime.InteropServices; using Internal.TypeSystem; @@ -29,7 +26,19 @@ private sealed class LoweringVisitor(int pointerSize) : FieldLayoutIntervalCalcu protected override bool IntervalsHaveCompatibleTags(LoweredType existingTag, LoweredType nextTag) { // Adjacent ranges mapped to opaque or empty can be combined. - return existingTag is LoweredType.Opaque or LoweredType.Empty && nextTag is LoweredType.Opaque or LoweredType.Empty; + if (existingTag is LoweredType.Empty + && nextTag is LoweredType.Empty) + { + return true; + } + + if (existingTag is LoweredType.Opaque + && nextTag is LoweredType.Opaque) + { + return true; + } + + return false; } protected override FieldLayoutInterval CombineIntervals(FieldLayoutInterval firstInterval, FieldLayoutInterval nextInterval) @@ -94,13 +103,39 @@ protected override LoweredType GetIntervalDataForType(int offset, TypeDesc field protected override bool NeedsRecursiveLayout(int offset, TypeDesc fieldType) => fieldType.IsValueType && !fieldType.IsPrimitiveNumeric; - public List GetLoweredTypeSequence() + private List CreateConsolidatedIntervals() { - // We need to track the sequence size to ensure we break up the opaque ranges - // into correctly-sized integers that do not require padding. - int loweredSequenceSize = 0; - List loweredTypes = new(); + // First, let's make a list of exclusively non-empty intervals. + List consolidatedIntervals = new(Intervals.Count); foreach (var interval in Intervals) + { + if (interval.Tag != LoweredType.Empty) + { + consolidatedIntervals.Add(interval); + } + } + + // Now, we'll look for adjacent opaque intervals and combine them. + for (int i = 0; i < consolidatedIntervals.Count - 1; i++) + { + // Only merge sequential opaque intervals that are within the same PointerSize-sized chunk. + if (consolidatedIntervals[i].Tag == LoweredType.Opaque + && consolidatedIntervals[i + 1].Tag == LoweredType.Opaque + && (consolidatedIntervals[i].EndSentinel - 1) / PointerSize == consolidatedIntervals[i + 1].Start / PointerSize) + { + consolidatedIntervals[i] = CombineIntervals(consolidatedIntervals[i], consolidatedIntervals[i + 1]); + consolidatedIntervals.RemoveAt(i + 1); + i--; + } + } + + return consolidatedIntervals; + } + + public List<(CorInfoType, int)> GetLoweredTypeSequence() + { + List<(CorInfoType, int)> loweredTypes = new(); + foreach (var interval in CreateConsolidatedIntervals()) { // Empty intervals at this point don't need to be represented in the lowered type sequence. // We want to skip over them. @@ -109,26 +144,20 @@ public List GetLoweredTypeSequence() if (interval.Tag == LoweredType.Float) { - loweredTypes.Add(CorInfoType.CORINFO_TYPE_FLOAT); - loweredSequenceSize = loweredSequenceSize.AlignUp(4); - loweredSequenceSize += 4; + loweredTypes.Add((CorInfoType.CORINFO_TYPE_FLOAT, interval.Start)); } if (interval.Tag == LoweredType.Double) { - loweredTypes.Add(CorInfoType.CORINFO_TYPE_DOUBLE); - loweredSequenceSize = loweredSequenceSize.AlignUp(8); - loweredSequenceSize += 8; + loweredTypes.Add((CorInfoType.CORINFO_TYPE_DOUBLE, interval.Start)); } if (interval.Tag == LoweredType.Int64) { - loweredTypes.Add(CorInfoType.CORINFO_TYPE_LONG); - loweredSequenceSize = loweredSequenceSize.AlignUp(8); - loweredSequenceSize += 8; + loweredTypes.Add((CorInfoType.CORINFO_TYPE_LONG, interval.Start)); } - if (interval.Tag == LoweredType.Opaque) + if (interval.Tag is LoweredType.Opaque) { // We need to split the opaque ranges into integer parameters. // As part of this splitting, we must ensure that we don't introduce alignment padding. @@ -138,40 +167,42 @@ public List GetLoweredTypeSequence() // The lowered range is allowed to extend past the end of the opaque range (including past the end of the struct), // but not into the next non-empty interval. // However, due to the properties of the lowering (the only non-8 byte elements of the lowering are 4-byte floats), - // we'll never encounter a scneario where we need would need to account for a correctly-aligned + // we'll never encounter a scenario where we need would need to account for a correctly-aligned // opaque range of > 4 bytes that we must not pad to 8 bytes. - // As long as we need to fill more than 4 bytes and the sequence is currently 8-byte aligned, we'll split into 8-byte integers. - // If we have more than 2 bytes but less than 4 and the sequence is 4-byte aligned, we'll use a 4-byte integer to represent the rest of the parameters. - // If we have 2 bytes and the sequence is 2-byte aligned, we'll use a 2-byte integer to represent the rest of the parameters. - // If we have 1 byte, we'll use a 1-byte integer to represent the rest of the parameters. + // While we need to fill more than 4 bytes and the sequence is currently 8-byte aligned, we'll split into 8-byte integers. + // While we need to fill more than 2 bytes but less than 4 and the sequence is 4-byte aligned, we'll use a 4-byte integer to represent the rest of the parameters. + // While we need to fill more than 1 bytes and the sequence is 2-byte aligned, we'll use a 2-byte integer to represent the rest of the parameters. + // While we need to fill at least 1 byte, we'll use a 1-byte integer to represent the rest of the parameters. + int opaqueIntervalStart = interval.Start; int remainingIntervalSize = interval.Size; - while (remainingIntervalSize > 4 && loweredSequenceSize == loweredSequenceSize.AlignUp(8)) + while (remainingIntervalSize > 0) { - loweredTypes.Add(CorInfoType.CORINFO_TYPE_LONG); - loweredSequenceSize += 8; - remainingIntervalSize -= 8; - } - - if (remainingIntervalSize > 2 && loweredSequenceSize == loweredSequenceSize.AlignUp(4)) - { - loweredTypes.Add(CorInfoType.CORINFO_TYPE_INT); - loweredSequenceSize += 4; - remainingIntervalSize -= 4; - } - - if (remainingIntervalSize > 1 && loweredSequenceSize == loweredSequenceSize.AlignUp(2)) - { - loweredTypes.Add(CorInfoType.CORINFO_TYPE_SHORT); - loweredSequenceSize += 2; - remainingIntervalSize -= 2; - } - - if (remainingIntervalSize == 1) - { - loweredSequenceSize += 1; - loweredTypes.Add(CorInfoType.CORINFO_TYPE_BYTE); + if (remainingIntervalSize > 4 && opaqueIntervalStart == opaqueIntervalStart.AlignUp(8)) + { + loweredTypes.Add((CorInfoType.CORINFO_TYPE_LONG, opaqueIntervalStart)); + opaqueIntervalStart += 8; + remainingIntervalSize -= 8; + } + else if (remainingIntervalSize > 2 && opaqueIntervalStart == opaqueIntervalStart.AlignUp(4)) + { + loweredTypes.Add((CorInfoType.CORINFO_TYPE_INT, opaqueIntervalStart)); + opaqueIntervalStart += 4; + remainingIntervalSize -= 4; + } + else if (remainingIntervalSize > 1 && opaqueIntervalStart == opaqueIntervalStart.AlignUp(2)) + { + loweredTypes.Add((CorInfoType.CORINFO_TYPE_SHORT, opaqueIntervalStart)); + opaqueIntervalStart += 2; + remainingIntervalSize -= 2; + } + else + { + opaqueIntervalStart++; + remainingIntervalSize--; + loweredTypes.Add((CorInfoType.CORINFO_TYPE_BYTE, opaqueIntervalStart)); + } } } } @@ -190,7 +221,7 @@ public static CORINFO_SWIFT_LOWERING LowerTypeForSwiftSignature(TypeDesc type) LoweringVisitor visitor = new(type.Context.Target.PointerSize); visitor.AddFields(type, addTrailingEmptyInterval: false); - List loweredTypes = visitor.GetLoweredTypeSequence(); + List<(CorInfoType type, int offset)> loweredTypes = visitor.GetLoweredTypeSequence(); // If a type has a primitive sequence with more than 4 elements, Swift passes it by reference. if (loweredTypes.Count > 4) @@ -204,7 +235,11 @@ public static CORINFO_SWIFT_LOWERING LowerTypeForSwiftSignature(TypeDesc type) numLoweredElements = loweredTypes.Count }; - CollectionsMarshal.AsSpan(loweredTypes).CopyTo(lowering.LoweredElements); + for (int i = 0; i < loweredTypes.Count; i++) + { + lowering.LoweredElements[i] = loweredTypes[i].type; + lowering.Offsets[i] = (uint)loweredTypes[i].offset; + } return lowering; } diff --git a/src/coreclr/tools/Common/TypeSystem/Common/FieldLayoutIntervalCalculator.cs b/src/coreclr/tools/Common/TypeSystem/Common/FieldLayoutIntervalCalculator.cs index d6edbfc795bc1d..9f909eccc35809 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/FieldLayoutIntervalCalculator.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/FieldLayoutIntervalCalculator.cs @@ -86,6 +86,11 @@ public void AddToFieldLayout(int offset, TypeDesc fieldType, bool addTrailingEmp { if (NeedsRecursiveLayout(offset, fieldType)) { + if (fieldType is MetadataType { IsInlineArray: true } mdType) + { + fieldType = new TypeWithRepeatedFields(mdType); + } + List nestedIntervals = new List(); foreach (FieldDesc field in fieldType.GetFields()) { @@ -123,6 +128,11 @@ private void AddToFieldLayout(List fieldLayout, int offset, { if (NeedsRecursiveLayout(offset, fieldType)) { + if (fieldType is MetadataType { IsInlineArray: true } mdType) + { + fieldType = new TypeWithRepeatedFields(mdType); + } + foreach (FieldDesc field in fieldType.GetFields()) { int fieldOffset = offset + field.Offset.AsInt; @@ -246,7 +256,7 @@ private void MergeIntervalWithNeighboringIntervals(List fie break; } - if ((previousInterval.EndSentinel == expandedInterval.Start) && !IntervalsHaveCompatibleTags(expandedInterval.Tag, previousInterval.Tag)) + if ((previousInterval.EndSentinel == expandedInterval.Start) && !IntervalsHaveCompatibleTags(previousInterval.Tag, expandedInterval.Tag)) { // Expanded interval starts just after previous interval, but does not match tag. Expansion succeeded break; diff --git a/src/coreclr/tools/Common/TypeSystem/Common/ImpliedRepeatedFieldDesc.Sorting.cs b/src/coreclr/tools/Common/TypeSystem/Common/ImpliedRepeatedFieldDesc.Sorting.cs new file mode 100644 index 00000000000000..ef3b77aecbec20 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Common/ImpliedRepeatedFieldDesc.Sorting.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Internal.TypeSystem +{ + public sealed partial class ImpliedRepeatedFieldDesc : FieldDesc + { + protected internal override int CompareToImpl(FieldDesc other, TypeSystemComparer comparer) + { + var impliedRepeatedFieldDesc = (ImpliedRepeatedFieldDesc)other; + + int result = comparer.Compare(_underlyingFieldDesc, impliedRepeatedFieldDesc._underlyingFieldDesc); + + if (result != 0) + { + return result; + } + + return FieldIndex.CompareTo(impliedRepeatedFieldDesc.FieldIndex); + } + + protected internal override int ClassCode => 31666958; + } +} diff --git a/src/coreclr/tools/Common/Compiler/ImpliedRepeatedFieldDesc.cs b/src/coreclr/tools/Common/TypeSystem/Common/ImpliedRepeatedFieldDesc.cs similarity index 73% rename from src/coreclr/tools/Common/Compiler/ImpliedRepeatedFieldDesc.cs rename to src/coreclr/tools/Common/TypeSystem/Common/ImpliedRepeatedFieldDesc.cs index a4f6a4ab55b379..93829636916d31 100644 --- a/src/coreclr/tools/Common/Compiler/ImpliedRepeatedFieldDesc.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/ImpliedRepeatedFieldDesc.cs @@ -1,11 +1,9 @@ // 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; - -namespace ILCompiler +namespace Internal.TypeSystem { - public sealed class ImpliedRepeatedFieldDesc : FieldDesc + public sealed partial class ImpliedRepeatedFieldDesc : FieldDesc { private readonly FieldDesc _underlyingFieldDesc; @@ -36,26 +34,10 @@ public ImpliedRepeatedFieldDesc(DefType owningType, FieldDesc underlyingFieldDes public int FieldIndex { get; } - protected override int ClassCode => 31666958; - public override EmbeddedSignatureData[] GetEmbeddedSignatureData() => _underlyingFieldDesc.GetEmbeddedSignatureData(); public override bool HasCustomAttribute(string attributeNamespace, string attributeName) => _underlyingFieldDesc.HasCustomAttribute(attributeNamespace, attributeName); - protected override int CompareToImpl(FieldDesc other, TypeSystemComparer comparer) - { - var impliedRepeatedFieldDesc = (ImpliedRepeatedFieldDesc)other; - - int result = comparer.Compare(_underlyingFieldDesc, impliedRepeatedFieldDesc._underlyingFieldDesc); - - if (result != 0) - { - return result; - } - - return FieldIndex.CompareTo(impliedRepeatedFieldDesc.FieldIndex); - } - public override MarshalAsDescriptor GetMarshalAsDescriptor() => _underlyingFieldDesc.GetMarshalAsDescriptor(); public override string Name => $"{_underlyingFieldDesc.Name}[{FieldIndex}]"; diff --git a/src/coreclr/tools/Common/TypeSystem/Common/TypeWithRepeatedFields.Diagnostic.cs b/src/coreclr/tools/Common/TypeSystem/Common/TypeWithRepeatedFields.Diagnostic.cs new file mode 100644 index 00000000000000..7dc033e16292ac --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Common/TypeWithRepeatedFields.Diagnostic.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; + +namespace Internal.TypeSystem +{ + /// + /// This type represents a type that has one field in metadata, + /// but has that field repeated at runtime to represent an array of elements inline. + /// + public sealed partial class TypeWithRepeatedFields : MetadataType + { + public override string DiagnosticName => MetadataType.DiagnosticName; + + public override string DiagnosticNamespace => MetadataType.DiagnosticNamespace; + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Common/TypeWithRepeatedFields.Sorting.cs b/src/coreclr/tools/Common/TypeSystem/Common/TypeWithRepeatedFields.Sorting.cs new file mode 100644 index 00000000000000..6d9fc17daff3dc --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Common/TypeWithRepeatedFields.Sorting.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; + +namespace Internal.TypeSystem +{ + /// + /// This type represents a type that has one field in metadata, + /// but has that field repeated at runtime to represent an array of elements inline. + /// + public sealed partial class TypeWithRepeatedFields : MetadataType + { + protected internal override int CompareToImpl(TypeDesc other, TypeSystemComparer comparer) => comparer.Compare(MetadataType, ((TypeWithRepeatedFields)other).MetadataType); + protected internal override int ClassCode => 779393465; + } +} diff --git a/src/coreclr/tools/Common/Compiler/TypeWithRepeatedFields.cs b/src/coreclr/tools/Common/TypeSystem/Common/TypeWithRepeatedFields.cs similarity index 72% rename from src/coreclr/tools/Common/Compiler/TypeWithRepeatedFields.cs rename to src/coreclr/tools/Common/TypeSystem/Common/TypeWithRepeatedFields.cs index 48382f075ea0d8..158925c84e647d 100644 --- a/src/coreclr/tools/Common/Compiler/TypeWithRepeatedFields.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/TypeWithRepeatedFields.cs @@ -5,9 +5,8 @@ using System.Collections.Generic; using System.Diagnostics; using System.Threading; -using Internal.TypeSystem; -namespace ILCompiler +namespace Internal.TypeSystem { /// /// This type represents a type that has one field in metadata, @@ -90,49 +89,8 @@ public override IEnumerable GetFields() public override MethodImplRecord[] FindMethodsImplWithMatchingDeclName(string name) => MetadataType.FindMethodsImplWithMatchingDeclName(name); public override int GetHashCode() => MetadataType.GetHashCode(); protected override MethodImplRecord[] ComputeVirtualMethodImplsForType() => Array.Empty(); - protected override int CompareToImpl(TypeDesc other, TypeSystemComparer comparer) => comparer.Compare(MetadataType, ((TypeWithRepeatedFields)other).MetadataType); - protected override TypeFlags ComputeTypeFlags(TypeFlags mask) - { - TypeFlags flags = 0; - - if ((mask & TypeFlags.CategoryMask) != 0) - { - flags |= MetadataType.Category; - } - - if ((mask & TypeFlags.HasGenericVarianceComputed) != 0) - { - flags |= TypeFlags.HasGenericVarianceComputed; - - if (MetadataType.HasVariance) - flags |= TypeFlags.HasGenericVariance; - } - - if ((mask & TypeFlags.HasFinalizerComputed) != 0) - { - flags |= TypeFlags.HasFinalizerComputed; - - if (MetadataType.HasFinalizer) - flags |= TypeFlags.HasFinalizer; - } - - if ((mask & TypeFlags.AttributeCacheComputed) != 0) - { - flags |= TypeFlags.AttributeCacheComputed; - - if (MetadataType.IsByRefLike) - flags |= TypeFlags.IsByRefLike; - - if (MetadataType.IsInlineArray) - flags |= TypeFlags.IsInlineArray; - - if (MetadataType.IsIntrinsic) - flags |= TypeFlags.IsIntrinsic; - } - - return flags; - } + protected override TypeFlags ComputeTypeFlags(TypeFlags mask) => MetadataType.GetTypeFlags(mask); public override string Namespace => MetadataType.Namespace; @@ -160,14 +118,8 @@ protected override TypeFlags ComputeTypeFlags(TypeFlags mask) public override PInvokeStringFormat PInvokeStringFormat => MetadataType.PInvokeStringFormat; - public override string DiagnosticName => MetadataType.DiagnosticName; - - public override string DiagnosticNamespace => MetadataType.DiagnosticNamespace; - public override TypeSystemContext Context => MetadataType.Context; - protected override int ClassCode => 779393465; - public override IEnumerable GetMethods() => MethodDesc.EmptyMethods; } } diff --git a/src/coreclr/tools/Common/Compiler/TypeWithRepeatedFieldsFieldLayoutAlgorithm.cs b/src/coreclr/tools/Common/TypeSystem/Common/TypeWithRepeatedFieldsFieldLayoutAlgorithm.cs similarity index 99% rename from src/coreclr/tools/Common/Compiler/TypeWithRepeatedFieldsFieldLayoutAlgorithm.cs rename to src/coreclr/tools/Common/TypeSystem/Common/TypeWithRepeatedFieldsFieldLayoutAlgorithm.cs index 93da0f28a44827..b9537b9af8baf0 100644 --- a/src/coreclr/tools/Common/Compiler/TypeWithRepeatedFieldsFieldLayoutAlgorithm.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/TypeWithRepeatedFieldsFieldLayoutAlgorithm.cs @@ -3,7 +3,7 @@ using Internal.TypeSystem; -namespace ILCompiler +namespace Internal.TypeSystem { /// /// Represents an algorithm that computes field layout for intrinsic vector types (Vector64/Vector128/Vector256). diff --git a/src/coreclr/tools/ILVerification/ILVerification.projitems b/src/coreclr/tools/ILVerification/ILVerification.projitems index e3e8b2bf94f3e1..81ce9f65d1139a 100644 --- a/src/coreclr/tools/ILVerification/ILVerification.projitems +++ b/src/coreclr/tools/ILVerification/ILVerification.projitems @@ -111,6 +111,9 @@ TypeSystem\Common\FieldLayoutAlgorithm.cs + + TypeSystem\Common\ImpliedRepeatedFieldDesc.cs + TypeSystem\Common\IModuleResolver.cs @@ -252,6 +255,12 @@ TypeSystem\Common\ThrowHelper.cs + + TypeSystem\Common\TypeWithRepeatedFields.cs + + + TypeSystem\Common\TypeWithRepeatedFieldsFieldLayoutAlgorithm.cs + TypeSystem\Common\Utilities\ExceptionTypeNameFormatter.cs diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/SwiftTypes.cs b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/SwiftTypes.cs index caa7eadf402f1c..c83a084c7ce09a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/SwiftTypes.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/SwiftTypes.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace ILCompiler.Compiler.Tests.Assets.SwiftTypes; @@ -92,3 +93,72 @@ public struct F114_S0 public short F2; public ushort F3; } + +[ExpectedLowering(ExpectedLoweringAttribute.Lowered.Double, ExpectedLoweringAttribute.Lowered.Float, ExpectedLoweringAttribute.Lowered.Int32, ExpectedLoweringAttribute.Lowered.Int32)] +[StructLayout(LayoutKind.Sequential, Size = 20)] +struct F352_S0 +{ + public double F0; + public float F1; + public uint F2; + public int F3; +} + +[ExpectedLowering(ExpectedLoweringAttribute.Lowered.Int64, ExpectedLoweringAttribute.Lowered.Int64, ExpectedLoweringAttribute.Lowered.Int64, ExpectedLoweringAttribute.Lowered.Int64)] +[InlineArray(4)] +public struct InlineArray4Longs +{ + private long l; +} + +[ExpectedLowering(ExpectedLoweringAttribute.Lowered.Float, ExpectedLoweringAttribute.Lowered.Int32, ExpectedLoweringAttribute.Lowered.Int64)] +public struct UnalignedLargeOpaque +{ + public float F0; + public short F1; + public short F2; + public int F3; + public int F4; +} + +[ExpectedLowering(ExpectedLoweringAttribute.Lowered.Int16, ExpectedLoweringAttribute.Lowered.Int64, ExpectedLoweringAttribute.Lowered.Int64)] +[StructLayout(LayoutKind.Sequential, Size = 21)] +public struct PointerSizeOpaqueBlocks +{ + public short F0; + public nint F1; + public int F2; + public byte F3; +} + +public struct PointerSizeOpaqueBlocksNonNaturalAlignment_S0 +{ + public byte F0; + public nint F1; +} + +[ExpectedLowering(ExpectedLoweringAttribute.Lowered.Int16, ExpectedLoweringAttribute.Lowered.Int8, ExpectedLoweringAttribute.Lowered.Int64, ExpectedLoweringAttribute.Lowered.Int64, Offsets = [0x0, 0x8, 0x10, 0x18])] +[StructLayout(LayoutKind.Sequential, Size = 21)] +public struct PointerSizeOpaqueBlocksNonNaturalAlignment +{ + public short F0; + public PointerSizeOpaqueBlocksNonNaturalAlignment_S0 F1; + public int F2; + public byte F3; +} + +[ExpectedLowering(ExpectedLoweringAttribute.Lowered.Int64)] +public struct F128_S_S0 +{ + public sbyte F0; + public short F1; + public int F2; +} + +[ExpectedLowering(ExpectedLoweringAttribute.Lowered.Float, ExpectedLoweringAttribute.Lowered.Int32, ExpectedLoweringAttribute.Lowered.Int64)] +public struct F128_S +{ + public float F0; + public F128_S_S0 F1; + public uint F2; +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/SwiftTypesSupport.cs b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/SwiftTypesSupport.cs index 7c5a0232259f0f..d37109b4bd7d5f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/SwiftTypesSupport.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/SwiftTypesSupport.cs @@ -38,4 +38,6 @@ public enum Lowered Int32, Int64 } + + public int[] Offsets { get; set; } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/SwiftLoweringTests.cs b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/SwiftLoweringTests.cs index 0f8625a24e6ee7..1487f1b5e38804 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/SwiftLoweringTests.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/SwiftLoweringTests.cs @@ -13,6 +13,7 @@ using Internal.TypeSystem; using Internal.TypeSystem.Ecma; using Xunit; +using System.Reflection.Metadata; namespace ILCompiler.Compiler.Tests { @@ -44,33 +45,79 @@ public static IEnumerable DiscoverSwiftTypes() var testModule = context.GetModuleForSimpleName("ILCompiler.Compiler.Tests.Assets"); foreach (var type in testModule.GetAllTypes()) { - if (type.Namespace == "ILCompiler.Compiler.Tests.Assets.SwiftTypes" && type.IsValueType) + if (type is EcmaType { Namespace: "ILCompiler.Compiler.Tests.Assets.SwiftTypes", IsValueType: true } ecmaType + && ecmaType.GetDecodedCustomAttribute("ILCompiler.Compiler.Tests.Assets.SwiftTypes", "ExpectedLoweringAttribute") is { } expectedLoweringAttribute) { - yield return new object[] { type.Name, type }; + // By default, we assume that our lowered representation is meant to be naturally aligned. + // For types that are not naturally aligned, the test can specify the offsets. + int[]? offsets = null; + if (expectedLoweringAttribute.NamedArguments.FirstOrDefault(a => a.Name == "Offsets").Value is ImmutableArray> offsetsArgument) + { + offsets = new int[offsetsArgument.Length]; + for (int i = 0; i < offsetsArgument.Length; i++) + { + offsets[i] = (int)offsetsArgument[i].Value; + } + } + + CORINFO_SWIFT_LOWERING expected; + if (expectedLoweringAttribute.FixedArguments.Length == 0) + { + expected = new CORINFO_SWIFT_LOWERING { byReference = true }; + } + else + { + expected = new CORINFO_SWIFT_LOWERING + { + numLoweredElements = expectedLoweringAttribute.FixedArguments.Length, + }; + int naturalOffset = 0; + for (int i = 0; i < expectedLoweringAttribute.FixedArguments.Length; i++) + { + ExpectedLowering lowering = (ExpectedLowering)(int)expectedLoweringAttribute.FixedArguments[i].Value; + expected.LoweredElements[i] = GetCorType(lowering); + if (offsets is not null) + { + expected.Offsets[i] = (uint)offsets[i]; + } + else + { + // For all types that we lower to, alignment == size + int size = GetSize(lowering); + if (size > 1) + { + expected.Offsets[i] = (uint)naturalOffset.AlignUp(size); + } + naturalOffset += size; + } + } + } + yield return new object[] { type.Name, type, expected }; } } } [Theory] [MemberData(nameof(DiscoverSwiftTypes))] - public void VerifyLowering(string typeName, EcmaType type) + public void VerifyLowering(string typeName, EcmaType type, CORINFO_SWIFT_LOWERING expectedLowering) { _ = typeName; - var expectedLoweringAttribute = type.GetDecodedCustomAttribute("ILCompiler.Compiler.Tests.Assets.SwiftTypes", "ExpectedLoweringAttribute"); - var actualLowering = SwiftPhysicalLowering.LowerTypeForSwiftSignature(type); + Assert.Equal(expectedLowering, SwiftPhysicalLowering.LowerTypeForSwiftSignature(type)); + } - if (expectedLoweringAttribute.Value.FixedArguments.Length == 0) - { - Assert.Equal(new CORINFO_SWIFT_LOWERING { byReference = true }, actualLowering, SwiftLoweringComparer.Instance); - } - else + private static int GetSize(ExpectedLowering expectedLowering) + { + return expectedLowering switch { - CORINFO_SWIFT_LOWERING expectedLowering = default; - expectedLowering.numLoweredElements = expectedLoweringAttribute.Value.FixedArguments.Length; - expectedLoweringAttribute.Value.FixedArguments.Select(na => GetCorType((ExpectedLowering)(int)na.Value)).ToArray().AsSpan().CopyTo(expectedLowering.LoweredElements); - Assert.Equal(expectedLowering, actualLowering); - } + ExpectedLowering.Float => 4, + ExpectedLowering.Double => 8, + ExpectedLowering.Int8 => 1, + ExpectedLowering.Int16 => 2, + ExpectedLowering.Int32 => 4, + ExpectedLowering.Int64 => 8, + _ => throw new ArgumentOutOfRangeException(nameof(expectedLowering)) + }; } private static CorInfoType GetCorType(ExpectedLowering expectedLowering) @@ -86,33 +133,5 @@ private static CorInfoType GetCorType(ExpectedLowering expectedLowering) _ => throw new ArgumentOutOfRangeException(nameof(expectedLowering)) }; } - - private sealed class SwiftLoweringComparer : IEqualityComparer - { - public static SwiftLoweringComparer Instance { get; } = new SwiftLoweringComparer(); - - public bool Equals(CORINFO_SWIFT_LOWERING x, CORINFO_SWIFT_LOWERING y) - { - if (x.byReference != y.byReference) - return false; - - return x.LoweredElements.SequenceEqual(y.LoweredElements); - } - - public int GetHashCode([DisallowNull] CORINFO_SWIFT_LOWERING obj) - { - HashCode code = default; - code.Add(obj.byReference); - if (obj.byReference) - { - code.Add(obj.numLoweredElements); - foreach (var type in obj.LoweredElements[0..(int)obj.numLoweredElements]) - { - code.Add(type); - } - } - return code.ToHashCode(); - } - } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 3e18e278ac100c..8a485509eb63be 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -31,9 +31,6 @@ - - - diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index 2aab7594ddda9e..04fb0838bee7d4 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -33,9 +33,6 @@ - - - diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj b/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj index 3bbccd9010c2e7..2c7ae3b4cc230c 100644 --- a/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj @@ -176,6 +176,18 @@ TypeSystem\Common\ThrowHelper.Common.cs + + TypeSystem\Common\TypeWithRepeatedFields.cs + + + TypeSystem\Common\TypeWithRepeatedFields.Sorting.cs + + + TypeSystem\Common\TypeWithRepeatedFields.Diagnostic.cs + + + TypeSystem\Common\TypeWithRepeatedFieldsFieldLayoutAlgorithm.cs + TypeSystem\Common\UniversalCanonLayoutAlgorithm.cs @@ -236,6 +248,12 @@ TypeSystem\Common\FieldLayoutAlgorithm.cs + + TypeSystem\Common\ImpliedRepeatedFieldDesc.cs + + + TypeSystem\Common\ImpliedRepeatedFieldDesc.Sorting.cs + TypeSystem\Common\InstantiatedMethod.cs