Skip to content

Commit bee48e8

Browse files
authored
Fix ABI and Interop issues with InlineArray types (#89519)
1 parent 60c49c6 commit bee48e8

18 files changed

+607
-167
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Internal.TypeSystem;
5+
6+
namespace ILCompiler
7+
{
8+
public sealed class ImpliedRepeatedFieldDesc : FieldDesc
9+
{
10+
private readonly FieldDesc _underlyingFieldDesc;
11+
12+
public ImpliedRepeatedFieldDesc(DefType owningType, FieldDesc underlyingFieldDesc, int fieldIndex)
13+
{
14+
OwningType = owningType;
15+
_underlyingFieldDesc = underlyingFieldDesc;
16+
FieldIndex = fieldIndex;
17+
}
18+
19+
public override DefType OwningType { get; }
20+
21+
public override TypeDesc FieldType => _underlyingFieldDesc.FieldType;
22+
23+
public override bool HasEmbeddedSignatureData => _underlyingFieldDesc.HasEmbeddedSignatureData;
24+
25+
public override bool IsStatic => _underlyingFieldDesc.IsStatic;
26+
27+
public override bool IsInitOnly => _underlyingFieldDesc.IsInitOnly;
28+
29+
public override bool IsThreadStatic => _underlyingFieldDesc.IsThreadStatic;
30+
31+
public override bool HasRva => _underlyingFieldDesc.HasRva;
32+
33+
public override bool IsLiteral => _underlyingFieldDesc.IsLiteral;
34+
35+
public override TypeSystemContext Context => _underlyingFieldDesc.Context;
36+
37+
public int FieldIndex { get; }
38+
39+
protected override int ClassCode => 31666958;
40+
41+
public override EmbeddedSignatureData[] GetEmbeddedSignatureData() => _underlyingFieldDesc.GetEmbeddedSignatureData();
42+
43+
public override bool HasCustomAttribute(string attributeNamespace, string attributeName) => _underlyingFieldDesc.HasCustomAttribute(attributeNamespace, attributeName);
44+
45+
protected override int CompareToImpl(FieldDesc other, TypeSystemComparer comparer)
46+
{
47+
var impliedRepeatedFieldDesc = (ImpliedRepeatedFieldDesc)other;
48+
49+
int result = comparer.Compare(_underlyingFieldDesc, impliedRepeatedFieldDesc._underlyingFieldDesc);
50+
51+
if (result != 0)
52+
{
53+
return result;
54+
}
55+
56+
return FieldIndex.CompareTo(impliedRepeatedFieldDesc.FieldIndex);
57+
}
58+
59+
public override MarshalAsDescriptor GetMarshalAsDescriptor() => _underlyingFieldDesc.GetMarshalAsDescriptor();
60+
61+
public override string Name => $"{_underlyingFieldDesc.Name}[{FieldIndex}]";
62+
}
63+
}

src/coreclr/tools/Common/Compiler/TypeExtensions.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5+
using System.Collections.Generic;
56
using System.Runtime.CompilerServices;
67
using Internal.TypeSystem;
78

@@ -753,5 +754,58 @@ public static bool IsDynamicInterfaceCastableImplementation(this MetadataType in
753754
Debug.Assert(interfaceType.IsInterface);
754755
return interfaceType.HasCustomAttribute("System.Runtime.InteropServices", "DynamicInterfaceCastableImplementationAttribute");
755756
}
757+
758+
public static bool HasImpliedRepeatedFields(this MetadataType mdType)
759+
{
760+
if (mdType.IsInlineArray)
761+
{
762+
return true;
763+
}
764+
765+
// If the type is not an [InlineArray] type, do a best-effort detection of whether the type is a fixed buffer type
766+
// as emitted by the C# compiler.
767+
768+
if (!mdType.IsSequentialLayout)
769+
{
770+
return false;
771+
}
772+
773+
if (mdType.GetClassLayout().Size == 0)
774+
{
775+
// Unsafe fixed buffers have a specified size in the class layout information.
776+
return false;
777+
}
778+
779+
FieldDesc firstField = null;
780+
foreach (FieldDesc field in mdType.GetFields())
781+
{
782+
if (!field.IsStatic)
783+
{
784+
// A type is only an unsafe fixed buffer type if it has exactly one field.
785+
if (firstField is not null)
786+
{
787+
return false;
788+
}
789+
firstField = field;
790+
}
791+
}
792+
793+
if (firstField is null)
794+
{
795+
return false;
796+
}
797+
TypeDesc firstFieldElementType = firstField.FieldType;
798+
799+
// A fixed buffer type is always a value type that has exactly one value type field at offset 0
800+
// and whose size is an exact multiple of the size of the field.
801+
// It is possible that we catch a false positive with this check, but that chance is extremely slim
802+
// and the user can always change their structure to something more descriptive of what they want
803+
// instead of adding additional padding at the end of a one-field structure.
804+
// We do this check here to save looking up the FixedBufferAttribute when loading the field
805+
// from metadata.
806+
return firstFieldElementType.IsValueType
807+
&& firstField.Offset.AsInt == 0
808+
&& ((mdType.GetElementSize().AsInt % firstFieldElementType.GetElementSize().AsInt) == 0);
809+
}
756810
}
757811
}
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Diagnostics;
7+
using System.Threading;
8+
using Internal.TypeSystem;
9+
10+
namespace ILCompiler
11+
{
12+
/// <summary>
13+
/// This type represents a type that has one field in metadata,
14+
/// but has that field repeated at runtime to represent an array of elements inline.
15+
/// </summary>
16+
public sealed partial class TypeWithRepeatedFields : MetadataType
17+
{
18+
private int? _numFields;
19+
private FieldDesc[] _fields;
20+
21+
public TypeWithRepeatedFields(MetadataType underlyingType)
22+
{
23+
MetadataType = underlyingType;
24+
}
25+
26+
internal MetadataType MetadataType { get; }
27+
28+
internal int NumFields
29+
{
30+
get
31+
{
32+
if (_numFields.HasValue)
33+
{
34+
return _numFields.Value;
35+
}
36+
int size = MetadataType.InstanceFieldSize.AsInt;
37+
FieldDesc firstField = GetFirstInstanceField();
38+
39+
_numFields = size / firstField.FieldType.GetElementSize().AsInt;
40+
return _numFields.Value;
41+
}
42+
}
43+
44+
private FieldDesc GetFirstInstanceField()
45+
{
46+
FieldDesc firstField = null;
47+
foreach (var field in MetadataType.GetFields())
48+
{
49+
if (field.IsStatic)
50+
{
51+
continue;
52+
}
53+
54+
firstField = field;
55+
break;
56+
}
57+
58+
Debug.Assert(firstField is not null);
59+
return firstField;
60+
}
61+
62+
private FieldDesc[] ComputeFields()
63+
{
64+
var fields = new FieldDesc[NumFields];
65+
66+
FieldDesc firstField = GetFirstInstanceField();
67+
68+
for (int i = 0; i < NumFields; i++)
69+
{
70+
fields[i] = new ImpliedRepeatedFieldDesc(this, firstField, i);
71+
}
72+
73+
return fields;
74+
}
75+
76+
public override IEnumerable<FieldDesc> GetFields()
77+
{
78+
if (_fields is null)
79+
{
80+
Interlocked.CompareExchange(ref _fields, ComputeFields(), null);
81+
}
82+
return _fields;
83+
}
84+
85+
public override ClassLayoutMetadata GetClassLayout() => MetadataType.GetClassLayout();
86+
public override bool HasCustomAttribute(string attributeNamespace, string attributeName) => MetadataType.HasCustomAttribute(attributeNamespace, attributeName);
87+
public override IEnumerable<MetadataType> GetNestedTypes() => (IEnumerable<MetadataType>)EmptyTypes;
88+
public override MetadataType GetNestedType(string name) => null;
89+
public override int GetInlineArrayLength() => MetadataType.GetInlineArrayLength();
90+
public override MethodImplRecord[] FindMethodsImplWithMatchingDeclName(string name) => MetadataType.FindMethodsImplWithMatchingDeclName(name);
91+
public override int GetHashCode() => MetadataType.GetHashCode();
92+
protected override MethodImplRecord[] ComputeVirtualMethodImplsForType() => Array.Empty<MethodImplRecord>();
93+
protected override int CompareToImpl(TypeDesc other, TypeSystemComparer comparer) => comparer.Compare(MetadataType, ((TypeWithRepeatedFields)other).MetadataType);
94+
95+
protected override TypeFlags ComputeTypeFlags(TypeFlags mask)
96+
{
97+
TypeFlags flags = 0;
98+
99+
if ((mask & TypeFlags.CategoryMask) != 0)
100+
{
101+
flags |= MetadataType.Category;
102+
}
103+
104+
if ((mask & TypeFlags.HasGenericVarianceComputed) != 0)
105+
{
106+
flags |= TypeFlags.HasGenericVarianceComputed;
107+
108+
if (MetadataType.HasVariance)
109+
flags |= TypeFlags.HasGenericVariance;
110+
}
111+
112+
if ((mask & TypeFlags.HasFinalizerComputed) != 0)
113+
{
114+
flags |= TypeFlags.HasFinalizerComputed;
115+
116+
if (MetadataType.HasFinalizer)
117+
flags |= TypeFlags.HasFinalizer;
118+
}
119+
120+
if ((mask & TypeFlags.AttributeCacheComputed) != 0)
121+
{
122+
flags |= TypeFlags.AttributeCacheComputed;
123+
124+
if (MetadataType.IsByRefLike)
125+
flags |= TypeFlags.IsByRefLike;
126+
127+
if (MetadataType.IsInlineArray)
128+
flags |= TypeFlags.IsInlineArray;
129+
130+
if (MetadataType.IsIntrinsic)
131+
flags |= TypeFlags.IsIntrinsic;
132+
}
133+
134+
return flags;
135+
}
136+
137+
public override string Namespace => MetadataType.Namespace;
138+
139+
public override string Name => MetadataType.Name;
140+
141+
public override DefType[] ExplicitlyImplementedInterfaces => Array.Empty<DefType>();
142+
143+
public override bool IsExplicitLayout => MetadataType.IsExplicitLayout;
144+
145+
public override bool IsSequentialLayout => MetadataType.IsSequentialLayout;
146+
147+
public override bool IsBeforeFieldInit => MetadataType.IsBeforeFieldInit;
148+
149+
public override ModuleDesc Module => MetadataType.Module;
150+
151+
public override MetadataType MetadataBaseType => MetadataType.MetadataBaseType;
152+
153+
public override DefType BaseType => MetadataType.BaseType;
154+
155+
public override bool IsSealed => true;
156+
157+
public override bool IsAbstract => false;
158+
159+
public override DefType ContainingType => MetadataType.ContainingType;
160+
161+
public override PInvokeStringFormat PInvokeStringFormat => MetadataType.PInvokeStringFormat;
162+
163+
public override string DiagnosticName => MetadataType.DiagnosticName;
164+
165+
public override string DiagnosticNamespace => MetadataType.DiagnosticNamespace;
166+
167+
public override TypeSystemContext Context => MetadataType.Context;
168+
169+
protected override int ClassCode => 779393465;
170+
171+
public override IEnumerable<MethodDesc> GetMethods() => MethodDesc.EmptyMethods;
172+
}
173+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Internal.TypeSystem;
5+
6+
namespace ILCompiler
7+
{
8+
/// <summary>
9+
/// Represents an algorithm that computes field layout for intrinsic vector types (Vector64/Vector128/Vector256).
10+
/// </summary>
11+
public class TypeWithRepeatedFieldsFieldLayoutAlgorithm : FieldLayoutAlgorithm
12+
{
13+
private readonly FieldLayoutAlgorithm _fallbackAlgorithm;
14+
15+
public TypeWithRepeatedFieldsFieldLayoutAlgorithm(FieldLayoutAlgorithm fallbackAlgorithm)
16+
{
17+
_fallbackAlgorithm = fallbackAlgorithm;
18+
}
19+
20+
public override ComputedInstanceFieldLayout ComputeInstanceLayout(DefType defType, InstanceLayoutKind layoutKind)
21+
{
22+
var type = (TypeWithRepeatedFields)defType;
23+
24+
ComputedInstanceFieldLayout layoutFromMetadata = _fallbackAlgorithm.ComputeInstanceLayout(type.MetadataType, layoutKind);
25+
26+
FieldAndOffset[] offsets = layoutFromMetadata.Offsets;
27+
28+
if (offsets is not null)
29+
{
30+
var fieldOffsets = new FieldAndOffset[type.NumFields];
31+
32+
LayoutInt cumulativeOffset = new LayoutInt(0);
33+
int fieldIndex = 0;
34+
foreach (FieldDesc field in type.GetFields())
35+
{
36+
if (field.IsStatic)
37+
{
38+
continue;
39+
}
40+
fieldOffsets[fieldIndex++] = new FieldAndOffset(field, cumulativeOffset);
41+
cumulativeOffset += field.FieldType.GetElementSize();
42+
}
43+
44+
offsets = fieldOffsets;
45+
}
46+
47+
return new ComputedInstanceFieldLayout
48+
{
49+
ByteCountUnaligned = layoutFromMetadata.ByteCountUnaligned,
50+
ByteCountAlignment = layoutFromMetadata.ByteCountAlignment,
51+
FieldAlignment = layoutFromMetadata.FieldAlignment,
52+
FieldSize = layoutFromMetadata.FieldSize,
53+
Offsets = offsets,
54+
LayoutAbiStable = layoutFromMetadata.LayoutAbiStable
55+
};
56+
}
57+
58+
public override ComputedStaticFieldLayout ComputeStaticFieldLayout(DefType defType, StaticLayoutKind layoutKind)
59+
{
60+
return _fallbackAlgorithm.ComputeStaticFieldLayout(((TypeWithRepeatedFields)defType).MetadataType, layoutKind);
61+
}
62+
63+
public override bool ComputeContainsGCPointers(DefType type)
64+
{
65+
return _fallbackAlgorithm.ComputeContainsGCPointers(((TypeWithRepeatedFields)type).MetadataType);
66+
}
67+
68+
public override bool ComputeIsUnsafeValueType(DefType type)
69+
{
70+
return _fallbackAlgorithm.ComputeIsUnsafeValueType(((TypeWithRepeatedFields)type).MetadataType);
71+
}
72+
73+
public override ValueTypeShapeCharacteristics ComputeValueTypeShapeCharacteristics(DefType type)
74+
{
75+
return _fallbackAlgorithm.ComputeValueTypeShapeCharacteristics(((TypeWithRepeatedFields)type).MetadataType);
76+
}
77+
}
78+
}

0 commit comments

Comments
 (0)