internal void GetFieldSizeAlignment(TypeDesc fieldType, out LayoutInt size, out LayoutInt alignment) { Debug.Assert(!fieldType.IsCanonicalSubtype(CanonicalFormKind.Any)); // All reference and array types are pointer-sized if (!fieldType.IsValueType) { size = new LayoutInt(IntPtr.Size); alignment = new LayoutInt(IntPtr.Size); return; } // Is this a type that already exists? If so, get its size from the EEType directly if (fieldType.RetrieveRuntimeTypeHandleIfPossible()) { unsafe { EEType *eeType = fieldType.RuntimeTypeHandle.ToEETypePtr(); size = new LayoutInt((int)eeType->ValueTypeSize); alignment = new LayoutInt(eeType->FieldAlignmentRequirement); return; } } // The type of the field must be a generic valuetype that is dynamically being constructed Debug.Assert(fieldType.IsValueType); DefType fieldDefType = (DefType)fieldType; TypeBuilderState state = fieldType.GetOrCreateTypeBuilderState(); size = fieldDefType.InstanceFieldSize; alignment = fieldDefType.InstanceFieldAlignment; }
public override ComputedInstanceFieldLayout ComputeInstanceLayout(DefType defType, InstanceLayoutKind layoutKind) { TargetDetails targetDetails = defType.Context.Target; ComputedInstanceFieldLayout layoutFromMetadata = _fallbackAlgorithm.ComputeInstanceLayout(defType, layoutKind); LayoutInt instanceFieldSize; if (targetDetails.MaximumSimdVectorLength == SimdVectorLength.Vector128Bit) { instanceFieldSize = new LayoutInt(16); } else if (targetDetails.MaximumSimdVectorLength == SimdVectorLength.Vector256Bit) { instanceFieldSize = new LayoutInt(32); } else { Debug.Assert(targetDetails.MaximumSimdVectorLength == SimdVectorLength.None); return(layoutFromMetadata); } return(new ComputedInstanceFieldLayout { ByteCountUnaligned = instanceFieldSize, ByteCountAlignment = layoutFromMetadata.ByteCountAlignment, FieldAlignment = layoutFromMetadata.FieldAlignment, FieldSize = instanceFieldSize, Offsets = layoutFromMetadata.Offsets, }); }
private void CommonClassLayoutTestBits(ModuleDesc testModule, TypeSystemContext context, LayoutInt expectedIndeterminateByteAlignment, out InstantiatedType genOfIU, out InstantiatedType genOfLU, out InstantiatedType genOfUU, out InstantiatedType genOfUI, out InstantiatedType genOfUL) { MetadataType tDerivedGen = testModule.GetType("GenericTypes", "GenDerivedClass`2"); genOfIU = tDerivedGen.MakeInstantiatedType(context.GetWellKnownType(WellKnownType.Int32), context.UniversalCanonType); genOfLU = tDerivedGen.MakeInstantiatedType(context.GetWellKnownType(WellKnownType.Int64), context.UniversalCanonType); genOfUU = tDerivedGen.MakeInstantiatedType(context.UniversalCanonType, context.UniversalCanonType); genOfUI = tDerivedGen.MakeInstantiatedType(context.UniversalCanonType, context.GetWellKnownType(WellKnownType.Int32)); genOfUL = tDerivedGen.MakeInstantiatedType(context.UniversalCanonType, context.GetWellKnownType(WellKnownType.Int64)); // Assert that the class as a whole is known to be of undefined size AssertClassIndeterminateSize(context, genOfIU, expectedIndeterminateByteAlignment); AssertClassIndeterminateSize(context, genOfLU, expectedIndeterminateByteAlignment); AssertClassIndeterminateSize(context, genOfUU, expectedIndeterminateByteAlignment); AssertClassIndeterminateSize(context, genOfUI, expectedIndeterminateByteAlignment); AssertClassIndeterminateSize(context, genOfUL, expectedIndeterminateByteAlignment); }
protected override void OutputBaseSize(ref ObjectDataBuilder objData) { bool emitMinimumObjectSize = false; if (_type.IsCanonicalSubtype(CanonicalFormKind.Universal) && _type.IsDefType) { LayoutInt instanceByteCount = ((DefType)_type).InstanceByteCount; if (instanceByteCount.IsIndeterminate) { // For USG types, they may be of indeterminate size, and the size of the type may be meaningless. // In that case emit a fixed constant. emitMinimumObjectSize = true; } } if (emitMinimumObjectSize) { objData.EmitInt(MinimumObjectSize); } else { base.OutputBaseSize(ref objData); } }
protected override void PrepareRuntimeSpecificStaticFieldLayout(TypeSystemContext context, ref ComputedStaticFieldLayout layout) { LayoutInt offset = GetGCStaticFieldOffset(context); layout.GcStatics.Size = offset; layout.ThreadGcStatics.Size = offset; }
public override ComputedInstanceFieldLayout ComputeInstanceLayout(DefType type, InstanceLayoutKind layoutKind) { if (!type.IsTemplateUniversal() && (layoutKind == InstanceLayoutKind.TypeOnly)) { // Non universal generics can just use the template's layout DefType template = (DefType)type.ComputeTemplate(); return(_noMetadataFieldLayoutAlgorithm.ComputeInstanceLayout(template, InstanceLayoutKind.TypeOnly)); } // Only needed for universal generics, or when looking up an offset for a field for a universal generic LowLevelList <LayoutInt> fieldOffsets; LayoutInt[] position = ComputeTypeSizeAndAlignment(type, FieldLoadState.Instance, out fieldOffsets); int numInstanceFields = 0; foreach (NativeLayoutFieldDesc field in type.NativeLayoutFields) { if (!field.IsStatic) { numInstanceFields++; } } LayoutInt byteCountAlignment = position[InstanceAlignmentEntry]; byteCountAlignment = type.Context.Target.GetObjectAlignment(byteCountAlignment); ComputedInstanceFieldLayout layout = new ComputedInstanceFieldLayout() { Offsets = new FieldAndOffset[numInstanceFields], ByteCountAlignment = byteCountAlignment, ByteCountUnaligned = position[(int)NativeFormat.FieldStorage.Instance], PackValue = 0 // TODO, as we add more metadata handling logic, find out if its necessary to use a meaningful value here }; if (!type.IsValueType) { layout.FieldAlignment = type.Context.Target.LayoutPointerSize; layout.FieldSize = type.Context.Target.LayoutPointerSize; } else { layout.FieldAlignment = position[InstanceAlignmentEntry]; layout.FieldSize = LayoutInt.AlignUp(position[(int)NativeFormat.FieldStorage.Instance], layout.FieldAlignment); } int curInstanceField = 0; foreach (NativeLayoutFieldDesc field in type.NativeLayoutFields) { if (!field.IsStatic) { layout.Offsets[curInstanceField] = new FieldAndOffset(field, fieldOffsets[curInstanceField]); curInstanceField++; } } return(layout); }
private uint GetArrayTypeIndex(TypeDesc type) { System.Diagnostics.Debug.Assert(type.IsArray, "GetArrayTypeIndex was called with wrong type"); ArrayType arrayType = (ArrayType)type; uint elementSize = (uint)type.Context.Target.PointerSize; LayoutInt layoutElementSize = arrayType.GetElementSize(); if (!layoutElementSize.IsIndeterminate) { elementSize = (uint)layoutElementSize.AsInt; } ArrayTypeDescriptor arrayTypeDescriptor = new ArrayTypeDescriptor { Rank = (uint)arrayType.Rank, ElementType = GetVariableTypeIndex(arrayType.ElementType, false), Size = elementSize, IsMultiDimensional = arrayType.IsMdArray ? 1 : 0 }; ClassTypeDescriptor classDescriptor = new ClassTypeDescriptor { IsStruct = 0, Name = _objectWriter.GetMangledName(type), BaseClassId = GetTypeIndex(arrayType.BaseType, false) }; uint typeIndex = _objectWriter.GetArrayTypeIndex(classDescriptor, arrayTypeDescriptor); _knownTypes[type] = typeIndex; _completeKnownTypes[type] = typeIndex; return(typeIndex); }
public override ComputedInstanceFieldLayout ComputeInstanceLayout(DefType defType, InstanceLayoutKind layoutKind) { Debug.Assert(IsVectorType(defType)); LayoutInt alignment; string name = defType.Name; if (name == "Vector64`1") { alignment = new LayoutInt(8); } else if (name == "Vector128`1") { if (defType.Context.Target.Architecture == TargetArchitecture.ARM) { // The Procedure Call Standard for ARM defaults to 8-byte alignment for __m128 alignment = new LayoutInt(8); } else { alignment = new LayoutInt(16); } } else { Debug.Assert(name == "Vector256`1"); if (defType.Context.Target.Architecture == TargetArchitecture.ARM) { // No such type exists for the Procedure Call Standard for ARM. We will default // to the same alignment as __m128, which is supported by the ABI. alignment = new LayoutInt(8); } else if (defType.Context.Target.Architecture == TargetArchitecture.ARM64) { // The Procedure Call Standard for ARM 64-bit (with SVE support) defaults to // 16-byte alignment for __m256. alignment = new LayoutInt(16); } else { alignment = new LayoutInt(32); } } ComputedInstanceFieldLayout layoutFromMetadata = _fallbackAlgorithm.ComputeInstanceLayout(defType, layoutKind); return(new ComputedInstanceFieldLayout { ByteCountUnaligned = layoutFromMetadata.ByteCountUnaligned, ByteCountAlignment = layoutFromMetadata.ByteCountAlignment, FieldAlignment = alignment, FieldSize = layoutFromMetadata.FieldSize, Offsets = layoutFromMetadata.Offsets, LayoutAbiStable = _vectorAbiIsStable }); }
private ComputedStaticFieldLayout ParseStaticRegionSizesFromNativeLayout(TypeDesc type) { LayoutInt nonGcDataSize = LayoutInt.Zero; LayoutInt gcDataSize = LayoutInt.Zero; LayoutInt threadDataSize = LayoutInt.Zero; TypeBuilderState state = type.GetOrCreateTypeBuilderState(); NativeParser typeInfoParser = state.GetParserForNativeLayoutInfo(); BagElementKind kind; while ((kind = typeInfoParser.GetBagElementKind()) != BagElementKind.End) { switch (kind) { case BagElementKind.NonGcStaticDataSize: TypeLoaderLogger.WriteLine("Found BagElementKind.NonGcStaticDataSize"); // Use checked typecast to int to ensure there aren't any overflows/truncations (size value used in allocation of memory later) nonGcDataSize = new LayoutInt(checked ((int)typeInfoParser.GetUnsigned())); break; case BagElementKind.GcStaticDataSize: TypeLoaderLogger.WriteLine("Found BagElementKind.GcStaticDataSize"); // Use checked typecast to int to ensure there aren't any overflows/truncations (size value used in allocation of memory later) gcDataSize = new LayoutInt(checked ((int)typeInfoParser.GetUnsigned())); break; case BagElementKind.ThreadStaticDataSize: TypeLoaderLogger.WriteLine("Found BagElementKind.ThreadStaticDataSize"); // Use checked typecast to int to ensure there aren't any overflows/truncations (size value used in allocation of memory later) threadDataSize = new LayoutInt(checked ((int)typeInfoParser.GetUnsigned())); break; default: typeInfoParser.SkipInteger(); break; } } ComputedStaticFieldLayout staticLayout = new ComputedStaticFieldLayout() { GcStatics = new StaticsBlock() { Size = gcDataSize, LargestAlignment = DefType.MaximumAlignmentPossible }, NonGcStatics = new StaticsBlock() { Size = nonGcDataSize, LargestAlignment = DefType.MaximumAlignmentPossible }, Offsets = null, // We're not computing field offsets here, so return null ThreadStatics = new StaticsBlock() { Size = threadDataSize, LargestAlignment = DefType.MaximumAlignmentPossible }, }; return(staticLayout); }
public OffsetsForType(LayoutInt nonGcOffset, LayoutInt tlsNonGcOffset, LayoutInt gcOffset, LayoutInt tlsGcOffset) { NonGcOffsets = new LayoutInt[StaticIndex.Count] { nonGcOffset, tlsNonGcOffset }; GcOffsets = new LayoutInt[StaticIndex.Count] { gcOffset, tlsGcOffset }; }
public static string LowLevelToString(this LayoutInt arg) { if (arg.IsIndeterminate) { return("Indeterminate"); } else { return(((uint)arg.AsInt).LowLevelToString()); } }
public static LayoutInt FieldBaseOffset(this TypeDesc type) { LayoutInt baseOffset = type.BaseType.InstanceByteCount; if (type.RequiresAlign8()) { baseOffset = LayoutInt.AlignUp(baseOffset, new LayoutInt(8), type.Context.Target); } return(baseOffset); }
public override ClassLayoutMetadata GetClassLayout() { ClassLayoutMetadata result; result.PackingSize = checked ((int)_typeDefinition.PackingSize); result.Size = checked ((int)_typeDefinition.Size); // Skip reading field offsets if this is not explicit layout if (IsExplicitLayout) { var fieldDefinitionHandles = _typeDefinition.Fields; var numInstanceFields = 0; foreach (var handle in fieldDefinitionHandles) { var fieldDefinition = MetadataReader.GetField(handle); if ((fieldDefinition.Flags & FieldAttributes.Static) != 0) { continue; } numInstanceFields++; } result.Offsets = new FieldAndOffset[numInstanceFields]; int index = 0; foreach (var handle in fieldDefinitionHandles) { var fieldDefinition = MetadataReader.GetField(handle); if ((fieldDefinition.Flags & FieldAttributes.Static) != 0) { continue; } // Note: GetOffset() returns -1 when offset was not set in the metadata int fieldOffsetInMetadata = (int)fieldDefinition.Offset; LayoutInt fieldOffset = fieldOffsetInMetadata == -1 ? FieldAndOffset.InvalidOffset : new LayoutInt(fieldOffsetInMetadata); result.Offsets[index] = new FieldAndOffset(_metadataUnit.GetField(handle, this), fieldOffset); index++; } } else { result.Offsets = null; } return(result); }
public void LayoutIntTests() { Assert.Throws <ArgumentException>(() => { return(new LayoutInt(int.MinValue)); }); Assert.Throws <ArgumentException>(() => { return(new LayoutInt(-1)); }); Assert.Equal(LayoutInt.Zero, new LayoutInt(0)); Assert.Equal(LayoutInt.One, new LayoutInt(1)); Assert.True(LayoutInt.Zero == new LayoutInt(0)); Assert.True(LayoutInt.One == new LayoutInt(1)); Assert.False(LayoutInt.Zero == new LayoutInt(1)); Assert.False(LayoutInt.One == new LayoutInt(0)); #pragma warning disable 1718 // Allow comparison to same variable Assert.True(LayoutInt.Indeterminate == LayoutInt.Indeterminate); #pragma warning restore 1718 Assert.False(LayoutInt.Zero != new LayoutInt(0)); Assert.False(LayoutInt.One != new LayoutInt(1)); Assert.True(LayoutInt.Zero != new LayoutInt(1)); Assert.True(LayoutInt.One != new LayoutInt(0)); #pragma warning disable 1718 // Allow comparison to same variable Assert.False(LayoutInt.Indeterminate != LayoutInt.Indeterminate); #pragma warning restore 1718 Assert.Equal(0, new LayoutInt(0).AsInt); Assert.Equal(1, new LayoutInt(1).AsInt); Assert.Equal(Int32.MaxValue, new LayoutInt(Int32.MaxValue).AsInt); Assert.Throws <InvalidOperationException>(() => { return(LayoutInt.Indeterminate.AsInt); }); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.Indeterminate + LayoutInt.Indeterminate); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.One + LayoutInt.Indeterminate); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.Indeterminate + LayoutInt.One); Assert.Equal(new LayoutInt(2), LayoutInt.One + LayoutInt.One); Assert.Throws <OverflowException>(() => { return(new LayoutInt(int.MaxValue) + LayoutInt.One); }); Assert.Throws <OverflowException>(() => { return(new LayoutInt(int.MaxValue) + LayoutInt.One); }); Assert.Equal(LayoutInt.One, LayoutInt.Max(LayoutInt.One, LayoutInt.Zero)); Assert.Equal(LayoutInt.One, LayoutInt.Max(LayoutInt.Zero, LayoutInt.One)); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.Max(LayoutInt.Indeterminate, LayoutInt.Zero)); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.Max(LayoutInt.Zero, LayoutInt.Indeterminate)); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.Max(LayoutInt.Indeterminate, LayoutInt.Indeterminate)); Assert.Equal(LayoutInt.Zero, LayoutInt.Min(LayoutInt.One, LayoutInt.Zero)); Assert.Equal(LayoutInt.Zero, LayoutInt.Min(LayoutInt.Zero, LayoutInt.One)); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.Min(LayoutInt.Indeterminate, LayoutInt.Zero)); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.Min(LayoutInt.Zero, LayoutInt.Indeterminate)); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.Min(LayoutInt.Indeterminate, LayoutInt.Indeterminate)); }
protected override void FinalizeRuntimeSpecificStaticFieldLayout(TypeSystemContext context, ref ComputedStaticFieldLayout layout) { LayoutInt offset = GetGCStaticFieldOffset(context); // If the size of GCStatics is equal to the size set in PrepareRuntimeSpecificStaticFieldLayout, we // don't have any GC statics if (layout.GcStatics.Size == offset) { layout.GcStatics.Size = LayoutInt.Zero; } if (layout.ThreadGcStatics.Size == offset) { layout.ThreadGcStatics.Size = LayoutInt.Zero; } // NativeAOT makes no distinction between Gc / non-Gc thread statics. All are placed into ThreadGcStatics since thread statics // are typically rare. Debug.Assert(layout.ThreadNonGcStatics.Size == LayoutInt.Zero); }
/// <summary> /// Determine the state of things before we start processing the fields of a specific type. /// This will initialize the state to be aware of the size/characteristics of base types, /// and whether or not this type is a valuetype. /// </summary> /// <param name="type">Type we are computing layout for</param> /// <param name="initialSize">What the initial Instance size should be</param> /// <param name="alignRequired">What is the basic alignment requirement of the base type or 1 if there is no base type to consider</param> internal void ComputeTypeSizeBeforeFields(TypeDesc type, out LayoutInt initialSize, out LayoutInt alignRequired) { // Account for the EEType pointer in objects... initialSize = new LayoutInt(IntPtr.Size); alignRequired = LayoutInt.One; if (type.IsValueType) { // ...unless the type is a ValueType which doesn't have the EEType pointer. initialSize = LayoutInt.Zero; } else if (type.BaseType != null) { // If there is a base type, use the initialSize and alignRequired from that DefType baseType = type.BaseType; initialSize = baseType.InstanceByteCountUnaligned; alignRequired = baseType.InstanceByteAlignment; } }
/// <summary> /// Validates that it will be possible to create an EEType for '<paramref name="type"/>'. /// </summary> public static void CheckCanGenerateEEType(NodeFactory factory, TypeDesc type) { // Don't validate generic definitons if (type.IsGenericDefinition) { return; } // System.__Canon or System.__UniversalCanon if (type.IsCanonicalDefinitionType(CanonicalFormKind.Any)) { return; } // It must be possible to create an EEType for the base type of this type TypeDesc baseType = type.BaseType; if (baseType != null) { // Make sure EEType can be created for this. factory.NecessaryTypeSymbol(GetFullCanonicalTypeForCanonicalType(baseType)); } // We need EETypes for interfaces foreach (var intf in type.RuntimeInterfaces) { // Make sure EEType can be created for this. factory.NecessaryTypeSymbol(GetFullCanonicalTypeForCanonicalType(intf)); } // Validate classes, structs, enums, interfaces, and delegates DefType defType = type as DefType; if (defType != null) { // Ensure we can compute the type layout defType.ComputeInstanceLayout(InstanceLayoutKind.TypeAndFields); // // The fact that we generated an EEType means that someone can call RuntimeHelpers.RunClassConstructor. // We need to make sure this is possible. // if (factory.TypeSystemContext.HasLazyStaticConstructor(defType)) { defType.ComputeStaticFieldLayout(StaticLayoutKind.StaticRegionSizesAndFields); } // Make sure instantiation length matches the expectation // TODO: it might be more resonable for the type system to enforce this (also for methods) if (defType.Instantiation.Length != defType.GetTypeDefinition().Instantiation.Length) { throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type); } foreach (TypeDesc typeArg in defType.Instantiation) { // ByRefs, pointers, function pointers, and System.Void are never valid instantiation arguments if (typeArg.IsByRef || typeArg.IsPointer || typeArg.IsFunctionPointer || typeArg.IsVoid || (typeArg.IsValueType && ((DefType)typeArg).IsByRefLike)) { throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type); } // TODO: validate constraints } // Check the type doesn't have bogus MethodImpls or overrides and we can get the finalizer. defType.GetFinalizer(); } // Validate parameterized types ParameterizedType parameterizedType = type as ParameterizedType; if (parameterizedType != null) { TypeDesc parameterType = parameterizedType.ParameterType; // Make sure EEType can be created for this. factory.NecessaryTypeSymbol(parameterType); if (parameterizedType.IsArray) { if (parameterType.IsFunctionPointer) { // Arrays of function pointers are not currently supported throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type); } LayoutInt elementSize = parameterType.GetElementSize(); if (!elementSize.IsIndeterminate && elementSize.AsInt >= ushort.MaxValue) { // Element size over 64k can't be encoded in the GCDesc throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadValueClassTooLarge, parameterType); } if (((ArrayType)parameterizedType).Rank > 32) { throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadRankTooLarge, type); } if ((parameterType.IsDefType) && ((DefType)parameterType).IsByRefLike) { // Arrays of byref-like types are not allowed throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type); } } // Validate we're not constructing a type over a ByRef if (parameterType.IsByRef) { // CLR compat note: "ldtoken int32&&" will actually fail with a message about int32&; "ldtoken int32&[]" // will fail with a message about being unable to create an array of int32&. This is a middle ground. throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type); } // It might seem reasonable to disallow array of void, but the CLR doesn't prevent that too hard. // E.g. "newarr void" will fail, but "newarr void[]" or "ldtoken void[]" will succeed. } // Function pointer EETypes are not currently supported if (type.IsFunctionPointer) { throw new TypeSystemException.TypeLoadException(ExceptionStringID.ClassLoadGeneral, type); } }
protected virtual void OutputBaseSize(ref ObjectDataBuilder objData) { int pointerSize = _type.Context.Target.PointerSize; int objectSize; if (_type.IsDefType) { LayoutInt instanceByteCount = ((DefType)_type).InstanceByteCount; if (instanceByteCount.IsIndeterminate) { // Some value must be put in, but the specific value doesn't matter as it // isn't used for specific instantiations, and the universal canon eetype // is never associated with an allocated object. objectSize = pointerSize; } else { objectSize = pointerSize + ((DefType)_type).InstanceByteCount.AsInt; // +pointerSize for SyncBlock } if (_type.IsValueType) { objectSize += pointerSize; // + EETypePtr field inherited from System.Object } } else if (_type.IsArray) { objectSize = 3 * pointerSize; // SyncBlock + EETypePtr + Length if (_type.IsMdArray) { objectSize += 2 * sizeof(int) * ((ArrayType)_type).Rank; } } else if (_type.IsPointer) { // These never get boxed and don't have a base size. Use a sentinel value recognized by the runtime. objData.EmitInt(ParameterizedTypeShapeConstants.Pointer); return; } else if (_type.IsByRef) { // These never get boxed and don't have a base size. Use a sentinel value recognized by the runtime. objData.EmitInt(ParameterizedTypeShapeConstants.ByRef); return; } else { throw new NotImplementedException(); } objectSize = AlignmentHelper.AlignUp(objectSize, pointerSize); objectSize = Math.Max(MinimumObjectSize, objectSize); if (_type.IsString) { // If this is a string, throw away objectSize we computed so far. Strings are special. // SyncBlock + EETypePtr + length + firstChar objectSize = 2 * pointerSize + sizeof(int) + sizeof(char); } objData.EmitInt(objectSize); }
public void TestLayoutIntAlignUp(TargetDetails target) { // AlignUp testing Assert.Equal(new LayoutInt(0), LayoutInt.AlignUp(new LayoutInt(0), new LayoutInt(1), target)); Assert.Equal(new LayoutInt(0), LayoutInt.AlignUp(new LayoutInt(0), new LayoutInt(2), target)); Assert.Equal(new LayoutInt(0), LayoutInt.AlignUp(new LayoutInt(0), new LayoutInt(4), target)); Assert.Equal(new LayoutInt(0), LayoutInt.AlignUp(new LayoutInt(0), new LayoutInt(8), target)); Assert.Equal(new LayoutInt(1), LayoutInt.AlignUp(new LayoutInt(1), new LayoutInt(1), target)); Assert.Equal(new LayoutInt(2), LayoutInt.AlignUp(new LayoutInt(2), new LayoutInt(1), target)); Assert.Equal(new LayoutInt(3), LayoutInt.AlignUp(new LayoutInt(3), new LayoutInt(1), target)); Assert.Equal(new LayoutInt(4), LayoutInt.AlignUp(new LayoutInt(4), new LayoutInt(1), target)); Assert.Equal(new LayoutInt(5), LayoutInt.AlignUp(new LayoutInt(5), new LayoutInt(1), target)); Assert.Equal(new LayoutInt(6), LayoutInt.AlignUp(new LayoutInt(6), new LayoutInt(1), target)); Assert.Equal(new LayoutInt(7), LayoutInt.AlignUp(new LayoutInt(7), new LayoutInt(1), target)); Assert.Equal(new LayoutInt(8), LayoutInt.AlignUp(new LayoutInt(8), new LayoutInt(1), target)); Assert.Equal(new LayoutInt(9), LayoutInt.AlignUp(new LayoutInt(9), new LayoutInt(1), target)); Assert.Equal(new LayoutInt(10), LayoutInt.AlignUp(new LayoutInt(10), new LayoutInt(1), target)); Assert.Equal(new LayoutInt(11), LayoutInt.AlignUp(new LayoutInt(11), new LayoutInt(1), target)); Assert.Equal(new LayoutInt(12), LayoutInt.AlignUp(new LayoutInt(12), new LayoutInt(1), target)); Assert.Equal(new LayoutInt(13), LayoutInt.AlignUp(new LayoutInt(13), new LayoutInt(1), target)); Assert.Equal(new LayoutInt(14), LayoutInt.AlignUp(new LayoutInt(14), new LayoutInt(1), target)); Assert.Equal(new LayoutInt(15), LayoutInt.AlignUp(new LayoutInt(15), new LayoutInt(1), target)); Assert.Equal(new LayoutInt(16), LayoutInt.AlignUp(new LayoutInt(16), new LayoutInt(1), target)); Assert.Equal(new LayoutInt(2), LayoutInt.AlignUp(new LayoutInt(1), new LayoutInt(2), target)); Assert.Equal(new LayoutInt(2), LayoutInt.AlignUp(new LayoutInt(2), new LayoutInt(2), target)); Assert.Equal(new LayoutInt(4), LayoutInt.AlignUp(new LayoutInt(3), new LayoutInt(2), target)); Assert.Equal(new LayoutInt(4), LayoutInt.AlignUp(new LayoutInt(4), new LayoutInt(2), target)); Assert.Equal(new LayoutInt(6), LayoutInt.AlignUp(new LayoutInt(5), new LayoutInt(2), target)); Assert.Equal(new LayoutInt(6), LayoutInt.AlignUp(new LayoutInt(6), new LayoutInt(2), target)); Assert.Equal(new LayoutInt(8), LayoutInt.AlignUp(new LayoutInt(7), new LayoutInt(2), target)); Assert.Equal(new LayoutInt(8), LayoutInt.AlignUp(new LayoutInt(8), new LayoutInt(2), target)); Assert.Equal(new LayoutInt(10), LayoutInt.AlignUp(new LayoutInt(9), new LayoutInt(2), target)); Assert.Equal(new LayoutInt(10), LayoutInt.AlignUp(new LayoutInt(10), new LayoutInt(2), target)); Assert.Equal(new LayoutInt(12), LayoutInt.AlignUp(new LayoutInt(11), new LayoutInt(2), target)); Assert.Equal(new LayoutInt(12), LayoutInt.AlignUp(new LayoutInt(12), new LayoutInt(2), target)); Assert.Equal(new LayoutInt(14), LayoutInt.AlignUp(new LayoutInt(13), new LayoutInt(2), target)); Assert.Equal(new LayoutInt(14), LayoutInt.AlignUp(new LayoutInt(14), new LayoutInt(2), target)); Assert.Equal(new LayoutInt(16), LayoutInt.AlignUp(new LayoutInt(15), new LayoutInt(2), target)); Assert.Equal(new LayoutInt(16), LayoutInt.AlignUp(new LayoutInt(16), new LayoutInt(2), target)); Assert.Equal(new LayoutInt(4), LayoutInt.AlignUp(new LayoutInt(1), new LayoutInt(4), target)); Assert.Equal(new LayoutInt(4), LayoutInt.AlignUp(new LayoutInt(2), new LayoutInt(4), target)); Assert.Equal(new LayoutInt(4), LayoutInt.AlignUp(new LayoutInt(3), new LayoutInt(4), target)); Assert.Equal(new LayoutInt(4), LayoutInt.AlignUp(new LayoutInt(4), new LayoutInt(4), target)); Assert.Equal(new LayoutInt(8), LayoutInt.AlignUp(new LayoutInt(5), new LayoutInt(4), target)); Assert.Equal(new LayoutInt(8), LayoutInt.AlignUp(new LayoutInt(6), new LayoutInt(4), target)); Assert.Equal(new LayoutInt(8), LayoutInt.AlignUp(new LayoutInt(7), new LayoutInt(4), target)); Assert.Equal(new LayoutInt(8), LayoutInt.AlignUp(new LayoutInt(8), new LayoutInt(4), target)); Assert.Equal(new LayoutInt(12), LayoutInt.AlignUp(new LayoutInt(9), new LayoutInt(4), target)); Assert.Equal(new LayoutInt(12), LayoutInt.AlignUp(new LayoutInt(10), new LayoutInt(4), target)); Assert.Equal(new LayoutInt(12), LayoutInt.AlignUp(new LayoutInt(11), new LayoutInt(4), target)); Assert.Equal(new LayoutInt(12), LayoutInt.AlignUp(new LayoutInt(12), new LayoutInt(4), target)); Assert.Equal(new LayoutInt(16), LayoutInt.AlignUp(new LayoutInt(13), new LayoutInt(4), target)); Assert.Equal(new LayoutInt(16), LayoutInt.AlignUp(new LayoutInt(14), new LayoutInt(4), target)); Assert.Equal(new LayoutInt(16), LayoutInt.AlignUp(new LayoutInt(15), new LayoutInt(4), target)); Assert.Equal(new LayoutInt(16), LayoutInt.AlignUp(new LayoutInt(16), new LayoutInt(4), target)); Assert.Equal(new LayoutInt(8), LayoutInt.AlignUp(new LayoutInt(1), new LayoutInt(8), target)); Assert.Equal(new LayoutInt(8), LayoutInt.AlignUp(new LayoutInt(2), new LayoutInt(8), target)); Assert.Equal(new LayoutInt(8), LayoutInt.AlignUp(new LayoutInt(3), new LayoutInt(8), target)); Assert.Equal(new LayoutInt(8), LayoutInt.AlignUp(new LayoutInt(4), new LayoutInt(8), target)); Assert.Equal(new LayoutInt(8), LayoutInt.AlignUp(new LayoutInt(5), new LayoutInt(8), target)); Assert.Equal(new LayoutInt(8), LayoutInt.AlignUp(new LayoutInt(6), new LayoutInt(8), target)); Assert.Equal(new LayoutInt(8), LayoutInt.AlignUp(new LayoutInt(7), new LayoutInt(8), target)); Assert.Equal(new LayoutInt(8), LayoutInt.AlignUp(new LayoutInt(8), new LayoutInt(8), target)); Assert.Equal(new LayoutInt(16), LayoutInt.AlignUp(new LayoutInt(9), new LayoutInt(8), target)); Assert.Equal(new LayoutInt(16), LayoutInt.AlignUp(new LayoutInt(10), new LayoutInt(8), target)); Assert.Equal(new LayoutInt(16), LayoutInt.AlignUp(new LayoutInt(11), new LayoutInt(8), target)); Assert.Equal(new LayoutInt(16), LayoutInt.AlignUp(new LayoutInt(12), new LayoutInt(8), target)); Assert.Equal(new LayoutInt(16), LayoutInt.AlignUp(new LayoutInt(13), new LayoutInt(8), target)); Assert.Equal(new LayoutInt(16), LayoutInt.AlignUp(new LayoutInt(14), new LayoutInt(8), target)); Assert.Equal(new LayoutInt(16), LayoutInt.AlignUp(new LayoutInt(15), new LayoutInt(8), target)); Assert.Equal(new LayoutInt(16), LayoutInt.AlignUp(new LayoutInt(16), new LayoutInt(8), target)); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.AlignUp(new LayoutInt(1), LayoutInt.Indeterminate, target)); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.AlignUp(new LayoutInt(2), LayoutInt.Indeterminate, target)); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.AlignUp(new LayoutInt(3), LayoutInt.Indeterminate, target)); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.AlignUp(new LayoutInt(4), LayoutInt.Indeterminate, target)); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.AlignUp(new LayoutInt(5), LayoutInt.Indeterminate, target)); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.AlignUp(new LayoutInt(6), LayoutInt.Indeterminate, target)); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.AlignUp(new LayoutInt(7), LayoutInt.Indeterminate, target)); if (target.MaximumAlignment > 8) { Assert.Equal(LayoutInt.Indeterminate, LayoutInt.AlignUp(new LayoutInt(8), LayoutInt.Indeterminate, target)); } Assert.Equal(LayoutInt.Indeterminate, LayoutInt.AlignUp(new LayoutInt(9), LayoutInt.Indeterminate, target)); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.AlignUp(new LayoutInt(10), LayoutInt.Indeterminate, target)); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.AlignUp(new LayoutInt(11), LayoutInt.Indeterminate, target)); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.AlignUp(new LayoutInt(12), LayoutInt.Indeterminate, target)); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.AlignUp(new LayoutInt(13), LayoutInt.Indeterminate, target)); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.AlignUp(new LayoutInt(14), LayoutInt.Indeterminate, target)); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.AlignUp(new LayoutInt(15), LayoutInt.Indeterminate, target)); if (target.MaximumAlignment > 16) { Assert.Equal(LayoutInt.Indeterminate, LayoutInt.AlignUp(new LayoutInt(16), LayoutInt.Indeterminate, target)); } // If we the value is aligned to the maximum supported alignment, we can consider it aligned no matter // the value of the alignment. Assert.Equal(new LayoutInt(target.MaximumAlignment), LayoutInt.AlignUp(new LayoutInt(target.MaximumAlignment), LayoutInt.Indeterminate, target)); Assert.Equal(new LayoutInt(target.MaximumAlignment * 2), LayoutInt.AlignUp(new LayoutInt(target.MaximumAlignment * 2), LayoutInt.Indeterminate, target)); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.AlignUp(LayoutInt.Indeterminate, new LayoutInt(1), target)); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.AlignUp(LayoutInt.Indeterminate, new LayoutInt(2), target)); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.AlignUp(LayoutInt.Indeterminate, new LayoutInt(4), target)); Assert.Equal(LayoutInt.Indeterminate, LayoutInt.AlignUp(LayoutInt.Indeterminate, new LayoutInt(8), target)); }
protected override ModuleFieldLayout CreateValueFromKey(EcmaModule module) { int typeCountInModule = module.MetadataReader.GetTableRowCount(TableIndex.TypeDef); int pointerSize = module.Context.Target.PointerSize; // 0 corresponds to "normal" statics, 1 to thread-local statics LayoutInt[] gcStatics = new LayoutInt[StaticIndex.Count] { LayoutInt.Zero, LayoutInt.Zero }; LayoutInt[] nonGcStatics = new LayoutInt[StaticIndex.Count] { new LayoutInt(DomainLocalModuleDataBlobOffsetAsIntPtrCount * pointerSize + typeCountInModule), new LayoutInt(ThreadLocalModuleDataBlobOffsetAsIntPtrCount * pointerSize + typeCountInModule), }; Dictionary <TypeDefinitionHandle, OffsetsForType> typeOffsets = new Dictionary <TypeDefinitionHandle, OffsetsForType>(); foreach (TypeDefinitionHandle typeDefHandle in module.MetadataReader.TypeDefinitions) { TypeDefinition typeDef = module.MetadataReader.GetTypeDefinition(typeDefHandle); if (typeDef.GetGenericParameters().Count != 0) { // Generic types are exempt from the static field layout algorithm, see // <a href="https://github.com/dotnet/coreclr/blob/659af58047a949ed50d11101708538d2e87f2568/src/vm/ceeload.cpp#L2049">this check</a>. continue; } // 0 corresponds to "normal" statics, 1 to thread-local statics int[] nonGcAlignment = new int[StaticIndex.Count] { 1, 1, }; int[] nonGcBytes = new int[StaticIndex.Count] { 0, 0, }; int[] gcBytes = new int[StaticIndex.Count] { 0, 0, }; foreach (FieldDefinitionHandle fieldDefHandle in typeDef.GetFields()) { FieldDefinition fieldDef = module.MetadataReader.GetFieldDefinition(fieldDefHandle); if ((fieldDef.Attributes & (FieldAttributes.Static | FieldAttributes.Literal)) == FieldAttributes.Static) { int index = (IsFieldThreadStatic(in fieldDef, module.MetadataReader) ? StaticIndex.ThreadLocal : StaticIndex.Regular); int alignment; int size; bool isGcPointerField; bool isGcBoxedField; CorElementType corElementType; EntityHandle valueTypeHandle; GetFieldElementTypeAndValueTypeHandle(in fieldDef, module.MetadataReader, out corElementType, out valueTypeHandle); FieldDesc fieldDesc = module.GetField(fieldDefHandle); GetElementTypeInfo(module, fieldDesc, valueTypeHandle, corElementType, pointerSize, moduleLayout: true, out alignment, out size, out isGcPointerField, out isGcBoxedField); if (size != 0) { nonGcBytes[index] += size; nonGcAlignment[index] = Math.Max(nonGcAlignment[index], alignment); } if (isGcPointerField || isGcBoxedField) { gcBytes[index] += pointerSize; } } } if (nonGcBytes[StaticIndex.Regular] != 0 || nonGcBytes[StaticIndex.ThreadLocal] != 0 || gcBytes[StaticIndex.Regular] != 0 || gcBytes[StaticIndex.ThreadLocal] != 0) { OffsetsForType offsetsForType = new OffsetsForType(LayoutInt.Indeterminate, LayoutInt.Indeterminate, LayoutInt.Indeterminate, LayoutInt.Indeterminate); for (int staticIndex = 0; staticIndex < StaticIndex.Count; staticIndex++) { if (nonGcBytes[staticIndex] != 0) { offsetsForType.NonGcOffsets[staticIndex] = LayoutInt.AlignUp(nonGcStatics[staticIndex], new LayoutInt(nonGcAlignment[staticIndex]), module.Context.Target); nonGcStatics[staticIndex] = offsetsForType.NonGcOffsets[staticIndex] + new LayoutInt(nonGcBytes[staticIndex]); } if (gcBytes[staticIndex] != 0) { offsetsForType.GcOffsets[staticIndex] = gcStatics[staticIndex]; gcStatics[staticIndex] += new LayoutInt(gcBytes[staticIndex]); } } typeOffsets.Add(typeDefHandle, offsetsForType); } } LayoutInt blockAlignment = new LayoutInt(TargetDetails.MaximumPrimitiveSize); return(new ModuleFieldLayout( module, gcStatics: new StaticsBlock() { Size = gcStatics[StaticIndex.Regular], LargestAlignment = blockAlignment }, nonGcStatics: new StaticsBlock() { Size = nonGcStatics[StaticIndex.Regular], LargestAlignment = blockAlignment }, threadGcStatics: new StaticsBlock() { Size = gcStatics[StaticIndex.ThreadLocal], LargestAlignment = blockAlignment }, threadNonGcStatics: new StaticsBlock() { Size = nonGcStatics[StaticIndex.ThreadLocal], LargestAlignment = blockAlignment }, typeOffsets: typeOffsets)); }
private void AssertClassIndeterminateSize(TypeSystemContext context, MetadataType type, LayoutInt expectedIndeterminateByteAlignment) { Assert.Equal(context.Target.LayoutPointerSize, type.InstanceFieldAlignment); Assert.Equal(context.Target.LayoutPointerSize, type.InstanceFieldSize); Assert.Equal(expectedIndeterminateByteAlignment, type.InstanceByteAlignment); Assert.Equal(LayoutInt.Indeterminate, type.InstanceByteCount); Assert.Equal(LayoutInt.Indeterminate, type.InstanceByteCountUnaligned); }
private uint GetClassTypeIndex(TypeDesc type, bool needsCompleteType) { DefType defType = type as DefType; System.Diagnostics.Debug.Assert(defType != null, "GetClassTypeIndex was called with non def type"); ClassTypeDescriptor classTypeDescriptor = new ClassTypeDescriptor { IsStruct = type.IsValueType ? 1 : 0, Name = _objectWriter.GetMangledName(type), BaseClassId = 0, InstanceSize = 0 }; uint typeIndex = _objectWriter.GetClassTypeIndex(classTypeDescriptor); _knownTypes[type] = typeIndex; if (!defType.InstanceByteCount.IsIndeterminate) { classTypeDescriptor.InstanceSize = (ulong)defType.InstanceByteCount.AsInt; } if (type.HasBaseType && !type.IsValueType) { classTypeDescriptor.BaseClassId = GetTypeIndex(defType.BaseType, true); } List <DataFieldDescriptor> fieldsDescs = new List <DataFieldDescriptor>(); List <DataFieldDescriptor> nonGcStaticFields = new List <DataFieldDescriptor>(); List <DataFieldDescriptor> gcStaticFields = new List <DataFieldDescriptor>(); List <DataFieldDescriptor> threadStaticFields = new List <DataFieldDescriptor>(); List <StaticDataFieldDescriptor> staticsDescs = new List <StaticDataFieldDescriptor>(); string nonGcStaticDataName = NodeFactory.NameMangler.NodeMangler.NonGCStatics(type); string gcStaticDataName = NodeFactory.NameMangler.NodeMangler.GCStatics(type); string threadStaticDataName = NodeFactory.NameMangler.NodeMangler.ThreadStatics(type); bool IsCoreRTAbi = Abi == TargetAbi.CoreRT; bool isCanonical = defType.IsCanonicalSubtype(CanonicalFormKind.Any); foreach (var fieldDesc in defType.GetFields()) { if (fieldDesc.HasRva || fieldDesc.IsLiteral) { continue; } if (isCanonical && fieldDesc.IsStatic) { continue; } LayoutInt fieldOffset = fieldDesc.Offset; int fieldOffsetEmit = fieldOffset.IsIndeterminate ? 0xBAAD : fieldOffset.AsInt; DataFieldDescriptor field = new DataFieldDescriptor { FieldTypeIndex = GetVariableTypeIndex(GetFieldDebugType(fieldDesc), false), Offset = (ulong)fieldOffsetEmit, Name = fieldDesc.Name }; if (fieldDesc.IsStatic) { if (NodeFactory.Target.OperatingSystem != TargetOS.Windows) { StaticDataFieldDescriptor staticDesc = new StaticDataFieldDescriptor { StaticOffset = (ulong)fieldOffsetEmit }; // Mark field as static field.Offset = 0xFFFFFFFF; if (fieldDesc.IsThreadStatic) { staticDesc.StaticDataName = threadStaticDataName; staticDesc.IsStaticDataInObject = IsCoreRTAbi ? 1 : 0; } else if (fieldDesc.HasGCStaticBase) { staticDesc.StaticDataName = gcStaticDataName; staticDesc.IsStaticDataInObject = IsCoreRTAbi ? 1 : 0; } else { staticDesc.StaticDataName = nonGcStaticDataName; staticDesc.IsStaticDataInObject = 0; } staticsDescs.Add(staticDesc); } if (fieldDesc.IsThreadStatic) { threadStaticFields.Add(field); } else if (fieldDesc.HasGCStaticBase) { gcStaticFields.Add(field); } else { nonGcStaticFields.Add(field); } } else { fieldsDescs.Add(field); } } if (NodeFactory.Target.OperatingSystem == TargetOS.Windows) { InsertStaticFieldRegionMember(fieldsDescs, defType, nonGcStaticFields, WindowsNodeMangler.NonGCStaticMemberName, "__type_" + WindowsNodeMangler.NonGCStaticMemberName, false); InsertStaticFieldRegionMember(fieldsDescs, defType, gcStaticFields, WindowsNodeMangler.GCStaticMemberName, "__type_" + WindowsNodeMangler.GCStaticMemberName, IsCoreRTAbi); InsertStaticFieldRegionMember(fieldsDescs, defType, threadStaticFields, WindowsNodeMangler.ThreadStaticMemberName, "__type_" + WindowsNodeMangler.ThreadStaticMemberName, IsCoreRTAbi); } else { fieldsDescs.AddRange(nonGcStaticFields); fieldsDescs.AddRange(gcStaticFields); fieldsDescs.AddRange(threadStaticFields); } DataFieldDescriptor[] fields = new DataFieldDescriptor[fieldsDescs.Count]; for (int i = 0; i < fieldsDescs.Count; ++i) { fields[i] = fieldsDescs[i]; } StaticDataFieldDescriptor[] statics = new StaticDataFieldDescriptor[staticsDescs.Count]; for (int i = 0; i < staticsDescs.Count; ++i) { statics[i] = staticsDescs[i]; } LayoutInt elementSize = defType.GetElementSize(); int elementSizeEmit = elementSize.IsIndeterminate ? 0xBAAD : elementSize.AsInt; ClassFieldsTypeDescriptor fieldsDescriptor = new ClassFieldsTypeDescriptor { Size = (ulong)elementSizeEmit, FieldsCount = fieldsDescs.Count, }; uint completeTypeIndex = _objectWriter.GetCompleteClassTypeIndex(classTypeDescriptor, fieldsDescriptor, fields, statics); _completeKnownTypes[type] = completeTypeIndex; if (needsCompleteType) { return(completeTypeIndex); } else { return(typeIndex); } }
// The layout algorithm should probably compute results and let the caller set things internal unsafe LayoutInt[] ComputeTypeSizeAndAlignment(TypeDesc type, FieldLoadState loadRequested, out LowLevelList <LayoutInt> fieldOffsets) { fieldOffsets = null; TypeLoaderLogger.WriteLine("Laying out type " + type.ToString() + ". IsValueType: " + (type.IsValueType ? "true" : "false") + ". LoadRequested = " + ((int)loadRequested).LowLevelToString()); Debug.Assert(loadRequested != FieldLoadState.None); Debug.Assert(type is ArrayType || (type is DefType && ((DefType)type).HasInstantiation)); bool isArray = type is ArrayType; LayoutInt[] position = new LayoutInt[5]; LayoutInt alignRequired = LayoutInt.One; if ((loadRequested & FieldLoadState.Instance) == FieldLoadState.Instance) { ComputeTypeSizeBeforeFields(type, out position[(int)NativeFormat.FieldStorage.Instance], out alignRequired); } if (!isArray) { // Once this is done, the NativeLayoutFields on the type are initialized EnsureFieldLayoutLoadedForGenericType((DefType)type); Debug.Assert(type.NativeLayoutFields != null); } int instanceFields = 0; if (!isArray && type.NativeLayoutFields.Length > 0) { fieldOffsets = new LowLevelList <LayoutInt>(type.NativeLayoutFields.Length); for (int i = 0; i < type.NativeLayoutFields.Length; i++) { TypeDesc fieldType = type.NativeLayoutFields[i].FieldType; int fieldStorage = (int)type.NativeLayoutFields[i].FieldStorage; if (!ShouldProcessField((NativeFormat.FieldStorage)fieldStorage, loadRequested)) { continue; } // For value types, we will attempt to get the size and alignment from // the runtime if possible, otherwise GetFieldSizeAndAlignment will // recurse to lay out nested struct fields. LayoutInt alignment; LayoutInt size; GetFieldSizeAlignment(fieldType, out size, out alignment); Debug.Assert(alignment.AsInt > 0); if (fieldStorage == (int)NativeFormat.FieldStorage.Instance) { instanceFields++; // Ensure alignment of type is sufficient for this field alignRequired = LayoutInt.Max(alignRequired, alignment); } position[fieldStorage] = LayoutInt.AlignUp(position[fieldStorage], alignment); TypeLoaderLogger.WriteLine(" --> Field type " + fieldType.ToString() + " storage " + ((uint)(type.NativeLayoutFields[i].FieldStorage)).LowLevelToString() + " offset " + position[fieldStorage].LowLevelToString() + " alignment " + alignment.LowLevelToString()); fieldOffsets.Add(position[fieldStorage]); position[fieldStorage] += size; } } // Pad the length of structs to be 1 if they are empty so we have no zero-length structures if ((position[(int)NativeFormat.FieldStorage.Instance] == LayoutInt.Zero) && type.IsValueType) { position[(int)NativeFormat.FieldStorage.Instance] = LayoutInt.One; } Debug.Assert(alignRequired == new LayoutInt(1) || alignRequired == new LayoutInt(2) || alignRequired == new LayoutInt(4) || alignRequired == new LayoutInt(8)); position[InstanceAlignmentEntry] = alignRequired; return(position); }
private static TypeDesc EnsureLoadableTypeUncached(TypeDesc type) { if (type.IsParameterizedType) { // Validate parameterized types var parameterizedType = (ParameterizedType)type; TypeDesc parameterType = parameterizedType.ParameterType; // Make sure type of the parameter is loadable. ((CompilerTypeSystemContext)type.Context).EnsureLoadableType(parameterType); // Validate we're not constructing a type over a ByRef if (parameterType.IsByRef) { // CLR compat note: "ldtoken int32&&" will actually fail with a message about int32&; "ldtoken int32&[]" // will fail with a message about being unable to create an array of int32&. This is a middle ground. ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type); } // Validate the parameter is not an uninstantiated generic. if (parameterType.IsGenericDefinition) { ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type); } if (parameterizedType.IsArray) { if (parameterType.IsFunctionPointer) { // Arrays of function pointers are not currently supported ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type); } if (!parameterType.IsRuntimeDeterminedSubtype) { LayoutInt elementSize = parameterType.GetElementSize(); if (!elementSize.IsIndeterminate && elementSize.AsInt >= ushort.MaxValue) { // Element size over 64k can't be encoded in the GCDesc ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadValueClassTooLarge, parameterType); } } if (((ArrayType)parameterizedType).Rank > 32) { ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadRankTooLarge, type); } if (parameterType.IsByRefLike) { // Arrays of byref-like types are not allowed ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type); } // It might seem reasonable to disallow array of void, but the CLR doesn't prevent that too hard. // E.g. "newarr void" will fail, but "newarr void[]" or "ldtoken void[]" will succeed. } } else if (type.IsFunctionPointer) { ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type); } else { // Validate classes, structs, enums, interfaces, and delegates Debug.Assert(type.IsDefType); // Don't validate generic definitons or runtime determined subtypes if (type.IsGenericDefinition || type.IsRuntimeDeterminedSubtype) { return(type); } // System.__Canon or System.__UniversalCanon if (type.IsCanonicalDefinitionType(CanonicalFormKind.Any)) { return(type); } // We need to be able to load interfaces foreach (var intf in type.RuntimeInterfaces) { ((CompilerTypeSystemContext)type.Context).EnsureLoadableType(intf.NormalizeInstantiation()); } if (type.BaseType != null) { ((CompilerTypeSystemContext)type.Context).EnsureLoadableType(type.BaseType); } var defType = (DefType)type; // Ensure we can compute the type layout defType.ComputeInstanceLayout(InstanceLayoutKind.TypeAndFields); defType.ComputeStaticFieldLayout(StaticLayoutKind.StaticRegionSizesAndFields); // Make sure instantiation length matches the expectation if (defType.Instantiation.Length != defType.GetTypeDefinition().Instantiation.Length) { ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type); } foreach (TypeDesc typeArg in defType.Instantiation) { // ByRefs, pointers, function pointers, and System.Void are never valid instantiation arguments if (typeArg.IsByRef || typeArg.IsPointer || typeArg.IsFunctionPointer || typeArg.IsVoid || typeArg.IsByRefLike) { ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type); } // TODO: validate constraints } // Check the type doesn't have bogus MethodImpls or overrides and we can get the finalizer. defType.GetFinalizer(); } return(type); }
private FieldAndOffset[] CreateDynamicLayout(DefType defType, EcmaModule module) { List <FieldAndOffset> fieldsForType = null; int pointerSize = module.Context.Target.PointerSize; // In accordance with CoreCLR runtime conventions, // index 0 corresponds to regular statics, index 1 to thread-local statics. int[][] nonGcStaticsCount = new int[StaticIndexCount][] { new int[TargetDetails.MaximumLog2PrimitiveSize + 1], new int[TargetDetails.MaximumLog2PrimitiveSize + 1], }; int[] gcPointerCount = new int[StaticIndexCount]; int[] gcBoxedCount = new int[StaticIndexCount]; foreach (FieldDesc field in defType.GetFields()) { FieldDefinition fieldDef = module.MetadataReader.GetFieldDefinition(((EcmaField)field.GetTypicalFieldDefinition()).Handle); if ((fieldDef.Attributes & (FieldAttributes.Static | FieldAttributes.Literal)) == FieldAttributes.Static) { int index = (IsFieldThreadStatic(in fieldDef, module.MetadataReader) ? StaticIndexThreadLocal : StaticIndexRegular); int alignment; int size; bool isGcPointerField; bool isGcBoxedField; CorElementType corElementType; EntityHandle valueTypeHandle; GetFieldElementTypeAndValueTypeHandle(in fieldDef, module.MetadataReader, out corElementType, out valueTypeHandle); GetElementTypeInfo(module, field, valueTypeHandle, corElementType, pointerSize, out alignment, out size, out isGcPointerField, out isGcBoxedField); if (isGcPointerField) { gcPointerCount[index]++; } else if (isGcBoxedField) { gcBoxedCount[index]++; } if (size != 0) { int log2Size = GetLog2Size(size); nonGcStaticsCount[index][log2Size]++; } } } int nonGcInitialOffset; switch (pointerSize) { case 4: nonGcInitialOffset = DomainLocalModuleNormalDynamicEntryOffsetOfDataBlob32Bit; break; case 8: nonGcInitialOffset = DomainLocalModuleNormalDynamicEntryOffsetOfDataBlob64Bit; break; default: throw new NotImplementedException(); } LayoutInt[] nonGcStaticFieldOffsets = new LayoutInt[StaticIndexCount] { new LayoutInt(nonGcInitialOffset), new LayoutInt(nonGcInitialOffset), }; LayoutInt[][] nonGcStatics = new LayoutInt[StaticIndexCount][] { new LayoutInt[TargetDetails.MaximumLog2PrimitiveSize + 1], new LayoutInt[TargetDetails.MaximumLog2PrimitiveSize + 1], }; for (int log2Size = TargetDetails.MaximumLog2PrimitiveSize; log2Size >= 0; log2Size--) { for (int index = 0; index < StaticIndexCount; index++) { LayoutInt offset = nonGcStaticFieldOffsets[index]; nonGcStatics[index][log2Size] = offset; offset += new LayoutInt(nonGcStaticsCount[index][log2Size] << log2Size); nonGcStaticFieldOffsets[index] = offset; } } LayoutInt[] gcBoxedFieldOffsets = new LayoutInt[StaticIndexCount]; LayoutInt[] gcPointerFieldOffsets = new LayoutInt[StaticIndexCount] { new LayoutInt(gcBoxedCount[StaticIndexRegular] * pointerSize), new LayoutInt(gcBoxedCount[StaticIndexThreadLocal] * pointerSize) }; foreach (FieldDesc field in defType.GetFields()) { FieldDefinitionHandle fieldDefHandle = ((EcmaField)field.GetTypicalFieldDefinition()).Handle; FieldDefinition fieldDef = module.MetadataReader.GetFieldDefinition(fieldDefHandle); if ((fieldDef.Attributes & (FieldAttributes.Static | FieldAttributes.Literal)) == FieldAttributes.Static) { int index = (IsFieldThreadStatic(in fieldDef, module.MetadataReader) ? StaticIndexThreadLocal : StaticIndexRegular); int alignment; int size; bool isGcPointerField; bool isGcBoxedField; CorElementType corElementType; EntityHandle valueTypeHandle; GetFieldElementTypeAndValueTypeHandle(in fieldDef, module.MetadataReader, out corElementType, out valueTypeHandle); GetElementTypeInfo(module, field, valueTypeHandle, corElementType, pointerSize, out alignment, out size, out isGcPointerField, out isGcBoxedField); LayoutInt offset = LayoutInt.Zero; if (size != 0) { int log2Size = GetLog2Size(size); offset = nonGcStatics[index][log2Size]; nonGcStatics[index][log2Size] += new LayoutInt(1 << log2Size); } if (isGcPointerField) { offset = gcPointerFieldOffsets[index]; gcPointerFieldOffsets[index] += new LayoutInt(pointerSize); } else if (isGcBoxedField) { offset = gcBoxedFieldOffsets[index]; gcBoxedFieldOffsets[index] += new LayoutInt(pointerSize); } if (fieldsForType == null) { fieldsForType = new List <FieldAndOffset>(); } fieldsForType.Add(new FieldAndOffset(field, offset)); } } return(fieldsForType == null ? null : fieldsForType.ToArray()); }
private uint GetClassTypeIndex(TypeDesc type, bool needsCompleteType) { DefType defType = type as DefType; System.Diagnostics.Debug.Assert(defType != null, "GetClassTypeIndex was called with non def type"); ClassTypeDescriptor classTypeDescriptor = new ClassTypeDescriptor { IsStruct = type.IsValueType ? 1 : 0, Name = _objectWriter.GetMangledName(type), BaseClassId = 0 }; uint typeIndex = _objectWriter.GetClassTypeIndex(classTypeDescriptor); _knownTypes[type] = typeIndex; if (type.HasBaseType && !type.IsValueType) { classTypeDescriptor.BaseClassId = GetTypeIndex(defType.BaseType, true); } List <DataFieldDescriptor> fieldsDescs = new List <DataFieldDescriptor>(); List <DataFieldDescriptor> nonGcStaticFields = new List <DataFieldDescriptor>(); List <DataFieldDescriptor> gcStaticFields = new List <DataFieldDescriptor>(); List <DataFieldDescriptor> threadStaticFields = new List <DataFieldDescriptor>(); bool isCanonical = defType.IsCanonicalSubtype(CanonicalFormKind.Any); foreach (var fieldDesc in defType.GetFields()) { if (fieldDesc.HasRva || fieldDesc.IsLiteral) { continue; } if (isCanonical && fieldDesc.IsStatic) { continue; } LayoutInt fieldOffset = fieldDesc.Offset; int fieldOffsetEmit = fieldOffset.IsIndeterminate ? 0xBAAD : fieldOffset.AsInt; DataFieldDescriptor field = new DataFieldDescriptor { FieldTypeIndex = GetVariableTypeIndex(GetFieldDebugType(fieldDesc), false), Offset = (ulong)fieldOffsetEmit, Name = fieldDesc.Name }; if (fieldDesc.IsStatic) { if (fieldDesc.IsThreadStatic) { threadStaticFields.Add(field); } else if (fieldDesc.HasGCStaticBase) { gcStaticFields.Add(field); } else { nonGcStaticFields.Add(field); } } else { fieldsDescs.Add(field); } } InsertStaticFieldRegionMember(fieldsDescs, defType, nonGcStaticFields, WindowsNodeMangler.NonGCStaticMemberName, "__type_" + WindowsNodeMangler.NonGCStaticMemberName, false); InsertStaticFieldRegionMember(fieldsDescs, defType, gcStaticFields, WindowsNodeMangler.GCStaticMemberName, "__type_" + WindowsNodeMangler.GCStaticMemberName, Abi == TargetAbi.CoreRT); InsertStaticFieldRegionMember(fieldsDescs, defType, threadStaticFields, WindowsNodeMangler.ThreadStaticMemberName, "__type_" + WindowsNodeMangler.ThreadStaticMemberName, Abi == TargetAbi.CoreRT); DataFieldDescriptor[] fields = new DataFieldDescriptor[fieldsDescs.Count]; for (int i = 0; i < fieldsDescs.Count; ++i) { fields[i] = fieldsDescs[i]; } LayoutInt elementSize = defType.GetElementSize(); int elementSizeEmit = elementSize.IsIndeterminate ? 0xBAAD : elementSize.AsInt; ClassFieldsTypeDescriptor fieldsDescriptor = new ClassFieldsTypeDescriptor { Size = (ulong)elementSizeEmit, FieldsCount = fieldsDescs.Count }; uint completeTypeIndex = _objectWriter.GetCompleteClassTypeIndex(classTypeDescriptor, fieldsDescriptor, fields); _completeKnownTypes[type] = completeTypeIndex; if (needsCompleteType) { return(completeTypeIndex); } else { return(typeIndex); } }
protected override ModuleFieldLayout CreateValueFromKey(EcmaModule module) { int typeCountInModule = module.MetadataReader.GetTableRowCount(TableIndex.TypeDef); int pointerSize = module.Context.Target.PointerSize; // 0 corresponds to "normal" statics, 1 to thread-local statics LayoutInt[] gcStatics = new LayoutInt[StaticIndexCount] { LayoutInt.Zero, LayoutInt.Zero }; LayoutInt[] nonGcStatics = new LayoutInt[StaticIndexCount] { new LayoutInt(DomainLocalModuleDataBlobOffsetAsIntPtrCount * pointerSize + typeCountInModule), new LayoutInt(ThreadLocalModuleDataBlobOffsetAsIntPtrCount * pointerSize + typeCountInModule), }; Dictionary <TypeDefinitionHandle, FieldAndOffset[]> typeToFieldMap = new Dictionary <TypeDefinitionHandle, FieldAndOffset[]>(); foreach (TypeDefinitionHandle typeDefHandle in module.MetadataReader.TypeDefinitions) { TypeDefinition typeDef = module.MetadataReader.GetTypeDefinition(typeDefHandle); List <FieldAndOffset> fieldsForType = null; if (typeDef.GetGenericParameters().Count != 0) { // Generic types are exempt from the static field layout algorithm, see // <a href="https://github.com/dotnet/coreclr/blob/659af58047a949ed50d11101708538d2e87f2568/src/vm/ceeload.cpp#L2049">this check</a>. continue; } foreach (FieldDefinitionHandle fieldDefHandle in typeDef.GetFields()) { FieldDefinition fieldDef = module.MetadataReader.GetFieldDefinition(fieldDefHandle); if ((fieldDef.Attributes & (FieldAttributes.Static | FieldAttributes.Literal)) == FieldAttributes.Static) { int index = (IsFieldThreadStatic(in fieldDef, module.MetadataReader) ? StaticIndexThreadLocal : StaticIndexRegular); int alignment; int size; bool isGcPointerField; bool isGcBoxedField; CorElementType corElementType; EntityHandle valueTypeHandle; GetFieldElementTypeAndValueTypeHandle(in fieldDef, module.MetadataReader, out corElementType, out valueTypeHandle); FieldDesc fieldDesc = module.GetField(fieldDefHandle); GetElementTypeInfo(module, fieldDesc, valueTypeHandle, corElementType, pointerSize, out alignment, out size, out isGcPointerField, out isGcBoxedField); LayoutInt offset = LayoutInt.Zero; if (size != 0) { offset = LayoutInt.AlignUp(nonGcStatics[index], new LayoutInt(alignment)); nonGcStatics[index] = offset + new LayoutInt(size); } if (isGcPointerField || isGcBoxedField) { offset = LayoutInt.AlignUp(gcStatics[index], new LayoutInt(pointerSize)); gcStatics[index] = offset + new LayoutInt(pointerSize); } if (fieldsForType == null) { fieldsForType = new List <FieldAndOffset>(); } fieldsForType.Add(new FieldAndOffset(fieldDesc, offset)); } } if (fieldsForType != null) { typeToFieldMap.Add(typeDefHandle, fieldsForType.ToArray()); } } LayoutInt blockAlignment = new LayoutInt(TargetDetails.MaximumPrimitiveSize); return(new ModuleFieldLayout( module, gcStatics: new StaticsBlock() { Size = gcStatics[StaticIndexRegular], LargestAlignment = blockAlignment }, nonGcStatics: new StaticsBlock() { Size = nonGcStatics[StaticIndexRegular], LargestAlignment = blockAlignment }, threadGcStatics: new StaticsBlock() { Size = gcStatics[StaticIndexThreadLocal], LargestAlignment = blockAlignment }, threadNonGcStatics: new StaticsBlock() { Size = nonGcStatics[StaticIndexThreadLocal], LargestAlignment = blockAlignment }, typeToFieldMap: typeToFieldMap)); }