diff options
author | Jan Kotas <jkotas@microsoft.com> | 2015-12-11 06:36:06 +0300 |
---|---|---|
committer | Jan Kotas <jkotas@microsoft.com> | 2015-12-11 06:36:06 +0300 |
commit | 78d9cf9f7a3d3d68a5a7a626e2193a4de7b38370 (patch) | |
tree | e9fd5002140029642e08f183c27e608db1e81c5c /src/ILCompiler.Compiler | |
parent | aca70b738cd7c5c3c5c693c0aac1db8c2c26ed72 (diff) | |
parent | 0b395e6ba4476b5b63e3610032e7c970b0e925cb (diff) |
Merge pull request #507 from nattress/eetype_optional_fields
Populate EEType Optional Fields
Diffstat (limited to 'src/ILCompiler.Compiler')
6 files changed, 347 insertions, 51 deletions
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs index 782f03a03..23fadade0 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs @@ -47,7 +47,9 @@ namespace ILCompiler.DependencyAnalysis { private TypeDesc _type; private bool _constructed; - + EETypeOptionalFieldsBuilder _optionalFieldsBuilder = new EETypeOptionalFieldsBuilder(); + EETypeOptionalFieldsNode _optionalFieldsNode; + public EETypeNode(TypeDesc type, bool constructed) { _type = type; @@ -128,6 +130,12 @@ namespace ILCompiler.DependencyAnalysis objData.Alignment = 16; objData.DefinedSymbols.Add(this); + ComputeOptionalEETypeFields(factory); + if (null == _optionalFieldsNode) + { + _optionalFieldsNode = factory.EETypeOptionalFields(_optionalFieldsBuilder); + } + OutputComponentSize(ref objData); OutputFlags(factory, ref objData); OutputBaseSize(ref objData); @@ -140,9 +148,10 @@ namespace ILCompiler.DependencyAnalysis { OutputVirtualSlots(factory, ref objData, _type, _type); OutputFinalizerMethod(factory, ref objData); + OutputOptionalFields(factory, ref objData); OutputNullableTypeParameter(factory, ref objData); } - + return objData.ToObjectData(); } @@ -189,7 +198,7 @@ namespace ILCompiler.DependencyAnalysis { return 16 + pointerSize; } - + private void OutputComponentSize(ref ObjectDataBuilder objData) { if (_type.IsArray) @@ -205,21 +214,20 @@ namespace ILCompiler.DependencyAnalysis objData.EmitShort(0); } } - + private void OutputFlags(NodeFactory factory, ref ObjectDataBuilder objData) { // Todo: RelatedTypeViaIATFlag when we support cross-module EETypes // Todo: GenericVarianceFlag when we support variance - // Todo: OptionalFieldsFlag // Todo: Generic Type Definition EETypes - + UInt16 flags = (UInt16)EETypeKind.CanonicalEEType; - + if (_type.IsArray || _type.IsPointer) { flags = (UInt16)EETypeKind.ParameterizedEEType; } - + if (_type.IsValueType) { flags |= (UInt16)EETypeFlags.ValueTypeFlag; @@ -229,7 +237,7 @@ namespace ILCompiler.DependencyAnalysis { flags |= (UInt16)EETypeFlags.HasFinalizerFlag; } - + if (_type is MetadataType && ((MetadataType)_type).ContainsPointers) { flags |= (UInt16)EETypeFlags.HasPointersFlag; @@ -243,7 +251,7 @@ namespace ILCompiler.DependencyAnalysis flags |= (UInt16)EETypeFlags.HasPointersFlag; } } - + if (_type.IsInterface) { flags |= (UInt16)EETypeFlags.IsInterfaceFlag; @@ -254,8 +262,13 @@ namespace ILCompiler.DependencyAnalysis flags |= (UInt16)EETypeFlags.IsGenericFlag; } + if (_optionalFieldsBuilder.IsAtLeastOneFieldUsed()) + { + flags |= (UInt16)EETypeFlags.OptionalFieldsFlag; + } + int corElementType = 0; - + // The top 5 bits of flags are used to convey enum underlying type, primitive type, or mark the type as being System.Array if (_type.IsEnum) { @@ -271,7 +284,7 @@ namespace ILCompiler.DependencyAnalysis { corElementType = 0x14; // ELEMENT_TYPE_ARRAY } - + if (corElementType > 0) { flags |= (UInt16)(corElementType << (UInt16)EETypeFlags.CorElementTypeShift); @@ -279,8 +292,8 @@ namespace ILCompiler.DependencyAnalysis objData.EmitShort((short)flags); } - - private int ComputeRhCorElementType(TypeDesc type) + + private static int ComputeRhCorElementType(TypeDesc type) { Debug.Assert(type.IsPrimitive); Debug.Assert(type.Category != TypeFlags.Unknown); @@ -324,7 +337,30 @@ namespace ILCompiler.DependencyAnalysis Debug.Assert(false, "Primitive type value expected."); return 0; } - + + private static bool ComputeRequiresAlign8(TypeDesc type) + { + if (type.Context.Target.Architecture != TargetArchitecture.ARM) + { + return false; + } + + if (type.IsArray) + { + var elementType = ((ArrayType)type).ElementType; + if ((elementType.IsValueType) && ((DefType)elementType).InstanceByteAlignment > 4) + { + return true; + } + } + else if (type is DefType && ((DefType)type).InstanceByteAlignment > 4) + { + return true; + } + + return false; + } + private void OutputBaseSize(ref ObjectDataBuilder objData) { int pointerSize = _type.Context.Target.PointerSize; @@ -352,10 +388,10 @@ namespace ILCompiler.DependencyAnalysis } 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. @@ -375,7 +411,7 @@ namespace ILCompiler.DependencyAnalysis { relatedType = ((ParameterizedType)_type).ParameterType; } - + if (relatedType != null) { if (_constructed) @@ -409,7 +445,7 @@ namespace ILCompiler.DependencyAnalysis currentTypeSlice = currentTypeSlice.BaseType; } - + objData.EmitShort(checked((short)virtualSlotCount)); // Todo: Number of slots of EEInterfaceInfo when we add interface support @@ -450,6 +486,14 @@ namespace ILCompiler.DependencyAnalysis } } + private void OutputOptionalFields(NodeFactory factory, ref ObjectDataBuilder objData) + { + if(_optionalFieldsBuilder.IsAtLeastOneFieldUsed()) + { + objData.EmitPointerReloc(_optionalFieldsNode); + } + } + private void OutputNullableTypeParameter(NodeFactory factory, ref ObjectDataBuilder objData) { if (_type.IsNullable) @@ -457,5 +501,128 @@ namespace ILCompiler.DependencyAnalysis objData.EmitPointerReloc(factory.NecessaryTypeSymbol(_type.Instantiation[0])); } } + + /// <summary> + /// Populate the OptionalFieldsRuntimeBuilder if any optional fields are required. Returns true iff + /// at least one optional field was set. + /// </summary> + private void ComputeOptionalEETypeFields(NodeFactory factory) + { + // Todo: DispatchMap table index when we support interface dispatch maps + ComputeRareFlags(); + ComputeNullableValueOffset(); + ComputeICastableVirtualMethodSlots(factory); + ComputeValueTypeFieldPadding(); + } + + void ComputeRareFlags() + { + uint flags = 0; + + if (_type.IsNullable) + { + flags |= (uint)EETypeRareFlags.IsNullableFlag; + } + + if (_type.HasStaticConstructor) + { + flags |= (uint)EETypeRareFlags.HasCctorFlag; + } + + if (ComputeRequiresAlign8(_type)) + { + flags |= (uint)EETypeRareFlags.RequiresAlign8Flag; + } + + if (_type is DefType && ((DefType)_type).IsHFA()) + { + flags |= (uint)EETypeRareFlags.IsHFAFlag; + } + + if (flags != 0) + { + _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldsElement.RareFlags, flags); + } + } + + /// <summary> + /// To support boxing / unboxing, the offset of the value field of a Nullable type is recorded on the EEType. + /// This is variable according to the alignment requirements of the Nullable<T> type parameter. + /// </summary> + void ComputeNullableValueOffset() + { + if (!_type.IsNullable) + return; + + var field = _type.GetField("value"); + + // Ensure the definition of Nullable<T> didn't change on us + Debug.Assert(field != null); + + // In the definition of Nullable<T>, the first field should be the boolean representing "hasValue" + Debug.Assert(field.Offset > 0); + + // The contract with the runtime states the Nullable value offset is stored with the boolean "hasValue" size subtracted + // to get a small encoding size win. + _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldsElement.NullableValueOffset, (uint)field.Offset - 1); + } + + /// <summary> + /// ICastable is a special interface whose two methods are not invoked using regular interface dispatch. + /// Instead, their VTable slots are recorded on the EEType of an object implementing ICastable and are + /// called directly. + /// </summary> + void ComputeICastableVirtualMethodSlots(NodeFactory factory) + { + // TODO: This method is untested (we don't support interfaces yet) + if (_type.IsInterface) + return; + + foreach (DefType itf in _type.RuntimeInterfaces) + { + if (itf == factory.ICastableInterface) + { + var isInstMethod = itf.GetMethod("IsInstanceOfInterface", null); + var getImplTypeMethod = itf.GetMethod("GetImplType", null); + Debug.Assert(isInstMethod != null && getImplTypeMethod != null); + + int isInstMethodSlot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, isInstMethod); + int getImplTypeMethodSlot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, getImplTypeMethod); + + if (isInstMethodSlot != -1 || getImplTypeMethodSlot != -1) + { + var rareFlags = _optionalFieldsBuilder.GetFieldValue(EETypeOptionalFieldsElement.RareFlags, 0); + rareFlags |= (uint)EETypeRareFlags.ICastableFlag; + _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldsElement.RareFlags, rareFlags); + } + + if (isInstMethodSlot != -1) + { + _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldsElement.ICastableIsInstSlot, (uint)isInstMethodSlot); + } + if (getImplTypeMethodSlot != -1) + { + _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldsElement.ICastableGetImplTypeSlot, (uint)getImplTypeMethodSlot); + } + } + } + } + + void ComputeValueTypeFieldPadding() + { + if (!_type.IsValueType) + return; + + DefType defType = _type as DefType; + Debug.Assert(defType != null); + + uint valueTypeFieldPadding = checked((uint)(defType.InstanceByteCount - defType.InstanceByteCountUnaligned)); + uint valueTypeFieldPaddingEncoded = EEType.ComputeValueTypeFieldPaddingFieldValue(valueTypeFieldPadding, (uint)defType.InstanceFieldAlignment); + + if (valueTypeFieldPaddingEncoded != 0) + { + _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldsElement.ValueTypeFieldPadding, valueTypeFieldPaddingEncoded); + } + } } } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeOptionalFieldsNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeOptionalFieldsNode.cs new file mode 100644 index 000000000..357de99c4 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeOptionalFieldsNode.cs @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using ILCompiler.DependencyAnalysisFramework; +using Internal.Runtime; +using Internal.TypeSystem; +using System; +using System.Collections.Generic; +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + internal class EETypeOptionalFieldsNode : ObjectNode, ISymbolNode + { + EETypeOptionalFieldsBuilder _fieldBuilder = new EETypeOptionalFieldsBuilder(); + + public EETypeOptionalFieldsNode(EETypeOptionalFieldsBuilder fieldBuilder) + { + _fieldBuilder = fieldBuilder; + } + + public override string Section + { + get + { + return "data"; + } + } + + public override bool StaticDependenciesAreComputed + { + get + { + return true; + } + } + + int ISymbolNode.Offset + { + get + { + return 0; + } + } + + string ISymbolNode.MangledName + { + get + { + return "optionalfields_" + _fieldBuilder.ToString(); + } + } + + public override string GetName() + { + return ((ISymbolNode)this).MangledName; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataBuilder objData = new ObjectDataBuilder(factory); + objData.RequirePointerAlignment(); + objData.DefinedSymbols.Add(this); + objData.EmitBytes(_fieldBuilder.GetBytes()); + + return objData.ToObjectData(); + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs index c8a174edc..8949d6ad4 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs @@ -3,9 +3,11 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using ILCompiler.DependencyAnalysisFramework; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; +using Internal.Runtime; namespace ILCompiler.DependencyAnalysis { @@ -136,6 +138,11 @@ namespace ILCompiler.DependencyAnalysis { return new StringIndirectionNode(data); }); + + _typeOptionalFields = new NodeCache<EETypeOptionalFieldsBuilder, EETypeOptionalFieldsNode>((EETypeOptionalFieldsBuilder fieldBuilder) => + { + return new EETypeOptionalFieldsNode(fieldBuilder); + }); } private NodeCache<TypeDesc, EETypeNode> _typeSymbols; @@ -222,6 +229,13 @@ namespace ILCompiler.DependencyAnalysis return _readOnlyDataBlobs.GetOrAdd(new Tuple<string, byte[], int>(name, blobData, alignment)); } + private NodeCache<EETypeOptionalFieldsBuilder, EETypeOptionalFieldsNode> _typeOptionalFields; + + internal EETypeOptionalFieldsNode EETypeOptionalFields(EETypeOptionalFieldsBuilder fieldBuilder) + { + return _typeOptionalFields.GetOrAdd(fieldBuilder); + } + private NodeCache<string, ExternSymbolNode> _externSymbols; public ISymbolNode ExternSymbol(string name) @@ -283,6 +297,21 @@ namespace ILCompiler.DependencyAnalysis return symbol; } + private TypeDesc _systemICastableType; + + public TypeDesc ICastableInterface + { + get + { + if (_systemICastableType == null) + { + _systemICastableType = _context.SystemModule.GetType("System.Runtime.CompilerServices", "ICastable"); + Debug.Assert(_systemICastableType != null); + } + return _systemICastableType; + } + } + private NodeCache<MethodDesc, VirtualMethodUseNode> _virtMethods; public DependencyNode VirtualMethodUse(MethodDesc decl) diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs index c50307fd2..d9e3a7890 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using ILCompiler.DependencyAnalysis.X64; using Internal.TypeSystem; +using ILCompiler; namespace ILCompiler.DependencyAnalysis { @@ -30,37 +31,10 @@ namespace ILCompiler.DependencyAnalysis AddrMode loadFromRcx = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRcx); - // TODO: More efficient lookup of the slot { - MethodDesc method = (MethodDesc)Target; - TypeDesc owningType = method.OwningType; - - int baseSlots = 0; - var baseType = owningType.BaseType; - - while (baseType != null) - { - List<MethodDesc> baseVirtualSlots; - factory.VirtualSlots.TryGetValue(baseType, out baseVirtualSlots); - - if (baseVirtualSlots != null) - baseSlots += baseVirtualSlots.Count; - baseType = baseType.BaseType; - } - - List<MethodDesc> virtualSlots = factory.VirtualSlots[owningType]; - int methodSlot = -1; - for (int slot = 0; slot < virtualSlots.Count; slot++) - { - if (virtualSlots[slot] == method) - { - methodSlot = slot; - break; - } - } - - Debug.Assert(methodSlot != -1); - AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (baseSlots + methodSlot) * factory.Target.PointerSize, 0, AddrModeSize.Int64); + int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, (MethodDesc)Target); + Debug.Assert(slot != -1); + AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64); encoder.EmitJmpToAddrMode(ref jmpAddrMode); } break; diff --git a/src/ILCompiler.Compiler/src/Compiler/VirtualMethodCallHelper.cs b/src/ILCompiler.Compiler/src/Compiler/VirtualMethodCallHelper.cs new file mode 100644 index 000000000..fa53f5660 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/VirtualMethodCallHelper.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Internal.TypeSystem; +using ILCompiler.DependencyAnalysis; + +namespace ILCompiler +{ + class VirtualMethodSlotHelper + { + /// <summary> + /// Given a virtual method decl, return its VTable slot if the method is used on its containing type. + /// Return -1 if the virtual method is not used. + /// </summary> + public static int GetVirtualMethodSlot(NodeFactory factory, MethodDesc method) + { + // TODO: More efficient lookup of the slot + TypeDesc owningType = method.OwningType; + int baseSlots = 0; + var baseType = owningType.BaseType; + + while (baseType != null) + { + List<MethodDesc> baseVirtualSlots; + factory.VirtualSlots.TryGetValue(baseType, out baseVirtualSlots); + + if (baseVirtualSlots != null) + baseSlots += baseVirtualSlots.Count; + baseType = baseType.BaseType; + } + + List<MethodDesc> virtualSlots = factory.VirtualSlots[owningType]; + int methodSlot = -1; + for (int slot = 0; slot < virtualSlots.Count; slot++) + { + if (virtualSlots[slot] == method) + { + methodSlot = slot; + break; + } + } + + return methodSlot == -1 ? -1 : baseSlots + methodSlot; + } + } +} diff --git a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj index e8f4c1ef4..ab7735480 100644 --- a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj +++ b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj @@ -38,6 +38,7 @@ <Compile Include="Compiler\CompilerTypeSystemContext.cs" /> <Compile Include="Compiler\DelegateInfo.cs" /> <Compile Include="Compiler\DependencyAnalysis\ArrayOfEmbeddedDataNode.cs" /> + <Compile Include="Compiler\DependencyAnalysis\EETypeOptionalFieldsNode.cs" /> <Compile Include="Compiler\DependencyAnalysis\EmbeddedObjectNode.cs" /> <Compile Include="Compiler\DependencyAnalysis\JumpStubNode.cs" /> <Compile Include="Compiler\DependencyAnalysis\StringDataNode.cs" /> @@ -77,14 +78,24 @@ <Compile Include="Compiler\RegisteredMethod.cs" /> <Compile Include="Compiler\RegisteredType.cs" /> <Compile Include="Compiler\IntrinsicMethods.cs" /> + <Compile Include="Compiler\VirtualMethodCallHelper.cs" /> <Compile Include="CppCodeGen\CppWriter.cs" /> </ItemGroup> <ItemGroup> <Compile Include="..\..\Common\src\System\Collections\Generic\ArrayBuilder.cs"> <Link>Common\ArrayBuilder.cs</Link> </Compile> - <Compile Include="..\..\Common\src\Internal\Runtime\EETypeFlags.cs" > - <Link>Common\EETypeFlags.cs</Link> + <Compile Include="..\..\Common\src\Internal\Runtime\EEType.Constants.cs"> + <Link>Common\EEType.Constants.cs</Link> + </Compile> + <Compile Include="..\..\Common\src\Internal\Runtime\EETypeOptionalFieldsBuilder.cs"> + <Link>Common\EETypeOptionalFieldsBuilder.cs</Link> + </Compile> + <Compile Include="..\..\Common\src\Internal\Runtime\EEType.cs"> + <Link>Common\EEType.cs</Link> + </Compile> + <Compile Include="..\..\Common\src\Internal\NativeFormat\NativeFormatWriter.Primitives.cs"> + <Link>Common\NativeFormat\NativeFormatWriter.Primitives.cs</Link> </Compile> </ItemGroup> <ItemGroup> |