diff --git a/src/ILToNative/src/Compiler/AsmWriter.cs b/src/ILToNative/src/Compiler/AsmWriter.cs index 773b8ea0a5e..ae84f56ea54 100644 --- a/src/ILToNative/src/Compiler/AsmWriter.cs +++ b/src/ILToNative/src/Compiler/AsmWriter.cs @@ -32,6 +32,11 @@ void OutputCode() OutputEETypes(); + Out.WriteLine(); + Out.WriteLine(".data"); + + OutputStatics(); + Out.Dispose(); } @@ -222,6 +227,13 @@ void OutputReadyToHelpers() Out.WriteLine("jmp __castclass_class"); break; + case ReadyToRunHelperId.GetNonGCStaticBase: + Out.Write("leaq __NonGCStaticBase_"); + Out.Write(GetMangledTypeName((TypeDesc)helper.Target)); + Out.WriteLine("(%rip), %rax"); + Out.WriteLine("ret"); + break; + default: throw new NotImplementedException(); } @@ -260,6 +272,33 @@ void OutputEETypes() } } + void OutputStatics() + { + foreach (var t in _registeredTypes.Values) + { + if (!t.IncludedInCompilation) + continue; + + var type = t.Type as MetadataType; + if (type == null) + continue; + + if (type.NonGCStaticFieldSize > 0) + { + Out.Write(".align "); + Out.WriteLine(type.NonGCStaticFieldAlignment); + Out.Write("__NonGCStaticBase_"); + Out.Write(GetMangledTypeName(type)); + Out.WriteLine(":"); + Out.Write(".rept "); + Out.WriteLine(type.NonGCStaticFieldSize); + Out.WriteLine(".byte 0"); + Out.WriteLine(".endr"); + Out.WriteLine(); + } + } + } + void OutputVirtualSlots(TypeDesc implType, TypeDesc declType) { var baseType = declType.BaseType; diff --git a/src/ILToNative/src/Compiler/ReadyToRunHelper.cs b/src/ILToNative/src/Compiler/ReadyToRunHelper.cs index 797c995899e..57362b293a3 100644 --- a/src/ILToNative/src/Compiler/ReadyToRunHelper.cs +++ b/src/ILToNative/src/Compiler/ReadyToRunHelper.cs @@ -17,6 +17,7 @@ public enum ReadyToRunHelperId VirtualCall, IsInstanceOf, CastClass, + GetNonGCStaticBase, } class ReadyToRunHelper @@ -48,6 +49,8 @@ public string MangledName return "__IsInstanceOf_" + _compilation.GetMangledTypeName((TypeDesc)this.Target); case ReadyToRunHelperId.CastClass: return "__CastClass_" + _compilation.GetMangledTypeName((TypeDesc)this.Target); + case ReadyToRunHelperId.GetNonGCStaticBase: + return "__GetNonGCStaticBase_" + _compilation.GetMangledTypeName((TypeDesc)this.Target); default: throw new NotImplementedException(); } diff --git a/src/JitInterface/src/CorInfoImpl.cs b/src/JitInterface/src/CorInfoImpl.cs index 6a99659b57b..53947bd89f0 100644 --- a/src/JitInterface/src/CorInfoImpl.cs +++ b/src/JitInterface/src/CorInfoImpl.cs @@ -782,7 +782,10 @@ CorInfoIsAccessAllowedResult canAccessClass(IntPtr _this, ref CORINFO_RESOLVED_T } CORINFO_CLASS_STRUCT_* getFieldClass(IntPtr _this, CORINFO_FIELD_STRUCT_* field) - { throw new NotImplementedException(); } + { + var fieldDesc = HandleToObject(field); + return ObjectToHandle(fieldDesc.OwningType); + } CorInfoType getFieldType(IntPtr _this, CORINFO_FIELD_STRUCT_* field, ref CORINFO_CLASS_STRUCT_* structType, CORINFO_CLASS_STRUCT_* memberParent) { @@ -825,10 +828,30 @@ void getFieldInfo(IntPtr _this, ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORIN if (field.IsStatic) { fieldFlags |= CORINFO_FIELD_FLAGS.CORINFO_FLG_FIELD_STATIC; - fieldAccessor = CORINFO_FIELD_ACCESSOR.CORINFO_FIELD_STATIC_ADDRESS; - // TODO: Shared statics/HasFieldRVA - throw new NotImplementedException(); + if (field.HasRva) + { + throw new NotSupportedException(); + } + + // Make sure to include the type in the compilation + _compilation.AddType(field.OwningType); + + fieldAccessor = CORINFO_FIELD_ACCESSOR.CORINFO_FIELD_STATIC_SHARED_STATIC_HELPER; + pResult.helper = CorInfoHelpFunc.CORINFO_HELP_READYTORUN_STATIC_BASE; + + ReadyToRunHelperId helperId; + if (field.IsThreadStatic || field.HasGCStaticBase) + { + throw new NotImplementedException(); + } + else + { + helperId = ReadyToRunHelperId.GetNonGCStaticBase; + } + + pResult.fieldLookup.addr = (void*)ObjectToHandle(_compilation.GetReadyToRunHelper(helperId, field.OwningType)); + pResult.fieldLookup.accessType = InfoAccessType.IAT_VALUE; } else { diff --git a/src/TypeSystem/src/Common/FieldDesc.cs b/src/TypeSystem/src/Common/FieldDesc.cs index 49abcd383b7..1130230c876 100644 --- a/src/TypeSystem/src/Common/FieldDesc.cs +++ b/src/TypeSystem/src/Common/FieldDesc.cs @@ -4,6 +4,8 @@ using System; using System.Runtime.CompilerServices; +using Debug = System.Diagnostics.Debug; + namespace Internal.TypeSystem { public abstract partial class FieldDesc @@ -54,6 +56,16 @@ public abstract bool IsInitOnly get; } + public abstract bool IsThreadStatic + { + get; + } + + public abstract bool HasRva + { + get; + } + public virtual FieldDesc GetTypicalFieldDefinition() { return this; diff --git a/src/TypeSystem/src/Common/FieldForInstantiatedType.cs b/src/TypeSystem/src/Common/FieldForInstantiatedType.cs index dde3df0bccb..e48f66377fa 100644 --- a/src/TypeSystem/src/Common/FieldForInstantiatedType.cs +++ b/src/TypeSystem/src/Common/FieldForInstantiatedType.cs @@ -64,6 +64,22 @@ public override bool IsInitOnly } } + public override bool IsThreadStatic + { + get + { + return _fieldDef.IsThreadStatic; + } + } + + public override bool HasRva + { + get + { + return _fieldDef.HasRva; + } + } + public override FieldDesc GetTypicalFieldDefinition() { return _fieldDef; diff --git a/src/TypeSystem/src/Common/FieldLayout.cs b/src/TypeSystem/src/Common/FieldLayout.cs index 9d87f6b681a..93da4f4c387 100644 --- a/src/TypeSystem/src/Common/FieldLayout.cs +++ b/src/TypeSystem/src/Common/FieldLayout.cs @@ -8,21 +8,21 @@ namespace Internal.TypeSystem { - public struct InstanceFieldMason + public struct FieldLayoutAlgorithm { private MetadataType _type; private int _numInstanceFields; private ComputedInstanceFieldLayout _computedLayout; - private InstanceFieldMason(MetadataType type, int numInstanceFields) + private FieldLayoutAlgorithm(MetadataType type, int numInstanceFields) { _type = type; _computedLayout = new ComputedInstanceFieldLayout(); _numInstanceFields = numInstanceFields; } - public static ComputedInstanceFieldLayout ComputeFieldLayout(MetadataType type) + public static ComputedInstanceFieldLayout ComputeInstanceFieldLayout(MetadataType type) { // CLI - Partition 1, section 9.5 - Generic types shall not be marked explicitlayout. if (type.HasInstantiation && type.IsExplicitLayout) @@ -139,19 +139,75 @@ out byteCount // At this point all special cases are handled and all inputs validated - InstanceFieldMason mason = new InstanceFieldMason(type, numInstanceFields); + FieldLayoutAlgorithm algorithm = new FieldLayoutAlgorithm(type, numInstanceFields); if (type.IsExplicitLayout) { - mason.ComputeExplicitFieldLayout(); + algorithm.ComputeExplicitFieldLayout(); } else { // Treat auto layout as sequential for now - mason.ComputeSequentialFieldLayout(); + algorithm.ComputeSequentialFieldLayout(); } - return mason._computedLayout; + return algorithm._computedLayout; + } + + public static unsafe ComputedStaticFieldLayout ComputeStaticFieldLayout(MetadataType type) + { + int numStaticFields = 0; + + foreach (var field in type.GetFields()) + { + if (!field.IsStatic) + continue; + + numStaticFields++; + } + + ComputedStaticFieldLayout result; + result.GcStatics = new StaticsBlock(); + result.NonGcStatics = new StaticsBlock(); + result.ThreadStatics = new StaticsBlock(); + + if (numStaticFields == 0) + { + result.Offsets = null; + return result; + } + + result.Offsets = new FieldAndOffset[numStaticFields]; + + int index = 0; + + foreach (var field in type.GetFields()) + { + if (!field.IsStatic) + continue; + + StaticsBlock* block = + field.IsThreadStatic ? &result.ThreadStatics : + field.HasGCStaticBase ? &result.GcStatics : + &result.NonGcStatics; + + if (field.HasRva) + { + throw new NotImplementedException(); + } + + SizeAndAlignment sizeAndAlignment = ComputeFieldSizeAndAlignment(field.FieldType, type.Context.Target.DefaultPackingSize); + + block->Size = AlignmentHelper.AlignUp(block->Size, sizeAndAlignment.Alignment); + result.Offsets[index] = new FieldAndOffset(field, block->Size); + block->Size = checked(block->Size + sizeAndAlignment.Size); + + block->LargestAlignment = Math.Max(block->LargestAlignment, sizeAndAlignment.Alignment); + + index++; + } + + return result; } private void ComputeExplicitFieldLayout() @@ -392,6 +448,14 @@ private class FieldLayoutFlags public const int HasContainsPointers = 1; public const int ContainsPointers = 2; public const int HasInstanceFieldLayout = 4; + public const int HasStaticFieldLayout = 8; + } + + private class StaticBlockInfo + { + public StaticsBlock NonGcStatics; + public StaticsBlock GcStatics; + public StaticsBlock ThreadStatics; } volatile int _fieldLayoutFlags; @@ -400,6 +464,9 @@ private class FieldLayoutFlags int _instanceByteCount; int _instanceFieldAlignment; + // Information about various static blocks is rare, so we keep it out of line. + StaticBlockInfo _staticBlockInfo; + public bool ContainsPointers { get @@ -453,9 +520,33 @@ public int InstanceByteCount } } + public int NonGCStaticFieldSize + { + get + { + if ((_fieldLayoutFlags & FieldLayoutFlags.HasStaticFieldLayout) == 0) + { + ComputeStaticFieldLayout(); + } + return _staticBlockInfo == null ? 0 : _staticBlockInfo.NonGcStatics.Size; + } + } + + public int NonGCStaticFieldAlignment + { + get + { + if ((_fieldLayoutFlags & FieldLayoutFlags.HasStaticFieldLayout) == 0) + { + ComputeStaticFieldLayout(); + } + return _staticBlockInfo == null ? 0 : _staticBlockInfo.NonGcStatics.LargestAlignment; + } + } + internal void ComputeInstanceFieldLayout() { - var computedLayout = InstanceFieldMason.ComputeFieldLayout(this); + var computedLayout = FieldLayoutAlgorithm.ComputeInstanceFieldLayout(this); _instanceFieldSize = computedLayout.FieldSize; _instanceFieldAlignment = computedLayout.FieldAlignment; @@ -470,6 +561,32 @@ internal void ComputeInstanceFieldLayout() EnableFieldLayoutFlags(FieldLayoutFlags.HasInstanceFieldLayout); } + internal void ComputeStaticFieldLayout() + { + var computedStaticLayout = FieldLayoutAlgorithm.ComputeStaticFieldLayout(this); + + if (computedStaticLayout.Offsets != null) + { + Debug.Assert(computedStaticLayout.Offsets.Length > 0); + + var staticBlockInfo = new StaticBlockInfo + { + NonGcStatics = computedStaticLayout.NonGcStatics, + GcStatics = computedStaticLayout.GcStatics, + ThreadStatics = computedStaticLayout.ThreadStatics + }; + _staticBlockInfo = staticBlockInfo; + + foreach (var fieldAndOffset in computedStaticLayout.Offsets) + { + Debug.Assert(fieldAndOffset.Field.OwningType == this); + fieldAndOffset.Field.InitializeOffset(fieldAndOffset.Offset); + } + } + + EnableFieldLayoutFlags(FieldLayoutFlags.HasStaticFieldLayout); + } + private bool ComputeTypeContainsPointers() { if (!IsValueType && HasBaseType && BaseType.ContainsPointers) @@ -546,12 +663,33 @@ public int Offset { if (_offset == FieldAndOffset.InvalidOffset) { - OwningType.ComputeInstanceFieldLayout(); + if (IsStatic) + OwningType.ComputeStaticFieldLayout(); + else + OwningType.ComputeInstanceFieldLayout(); + + if (_offset == FieldAndOffset.InvalidOffset) + { + // Must be a field that doesn't participate in layout (literal?) + throw new BadImageFormatException(); + } } return _offset; } } + public bool HasGCStaticBase + { + get + { + if (!FieldType.IsValueType) + return true; + + MetadataType fieldType = FieldType as MetadataType; + return fieldType != null && fieldType.ContainsPointers; + } + } + internal void InitializeOffset(int offset) { Debug.Assert(_offset == FieldAndOffset.InvalidOffset || _offset == offset); @@ -567,4 +705,19 @@ public struct ComputedInstanceFieldLayout public int ByteCount; public FieldAndOffset[] Offsets; } + + public struct StaticsBlock + { + public int Size; + public int LargestAlignment; + } + + public struct ComputedStaticFieldLayout + { + public StaticsBlock NonGcStatics; + public StaticsBlock GcStatics; + public StaticsBlock ThreadStatics; + + public FieldAndOffset[] Offsets; + } } diff --git a/src/TypeSystem/src/Ecma/EcmaField.cs b/src/TypeSystem/src/Ecma/EcmaField.cs index 8e4eab0dbc4..4c88b4f5efa 100644 --- a/src/TypeSystem/src/Ecma/EcmaField.cs +++ b/src/TypeSystem/src/Ecma/EcmaField.cs @@ -9,23 +9,28 @@ using Internal.TypeSystem; +using Interlocked = System.Threading.Interlocked; + namespace Internal.TypeSystem.Ecma { public sealed class EcmaField : FieldDesc { - [Flags] - enum FieldFlags + static class FieldFlags { - BasicMetadataCache = 0x01, - Static = 0x02, - InitOnly = 0x04, + public const int BasicMetadataCache = 0x0001; + public const int Static = 0x0002; + public const int InitOnly = 0x0004; + public const int Literal = 0x0008; + + public const int AttributeMetadataCache = 0x0100; + public const int ThreadStatic = 0x0200; }; EcmaType _type; FieldDefinitionHandle _handle; TypeDesc _fieldType; - FieldFlags _fieldFlags; + volatile int _fieldFlags; internal EcmaField(EcmaType type, FieldDefinitionHandle handle) { @@ -101,9 +106,9 @@ public override TypeDesc FieldType } [MethodImpl(MethodImplOptions.NoInlining)] - private FieldFlags InitializeFieldFlags(FieldFlags mask) + private int InitializeFieldFlags(int mask) { - FieldFlags flags = 0; + int flags = 0; if ((mask & FieldFlags.BasicMetadataCache) != 0) { @@ -116,19 +121,55 @@ private FieldFlags InitializeFieldFlags(FieldFlags mask) if ((fieldAttributes & FieldAttributes.InitOnly) != 0) flags |= FieldFlags.InitOnly; + if ((fieldAttributes & FieldAttributes.Literal) != 0) + flags |= FieldFlags.Literal; + flags |= FieldFlags.BasicMetadataCache; } + // Fetching custom attribute based properties is more expensive, so keep that under + // a separate cache that might not be accessed very frequently. + if ((mask & FieldFlags.AttributeMetadataCache) != 0) + { + var fieldDefinition = this.MetadataReader.GetFieldDefinition(_handle); + + foreach (var customAttributeHandle in fieldDefinition.GetCustomAttributes()) + { + var customAttribute = this.MetadataReader.GetCustomAttribute(customAttributeHandle); + var constructorHandle = customAttribute.Constructor; + + var constructor = Module.GetMethod(constructorHandle); + var type = constructor.OwningType; + + switch (type.Name) + { + case "System.ThreadStaticAttribute": + flags |= FieldFlags.ThreadStatic; + break; + } + } + + flags |= FieldFlags.AttributeMetadataCache; + } + Debug.Assert((flags & mask) != 0); + + // Atomically update flags + var originalFlags = _fieldFlags; + while (Interlocked.CompareExchange(ref _fieldFlags, (int)(originalFlags | flags), originalFlags) != originalFlags) + { + originalFlags = _fieldFlags; + } + _fieldFlags |= flags; return flags & mask; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private FieldFlags GetFieldFlags(FieldFlags mask) + private int GetFieldFlags(int mask) { - FieldFlags flags = _fieldFlags & mask; + int flags = _fieldFlags & mask; if (flags != 0) return flags; return InitializeFieldFlags(mask); @@ -142,6 +183,15 @@ public override bool IsStatic } } + public override bool IsThreadStatic + { + get + { + return IsStatic && + (GetFieldFlags(FieldFlags.AttributeMetadataCache | FieldFlags.ThreadStatic) & FieldFlags.ThreadStatic) != 0; + } + } + public override bool IsInitOnly { get @@ -150,6 +200,14 @@ public override bool IsInitOnly } } + public bool IsLiteral + { + get + { + return (GetFieldFlags(FieldFlags.BasicMetadataCache | FieldFlags.Literal) & FieldFlags.Literal) != 0; + } + } + public FieldAttributes Attributes { get @@ -169,6 +227,14 @@ public override string Name } } + public override bool HasRva + { + get + { + return (FieldDefinition.Attributes & FieldAttributes.HasFieldRVA) != 0; + } + } + public override string ToString() { return _type.ToString() + "." + this.Name; diff --git a/src/TypeSystem/src/Ecma/EcmaType.cs b/src/TypeSystem/src/Ecma/EcmaType.cs index dc8a7988bd8..e8874834d0b 100644 --- a/src/TypeSystem/src/Ecma/EcmaType.cs +++ b/src/TypeSystem/src/Ecma/EcmaType.cs @@ -257,7 +257,11 @@ public override IEnumerable GetFields() { foreach (var handle in _typeDefinition.GetFields()) { - yield return (FieldDesc)this.Module.GetObject(handle); + var field = (EcmaField)this.Module.GetObject(handle); + + // Literal fields are not interesting for codegen purposes + if (!field.IsLiteral) + yield return field; } } @@ -269,7 +273,13 @@ public override FieldDesc GetField(string name) foreach (var handle in _typeDefinition.GetFields()) { if (stringComparer.Equals(metadataReader.GetFieldDefinition(handle).Name, name)) - return (FieldDesc)this.Module.GetObject(handle); + { + var field = (EcmaField)this.Module.GetObject(handle); + + // Literal fields are not interesting for codegen purposes + if (!field.IsLiteral) + return field; + } } return null;