From 3ef1ae9b930e7c16cd347601851047c745888bdb Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Tue, 10 Jan 2017 09:36:43 -0800 Subject: Delete unused file (#2477) --- .../Unix/System.Native/Interop.GetUnixName.cs | 21 --------------------- .../src/System.Private.CoreLib.csproj | 3 --- 2 files changed, 24 deletions(-) delete mode 100644 src/Common/src/Interop/Unix/System.Native/Interop.GetUnixName.cs diff --git a/src/Common/src/Interop/Unix/System.Native/Interop.GetUnixName.cs b/src/Common/src/Interop/Unix/System.Native/Interop.GetUnixName.cs deleted file mode 100644 index 33664c4d3..000000000 --- a/src/Common/src/Interop/Unix/System.Native/Interop.GetUnixName.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.InteropServices; - -internal static partial class Interop -{ - internal static partial class Sys - { - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetUnixName")] - private static extern IntPtr GetUnixNamePrivate(); - - internal static string GetUnixName() - { - IntPtr ptr = GetUnixNamePrivate(); - return Marshal.PtrToStringAnsi(ptr); - } - } -} diff --git a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj index dfb40c395..d5b115c32 100644 --- a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -1005,9 +1005,6 @@ Interop\Unix\System.Native\Interop.GetCwd.cs - - Interop\Unix\System.Native\Interop.GetUnixName.cs - Interop\Unix\System.Native\Interop.LSeek.cs -- cgit v1.2.3 From b436eadd909f3357ab1e0183c2cba251ce00d6ae Mon Sep 17 00:00:00 2001 From: Faizur Rahman Date: Mon, 9 Jan 2017 15:57:46 -0800 Subject: Parse MarshalAs Descriptors --- src/Common/src/TypeSystem/Ecma/EcmaMethod.cs | 17 ++++++-- .../src/TypeSystem/Ecma/EcmaSignatureParser.cs | 22 ++++++++++ src/Common/src/TypeSystem/Interop/IL/Marshaller.cs | 5 +-- .../src/TypeSystem/Interop/MarshalAsDescriptor.cs | 50 ++++++++++++++++++++++ .../src/TypeSystem/Interop/MethodDesc.Interop.cs | 6 +-- .../src/ILCompiler.TypeSystem.csproj | 3 ++ 6 files changed, 94 insertions(+), 9 deletions(-) create mode 100644 src/Common/src/TypeSystem/Interop/MarshalAsDescriptor.cs diff --git a/src/Common/src/TypeSystem/Ecma/EcmaMethod.cs b/src/Common/src/TypeSystem/Ecma/EcmaMethod.cs index 2a85cbf8d..79b28c58e 100644 --- a/src/Common/src/TypeSystem/Ecma/EcmaMethod.cs +++ b/src/Common/src/TypeSystem/Ecma/EcmaMethod.cs @@ -429,18 +429,29 @@ namespace Internal.TypeSystem.Ecma Debug.Assert((int)ParameterAttributes.HasDefault == (int)ParameterMetadataAttributes.HasDefault); Debug.Assert((int)ParameterAttributes.HasFieldMarshal == (int)ParameterMetadataAttributes.HasFieldMarshal); - ParameterHandleCollection parameterHandles = metadataReader.GetMethodDefinition(_handle).GetParameters(); ParameterMetadata[] parameterMetadataArray = new ParameterMetadata[parameterHandles.Count]; int index = 0; foreach (ParameterHandle parameterHandle in parameterHandles) { Parameter parameter = metadataReader.GetParameter(parameterHandle); - TypeDesc marshalAs = null; // TODO: read marshalAs argument; - ParameterMetadata data = new ParameterMetadata(parameter.SequenceNumber, (ParameterMetadataAttributes)parameter.Attributes, marshalAs); + MarshalAsDescriptor marshalAsDescriptor = GetMarshalAsDescriptor(parameter); + ParameterMetadata data = new ParameterMetadata(parameter.SequenceNumber, (ParameterMetadataAttributes)parameter.Attributes, marshalAsDescriptor); parameterMetadataArray[index++] = data; } return parameterMetadataArray; } + + private MarshalAsDescriptor GetMarshalAsDescriptor(Parameter parameter) + { + if ((parameter.Attributes & ParameterAttributes.HasFieldMarshal) == ParameterAttributes.HasFieldMarshal) + { + MetadataReader metadataReader = MetadataReader; + BlobReader marshalAsReader = metadataReader.GetBlobReader(parameter.GetMarshallingDescriptor()); + EcmaSignatureParser parser = new EcmaSignatureParser(Module, marshalAsReader); + return parser.ParseMarshalAsDescriptor(); + } + return null; + } } } diff --git a/src/Common/src/TypeSystem/Ecma/EcmaSignatureParser.cs b/src/Common/src/TypeSystem/Ecma/EcmaSignatureParser.cs index 398029ea1..deeb9514b 100644 --- a/src/Common/src/TypeSystem/Ecma/EcmaSignatureParser.cs +++ b/src/Common/src/TypeSystem/Ecma/EcmaSignatureParser.cs @@ -288,5 +288,27 @@ namespace Internal.TypeSystem.Ecma } return arguments; } + + public MarshalAsDescriptor ParseMarshalAsDescriptor() + { + NativeType type = (NativeType)_reader.ReadByte(); + NativeType arraySubType = NativeType.Invalid; + uint paramNum = 0, numElem = 0; + if (type == NativeType.Array) + { + arraySubType = (NativeType)_reader.ReadByte(); + if (_reader.RemainingBytes != 0) + { + paramNum = (uint)_reader.ReadCompressedInteger(); + + if (_reader.RemainingBytes != 0) + numElem = (uint)_reader.ReadCompressedInteger(); + } + } + + Debug.Assert(_reader.RemainingBytes == 0); + + return new MarshalAsDescriptor(type, arraySubType, paramNum, numElem); + } } } diff --git a/src/Common/src/TypeSystem/Interop/IL/Marshaller.cs b/src/Common/src/TypeSystem/Interop/IL/Marshaller.cs index 87e0e4259..abfec6ab7 100644 --- a/src/Common/src/TypeSystem/Interop/IL/Marshaller.cs +++ b/src/Common/src/TypeSystem/Interop/IL/Marshaller.cs @@ -55,8 +55,7 @@ namespace Internal.TypeSystem.Interop /// The created Marshaller public static Marshaller CreateMarshaller(TypeDesc parameterType, PInvokeMethodData pInvokeMethodData, ParameterMetadata pInvokeParameterdata) { - - MarshallerKind marshallerKind = GetMarshallerKind(parameterType, pInvokeParameterdata.MarshalAs, pInvokeMethodData, pInvokeParameterdata.Return); + MarshallerKind marshallerKind = GetMarshallerKind(parameterType, pInvokeParameterdata.MarshalAsDescriptor, pInvokeMethodData, pInvokeParameterdata.Return); // Create the marshaller based on MarshallerKind Marshaller marshaller = Marshaller.CreateMarshallerInternal(marshallerKind); @@ -93,7 +92,7 @@ namespace Internal.TypeSystem.Interop throw new NotSupportedException(); } } - private static MarshallerKind GetMarshallerKind(TypeDesc type, TypeDesc marshalAs, PInvokeMethodData PInvokeMethodData, bool isReturn) + private static MarshallerKind GetMarshallerKind(TypeDesc type, MarshalAsDescriptor marshalAs, PInvokeMethodData PInvokeMethodData, bool isReturn) { if (isReturn) { diff --git a/src/Common/src/TypeSystem/Interop/MarshalAsDescriptor.cs b/src/Common/src/TypeSystem/Interop/MarshalAsDescriptor.cs new file mode 100644 index 000000000..1d068dd91 --- /dev/null +++ b/src/Common/src/TypeSystem/Interop/MarshalAsDescriptor.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.CompilerServices; +using System; + +namespace Internal.TypeSystem +{ + [Flags] + public enum NativeType : byte + { + Invalid = 0x0, + Boolean = 0x2, + I1 = 0x3, + U1 = 0x4, + I2 = 0x5, + U2 = 0x6, + I4 = 0x7, + U4 = 0x8, + I8 = 0x9, + U8 = 0xa, + R4 = 0xb, + R8 = 0xc, + LPStr = 0x14, + LPWStr = 0x15, + Int = 0x1f, + UInt = 0x20, + Func = 0x26, + Array = 0x2a, + LPStruct = 0x2b, // This is not defined in Ecma-335(II.23.4) + Max = 0x50, // The value is of this not defined in Ecma-335(II.23.4) either, the one defined in CoreCLR is used here + } + + public class MarshalAsDescriptor + { + public NativeType Type { get; } + public NativeType ArraySubType { get; } + public uint SizeParamIndex { get; } + public uint SizeConst { get; } + + public MarshalAsDescriptor(NativeType type, NativeType arraySubType, uint sizeParamIndex, uint sizeConst) + { + Type = type; + ArraySubType = arraySubType; + SizeParamIndex = sizeParamIndex; + SizeConst = sizeConst; + } + } +} \ No newline at end of file diff --git a/src/Common/src/TypeSystem/Interop/MethodDesc.Interop.cs b/src/Common/src/TypeSystem/Interop/MethodDesc.Interop.cs index 625df6613..350fd8cf1 100644 --- a/src/Common/src/TypeSystem/Interop/MethodDesc.Interop.cs +++ b/src/Common/src/TypeSystem/Interop/MethodDesc.Interop.cs @@ -52,7 +52,7 @@ namespace Internal.TypeSystem public struct ParameterMetadata { private readonly ParameterMetadataAttributes _attributes; - public readonly TypeDesc MarshalAs; + public readonly MarshalAsDescriptor MarshalAsDescriptor; public readonly int Index; public bool In { get { return (_attributes & ParameterMetadataAttributes.In) == ParameterMetadataAttributes.In; } } @@ -63,11 +63,11 @@ namespace Internal.TypeSystem public bool HasFieldMarshal { get { return (_attributes & ParameterMetadataAttributes.HasFieldMarshal) == ParameterMetadataAttributes.HasFieldMarshal; } } - public ParameterMetadata(int index, ParameterMetadataAttributes attributes, TypeDesc marshalAs) + public ParameterMetadata(int index, ParameterMetadataAttributes attributes, MarshalAsDescriptor marshalAsDescriptor) { Index = index; _attributes = attributes; - MarshalAs = marshalAs; + MarshalAsDescriptor = marshalAsDescriptor; } } diff --git a/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj b/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj index 484563182..aac62c84e 100644 --- a/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj +++ b/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj @@ -329,6 +329,9 @@ TypeSystem\Interop\MethodDesc.Interop.cs + + TypeSystem\Interop\MarshalAsDescriptor.cs + Utilities\ArrayBuilder.cs -- cgit v1.2.3 From 27cc71fc804f29150250949898231f1af6d71acb Mon Sep 17 00:00:00 2001 From: fadimounir Date: Tue, 3 Jan 2017 17:48:01 -0800 Subject: Hashtable of all exact (non-canonical) generic method instantiations compiled in the module Refactoring the NativeLayoutInfoNode into NativeLayoutVertexNode with multiple derived classes representing various vertex nodes, with a better dependency tracking in each overwritten GetStaticDependencies APIs. Rename Add APIs on GenericsHashtable and ExactMethodInstantiations Moving native layout stuff to NodeFactory.NativeLayout.cs --- .../ExactMethodInstantiationsNode.cs | 177 +++++++++ .../DependencyAnalysis/GenericsHashtableNode.cs | 7 +- .../Compiler/DependencyAnalysis/MethodCodeNode.cs | 10 + .../DependencyAnalysis/NativeLayoutInfoNode.cs | 235 ++---------- .../DependencyAnalysis/NativeLayoutVertexNode.cs | 406 +++++++++++++++++++++ .../DependencyAnalysis/NodeFactory.NativeLayout.cs | 87 +++++ .../src/Compiler/DependencyAnalysis/NodeFactory.cs | 2 + .../src/Compiler/MetadataGeneration.cs | 46 +-- .../src/ILCompiler.Compiler.csproj | 7 +- 9 files changed, 745 insertions(+), 232 deletions(-) create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExactMethodInstantiationsNode.cs create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExactMethodInstantiationsNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExactMethodInstantiationsNode.cs new file mode 100644 index 000000000..9c6ed6029 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExactMethodInstantiationsNode.cs @@ -0,0 +1,177 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; +using System.Diagnostics; +using System.Collections.Generic; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.NativeFormat; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Hashtable of all exact (non-canonical) generic method instantiations compiled in the module. + /// + internal sealed class ExactMethodInstantiationsNode : ObjectNode, ISymbolNode + { + private ObjectAndOffsetSymbolNode _endSymbol; + private ExternalReferencesTableNode _externalReferences; + + private HashSet _visitedMethods; + private List _exactMethodInstantiationsList; + + public ExactMethodInstantiationsNode(ExternalReferencesTableNode externalReferences) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__exact_method_instantiations_End", true); + _externalReferences = externalReferences; + + _visitedMethods = new HashSet(); + _exactMethodInstantiationsList = new List(); + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__exact_method_instantiations"); + } + + public ISymbolNode EndSymbol => _endSymbol; + public int Offset => 0; + public override bool IsShareable => false; + public override ObjectNodeSection Section => ObjectNodeSection.DataSection; + public override bool StaticDependenciesAreComputed => true; + protected override string GetName() => this.GetMangledName(); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // Dependencies for this node are tracked by the method code nodes + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolNode[] { this }); + + // Zero out the hashset so that we AV if someone tries to insert after we're done. + _visitedMethods = null; + + // Ensure the native layout data has been saved, in order to get valid Vertex offsets for the signature Vertices + factory.MetadataManager.NativeLayoutInfo.SaveNativeLayoutInfoWriter(factory); + + + NativeWriter nativeWriter = new NativeWriter(); + VertexHashtable hashtable = new VertexHashtable(); + Section nativeSection = nativeWriter.NewSection(); + nativeSection.Place(hashtable); + + + foreach (MethodDesc method in _exactMethodInstantiationsList) + { + // Get the method pointer vertex + + bool getUnboxingStub = method.OwningType.IsValueType && !method.Signature.IsStatic; + IMethodNode methodEntryPointNode = factory.MethodEntrypoint(method, getUnboxingStub); + Vertex methodPointer = nativeWriter.GetUnsignedConstant(_externalReferences.GetIndex(methodEntryPointNode)); + + // Get native layout vertices for the declaring type + + ISymbolNode declaringTypeNode = factory.NecessaryTypeSymbol(method.OwningType); + Vertex declaringType = nativeWriter.GetUnsignedConstant(_externalReferences.GetIndex(declaringTypeNode)); + + // Get a vertex sequence for the method instantiation args if any + + VertexSequence arguments = new VertexSequence(); + foreach (var arg in method.Instantiation) + { + ISymbolNode argNode = factory.NecessaryTypeSymbol(arg); + arguments.Append(nativeWriter.GetUnsignedConstant(_externalReferences.GetIndex(argNode))); + } + + // Get the name and sig of the method. + // Note: the method name and signature are stored in the NativeLayoutInfo blob, not in the hashtable we build here. + + NativeLayoutMethodNameAndSignatureVertexNode nameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(method.GetTypicalMethodDefinition()); + NativeLayoutPlacedSignatureVertexNode placedNameAndSig = factory.NativeLayout.PlacedSignatureVertex(nameAndSig); + Debug.Assert(placedNameAndSig.SavedVertex != null); + Vertex placedNameAndSigOffsetSig = nativeWriter.GetOffsetSignature(placedNameAndSig.SavedVertex); + + // Get the vertex for the completed method signature + + Vertex methodSignature = nativeWriter.GetTuple(declaringType, placedNameAndSigOffsetSig, arguments); + + // Make the generic method entry vertex + + Vertex entry = nativeWriter.GetTuple(methodSignature, methodPointer); + + // Add to the hash table, hashed by the containing type's hashcode + uint hashCode = (uint)method.OwningType.GetHashCode(); + hashtable.Append(hashCode, nativeSection.Place(entry)); + } + + MemoryStream stream = new MemoryStream(); + nativeWriter.Save(stream); + + byte[] streamBytes = stream.ToArray(); + + _endSymbol.SetSymbolOffset(streamBytes.Length); + + return new ObjectData(streamBytes, Array.Empty(), 1, new ISymbolNode[] { this, _endSymbol }); + } + + public static DependencyList GetExactMethodInstantiationDependenciesForMethod(NodeFactory factory, MethodDesc method) + { + if (!IsMethodEligibleForTracking(method)) + return null; + + DependencyList dependencies = new DependencyList(); + + // Method entry point dependency + bool getUnboxingStub = method.OwningType.IsValueType && !method.Signature.IsStatic; + IMethodNode methodEntryPointNode = factory.MethodEntrypoint(method, getUnboxingStub); + dependencies.Add(new DependencyListEntry(methodEntryPointNode, "Exact method instantiation entry")); + + // Get native layout dependencies for the declaring type + dependencies.Add(new DependencyListEntry(factory.NecessaryTypeSymbol(method.OwningType), "Exact method instantiation entry")); + + // Get native layout dependencies for the method instantiation args + foreach (var arg in method.Instantiation) + dependencies.Add(new DependencyListEntry(factory.NecessaryTypeSymbol(arg), "Exact method instantiation entry")); + + // Get native layout dependencies for the method signature. + NativeLayoutMethodNameAndSignatureVertexNode nameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(method.GetTypicalMethodDefinition()); + dependencies.Add(new DependencyListEntry(factory.NativeLayout.PlacedSignatureVertex(nameAndSig), "Exact method instantiation entry")); + + return dependencies; + } + + public void AddEntryIfEligible(NodeFactory factory, MethodDesc method) + { + // Check if we already saw this method + if (!_visitedMethods.Add(method)) + return; + + if (!IsMethodEligibleForTracking(method)) + return; + + _exactMethodInstantiationsList.Add(method); + } + + private static bool IsMethodEligibleForTracking(MethodDesc method) + { + // Runtime determined methods should never show up here. + Debug.Assert(!method.IsRuntimeDeterminedExactMethod); + + if (method.IsAbstract) + return false; + + if (!method.HasInstantiation) + return false; + + // This hashtable is only for method instantiations that don't use generic dictionaries, + // so check if the given method is shared before proceeding + if (method.IsSharedByGenericInstantiations || method.GetCanonMethodTarget(CanonicalFormKind.Specific) != method) + return false; + + return true; + } + } +} \ No newline at end of file diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericsHashtableNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericsHashtableNode.cs index fa2e07480..5511c4114 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericsHashtableNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericsHashtableNode.cs @@ -52,10 +52,13 @@ namespace ILCompiler.DependencyAnalysis public override bool StaticDependenciesAreComputed => true; protected override string GetName() => this.GetMangledName(); - public void AddInstantiatedTypeEntry(NodeFactory factory, TypeDesc type) + public void AddEntryIfEligible(NodeFactory factory, TypeDesc type) { - Debug.Assert(type.HasInstantiation && !type.IsGenericDefinition); + // If this is an instantiated non-canonical generic type, add it to the generic instantiations hashtable + if (!type.HasInstantiation || type.IsGenericDefinition || type.IsCanonicalSubtype(CanonicalFormKind.Any)) + return; + // Already added? if (!_genericTypeInstantiations.Add(type)) return; diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodCodeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodCodeNode.cs index 7c71a3c6e..fb45db873 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodCodeNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodCodeNode.cs @@ -99,6 +99,16 @@ namespace ILCompiler.DependencyAnalysis dependencies.Add(new DependencyListEntry(factory.MethodEntrypoint(invokeStub), "Reflection invoke")); } + if (_method.HasInstantiation) + { + var exactMethodInstantiationDependencies = ExactMethodInstantiationsNode.GetExactMethodInstantiationDependenciesForMethod(factory, _method); + if (exactMethodInstantiationDependencies != null) + { + dependencies = dependencies ?? new DependencyList(); + dependencies.AddRange(exactMethodInstantiationDependencies); + } + } + return dependencies; } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutInfoNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutInfoNode.cs index 76030201a..e299b9e1a 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutInfoNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutInfoNode.cs @@ -22,249 +22,74 @@ namespace ILCompiler.DependencyAnalysis private ExternalReferencesTableNode _externalReferences; private NativeWriter _writer; - private MemoryStream _writerStream; + private byte[] _writerSavedBytes; private Section _signaturesSection; private Section _ldTokenInfoSection; + private List _vertexNodesToWrite; + public NativeLayoutInfoNode(ExternalReferencesTableNode externalReferences) { _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__nativelayoutinfo_End", true); _externalReferences = externalReferences; + _writer = new NativeWriter(); _signaturesSection = _writer.NewSection(); _ldTokenInfoSection = _writer.NewSection(); - } - public ISymbolNode EndSymbol - { - get - { - return _endSymbol; - } + _vertexNodesToWrite = new List(); } public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append(nameMangler.CompilationUnitPrefix).Append("__nativelayoutinfo"); } + public ISymbolNode EndSymbol => _endSymbol; public int Offset => 0; public override bool IsShareable => false; - public override ObjectNodeSection Section => ObjectNodeSection.DataSection; - public override bool StaticDependenciesAreComputed => true; - protected override string GetName() => this.GetMangledName(); - public void SaveNativeLayoutInfoWriter() - { - if (_writerStream != null) - { -#if DEBUG - // Sanity check... We should not write new items to the native layout after - // we've already saved it. - - MemoryStream debugStream = new MemoryStream(); - _writer.Save(debugStream); - byte[] debugStreamBytes = debugStream.ToArray(); - byte[] nativeLayoutInfoBytes = _writerStream.ToArray(); - Debug.Assert(debugStreamBytes.Length == nativeLayoutInfoBytes.Length); - for (int i = 0; i < debugStreamBytes.Length; i++) - Debug.Assert(debugStreamBytes[i] == nativeLayoutInfoBytes[i]); -#endif - return; - } - - _writerStream = new MemoryStream(); - _writer.Save(_writerStream); - } - - public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) - { - // This node does not trigger generation of other nodes. - if (relocsOnly) - return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolNode[] { this }); - - SaveNativeLayoutInfoWriter(); - - byte[] nativeLayoutInfoBytes = _writerStream.ToArray(); - - _endSymbol.SetSymbolOffset(nativeLayoutInfoBytes.Length); - - return new ObjectData(nativeLayoutInfoBytes, Array.Empty(), 1, new ISymbolNode[] { this, _endSymbol }); - } - - #region Vertex Building Functions - public Vertex GetNativeLayoutInfoSignatureForLdToken(NodeFactory factory, MethodDesc method) - { - // TODO: option to return the uninstantiated signature info. Current implementation will only encode - // the instantiated signature (i.e. with containing type, return type, args, etc... encoded as external types) - - Vertex signature = null; - - Vertex containingType = GetNativeLayoutInfoSignatureForEEType(factory, method.OwningType); - Vertex nameAndSig = _writer.GetMethodNameAndSigSignature( - method.Name, - GetNativeLayoutInfoSignatureForMethodSignature(factory, method.GetTypicalMethodDefinition())); - Vertex[] args = null; - MethodFlags flags = 0; - - if (method.HasInstantiation) - { - flags |= MethodFlags.HasInstantiation; - args = new Vertex[method.Instantiation.Length]; - for (int i = 0; i < args.Length; i++) - args[i] = GetNativeLayoutInfoSignatureForEEType(factory, method.Instantiation[i]); - } - - signature = _writer.GetMethodSignature((uint)flags, 0, containingType, nameAndSig, args); - - return _ldTokenInfoSection.Place(signature); - } - - public Vertex GetNativeLayoutInfoSignatureForEEType(NodeFactory factory, TypeDesc type) - { - IEETypeNode typeSymbol = factory.NecessaryTypeSymbol(type); - uint typeIndex = _externalReferences.GetIndex(typeSymbol); - return _writer.GetExternalTypeSignature(typeIndex); - } + public Section LdTokenInfoSection => _ldTokenInfoSection; + public Section SignaturesSection => _signaturesSection; + public ExternalReferencesTableNode ExternalReferences => _externalReferences; + public NativeWriter Writer => _writer; - public Vertex GetNativeLayoutInfoSignatureForMethodSignature(NodeFactory factory, MethodDesc method) + public void AddVertexNodeToNativeLayout(NativeLayoutVertexNode vertexNode) { - MethodCallingConvention methodCallingConvention = default(MethodCallingConvention); - - if (method.Signature.GenericParameterCount > 0) - methodCallingConvention |= MethodCallingConvention.Generic; - if (method.Signature.IsStatic) - methodCallingConvention |= MethodCallingConvention.Static; - - int parameterCount = method.Signature.Length; - Vertex returnType = GetNativeLayoutInfoSignatureForTypeSignature(factory, method.Signature.ReturnType); - Vertex[] parameters = new Vertex[parameterCount]; - for (int i = 0; i < parameterCount; i++) - { - parameters[i] = GetNativeLayoutInfoSignatureForTypeSignature(factory, method.Signature[i]); - } - - return _signaturesSection.Place(_writer.GetMethodSigSignature((uint)methodCallingConvention, (uint)method.Signature.GenericParameterCount, returnType, parameters)); + _vertexNodesToWrite.Add(vertexNode); } - public Vertex GetNativeLayoutInfoSignatureForPlacedNameAndSignature(NodeFactory factory, MethodDesc method) + public void SaveNativeLayoutInfoWriter(NodeFactory factory) { - // Always use the NativeLayoutInfo node for names and sigs. This saves space, - // since we can Unify more signatures, allows optimizations in comparing sigs in the same module, and prevents the dynamic - // type loader having to know about other native layout sections (since sigs contain types). If we are using a non-native - // layout info writer, write the sig to the native layout info, and refer to it by offset in its own section. At runtime, - // we will assume all names and sigs are in the native layout and find it. - - Vertex methodSig = GetNativeLayoutInfoSignatureForMethodSignature(factory, method); - Vertex nameAndSig = _writer.GetMethodNameAndSigSignature(method.Name, methodSig); - return _signaturesSection.Place(nameAndSig); - } + if (_writerSavedBytes != null) + return; - public Vertex GetNativeLayoutInfoSignatureForPlacedTypeSignature(NodeFactory factory, TypeDesc type) - { - // Similar to method name and signatures, we always use the NativeLayoutInfo blob for type signatures too (same reasons). + foreach (var vertexNode in _vertexNodesToWrite) + vertexNode.WriteVertex(factory); - Vertex typeSignature = GetNativeLayoutInfoSignatureForTypeSignature(factory, type); - return _signaturesSection.Place(typeSignature); - } + MemoryStream writerStream = new MemoryStream(); + _writer.Save(writerStream); + _writerSavedBytes = writerStream.ToArray(); - public Vertex GetNativeLayoutInfoOffsetSignature(Vertex nativeLayoutInfoVertex) - { - // Creates a vertex that holds the offset to a signature in the native layout - return _writer.GetOffsetSignature(nativeLayoutInfoVertex); + // Zero out the native writer and vertex list so that we AV if someone tries to insert after we're done. + _writer = null; + _vertexNodesToWrite = null; } - public Vertex GetNativeLayoutInfoSignatureForTypeSignature(NodeFactory factory, TypeDesc type) + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { - Vertex signature = null; - - switch (type.Category) - { - case Internal.TypeSystem.TypeFlags.SzArray: - signature = _writer.GetModifierTypeSignature(TypeModifierKind.Array, GetNativeLayoutInfoSignatureForTypeSignature(factory, ((ArrayType)type).ElementType)); - break; - - case Internal.TypeSystem.TypeFlags.Pointer: - signature = _writer.GetModifierTypeSignature(TypeModifierKind.Pointer, GetNativeLayoutInfoSignatureForTypeSignature(factory, ((PointerType)type).ParameterType)); - break; - - case Internal.TypeSystem.TypeFlags.ByRef: - signature = _writer.GetModifierTypeSignature(TypeModifierKind.ByRef, GetNativeLayoutInfoSignatureForTypeSignature(factory, ((ByRefType)type).ParameterType)); - break; - - case Internal.TypeSystem.TypeFlags.SignatureTypeVariable: - signature = _writer.GetVariableTypeSignature((uint)((SignatureVariable)type).Index, false); - break; - - case Internal.TypeSystem.TypeFlags.SignatureMethodVariable: - signature = _writer.GetVariableTypeSignature((uint)((SignatureMethodVariable)type).Index, true); - break; - - case Internal.TypeSystem.TypeFlags.Void: - case Internal.TypeSystem.TypeFlags.Boolean: - case Internal.TypeSystem.TypeFlags.Char: - case Internal.TypeSystem.TypeFlags.SByte: - case Internal.TypeSystem.TypeFlags.Byte: - case Internal.TypeSystem.TypeFlags.Int16: - case Internal.TypeSystem.TypeFlags.UInt16: - case Internal.TypeSystem.TypeFlags.Int32: - case Internal.TypeSystem.TypeFlags.UInt32: - case Internal.TypeSystem.TypeFlags.Int64: - case Internal.TypeSystem.TypeFlags.UInt64: - case Internal.TypeSystem.TypeFlags.Single: - case Internal.TypeSystem.TypeFlags.Double: - case Internal.TypeSystem.TypeFlags.IntPtr: - case Internal.TypeSystem.TypeFlags.UIntPtr: - case Internal.TypeSystem.TypeFlags.Enum: - signature = GetNativeLayoutInfoSignatureForEEType(factory, type); - break; - - case Internal.TypeSystem.TypeFlags.Class: - case Internal.TypeSystem.TypeFlags.ValueType: - case Internal.TypeSystem.TypeFlags.Interface: - if (type.HasInstantiation && !type.IsGenericDefinition) - { - TypeDesc typeDef = type.GetTypeDefinition(); - - Vertex typeDefVertex = GetNativeLayoutInfoSignatureForTypeSignature(factory, typeDef); - Vertex[] args = new Vertex[type.Instantiation.Length]; - for (int i = 0; i < args.Length; i++) - args[i] = GetNativeLayoutInfoSignatureForTypeSignature(factory, type.Instantiation[i]); - - signature = _writer.GetInstantiationTypeSignature(typeDefVertex, args); - } - else - { - signature = GetNativeLayoutInfoSignatureForEEType(factory, type); - } - break; - - case Internal.TypeSystem.TypeFlags.Array: - { - ArrayType arrayType = type as ArrayType; - - Vertex elementType = GetNativeLayoutInfoSignatureForTypeSignature(factory, arrayType.ElementType); - - // Skip bounds and lobounds (TODO) - var bounds = Array.Empty(); - var lobounds = Array.Empty(); - - signature = _writer.GetMDArrayTypeSignature(elementType, (uint)arrayType.Rank, bounds, lobounds); - } - break; + // Dependencies of the NativeLayoutInfo node are tracked by the callers that emit data into the native layout writer + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolNode[] { this }); - // TODO case Internal.TypeSystem.TypeFlags.FunctionPointer: + SaveNativeLayoutInfoWriter(factory); - default: - throw new NotImplementedException("NYI"); - } + _endSymbol.SetSymbolOffset(_writerSavedBytes.Length); - Debug.Assert(signature != null); - return signature; + return new ObjectData(_writerSavedBytes, Array.Empty(), 1, new ISymbolNode[] { this, _endSymbol }); } - #endregion } } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs new file mode 100644 index 000000000..132984561 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs @@ -0,0 +1,406 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.NativeFormat; +using Internal.TypeSystem; +using ILCompiler.DependencyAnalysisFramework; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Wrapper nodes for native layout vertex structures. These wrapper nodes are "abstract" as they do not + /// generate any data. They are used to keep track of the dependency nodes required by a Vertex structure. + /// + /// Any node in the graph that references data in the native layout blob needs to create one of these + /// NativeLayoutVertexNode nodes, and track it as a dependency of itself. + /// Example: MethodCodeNodes that are saved to the table in the ExactMethodInstantiationsNode reference + /// signatures stored in the native layout blob, so a NativeLayoutPlacedSignatureVertexNode node is created + /// and returned as a static dependency of the associated MethodCodeNode (in the GetStaticDependencies API). + /// + /// Each NativeLayoutVertexNode that gets marked in the graph will register itself with the NativeLayoutInfoNode, + /// so that the NativeLayoutInfoNode can write it later to the native layout blob during the call to its GetData API. + /// + internal abstract class NativeLayoutVertexNode : DependencyNodeCore + { + public override bool HasConditionalStaticDependencies => false; + public override bool HasDynamicDependencies => false; + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool StaticDependenciesAreComputed => true; + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) + { + return Array.Empty(); + } + + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) + { + return Array.Empty(); + } + + protected override void OnMarked(NodeFactory context) + { + context.MetadataManager.NativeLayoutInfo.AddVertexNodeToNativeLayout(this); + } + + public abstract Vertex WriteVertex(NodeFactory factory); + + protected NativeWriter GetNativeWriter(NodeFactory factory) + { + // There is only one native layout info blob, so only one writer for now... + return factory.MetadataManager.NativeLayoutInfo.Writer; + } + } + + internal abstract class NativeLayoutSavedVertexNode : NativeLayoutVertexNode + { + public Vertex SavedVertex { get; private set; } + protected Vertex SetSavedVertex(Vertex value) + { + Debug.Assert(SavedVertex == null || Object.ReferenceEquals(SavedVertex, value)); + SavedVertex = value; + return value; + } + } + + internal sealed class NativeLayoutMethodLdTokenVertexNode : NativeLayoutVertexNode + { + private MethodDesc _method; + private NativeLayoutTypeSignatureVertexNode _containingTypeSig; + private NativeLayoutMethodSignatureVertexNode _methodSig; + private NativeLayoutTypeSignatureVertexNode[] _instantiationArgsSig; + + protected override string GetName() => "NativeLayoutMethodLdTokenVertexNode_" + NodeFactory.NameMangler.GetMangledMethodName(_method); + + public NativeLayoutMethodLdTokenVertexNode(NodeFactory factory, MethodDesc method) + { + _method = method; + _containingTypeSig = factory.NativeLayout.TypeSignatureVertex(method.OwningType); + _methodSig = factory.NativeLayout.MethodSignatureVertex(method.GetTypicalMethodDefinition()); + if (method.HasInstantiation) + { + _instantiationArgsSig = new NativeLayoutTypeSignatureVertexNode[method.Instantiation.Length]; + for (int i = 0; i < _instantiationArgsSig.Length; i++) + _instantiationArgsSig[i] = factory.NativeLayout.TypeSignatureVertex(method.Instantiation[i]); + } + } + + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + DependencyList dependencies = new DependencyList(); + + dependencies.Add(new DependencyListEntry(_containingTypeSig, "NativeLayoutLdTokenVertexNode containing type signature")); + dependencies.Add(new DependencyListEntry(_methodSig, "NativeLayoutLdTokenVertexNode method signature")); + foreach (var arg in _instantiationArgsSig) + dependencies.Add(new DependencyListEntry(arg, "NativeLayoutLdTokenVertexNode instantiation argument signature")); + + return dependencies; + } + + public override Vertex WriteVertex(NodeFactory factory) + { + Vertex containingType = _containingTypeSig.WriteVertex(factory); + Vertex methodSig = _methodSig.WriteVertex(factory); + Vertex methodNameAndSig = GetNativeWriter(factory).GetMethodNameAndSigSignature(_method.Name, methodSig); + + Debug.Assert(_instantiationArgsSig == null || (_method.HasInstantiation && _method.Instantiation.Length == _instantiationArgsSig.Length)); + + Vertex[] args = null; + MethodFlags flags = 0; + if (_method.HasInstantiation) + { + flags |= MethodFlags.HasInstantiation; + args = new Vertex[_method.Instantiation.Length]; + for (int i = 0; i < args.Length; i++) + args[i] = _instantiationArgsSig[i].WriteVertex(factory); + } + + Vertex signature = GetNativeWriter(factory).GetMethodSignature((uint)flags, 0, containingType, methodNameAndSig, args); + return factory.MetadataManager.NativeLayoutInfo.LdTokenInfoSection.Place(signature); + } + } + + internal sealed class NativeLayoutMethodSignatureVertexNode : NativeLayoutVertexNode + { + private MethodDesc _method; + private NativeLayoutTypeSignatureVertexNode _returnTypeSig; + private NativeLayoutTypeSignatureVertexNode[] _parametersSig; + + protected override string GetName() => "NativeLayoutMethodSignatureVertexNode" + NodeFactory.NameMangler.GetMangledMethodName(_method); + + public NativeLayoutMethodSignatureVertexNode(NodeFactory factory, MethodDesc method) + { + _method = method; + _returnTypeSig = factory.NativeLayout.TypeSignatureVertex(method.Signature.ReturnType); + _parametersSig = new NativeLayoutTypeSignatureVertexNode[method.Signature.Length]; + for (int i = 0; i < _parametersSig.Length; i++) + _parametersSig[i] = factory.NativeLayout.TypeSignatureVertex(method.Signature[i]); + } + + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + DependencyList dependencies = new DependencyList(); + + dependencies.Add(new DependencyListEntry(_returnTypeSig, "NativeLayoutMethodSignatureVertexNode return type signature")); + foreach (var arg in _parametersSig) + dependencies.Add(new DependencyListEntry(arg, "NativeLayoutMethodSignatureVertexNode parameter signature")); + + return dependencies; + } + + public override Vertex WriteVertex(NodeFactory factory) + { + MethodCallingConvention methodCallingConvention = default(MethodCallingConvention); + + if (_method.Signature.GenericParameterCount > 0) + methodCallingConvention |= MethodCallingConvention.Generic; + if (_method.Signature.IsStatic) + methodCallingConvention |= MethodCallingConvention.Static; + + Debug.Assert(_method.Signature.Length == _parametersSig.Length); + + Vertex returnType = _returnTypeSig.WriteVertex(factory); + Vertex[] parameters = new Vertex[_parametersSig.Length]; + for (int i = 0; i < _parametersSig.Length; i++) + parameters[i] = _parametersSig[i].WriteVertex(factory); + + Vertex signature = GetNativeWriter(factory).GetMethodSigSignature((uint)methodCallingConvention, (uint)_method.Signature.GenericParameterCount, returnType, parameters); + return factory.MetadataManager.NativeLayoutInfo.SignaturesSection.Place(signature); + } + } + + internal sealed class NativeLayoutMethodNameAndSignatureVertexNode : NativeLayoutVertexNode + { + private MethodDesc _method; + private NativeLayoutMethodSignatureVertexNode _methodSig; + + protected override string GetName() => "NativeLayoutMethodNameAndSignatureVertexNode" + NodeFactory.NameMangler.GetMangledMethodName(_method); + + public NativeLayoutMethodNameAndSignatureVertexNode(NodeFactory factory, MethodDesc method) + { + _method = method; + _methodSig = factory.NativeLayout.MethodSignatureVertex(method); + } + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + return new DependencyListEntry[] { new DependencyListEntry(_methodSig, "NativeLayoutMethodNameAndSignatureVertexNode signature vertex") }; + } + public override Vertex WriteVertex(NodeFactory factory) + { + Vertex methodSig = _methodSig.WriteVertex(factory); + return GetNativeWriter(factory).GetMethodNameAndSigSignature(_method.Name, methodSig); + } + } + + internal abstract class NativeLayoutTypeSignatureVertexNode : NativeLayoutVertexNode + { + protected readonly TypeDesc _type; + + protected NativeLayoutTypeSignatureVertexNode(TypeDesc type) + { + _type = type; + } + + protected override string GetName() => "NativeLayoutTypeSignatureVertexNode" + NodeFactory.NameMangler.GetMangledTypeName(_type); + + public static NativeLayoutTypeSignatureVertexNode NewTypeSignatureVertexNode(NodeFactory factory, TypeDesc type) + { + switch (type.Category) + { + case Internal.TypeSystem.TypeFlags.Array: + case Internal.TypeSystem.TypeFlags.SzArray: + case Internal.TypeSystem.TypeFlags.Pointer: + case Internal.TypeSystem.TypeFlags.ByRef: + return new NativeLayoutParameterizedTypeSignatureVertexNode(factory, type); + + case Internal.TypeSystem.TypeFlags.SignatureTypeVariable: + case Internal.TypeSystem.TypeFlags.SignatureMethodVariable: + return new NativeLayoutGenericVarSignatureVertexNode(factory, type); + + case Internal.TypeSystem.TypeFlags.Void: + case Internal.TypeSystem.TypeFlags.Boolean: + case Internal.TypeSystem.TypeFlags.Char: + case Internal.TypeSystem.TypeFlags.SByte: + case Internal.TypeSystem.TypeFlags.Byte: + case Internal.TypeSystem.TypeFlags.Int16: + case Internal.TypeSystem.TypeFlags.UInt16: + case Internal.TypeSystem.TypeFlags.Int32: + case Internal.TypeSystem.TypeFlags.UInt32: + case Internal.TypeSystem.TypeFlags.Int64: + case Internal.TypeSystem.TypeFlags.UInt64: + case Internal.TypeSystem.TypeFlags.Single: + case Internal.TypeSystem.TypeFlags.Double: + case Internal.TypeSystem.TypeFlags.IntPtr: + case Internal.TypeSystem.TypeFlags.UIntPtr: + case Internal.TypeSystem.TypeFlags.Enum: + case Internal.TypeSystem.TypeFlags.Class: + case Internal.TypeSystem.TypeFlags.ValueType: + case Internal.TypeSystem.TypeFlags.Interface: + if (type.HasInstantiation && !type.IsGenericDefinition) + return new NativeLayoutInstantiatedTypeSignatureVertexNode(factory, type); + else + return new NativeLayoutEETypeSignatureVertexNode(factory, type); + + // TODO case Internal.TypeSystem.TypeFlags.FunctionPointer: + + default: + throw new NotImplementedException("NYI"); + } + } + + sealed class NativeLayoutParameterizedTypeSignatureVertexNode : NativeLayoutTypeSignatureVertexNode + { + private NativeLayoutVertexNode _parameterTypeSig; + + public NativeLayoutParameterizedTypeSignatureVertexNode(NodeFactory factory, TypeDesc type) : base(type) + { + _parameterTypeSig = factory.NativeLayout.TypeSignatureVertex(((ParameterizedType)type).ParameterType); + } + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + return new DependencyListEntry[] { new DependencyListEntry(_parameterTypeSig, "NativeLayoutParameterizedTypeSignatureVertexNode parameter type signature") }; + } + public override Vertex WriteVertex(NodeFactory factory) + { + switch (_type.Category) + { + case Internal.TypeSystem.TypeFlags.SzArray: + return GetNativeWriter(factory).GetModifierTypeSignature(TypeModifierKind.Array, _parameterTypeSig.WriteVertex(factory)); + + case Internal.TypeSystem.TypeFlags.Pointer: + return GetNativeWriter(factory).GetModifierTypeSignature(TypeModifierKind.Pointer, _parameterTypeSig.WriteVertex(factory)); + + case Internal.TypeSystem.TypeFlags.ByRef: + return GetNativeWriter(factory).GetModifierTypeSignature(TypeModifierKind.ByRef, _parameterTypeSig.WriteVertex(factory)); + + case Internal.TypeSystem.TypeFlags.Array: + { + Vertex elementType = _parameterTypeSig.WriteVertex(factory); + + // Skip bounds and lobounds (TODO) + var bounds = Array.Empty(); + var lobounds = Array.Empty(); + + return GetNativeWriter(factory).GetMDArrayTypeSignature(elementType, (uint)((ArrayType)_type).Rank, bounds, lobounds); + } + } + + Debug.Assert(false, "UNREACHABLE"); + return null; + } + } + + sealed class NativeLayoutGenericVarSignatureVertexNode : NativeLayoutTypeSignatureVertexNode + { + public NativeLayoutGenericVarSignatureVertexNode(NodeFactory factory, TypeDesc type) : base(type) + { + } + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + return Array.Empty(); + } + public override Vertex WriteVertex(NodeFactory factory) + { + switch (_type.Category) + { + case Internal.TypeSystem.TypeFlags.SignatureTypeVariable: + return GetNativeWriter(factory).GetVariableTypeSignature((uint)((SignatureVariable)_type).Index, false); + + case Internal.TypeSystem.TypeFlags.SignatureMethodVariable: + return GetNativeWriter(factory).GetVariableTypeSignature((uint)((SignatureMethodVariable)_type).Index, true); + } + + Debug.Assert(false, "UNREACHABLE"); + return null; + } + } + + sealed class NativeLayoutInstantiatedTypeSignatureVertexNode : NativeLayoutTypeSignatureVertexNode + { + private NativeLayoutTypeSignatureVertexNode _genericTypeDefSig; + private NativeLayoutTypeSignatureVertexNode[] _instantiationArgs; + + public NativeLayoutInstantiatedTypeSignatureVertexNode(NodeFactory factory, TypeDesc type) : base(type) + { + Debug.Assert(type.HasInstantiation && !type.IsGenericDefinition); + + _genericTypeDefSig = factory.NativeLayout.TypeSignatureVertex(type.GetTypeDefinition()); + _instantiationArgs = new NativeLayoutTypeSignatureVertexNode[type.Instantiation.Length]; + for (int i = 0; i < _instantiationArgs.Length; i++) + _instantiationArgs[i] = factory.NativeLayout.TypeSignatureVertex(type.Instantiation[i]); + + } + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + DependencyList dependencies = new DependencyList(); + + dependencies.Add(new DependencyListEntry(_genericTypeDefSig, "NativeLayoutInstantiatedTypeSignatureVertexNode generic definition signature")); + foreach (var arg in _instantiationArgs) + dependencies.Add(new DependencyListEntry(arg, "NativeLayoutInstantiatedTypeSignatureVertexNode instantiation argument signature")); + + return dependencies; + } + public override Vertex WriteVertex(NodeFactory factory) + { + Vertex genericDefVertex = _genericTypeDefSig.WriteVertex(factory); + Vertex[] args = new Vertex[_instantiationArgs.Length]; + for (int i = 0; i < args.Length; i++) + args[i] = _instantiationArgs[i].WriteVertex(factory); + + return GetNativeWriter(factory).GetInstantiationTypeSignature(genericDefVertex, args); + } + } + + sealed class NativeLayoutEETypeSignatureVertexNode : NativeLayoutTypeSignatureVertexNode + { + public NativeLayoutEETypeSignatureVertexNode(NodeFactory factory, TypeDesc type) : base(type) + { + Debug.Assert(!type.HasInstantiation || type.IsGenericDefinition); + } + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + return new DependencyListEntry[] + { + new DependencyListEntry(context.NecessaryTypeSymbol(_type), "NativeLayoutEETypeVertexNode containing type signature") + }; + } + public override Vertex WriteVertex(NodeFactory factory) + { + IEETypeNode eetypeNode = factory.NecessaryTypeSymbol(_type); + uint typeIndex = factory.MetadataManager.NativeLayoutInfo.ExternalReferences.GetIndex(eetypeNode); + return GetNativeWriter(factory).GetExternalTypeSignature(typeIndex); + } + } + } + + internal sealed class NativeLayoutPlacedSignatureVertexNode : NativeLayoutSavedVertexNode + { + private NativeLayoutVertexNode _signatureToBePlaced; + + protected override string GetName() => "NativeLayoutTypeSignatureVertexNode"; + + public NativeLayoutPlacedSignatureVertexNode(NativeLayoutVertexNode signatureToBePlaced) + { + _signatureToBePlaced = signatureToBePlaced; + } + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + return new DependencyListEntry[] { new DependencyListEntry(_signatureToBePlaced, "NativeLayoutPlacedSignatureVertexNode placed signature") }; + } + public override Vertex WriteVertex(NodeFactory factory) + { + // Always use the NativeLayoutInfo blob for names and sigs, even if the associated types/methods are written elsewhere. + // This saves space, since we can Unify more signatures, allows optimizations in comparing sigs in the same module, and + // prevents the dynamic type loader having to know about other native layout sections (since sigs contain types). If we are + // using a non-native layout info writer, write the sig to the native layout info, and refer to it by offset in its own + // section. At runtime, we will assume all names and sigs are in the native layout and find it. + + Vertex signature = _signatureToBePlaced.WriteVertex(factory); + return SetSavedVertex(factory.MetadataManager.NativeLayoutInfo.SignaturesSection.Place(signature)); + } + } +} \ No newline at end of file diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs new file mode 100644 index 000000000..8b9cff2d2 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs @@ -0,0 +1,87 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// Part of Node factory that deals with nodes describing native layout information + partial class NodeFactory + { + /// + /// Helper class that provides a level of grouping for all the native layout lookups + /// + public class NativeLayoutHelper + { + NodeFactory _factory; + + public NativeLayoutHelper(NodeFactory factory) + { + _factory = factory; + CreateNodeCaches(); + } + + private void CreateNodeCaches() + { + _typeSignatures = new NodeCache(type => + { + return NativeLayoutTypeSignatureVertexNode.NewTypeSignatureVertexNode(_factory, type); + }); + + _methodSignatures = new NodeCache(method => + { + return new NativeLayoutMethodSignatureVertexNode(_factory, method); + }); + + _methodNameAndSignatures = new NodeCache(method => + { + return new NativeLayoutMethodNameAndSignatureVertexNode(_factory, method); + }); + + _placedSignatures = new NodeCache(vertexNode => + { + return new NativeLayoutPlacedSignatureVertexNode(vertexNode); + }); + + _methodLdTokenSignatures = new NodeCache(method => + { + return new NativeLayoutMethodLdTokenVertexNode(_factory, method); + }); + } + + private NodeCache _typeSignatures; + internal NativeLayoutTypeSignatureVertexNode TypeSignatureVertex(TypeDesc type) + { + return _typeSignatures.GetOrAdd(type); + } + + private NodeCache _methodSignatures; + internal NativeLayoutMethodSignatureVertexNode MethodSignatureVertex(MethodDesc method) + { + return _methodSignatures.GetOrAdd(method); + } + + private NodeCache _methodNameAndSignatures; + internal NativeLayoutMethodNameAndSignatureVertexNode MethodNameAndSignatureVertex(MethodDesc method) + { + return _methodNameAndSignatures.GetOrAdd(method); + } + + private NodeCache _placedSignatures; + internal NativeLayoutPlacedSignatureVertexNode PlacedSignatureVertex(NativeLayoutVertexNode vertexNode) + { + return _placedSignatures.GetOrAdd(vertexNode); + } + + private NodeCache _methodLdTokenSignatures; + internal NativeLayoutMethodLdTokenVertexNode MethodLdTokenVertex(MethodDesc method) + { + return _methodLdTokenSignatures.GetOrAdd(method); + } + + } + + public NativeLayoutHelper NativeLayout; + } +} \ No newline at end of file diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs index e9cb6ca7d..97bc9cc5d 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs @@ -283,6 +283,8 @@ namespace ILCompiler.DependencyAnalysis { return new StringAllocatorMethodNode(constructor); }); + + NativeLayout = new NativeLayoutHelper(this); } protected abstract IMethodNode CreateMethodEntrypointNode(MethodDesc method); diff --git a/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs b/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs index bad225bff..4170edbad 100644 --- a/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs +++ b/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs @@ -46,8 +46,13 @@ namespace ILCompiler private Dictionary _dynamicInvokeThunks = new Dictionary(); + private ExternalReferencesTableNode _commonFixupsTableNode; + private ExternalReferencesTableNode _nativeReferencesTableNode; + + private GenericsHashtableNode _genericsHashtable; + private ExactMethodInstantiationsNode _exactMethodInstantiations; + internal NativeLayoutInfoNode NativeLayoutInfo { get; private set; } - internal GenericsHashtableNode GenericsHashtable { get; private set; } public MetadataGeneration(NodeFactory factory) { @@ -71,35 +76,36 @@ namespace ILCompiler var metadataNode = new MetadataNode(); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.EmbeddedMetadata), metadataNode, metadataNode, metadataNode.EndSymbol); - var commonFixupsTableNode = new ExternalReferencesTableNode("CommonFixupsTable"); + _commonFixupsTableNode = new ExternalReferencesTableNode("CommonFixupsTable"); + _nativeReferencesTableNode = new ExternalReferencesTableNode("NativeReferences"); - var typeMapNode = new TypeMetadataMapNode(commonFixupsTableNode); + var typeMapNode = new TypeMetadataMapNode(_commonFixupsTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.TypeMap), typeMapNode, typeMapNode, typeMapNode.EndSymbol); - var cctorContextMapNode = new ClassConstructorContextMap(commonFixupsTableNode); + var cctorContextMapNode = new ClassConstructorContextMap(_commonFixupsTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.CCtorContextMap), cctorContextMapNode, cctorContextMapNode, cctorContextMapNode.EndSymbol); - var invokeMapNode = new ReflectionInvokeMapNode(commonFixupsTableNode); + var invokeMapNode = new ReflectionInvokeMapNode(_commonFixupsTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.InvokeMap), invokeMapNode, invokeMapNode, invokeMapNode.EndSymbol); - var arrayMapNode = new ArrayMapNode(commonFixupsTableNode); + var arrayMapNode = new ArrayMapNode(_commonFixupsTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.ArrayMap), arrayMapNode, arrayMapNode, arrayMapNode.EndSymbol); - var fieldMapNode = new ReflectionFieldMapNode(commonFixupsTableNode); + var fieldMapNode = new ReflectionFieldMapNode(_commonFixupsTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.FieldAccessMap), fieldMapNode, fieldMapNode, fieldMapNode.EndSymbol); - var externalNativeReferencesTableNode = new ExternalReferencesTableNode("NativeReferences"); - header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.NativeReferences), externalNativeReferencesTableNode, externalNativeReferencesTableNode, externalNativeReferencesTableNode.EndSymbol); - - NativeLayoutInfo = new NativeLayoutInfoNode(externalNativeReferencesTableNode); + NativeLayoutInfo = new NativeLayoutInfoNode(_nativeReferencesTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.NativeLayoutInfo), NativeLayoutInfo, NativeLayoutInfo, NativeLayoutInfo.EndSymbol); - GenericsHashtable = new GenericsHashtableNode(externalNativeReferencesTableNode); - header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.GenericsHashtable), GenericsHashtable, GenericsHashtable, GenericsHashtable.EndSymbol); + _exactMethodInstantiations = new ExactMethodInstantiationsNode(_nativeReferencesTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.ExactMethodInstantiationsHashtable), _exactMethodInstantiations, _exactMethodInstantiations, _exactMethodInstantiations.EndSymbol); + + _genericsHashtable = new GenericsHashtableNode(_nativeReferencesTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.GenericsHashtable), _genericsHashtable, _genericsHashtable, _genericsHashtable.EndSymbol); // This one should go last - header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.CommonFixupsTable), - commonFixupsTableNode, commonFixupsTableNode, commonFixupsTableNode.EndSymbol); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.CommonFixupsTable), _commonFixupsTableNode, _commonFixupsTableNode, _commonFixupsTableNode.EndSymbol); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.NativeReferences), _nativeReferencesTableNode, _nativeReferencesTableNode, _nativeReferencesTableNode.EndSymbol); } private void Graph_NewMarkedNode(DependencyNodeCore obj) @@ -109,14 +115,7 @@ namespace ILCompiler { _typesWithEETypesGenerated.Add(eetypeNode.Type); AddGeneratedType(eetypeNode.Type); - - // If this is an instantiated non-canonical generic type, add it to the generic instantiations hashtable - if (eetypeNode.Type.HasInstantiation && !eetypeNode.Type.IsGenericDefinition) - { - if (!eetypeNode.Type.IsCanonicalSubtype(CanonicalFormKind.Any)) - GenericsHashtable.AddInstantiatedTypeEntry(_nodeFactory, eetypeNode.Type); - } - + _genericsHashtable.AddEntryIfEligible(_nodeFactory, eetypeNode.Type); return; } @@ -134,6 +133,7 @@ namespace ILCompiler } AddGeneratedType(method.OwningType); + _exactMethodInstantiations.AddEntryIfEligible(_nodeFactory, method); _methodDefinitionsGenerated.Add(method.GetTypicalMethodDefinition()); _methodsGenerated.Add(method); return; diff --git a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj index f5f2f3f3d..8e161c011 100644 --- a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj +++ b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj @@ -103,7 +103,9 @@ + + @@ -125,7 +127,6 @@ - @@ -158,6 +159,8 @@ + + @@ -266,7 +269,7 @@ TypeSystem\Interop\IL\PInvokeMethodData.cs - + IL\Stubs\UnsafeIntrinsics.cs -- cgit v1.2.3 From 3ace23f08c3d1db64f3e356aae9c6cd4907bb6e4 Mon Sep 17 00:00:00 2001 From: Morgan Brown Date: Wed, 11 Jan 2017 17:52:31 -0800 Subject: Embedded manifest resources (#2481) Adds embedded manifest resources to ILCompiler. These will be used to enable ResourceManager. The class library is already capable of reading these. No test included because the test build system can't embed resources, but verified locally. --- .../DependencyAnalysis/ResourceDataNode.cs | 189 +++++++++++++++++++++ .../DependencyAnalysis/ResourceIndexNode.cs | 100 +++++++++++ .../src/Compiler/MetadataGeneration.cs | 15 ++ .../src/ILCompiler.Compiler.csproj | 4 +- 4 files changed, 307 insertions(+), 1 deletion(-) create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceDataNode.cs create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceIndexNode.cs diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceDataNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceDataNode.cs new file mode 100644 index 000000000..2596bcf0b --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceDataNode.cs @@ -0,0 +1,189 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Internal.Text; +using Internal.TypeSystem.Ecma; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Blob of data containing resources for all assemblies generated into the image. + /// Resources are simply copied from the inputs and concatenated into this blob. + /// All format information is provided by + /// + internal class ResourceDataNode : ObjectNode, ISymbolNode + { + /// + /// Resource index information generated while extracting resources into the data blob + /// + private List _indexData; + private int _totalLength; + + public ResourceDataNode() + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__embedded_resourcedata_End", true); + } + + private ObjectAndOffsetSymbolNode _endSymbol; + public ISymbolNode EndSymbol => _endSymbol; + + public override bool IsShareable => false; + + public override ObjectNodeSection Section => ObjectNodeSection.ReadOnlyDataSection; + + public override bool StaticDependenciesAreComputed => true; + + public int Offset => 0; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__embedded_resourcedata"); + } + + protected override string GetName() => this.GetMangledName(); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node has no relocations. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolNode[] { this }); + + byte[] blob = GenerateResourceBlob(factory); + return new ObjectData( + blob, + Array.Empty(), + 1, + new ISymbolNode[] + { + this, + EndSymbol + }); + } + + public IReadOnlyList GetOrCreateIndexData(NodeFactory factory) + { + if (_indexData != null) + { + return _indexData; + } + + _totalLength = 0; + _indexData = new List(); + // Build up index information + foreach (EcmaAssembly module in factory.MetadataManager.GetModulesWithMetadata().OfType()) + { + PEMemoryBlock resourceDirectory = module.PEReader.GetSectionData(module.PEReader.PEHeaders.CorHeader.ResourcesDirectory.RelativeVirtualAddress); + + try + { + checked + { + foreach (var resourceHandle in module.MetadataReader.ManifestResources) + { + ManifestResource resource = module.MetadataReader.GetManifestResource(resourceHandle); + + // Don't try to embed linked resources or resources in other assemblies + if (!resource.Implementation.IsNil) + { + continue; + } + + string resourceName = module.MetadataReader.GetString(resource.Name); + string assemblyName = module.GetName().FullName; + BlobReader reader = resourceDirectory.GetReader((int)resource.Offset, resourceDirectory.Length - (int)resource.Offset); + int length = (int)reader.ReadUInt32(); + ResourceIndexData indexData = new ResourceIndexData(assemblyName, resourceName, _totalLength, (int)resource.Offset + sizeof(Int32), module, length); + _indexData.Add(indexData); + _totalLength += length; + } + } + } + catch (OverflowException) + { + throw new BadImageFormatException(); + } + } + + return _indexData; + } + + /// + /// Extracts resources from all modules being compiled into a single blob and saves + /// the information needed to create an index into that blob. + /// + private byte[] GenerateResourceBlob(NodeFactory factory) + { + GetOrCreateIndexData(factory); + + // Read resources into the blob + byte[] resourceBlob = new byte[_totalLength]; + int currentPos = 0; + foreach (ResourceIndexData indexData in _indexData) + { + EcmaModule module = indexData.EcmaModule; + PEMemoryBlock resourceDirectory = module.PEReader.GetSectionData(module.PEReader.PEHeaders.CorHeader.ResourcesDirectory.RelativeVirtualAddress); + Debug.Assert(currentPos == indexData.NativeOffset); + BlobReader reader = resourceDirectory.GetReader(indexData.EcmaOffset, indexData.Length); + byte[] resourceData = reader.ReadBytes(indexData.Length); + Buffer.BlockCopy(resourceData, 0, resourceBlob, currentPos, resourceData.Length); + currentPos += resourceData.Length; + } + + _endSymbol.SetSymbolOffset(resourceBlob.Length); + return resourceBlob; + } + } + + /// + /// Data about individual manifest resources + /// + internal class ResourceIndexData + { + public ResourceIndexData(string assemblyName, string resourceName, int nativeOffset, int ecmaOffset, EcmaModule ecmaModule, int length) + { + AssemblyName = assemblyName; + ResourceName = resourceName; + NativeOffset = nativeOffset; + EcmaOffset = ecmaOffset; + EcmaModule = ecmaModule; + Length = length; + } + + /// + /// Full name of the assembly that contains the resource + /// + public string AssemblyName { get; } + + /// + /// Name of the resource + /// + public string ResourceName { get; } + + /// + /// Offset of the resource within the native resource blob + /// + public int NativeOffset { get; } + + /// + /// Offset of the resource within the .mresources section of the ECMA module + /// + public int EcmaOffset { get; } + + /// + /// Module the resource is defined in + /// + public EcmaModule EcmaModule { get; } + + /// + /// Length of the resource + /// + public int Length { get; } + } +} \ No newline at end of file diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceIndexNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceIndexNode.cs new file mode 100644 index 000000000..7a9bfa453 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ResourceIndexNode.cs @@ -0,0 +1,100 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Internal.NativeFormat; +using Internal.Text; +using System; +using System.IO; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a hash table of resources within the resource blob in the image. + /// + internal class ResourceIndexNode : ObjectNode, ISymbolNode + { + private ResourceDataNode _resourceDataNode; + + public ResourceIndexNode(ResourceDataNode resourceDataNode) + { + _resourceDataNode = resourceDataNode; + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__embedded_resourceindex_End", true); + } + + private ObjectAndOffsetSymbolNode _endSymbol; + + public ISymbolNode EndSymbol => _endSymbol; + + public override bool IsShareable => false; + + public override ObjectNodeSection Section => ObjectNodeSection.ReadOnlyDataSection; + + public override bool StaticDependenciesAreComputed => true; + + public int Offset => 0; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__embedded_resourceindex"); + } + + protected override string GetName() => this.GetMangledName(); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node has no relocations. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolNode[] { this }); + + byte[] blob = GenerateIndexBlob(factory); + return new ObjectData( + blob, + Array.Empty(), + 1, + new ISymbolNode[] + { + this, + EndSymbol + }); + } + + /// + /// Builds a native hashtable containing data about each manifest resource + /// + /// + private byte[] GenerateIndexBlob(NodeFactory factory) + { + NativeWriter nativeWriter = new NativeWriter(); + Section indexHashtableSection = nativeWriter.NewSection(); + VertexHashtable indexHashtable = new VertexHashtable(); + indexHashtableSection.Place(indexHashtable); + + // Build a table with a tuple of Assembly Full Name, Resource Name, Offset within the resource data blob, Length + // for each resource. + // This generates a hashtable for the convenience of managed code since there's + // a reader for VertexHashtable, but not for VertexSequence. + + foreach (ResourceIndexData indexData in _resourceDataNode.GetOrCreateIndexData(factory)) + { + Vertex asmName = nativeWriter.GetStringConstant(indexData.AssemblyName); + Vertex resourceName = nativeWriter.GetStringConstant(indexData.ResourceName); + Vertex offsetVertex = nativeWriter.GetUnsignedConstant((uint)indexData.NativeOffset); + Vertex lengthVertex = nativeWriter.GetUnsignedConstant((uint)indexData.Length); + + Vertex indexVertex = nativeWriter.GetTuple(asmName, resourceName); + indexVertex = nativeWriter.GetTuple(indexVertex, offsetVertex); + indexVertex = nativeWriter.GetTuple(indexVertex, lengthVertex); + + int hashCode = TypeHashingAlgorithms.ComputeNameHashCode(indexData.AssemblyName); + indexHashtable.Append((uint)hashCode, indexHashtableSection.Place(indexVertex)); + } + + MemoryStream stream = new MemoryStream(); + nativeWriter.Save(stream); + byte[] blob = stream.ToArray(); + _endSymbol.SetSymbolOffset(blob.Length); + return blob; + } + } +} \ No newline at end of file diff --git a/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs b/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs index 4170edbad..31a832f0e 100644 --- a/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs +++ b/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs @@ -79,7 +79,14 @@ namespace ILCompiler _commonFixupsTableNode = new ExternalReferencesTableNode("CommonFixupsTable"); _nativeReferencesTableNode = new ExternalReferencesTableNode("NativeReferences"); + var resourceDataNode = new ResourceDataNode(); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.BlobIdResourceData), resourceDataNode, resourceDataNode, resourceDataNode.EndSymbol); + + var resourceIndexNode = new ResourceIndexNode(resourceDataNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.BlobIdResourceIndex), resourceIndexNode, resourceIndexNode, resourceIndexNode.EndSymbol); + var typeMapNode = new TypeMetadataMapNode(_commonFixupsTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.TypeMap), typeMapNode, typeMapNode, typeMapNode.EndSymbol); var cctorContextMapNode = new ClassConstructorContextMap(_commonFixupsTableNode); @@ -343,6 +350,14 @@ namespace ILCompiler } } + /// + /// Returns a set of modules that will get some metadata emitted into the output module + /// + public HashSet GetModulesWithMetadata() + { + return _modulesSeen; + } + public byte[] GetMetadataBlob() { EnsureMetadataGenerated(); diff --git a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj index 8e161c011..c0c1e599a 100644 --- a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj +++ b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj @@ -128,7 +128,9 @@ - + + + -- cgit v1.2.3 From 21ca9315e85c959cd9cbef53a2963ea1b7aeafa9 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 12 Jan 2017 11:37:30 -0500 Subject: Remove array allocation from CTS.CreateLinkedTokenSource (#2492) --- .../System/Threading/CancellationTokenSource.cs | 100 +++++++++++++-------- 1 file changed, 63 insertions(+), 37 deletions(-) diff --git a/src/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs b/src/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs index 165708fb1..1777dfad9 100644 --- a/src/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs +++ b/src/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs @@ -841,12 +841,19 @@ namespace System.Threading /// The second CancellationToken to observe. /// A CancellationTokenSource that is linked /// to the source tokens. - public static CancellationTokenSource CreateLinkedTokenSource(CancellationToken token1, CancellationToken token2) - { - return token1.CanBeCanceled || token2.CanBeCanceled ? - new LinkedCancellationTokenSource(token1, token2) : - new CancellationTokenSource(); - } + public static CancellationTokenSource CreateLinkedTokenSource(CancellationToken token1, CancellationToken token2) => + !token1.CanBeCanceled ? CreateLinkedTokenSource(token2) : + token2.CanBeCanceled ? new Linked2CancellationTokenSource(token1, token2) : + (CancellationTokenSource)new Linked1CancellationTokenSource(token1); + + /// + /// Creates a that will be in the canceled state + /// when any of the source tokens are in the canceled state. + /// + /// The first CancellationToken to observe. + /// A that is linked to the source tokens. + internal static CancellationTokenSource CreateLinkedTokenSource(CancellationToken token) => + token.CanBeCanceled ? new Linked1CancellationTokenSource(token) : new CancellationTokenSource(); /// /// Creates a CancellationTokenSource that will be in the canceled state @@ -861,14 +868,19 @@ namespace System.Threading if (tokens == null) throw new ArgumentNullException(nameof(tokens)); - if (tokens.Length == 0) - throw new ArgumentException(SR.CancellationToken_CreateLinkedToken_TokensIsEmpty); - - // a defensive copy is not required as the array has value-items that have only a single IntPtr field, - // hence each item cannot be null itself, and reads of the payloads cannot be torn. - Contract.EndContractBlock(); - - return new LinkedCancellationTokenSource(tokens); + switch (tokens.Length) + { + case 0: + throw new ArgumentException(SR.CancellationToken_CreateLinkedToken_TokensIsEmpty); + case 1: + return CreateLinkedTokenSource(tokens[0]); + case 2: + return CreateLinkedTokenSource(tokens[0], tokens[1]); + default: + // a defensive copy is not required as the array has value-items that have only a single reference field, + // hence each item cannot be null itself, and reads of the payloads cannot be torn. + return new LinkedNCancellationTokenSource(tokens); + } } @@ -884,35 +896,50 @@ namespace System.Threading } } - private sealed class LinkedCancellationTokenSource : CancellationTokenSource + private sealed class Linked1CancellationTokenSource : CancellationTokenSource { - private static readonly Action s_linkedTokenCancelDelegate = - s => ((CancellationTokenSource)s).NotifyCancellation(throwOnFirstException: false); // skip ThrowIfDisposed() check in Cancel() - private CancellationTokenRegistration[] m_linkingRegistrations; + private readonly CancellationTokenRegistration _reg1; - internal LinkedCancellationTokenSource(CancellationToken token1, CancellationToken token2) + internal Linked1CancellationTokenSource(CancellationToken token1) { - bool token2CanBeCanceled = token2.CanBeCanceled; + _reg1 = token1.InternalRegisterWithoutEC(LinkedNCancellationTokenSource.s_linkedTokenCancelDelegate, this); + } - if (token1.CanBeCanceled) - { - m_linkingRegistrations = new CancellationTokenRegistration[token2CanBeCanceled ? 2 : 1]; // there will be at least 1 and at most 2 linkings - m_linkingRegistrations[0] = token1.InternalRegisterWithoutEC(s_linkedTokenCancelDelegate, this); - } + protected override void Dispose(bool disposing) + { + if (!disposing || m_disposed) return; + _reg1.Dispose(); + base.Dispose(disposing); + } + } - if (token2CanBeCanceled) - { - int index = 1; - if (m_linkingRegistrations == null) - { - m_linkingRegistrations = new CancellationTokenRegistration[1]; // this will be the only linking - index = 0; - } - m_linkingRegistrations[index] = token2.InternalRegisterWithoutEC(s_linkedTokenCancelDelegate, this); - } + private sealed class Linked2CancellationTokenSource : CancellationTokenSource + { + private readonly CancellationTokenRegistration _reg1; + private readonly CancellationTokenRegistration _reg2; + + internal Linked2CancellationTokenSource(CancellationToken token1, CancellationToken token2) + { + _reg1 = token1.InternalRegisterWithoutEC(LinkedNCancellationTokenSource.s_linkedTokenCancelDelegate, this); + _reg2 = token2.InternalRegisterWithoutEC(LinkedNCancellationTokenSource.s_linkedTokenCancelDelegate, this); } - internal LinkedCancellationTokenSource(params CancellationToken[] tokens) + protected override void Dispose(bool disposing) + { + if (!disposing || m_disposed) return; + _reg1.Dispose(); + _reg2.Dispose(); + base.Dispose(disposing); + } + } + + private sealed class LinkedNCancellationTokenSource : CancellationTokenSource + { + internal static readonly Action s_linkedTokenCancelDelegate = + s => ((CancellationTokenSource)s).NotifyCancellation(throwOnFirstException: false); // skip ThrowIfDisposed() check in Cancel() + private CancellationTokenRegistration[] m_linkingRegistrations; + + internal LinkedNCancellationTokenSource(params CancellationToken[] tokens) { m_linkingRegistrations = new CancellationTokenRegistration[tokens.Length]; @@ -945,7 +972,6 @@ namespace System.Threading base.Dispose(disposing); } - } } -- cgit v1.2.3 From 59133e10e2e13df6943b837f6cc813429e326f6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 12 Jan 2017 10:00:07 -0800 Subject: Add support for thread static base dictionary lookups (#2479) This adds support for generating helpers and dictionary entries to support thread static base lookups for generic types in shared generic context. I slightly tweaked how dictionary slots get emitted because the "every slot is an `ISymbolNode`" paradigm wasn't very flexible. --- .../DependencyAnalysis/GenericDictionaryNode.cs | 14 +++-- .../DependencyAnalysis/GenericLookupResult.cs | 53 ++++++++++++++++--- .../NodeFactory.GenericLookups.cs | 12 +++++ .../ReadyToRunGenericHelperNode.cs | 2 + .../Target_X64/X64ReadyToRunGenericHelperNode.cs | 29 +++++++++++ src/JitInterface/src/CorInfoImpl.cs | 2 +- tests/src/Simple/Generics/Generics.cs | 60 ++++++++++++++++++++++ 7 files changed, 160 insertions(+), 12 deletions(-) diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericDictionaryNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericDictionaryNode.cs index a66288e4a..5b8c98172 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericDictionaryNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericDictionaryNode.cs @@ -69,11 +69,17 @@ namespace ILCompiler.DependencyAnalysis Instantiation typeInst = this.TypeInstantiation; Instantiation methodInst = this.MethodInstantiation; - foreach (var entry in layout.Entries) + foreach (GenericLookupResult lookupResult in layout.Entries) { - ISymbolNode targetNode = entry.GetTarget(factory, typeInst, methodInst); - int targetDelta = entry.TargetDelta; - builder.EmitPointerReloc(targetNode, targetDelta); +#if DEBUG + int offsetBefore = builder.CountBytes; +#endif + + lookupResult.EmitDictionaryEntry(ref builder, factory, typeInst, methodInst); + +#if DEBUG + Debug.Assert(builder.CountBytes - offsetBefore == factory.Target.PointerSize); +#endif } } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericLookupResult.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericLookupResult.cs index 5760d328b..a8a19e172 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericLookupResult.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericLookupResult.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Diagnostics; using Internal.Text; @@ -24,11 +25,10 @@ namespace ILCompiler.DependencyAnalysis public abstract void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb); public abstract override string ToString(); - /// - /// Gets the offset to be applied to the symbol returned by - /// to get the actual target. - /// - public virtual int TargetDelta => 0; + public virtual void EmitDictionaryEntry(ref ObjectDataBuilder builder, NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation) + { + builder.EmitPointerReloc(GetTarget(factory, typeInstantiation, methodInstantiation)); + } } /// @@ -101,14 +101,17 @@ namespace ILCompiler.DependencyAnalysis _method = method; } - public override int TargetDelta => FatFunctionPointerConstants.Offset; - public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation) { MethodDesc instantiatedMethod = _method.InstantiateSignature(typeInstantiation, methodInstantiation); return factory.FatFunctionPointer(instantiatedMethod); } + public override void EmitDictionaryEntry(ref ObjectDataBuilder builder, NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation) + { + builder.EmitPointerReloc(GetTarget(factory, typeInstantiation, methodInstantiation), FatFunctionPointerConstants.Offset); + } + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append("MethodEntry_"); @@ -216,6 +219,42 @@ namespace ILCompiler.DependencyAnalysis public override string ToString() => $"NonGCStaticBase: {_type}"; } + /// + /// Generic lookup result that points to the threadstatic base index of a type. + /// + internal sealed class TypeThreadStaticBaseIndexGenericLookupResult : GenericLookupResult + { + private MetadataType _type; + + public TypeThreadStaticBaseIndexGenericLookupResult(TypeDesc type) + { + Debug.Assert(type.IsRuntimeDeterminedSubtype, "Concrete static base in a generic dictionary?"); + Debug.Assert(type is MetadataType); + _type = (MetadataType)type; + } + + public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation) + { + var instantiatedType = (MetadataType)_type.InstantiateSignature(typeInstantiation, methodInstantiation); + return factory.TypeThreadStaticsSymbol(instantiatedType); + } + + public override void EmitDictionaryEntry(ref ObjectDataBuilder builder, NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation) + { + ThreadStaticsNode targetNode = (ThreadStaticsNode)GetTarget(factory, typeInstantiation, methodInstantiation); + int typeTlsIndex = factory.ThreadStaticsRegion.IndexOfEmbeddedObject(targetNode); + builder.EmitNaturalInt(typeTlsIndex); + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("ThreadStaticBase_"); + sb.Append(nameMangler.GetMangledTypeName(_type)); + } + + public override string ToString() => $"ThreadStaticBase: {_type}"; + } + /// /// Generic lookup result that points to the GC static base of a type. /// diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.GenericLookups.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.GenericLookups.cs index decf899d1..374358743 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.GenericLookups.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.GenericLookups.cs @@ -47,6 +47,11 @@ namespace ILCompiler.DependencyAnalysis return new VirtualResolveGenericLookupResult(method); }); + _typeThreadStaticBaseIndexSymbols = new NodeCache(type => + { + return new TypeThreadStaticBaseIndexGenericLookupResult(type); + }); + _typeGCStaticBaseSymbols = new NodeCache(type => { return new TypeGCStaticBaseGenericLookupResult(type); @@ -65,6 +70,13 @@ namespace ILCompiler.DependencyAnalysis return _typeSymbols.GetOrAdd(type); } + private NodeCache _typeThreadStaticBaseIndexSymbols; + + public GenericLookupResult TypeThreadStaticBaseIndex(TypeDesc type) + { + return _typeThreadStaticBaseIndexSymbols.GetOrAdd(type); + } + private NodeCache _typeGCStaticBaseSymbols; public GenericLookupResult TypeGCStaticBase(TypeDesc type) diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs index e68854235..8b9bde04e 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs @@ -36,6 +36,8 @@ namespace ILCompiler.DependencyAnalysis return factory.GenericLookup.TypeGCStaticBase((TypeDesc)target); case ReadyToRunHelperId.GetNonGCStaticBase: return factory.GenericLookup.TypeNonGCStaticBase((TypeDesc)target); + case ReadyToRunHelperId.GetThreadStaticBase: + return factory.GenericLookup.TypeThreadStaticBaseIndex((TypeDesc)target); case ReadyToRunHelperId.MethodDictionary: return factory.GenericLookup.MethodDictionary((MethodDesc)target); case ReadyToRunHelperId.VirtualCall: diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunGenericHelperNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunGenericHelperNode.cs index 9b5bba78b..95439629a 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunGenericHelperNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunGenericHelperNode.cs @@ -99,6 +99,35 @@ namespace ILCompiler.DependencyAnalysis } break; + case ReadyToRunHelperId.GetThreadStaticBase: + { + MetadataType target = (MetadataType)_target; + + // Look up the TLS slot index + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg1, _lookupSignature, relocsOnly); + + ISymbolNode helperEntrypoint; + if (factory.TypeSystemContext.HasLazyStaticConstructor(target)) + { + // There is a lazy class constructor. We need the non-GC static base because that's where the + // class constructor context lives. + GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target); + EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2, nonGcRegionLookup, relocsOnly); + + helperEntrypoint = factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase); + } + else + { + helperEntrypoint = factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType); + } + + // Load the type manager indirection (this clobbers the generic context in Arg0) + encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeManagerIndirection); + + encoder.EmitJMP(helperEntrypoint); + } + break; + // These are all simple: just get the thing from the dictionary and we're done case ReadyToRunHelperId.TypeHandle: case ReadyToRunHelperId.MethodDictionary: diff --git a/src/JitInterface/src/CorInfoImpl.cs b/src/JitInterface/src/CorInfoImpl.cs index 8e3c1859a..2064ed4de 100644 --- a/src/JitInterface/src/CorInfoImpl.cs +++ b/src/JitInterface/src/CorInfoImpl.cs @@ -1661,7 +1661,7 @@ namespace Internal.JitInterface // Find out what kind of base do we need to look up. if (field.IsThreadStatic) { - throw new NotImplementedException(); + helperId = ReadyToRunHelperId.GetThreadStaticBase; } else if (field.HasGCStaticBase) { diff --git a/tests/src/Simple/Generics/Generics.cs b/tests/src/Simple/Generics/Generics.cs index d181130ac..e4fc32932 100644 --- a/tests/src/Simple/Generics/Generics.cs +++ b/tests/src/Simple/Generics/Generics.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Runtime.CompilerServices; class Program { @@ -16,6 +17,7 @@ class Program TestSlotsInHierarchy.Run(); TestDelegateVirtualMethod.Run(); TestDelegateInterfaceMethod.Run(); + TestThreadStaticFieldAccess.Run(); TestNameManglingCollisionRegression.Run(); TestUnusedGVMsDoNotCrashCompiler.Run(); @@ -323,6 +325,64 @@ class Program } } + class TestThreadStaticFieldAccess + { + class TypeWithThreadStaticField + { + [ThreadStatic] + public static int X; + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int Read() + { + return X; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Write(int x) + { + X = x; + } + } + + class BeforeFieldInitType + { + [ThreadStatic] + public static int X = 1985; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static int ReadFromBeforeFieldInitType() + { + return BeforeFieldInitType.X; + } + + public static void Run() + { + // This will set the field to a value from non-shared code + TypeWithThreadStaticField.X = 42; + + // Now read the value from shared code + if (TypeWithThreadStaticField.Read() != 42) + throw new Exception(); + + // Set the value from shared code + TypeWithThreadStaticField.Write(112); + + // Now read the value from non-shared code + if (TypeWithThreadStaticField.X != 112) + throw new Exception(); + + // Check that the storage locations for string and object instantiations differ + if (TypeWithThreadStaticField.Read() != 42) + throw new Exception(); + + // Make sure we run the cctor + if (ReadFromBeforeFieldInitType() != 1985) + throw new Exception(); + } + } + // // Regression test for issue https://github.com/dotnet/corert/issues/1964 // -- cgit v1.2.3 From f1b398d4ffe2d6ad0f8095b8005bf45540cd64cd Mon Sep 17 00:00:00 2001 From: Daniel Harvey Date: Thu, 12 Jan 2017 11:58:56 -0800 Subject: Adds some Array APIs from coreclr. [tfs-changeset: 1644315] --- .../src/Resources/Strings.resx | 3 + src/System.Private.CoreLib/src/System/Array.cs | 173 ++++++++++++++++++--- 2 files changed, 157 insertions(+), 19 deletions(-) diff --git a/src/System.Private.CoreLib/src/Resources/Strings.resx b/src/System.Private.CoreLib/src/Resources/Strings.resx index 2e1ecbe88..24edf287c 100644 --- a/src/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/System.Private.CoreLib/src/Resources/Strings.resx @@ -687,6 +687,9 @@ The DateStart property must come before the DateEnd property. + + Arrays larger than 2GB are not supported. + Index was out of range. Must be non-negative and less than the size of the collection. diff --git a/src/System.Private.CoreLib/src/System/Array.cs b/src/System.Private.CoreLib/src/System/Array.cs index 5564ab734..2b96eced9 100644 --- a/src/System.Private.CoreLib/src/System/Array.cs +++ b/src/System.Private.CoreLib/src/System/Array.cs @@ -985,18 +985,6 @@ namespace System bool IList.IsReadOnly { get { return false; } } - bool IList.IsFixedSize - { - get { return true; } - } - - // Is this Array synchronized (i.e., thread-safe)? If you want a synchronized - // collection, you can use SyncRoot as an object to synchronize your - // collection with. You could also call GetSynchronized() - // to get a synchronized wrapper around the Array. - bool ICollection.IsSynchronized - { get { return false; } } - Object IList.this[int index] { get @@ -1061,13 +1049,6 @@ namespace System Array.Copy(this, 0, array, index, Length); } - // Returns an object appropriate for synchronizing access to this - // Array. - Object ICollection.SyncRoot - { - get { return this; } - } - // Make a new array which is a deep copy of the original array. // public Object Clone() @@ -1180,6 +1161,160 @@ namespace System return BinarySearch(array, 0, array.Length, value, null); } + public static TOutput[] ConvertAll(TInput[] array, Converter converter) + { + if (array == null) + throw new ArgumentNullException(nameof(array)); + + if (converter == null) + throw new ArgumentNullException(nameof(converter)); + + Contract.Ensures(Contract.Result() != null); + Contract.Ensures(Contract.Result().Length == array.Length); + Contract.EndContractBlock(); + + TOutput[] newArray = new TOutput[array.Length]; + for (int i = 0; i < array.Length; i++) + { + newArray[i] = converter(array[i]); + } + return newArray; + } + + public static void Copy(Array sourceArray, Array destinationArray, long length) + { + if (length > Int32.MaxValue || length < Int32.MinValue) + throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_HugeArrayNotSupported); + + Array.Copy(sourceArray, destinationArray, (int)length); + } + + public static void Copy(Array sourceArray, long sourceIndex, Array destinationArray, long destinationIndex, long length) + { + if (sourceIndex > Int32.MaxValue || sourceIndex < Int32.MinValue) + throw new ArgumentOutOfRangeException(nameof(sourceIndex), SR.ArgumentOutOfRange_HugeArrayNotSupported); + if (destinationIndex > Int32.MaxValue || destinationIndex < Int32.MinValue) + throw new ArgumentOutOfRangeException(nameof(destinationIndex), SR.ArgumentOutOfRange_HugeArrayNotSupported); + if (length > Int32.MaxValue || length < Int32.MinValue) + throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_HugeArrayNotSupported); + + Array.Copy(sourceArray, (int)sourceIndex, destinationArray, (int)destinationIndex, (int)length); + } + + [Pure] + public void CopyTo(Array array, long index) + { + if (index > Int32.MaxValue || index < Int32.MinValue) + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_HugeArrayNotSupported); + Contract.EndContractBlock(); + + this.CopyTo(array, (int)index); + } + + public static void ForEach(T[] array, Action action) + { + if (array == null) + throw new ArgumentNullException(nameof(array)); + + if (action == null) + throw new ArgumentNullException(nameof(action)); + + Contract.EndContractBlock(); + + for (int i = 0; i < array.Length; i++) + { + action(array[i]); + } + } + + public long LongLength + { + get + { + long ret = GetLength(0); + + for (int i = 1; i < Rank; ++i) + { + ret = ret * GetLength(i); + } + + return ret; + } + } + + public long GetLongLength(int dimension) + { + // This method does throw an IndexOutOfRangeException for compat if dimension < 0 or >= Rank + // by calling GetUpperBound + return GetLength(dimension); + } + + public Object GetValue(long index) + { + if (index > Int32.MaxValue || index < Int32.MinValue) + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_HugeArrayNotSupported); + Contract.EndContractBlock(); + + return this.GetValue((int)index); + } + + public Object GetValue(long index1, long index2) + { + if (index1 > Int32.MaxValue || index1 < Int32.MinValue) + throw new ArgumentOutOfRangeException(nameof(index1), SR.ArgumentOutOfRange_HugeArrayNotSupported); + if (index2 > Int32.MaxValue || index2 < Int32.MinValue) + throw new ArgumentOutOfRangeException(nameof(index2), SR.ArgumentOutOfRange_HugeArrayNotSupported); + Contract.EndContractBlock(); + + return this.GetValue((int)index1, (int)index2); + } + + public Object GetValue(long index1, long index2, long index3) + { + if (index1 > Int32.MaxValue || index1 < Int32.MinValue) + throw new ArgumentOutOfRangeException(nameof(index1), SR.ArgumentOutOfRange_HugeArrayNotSupported); + if (index2 > Int32.MaxValue || index2 < Int32.MinValue) + throw new ArgumentOutOfRangeException(nameof(index2), SR.ArgumentOutOfRange_HugeArrayNotSupported); + if (index3 > Int32.MaxValue || index3 < Int32.MinValue) + throw new ArgumentOutOfRangeException(nameof(index3), SR.ArgumentOutOfRange_HugeArrayNotSupported); + Contract.EndContractBlock(); + + return this.GetValue((int)index1, (int)index2, (int)index3); + } + + public Object GetValue(params long[] indices) + { + if (indices == null) + throw new ArgumentNullException(nameof(indices)); + if (Rank != indices.Length) + throw new ArgumentException(SR.Arg_RankIndices); + Contract.EndContractBlock(); + + int[] intIndices = new int[indices.Length]; + + for (int i = 0; i < indices.Length; ++i) + { + long index = indices[i]; + if (index > Int32.MaxValue || index < Int32.MinValue) + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_HugeArrayNotSupported); + intIndices[i] = (int)index; + } + + return this.GetValue(intIndices); + } + + public bool IsFixedSize { get { return true; } } + + // Is this Array synchronized (i.e., thread-safe)? If you want a synchronized + // collection, you can use SyncRoot as an object to synchronize your + // collection with. You could also call GetSynchronized() + // to get a synchronized wrapper around the Array. + public bool IsSynchronized { get { return false; } } + + // Returns an object appropriate for synchronizing access to this + // Array. + public Object SyncRoot { get { return this; } } + // Searches a section of an array for a given element using a binary search // algorithm. Elements of the array are compared to the search value using // the IComparable interface, which must be implemented by all -- cgit v1.2.3 From 8046975c66b48e2ab11b09da2e38e89726d7b989 Mon Sep 17 00:00:00 2001 From: Sedar Gokbulut Date: Thu, 12 Jan 2017 12:45:08 -0800 Subject: Fixing which asset we pick up for System.Runtime --- src/packaging/packages.targets | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/packaging/packages.targets b/src/packaging/packages.targets index 948263e36..b3926932a 100644 --- a/src/packaging/packages.targets +++ b/src/packaging/packages.targets @@ -112,7 +112,6 @@ - @@ -127,6 +126,7 @@ ]]> + @@ -144,6 +144,7 @@ + -- cgit v1.2.3 From f03875f0393599db136344ca11a2ad9745a62111 Mon Sep 17 00:00:00 2001 From: Simon Nattress Date: Thu, 12 Jan 2017 11:34:32 -0800 Subject: Run all tests during post-commit job Change the post-commit CI job to run the set of known good CoreCLR tests (currently ~5000 tests) --- netci.groovy | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/netci.groovy b/netci.groovy index 83d30da9a..917c6ba88 100644 --- a/netci.groovy +++ b/netci.groovy @@ -30,9 +30,9 @@ def osList = ['Ubuntu', 'OSX', 'Windows_NT'] // Calculate the build commands if (os == 'Windows_NT') { buildString = "build.cmd ${lowercaseConfiguration}" + testScriptString = "tests\\runtest.cmd /coreclr " } else { - // On other OS's we skipmscorlib but run the pal tests buildString = "./build.sh ${lowercaseConfiguration}" } @@ -46,8 +46,15 @@ def osList = ['Ubuntu', 'OSX', 'Windows_NT'] batchFile(buildString) if (configuration == 'Debug') { - prJobDescription += " + CoreCLR tests" - batchFile("tests\\runtest.cmd /coreclr Top200") + if (isPR) { + prJobDescription += " and CoreCLR tests" + // Run a small set of BVTs during PR validation + batchFile(testScriptString + "Top200") + } + else { + // Run the full set of known passing tests in the post-commit job + batchFile(testScriptString + "KnownGood") + } } } else { -- cgit v1.2.3 From aff69f3d1bf73b0a1857e7eecf1522262b215993 Mon Sep 17 00:00:00 2001 From: fadimounir Date: Wed, 4 Jan 2017 15:59:46 -0800 Subject: Adding support for RuntimeMethodHandle nodes. This is again part of the larger GVM work, which I'm splitting into multiple PRs. This change just enables the RMH support, but won't emit any RMH structures yet. --- .../NativeLayoutSignatureNode.cs | 66 ++++++++++++++++++++++ .../DependencyAnalysis/NativeLayoutVertexNode.cs | 20 ++++++- .../DependencyAnalysis/NodeFactory.NativeLayout.cs | 10 ++++ .../src/Compiler/DependencyAnalysis/NodeFactory.cs | 13 +++++ .../DependencyAnalysis/RuntimeMethodHandleNode.cs | 48 ++++++++++++++++ .../src/ILCompiler.Compiler.csproj | 2 + 6 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutSignatureNode.cs create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RuntimeMethodHandleNode.cs diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutSignatureNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutSignatureNode.cs new file mode 100644 index 000000000..b9fbce4cd --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutSignatureNode.cs @@ -0,0 +1,66 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +using Internal.Text; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a native layout signature. A signature is a pair where the first item is a pointer + /// to the TypeManager that contains the native layout info blob of interest, and the second item + /// is an offset into that native layout info blob + /// + class NativeLayoutSignatureNode : ObjectNode, ISymbolNode + { + private static int s_counter = 0; + + private int _id; + private NativeLayoutSavedVertexNode _nativeSignature; + + public NativeLayoutSignatureNode(NativeLayoutSavedVertexNode nativeSignature) + { + _nativeSignature = nativeSignature; + _id = s_counter++; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__NativeLayoutSignature_" + _id); + } + public int Offset => 0; + protected override string GetName() => this.GetMangledName(); + public override ObjectNodeSection Section => ObjectNodeSection.ReadOnlyDataSection; + public override bool IsShareable => false; + public override bool StaticDependenciesAreComputed => true; + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + DependencyList dependencies = new DependencyList(); + dependencies.Add(new DependencyListEntry(_nativeSignature, "NativeLayoutSignatureNode target vertex")); + return dependencies; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolNode[] { this }); + + // Ensure native layout is saved to get valid Vertex offsets + factory.MetadataManager.NativeLayoutInfo.SaveNativeLayoutInfoWriter(factory); + + ObjectDataBuilder objData = new ObjectDataBuilder(factory); + + objData.Alignment = objData.TargetPointerSize; + objData.DefinedSymbols.Add(this); + + objData.EmitPointerReloc(factory.TypeManagerIndirection); + objData.EmitNaturalInt(_nativeSignature.SavedVertex.VertexOffset); + + return objData.ToObjectData(); + } + } +} \ No newline at end of file diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs index 132984561..fd5a394d2 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs @@ -56,6 +56,22 @@ namespace ILCompiler.DependencyAnalysis } } + /// + /// Any NativeLayoutVertexNode that needs to expose the native layout Vertex after it has been saved + /// needs to derive from this NativeLayoutSavedVertexNode class. + /// + /// A nativelayout Vertex should typically only be exposed for Vertex offset fetching purposes, after the native + /// writer is saved (Vertex offsets get generated when the native writer gets saved). + /// + /// It is important for whoever derives from this class to produce unified Vertices. Calling the WriteVertex method + /// multiple times should always produce the same exact unified Vertex each time (hence the assert in SetSavedVertex). + /// All nativewriter.Getxyz methods return unified Vertices. + /// + /// When exposing a saved Vertex that is a result of a section placement operation (Section.Place(...)), always make + /// sure a unified Vertex is being placed in the section (Section.Place creates a PlacedVertex structure that wraps the + /// Vertex to be placed, so if the Vertex to be placed is unified, there will only be a single unified PlacedVertex + /// structure created for that placed Vertex). + /// internal abstract class NativeLayoutSavedVertexNode : NativeLayoutVertexNode { public Vertex SavedVertex { get; private set; } @@ -67,7 +83,7 @@ namespace ILCompiler.DependencyAnalysis } } - internal sealed class NativeLayoutMethodLdTokenVertexNode : NativeLayoutVertexNode + internal sealed class NativeLayoutMethodLdTokenVertexNode : NativeLayoutSavedVertexNode { private MethodDesc _method; private NativeLayoutTypeSignatureVertexNode _containingTypeSig; @@ -120,7 +136,7 @@ namespace ILCompiler.DependencyAnalysis } Vertex signature = GetNativeWriter(factory).GetMethodSignature((uint)flags, 0, containingType, methodNameAndSig, args); - return factory.MetadataManager.NativeLayoutInfo.LdTokenInfoSection.Place(signature); + return SetSavedVertex(factory.MetadataManager.NativeLayoutInfo.LdTokenInfoSection.Place(signature)); } } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs index 8b9cff2d2..0cc8c7102 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs @@ -48,6 +48,11 @@ namespace ILCompiler.DependencyAnalysis { return new NativeLayoutMethodLdTokenVertexNode(_factory, method); }); + + _nativeLayoutSignatureNodes = new NodeCache(signature => + { + return new NativeLayoutSignatureNode(signature); + }); } private NodeCache _typeSignatures; @@ -80,6 +85,11 @@ namespace ILCompiler.DependencyAnalysis return _methodLdTokenSignatures.GetOrAdd(method); } + private NodeCache _nativeLayoutSignatureNodes; + internal NativeLayoutSignatureNode NativeLayoutSignature(NativeLayoutSavedVertexNode signature) + { + return _nativeLayoutSignatureNodes.GetOrAdd(signature); + } } public NativeLayoutHelper NativeLayout; diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs index 97bc9cc5d..86491b464 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs @@ -13,6 +13,7 @@ using Internal.Text; using Internal.TypeSystem; using Internal.Runtime; using Internal.IL; +using Internal.NativeFormat; namespace ILCompiler.DependencyAnalysis { @@ -235,6 +236,11 @@ namespace ILCompiler.DependencyAnalysis return new InterfaceDispatchMapNode(type); }); + _runtimeMethodHandles = new NodeCache((MethodDesc method) => + { + return new RuntimeMethodHandleNode(this, method); + }); + _interfaceDispatchMapIndirectionNodes = new NodeCache((TypeDesc type) => { var dispatchMap = InterfaceDispatchMap(type); @@ -370,6 +376,13 @@ namespace ILCompiler.DependencyAnalysis return _interfaceDispatchCells.GetOrAdd(method); } + private NodeCache _runtimeMethodHandles; + + internal RuntimeMethodHandleNode RuntimeMethodHandle(MethodDesc method) + { + return _runtimeMethodHandles.GetOrAdd(method); + } + private class BlobTupleEqualityComparer : IEqualityComparer> { bool IEqualityComparer>.Equals(Tuple x, Tuple y) diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RuntimeMethodHandleNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RuntimeMethodHandleNode.cs new file mode 100644 index 000000000..ef95dfa27 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RuntimeMethodHandleNode.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; + +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + class RuntimeMethodHandleNode : ObjectNode, ISymbolNode + { + MethodDesc _targetMethod; + + public RuntimeMethodHandleNode(NodeFactory factory, MethodDesc targetMethod) + { + Debug.Assert(!targetMethod.IsSharedByGenericInstantiations); + _targetMethod = targetMethod; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix) + .Append("__RuntimeMethodHandle_") + .Append(NodeFactory.NameMangler.GetMangledMethodName(_targetMethod)); + } + public int Offset => 0; + protected override string GetName() => this.GetMangledName(); + public override ObjectNodeSection Section => ObjectNodeSection.ReadOnlyDataSection; + public override bool IsShareable => false; + public override bool StaticDependenciesAreComputed => true; + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataBuilder objData = new ObjectDataBuilder(factory); + + objData.Alignment = objData.TargetPointerSize; + objData.DefinedSymbols.Add(this); + + NativeLayoutMethodLdTokenVertexNode ldtokenSigNode = factory.NativeLayout.MethodLdTokenVertex(_targetMethod); + objData.EmitPointerReloc(factory.NativeLayout.NativeLayoutSignature(ldtokenSigNode)); + + return objData.ToObjectData(); + } + } +} diff --git a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj index 8e161c011..c6e1f98d3 100644 --- a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj +++ b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj @@ -106,6 +106,8 @@ + + -- cgit v1.2.3 From ee738f2da66742730dad656f1d9540f19e63c5ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 12 Jan 2017 13:44:23 -0800 Subject: Remove useless instance fields --- .../src/Compiler/MetadataGeneration.cs | 27 ++++++++++------------ 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs b/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs index 31a832f0e..2fa4c80d0 100644 --- a/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs +++ b/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs @@ -46,9 +46,6 @@ namespace ILCompiler private Dictionary _dynamicInvokeThunks = new Dictionary(); - private ExternalReferencesTableNode _commonFixupsTableNode; - private ExternalReferencesTableNode _nativeReferencesTableNode; - private GenericsHashtableNode _genericsHashtable; private ExactMethodInstantiationsNode _exactMethodInstantiations; @@ -76,8 +73,8 @@ namespace ILCompiler var metadataNode = new MetadataNode(); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.EmbeddedMetadata), metadataNode, metadataNode, metadataNode.EndSymbol); - _commonFixupsTableNode = new ExternalReferencesTableNode("CommonFixupsTable"); - _nativeReferencesTableNode = new ExternalReferencesTableNode("NativeReferences"); + var commonFixupsTableNode = new ExternalReferencesTableNode("CommonFixupsTable"); + var nativeReferencesTableNode = new ExternalReferencesTableNode("NativeReferences"); var resourceDataNode = new ResourceDataNode(); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.BlobIdResourceData), resourceDataNode, resourceDataNode, resourceDataNode.EndSymbol); @@ -85,34 +82,34 @@ namespace ILCompiler var resourceIndexNode = new ResourceIndexNode(resourceDataNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.BlobIdResourceIndex), resourceIndexNode, resourceIndexNode, resourceIndexNode.EndSymbol); - var typeMapNode = new TypeMetadataMapNode(_commonFixupsTableNode); + var typeMapNode = new TypeMetadataMapNode(commonFixupsTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.TypeMap), typeMapNode, typeMapNode, typeMapNode.EndSymbol); - var cctorContextMapNode = new ClassConstructorContextMap(_commonFixupsTableNode); + var cctorContextMapNode = new ClassConstructorContextMap(commonFixupsTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.CCtorContextMap), cctorContextMapNode, cctorContextMapNode, cctorContextMapNode.EndSymbol); - var invokeMapNode = new ReflectionInvokeMapNode(_commonFixupsTableNode); + var invokeMapNode = new ReflectionInvokeMapNode(commonFixupsTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.InvokeMap), invokeMapNode, invokeMapNode, invokeMapNode.EndSymbol); - var arrayMapNode = new ArrayMapNode(_commonFixupsTableNode); + var arrayMapNode = new ArrayMapNode(commonFixupsTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.ArrayMap), arrayMapNode, arrayMapNode, arrayMapNode.EndSymbol); - var fieldMapNode = new ReflectionFieldMapNode(_commonFixupsTableNode); + var fieldMapNode = new ReflectionFieldMapNode(commonFixupsTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.FieldAccessMap), fieldMapNode, fieldMapNode, fieldMapNode.EndSymbol); - NativeLayoutInfo = new NativeLayoutInfoNode(_nativeReferencesTableNode); + NativeLayoutInfo = new NativeLayoutInfoNode(nativeReferencesTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.NativeLayoutInfo), NativeLayoutInfo, NativeLayoutInfo, NativeLayoutInfo.EndSymbol); - _exactMethodInstantiations = new ExactMethodInstantiationsNode(_nativeReferencesTableNode); + _exactMethodInstantiations = new ExactMethodInstantiationsNode(nativeReferencesTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.ExactMethodInstantiationsHashtable), _exactMethodInstantiations, _exactMethodInstantiations, _exactMethodInstantiations.EndSymbol); - _genericsHashtable = new GenericsHashtableNode(_nativeReferencesTableNode); + _genericsHashtable = new GenericsHashtableNode(nativeReferencesTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.GenericsHashtable), _genericsHashtable, _genericsHashtable, _genericsHashtable.EndSymbol); // This one should go last - header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.CommonFixupsTable), _commonFixupsTableNode, _commonFixupsTableNode, _commonFixupsTableNode.EndSymbol); - header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.NativeReferences), _nativeReferencesTableNode, _nativeReferencesTableNode, _nativeReferencesTableNode.EndSymbol); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.CommonFixupsTable), commonFixupsTableNode, commonFixupsTableNode, commonFixupsTableNode.EndSymbol); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.NativeReferences), nativeReferencesTableNode, nativeReferencesTableNode, nativeReferencesTableNode.EndSymbol); } private void Graph_NewMarkedNode(DependencyNodeCore obj) -- cgit v1.2.3 From 85bff685f8be306a0f8abc737d91657e60aa6089 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 12 Jan 2017 13:51:05 -0800 Subject: Delete redundant collections and state --- .../ExactMethodInstantiationsNode.cs | 28 ++++------------------ .../src/Compiler/MetadataGeneration.cs | 11 +++++---- 2 files changed, 11 insertions(+), 28 deletions(-) diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExactMethodInstantiationsNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExactMethodInstantiationsNode.cs index 9c6ed6029..124b1854f 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExactMethodInstantiationsNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ExactMethodInstantiationsNode.cs @@ -5,7 +5,6 @@ using System; using System.IO; using System.Diagnostics; -using System.Collections.Generic; using Internal.Text; using Internal.TypeSystem; @@ -21,16 +20,10 @@ namespace ILCompiler.DependencyAnalysis private ObjectAndOffsetSymbolNode _endSymbol; private ExternalReferencesTableNode _externalReferences; - private HashSet _visitedMethods; - private List _exactMethodInstantiationsList; - public ExactMethodInstantiationsNode(ExternalReferencesTableNode externalReferences) { _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__exact_method_instantiations_End", true); _externalReferences = externalReferences; - - _visitedMethods = new HashSet(); - _exactMethodInstantiationsList = new List(); } public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) @@ -51,21 +44,20 @@ namespace ILCompiler.DependencyAnalysis if (relocsOnly) return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolNode[] { this }); - // Zero out the hashset so that we AV if someone tries to insert after we're done. - _visitedMethods = null; - // Ensure the native layout data has been saved, in order to get valid Vertex offsets for the signature Vertices factory.MetadataManager.NativeLayoutInfo.SaveNativeLayoutInfoWriter(factory); - NativeWriter nativeWriter = new NativeWriter(); VertexHashtable hashtable = new VertexHashtable(); Section nativeSection = nativeWriter.NewSection(); nativeSection.Place(hashtable); - foreach (MethodDesc method in _exactMethodInstantiationsList) + foreach (MethodDesc method in factory.MetadataManager.GetCompiledMethods()) { + if (!IsMethodEligibleForTracking(method)) + continue; + // Get the method pointer vertex bool getUnboxingStub = method.OwningType.IsValueType && !method.Signature.IsStatic; @@ -143,18 +135,6 @@ namespace ILCompiler.DependencyAnalysis return dependencies; } - public void AddEntryIfEligible(NodeFactory factory, MethodDesc method) - { - // Check if we already saw this method - if (!_visitedMethods.Add(method)) - return; - - if (!IsMethodEligibleForTracking(method)) - return; - - _exactMethodInstantiationsList.Add(method); - } - private static bool IsMethodEligibleForTracking(MethodDesc method) { // Runtime determined methods should never show up here. diff --git a/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs b/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs index 2fa4c80d0..3d07881b2 100644 --- a/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs +++ b/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs @@ -47,7 +47,6 @@ namespace ILCompiler private Dictionary _dynamicInvokeThunks = new Dictionary(); private GenericsHashtableNode _genericsHashtable; - private ExactMethodInstantiationsNode _exactMethodInstantiations; internal NativeLayoutInfoNode NativeLayoutInfo { get; private set; } @@ -101,8 +100,8 @@ namespace ILCompiler NativeLayoutInfo = new NativeLayoutInfoNode(nativeReferencesTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.NativeLayoutInfo), NativeLayoutInfo, NativeLayoutInfo, NativeLayoutInfo.EndSymbol); - _exactMethodInstantiations = new ExactMethodInstantiationsNode(nativeReferencesTableNode); - header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.ExactMethodInstantiationsHashtable), _exactMethodInstantiations, _exactMethodInstantiations, _exactMethodInstantiations.EndSymbol); + var exactMethodInstantiations = new ExactMethodInstantiationsNode(nativeReferencesTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.ExactMethodInstantiationsHashtable), exactMethodInstantiations, exactMethodInstantiations, exactMethodInstantiations.EndSymbol); _genericsHashtable = new GenericsHashtableNode(nativeReferencesTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.GenericsHashtable), _genericsHashtable, _genericsHashtable, _genericsHashtable.EndSymbol); @@ -137,7 +136,6 @@ namespace ILCompiler } AddGeneratedType(method.OwningType); - _exactMethodInstantiations.AddEntryIfEligible(_nodeFactory, method); _methodDefinitionsGenerated.Add(method.GetTypicalMethodDefinition()); _methodsGenerated.Add(method); return; @@ -389,6 +387,11 @@ namespace ILCompiler return _arrayTypesGenerated; } + internal IEnumerable GetCompiledMethods() + { + return _methodsGenerated; + } + internal bool TypeGeneratesEEType(TypeDesc type) { return _typesWithEETypesGenerated.Contains(type); -- cgit v1.2.3 From daabbb9b56e48de5d0f9aa6fbb7772c555e539ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 12 Jan 2017 13:59:30 -0800 Subject: Make GenericsHashtable like ExactMethodInstantiations These contain similar data, generated from similar things and should look the same. --- .../DependencyAnalysis/GenericsHashtableNode.cs | 53 ++++++++-------------- .../src/Compiler/MetadataGeneration.cs | 12 +++-- 2 files changed, 25 insertions(+), 40 deletions(-) diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericsHashtableNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericsHashtableNode.cs index 5511c4114..fd5315207 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericsHashtableNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericsHashtableNode.cs @@ -4,8 +4,6 @@ using System; using System.IO; -using System.Diagnostics; -using System.Collections.Generic; using Internal.Text; using Internal.TypeSystem; @@ -21,23 +19,10 @@ namespace ILCompiler.DependencyAnalysis private ObjectAndOffsetSymbolNode _endSymbol; private ExternalReferencesTableNode _externalReferences; - private NativeWriter _writer; - private Section _tableSection; - private VertexHashtable _hashtable; - - private HashSet _genericTypeInstantiations; - public GenericsHashtableNode(ExternalReferencesTableNode externalReferences) { _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__generics_hashtable_End", true); _externalReferences = externalReferences; - - _writer = new NativeWriter(); - _hashtable = new VertexHashtable(); - _tableSection = _writer.NewSection(); - _tableSection.Place(_hashtable); - - _genericTypeInstantiations = new HashSet(); } public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) @@ -52,34 +37,32 @@ namespace ILCompiler.DependencyAnalysis public override bool StaticDependenciesAreComputed => true; protected override string GetName() => this.GetMangledName(); - public void AddEntryIfEligible(NodeFactory factory, TypeDesc type) - { - // If this is an instantiated non-canonical generic type, add it to the generic instantiations hashtable - if (!type.HasInstantiation || type.IsGenericDefinition || type.IsCanonicalSubtype(CanonicalFormKind.Any)) - return; - - // Already added? - if (!_genericTypeInstantiations.Add(type)) - return; - - var typeSymbol = factory.NecessaryTypeSymbol(type); - uint instantiationId = _externalReferences.GetIndex(typeSymbol); - Vertex hashtableEntry = _writer.GetUnsignedConstant(instantiationId); - - _hashtable.Append((uint)type.GetHashCode(), _tableSection.Place(hashtableEntry)); - } - public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { // This node does not trigger generation of other nodes. if (relocsOnly) return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolNode[] { this }); - // Zero out the hashset so that we AV if someone tries to insert after we're done. - _genericTypeInstantiations = null; + NativeWriter nativeWriter = new NativeWriter(); + VertexHashtable hashtable = new VertexHashtable(); + Section nativeSection = nativeWriter.NewSection(); + nativeSection.Place(hashtable); + + foreach (var type in factory.MetadataManager.GetTypesWithEETypes()) + { + // If this is an instantiated non-canonical generic type, add it to the generic instantiations hashtable + if (!type.HasInstantiation || type.IsGenericDefinition || type.IsCanonicalSubtype(CanonicalFormKind.Any)) + continue; + + var typeSymbol = factory.NecessaryTypeSymbol(type); + uint instantiationId = _externalReferences.GetIndex(typeSymbol); + Vertex hashtableEntry = nativeWriter.GetUnsignedConstant(instantiationId); + + hashtable.Append((uint)type.GetHashCode(), nativeSection.Place(hashtableEntry)); + } MemoryStream stream = new MemoryStream(); - _writer.Save(stream); + nativeWriter.Save(stream); byte[] streamBytes = stream.ToArray(); _endSymbol.SetSymbolOffset(streamBytes.Length); diff --git a/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs b/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs index 3d07881b2..ff39e1b51 100644 --- a/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs +++ b/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs @@ -46,8 +46,6 @@ namespace ILCompiler private Dictionary _dynamicInvokeThunks = new Dictionary(); - private GenericsHashtableNode _genericsHashtable; - internal NativeLayoutInfoNode NativeLayoutInfo { get; private set; } public MetadataGeneration(NodeFactory factory) @@ -103,8 +101,8 @@ namespace ILCompiler var exactMethodInstantiations = new ExactMethodInstantiationsNode(nativeReferencesTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.ExactMethodInstantiationsHashtable), exactMethodInstantiations, exactMethodInstantiations, exactMethodInstantiations.EndSymbol); - _genericsHashtable = new GenericsHashtableNode(nativeReferencesTableNode); - header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.GenericsHashtable), _genericsHashtable, _genericsHashtable, _genericsHashtable.EndSymbol); + var genericsHashtable = new GenericsHashtableNode(nativeReferencesTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.GenericsHashtable), genericsHashtable, genericsHashtable, genericsHashtable.EndSymbol); // This one should go last header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.CommonFixupsTable), commonFixupsTableNode, commonFixupsTableNode, commonFixupsTableNode.EndSymbol); @@ -118,7 +116,6 @@ namespace ILCompiler { _typesWithEETypesGenerated.Add(eetypeNode.Type); AddGeneratedType(eetypeNode.Type); - _genericsHashtable.AddEntryIfEligible(_nodeFactory, eetypeNode.Type); return; } @@ -397,6 +394,11 @@ namespace ILCompiler return _typesWithEETypesGenerated.Contains(type); } + internal IEnumerable GetTypesWithEETypes() + { + return _typesWithEETypesGenerated; + } + private struct DummyMetadataPolicy : IMetadataPolicy { private MetadataGeneration _parent; -- cgit v1.2.3 From c2a2028eba7419e67b87e7b6e760b5d1fb5e1bfc Mon Sep 17 00:00:00 2001 From: Simon Nattress Date: Thu, 12 Jan 2017 16:55:51 -0800 Subject: Set larger timeout for push job The CoreCLR tests need more than the standard 2 hours allowed by CI. --- netci.groovy | 2 ++ 1 file changed, 2 insertions(+) diff --git a/netci.groovy b/netci.groovy index 83d30da9a..bd516703e 100644 --- a/netci.groovy +++ b/netci.groovy @@ -64,6 +64,8 @@ def osList = ['Ubuntu', 'OSX', 'Windows_NT'] Utilities.addGithubPRTriggerForBranch(newJob, branch, prJobDescription) } else { + // Set a large timeout since the default (2 hours) is insufficient + Utilities.setJobTimeout(newJob, 1440) Utilities.addGithubPushTrigger(newJob) } } -- cgit v1.2.3 From 7946f402e0954b16021f190ec5d5f115169fd7de Mon Sep 17 00:00:00 2001 From: Sedar Gokbulut Date: Fri, 13 Jan 2017 08:56:52 -0800 Subject: Picking up new libraries from CoreFX: 4.4.0-beta-24913-02 version of the packages brings in netcoreapp1.2corert configurations for some of the libraries. Updating the project.json for netcoreapp to pick the new version, packages.targets to pick the corert assets from those packages and project.json for uap to remove the stale dependencies --- src/packaging/netcoreapp/project.json | 72 +++++++++++++++++------------------ src/packaging/packages.targets | 25 ++++++------ src/packaging/uap/project.json | 9 +---- 3 files changed, 52 insertions(+), 54 deletions(-) diff --git a/src/packaging/netcoreapp/project.json b/src/packaging/netcoreapp/project.json index e84eff652..6c036cd40 100644 --- a/src/packaging/netcoreapp/project.json +++ b/src/packaging/netcoreapp/project.json @@ -2,43 +2,43 @@ "frameworks": { "netcoreapp1.2": { "dependencies": { - "System.Buffers": "4.4.0-beta-24906-01", - "System.Console": "4.4.0-beta-24906-01", - "System.Collections": "4.4.0-beta-24906-01", - "System.Collections.Concurrent": "4.4.0-beta-24906-01", - "System.Collections.NonGeneric": "4.4.0-beta-24906-01", - "System.Diagnostics.Debug": "4.4.0-beta-24906-01", - "System.Diagnostics.Tracing": "4.4.0-beta-24906-01", - "System.Diagnostics.Tools": "4.4.0-beta-24906-01", - "System.Globalization": "4.4.0-beta-24906-01", - "System.Globalization.Calendars": "4.4.0-beta-24906-01", - "System.IO": "4.4.0-beta-24906-01", - "System.IO.FileSystem": "4.4.0-beta-24906-01", - "System.IO.FileSystem.Primitives": "4.4.0-beta-24906-01", - "System.Linq": "4.4.0-beta-24906-01", - "System.Reflection": "4.4.0-beta-24906-01", - "System.Reflection.Primitives": "4.4.0-beta-24906-01", - "System.Reflection.Extensions": "4.4.0-beta-24906-01", - "System.Reflection.TypeExtensions": "4.4.0-beta-24906-01", - "System.Resources.ResourceManager": "4.4.0-beta-24906-01", - "System.Runtime": "4.4.0-beta-24906-01", - "System.Runtime.CompilerServices.Unsafe": "4.4.0-beta-24906-01", - "System.Runtime.Extensions": "4.4.0-beta-24906-01", - "System.Runtime.InteropServices": "4.4.0-beta-24906-01", - "System.Runtime.InteropServices.RuntimeInformation": "4.4.0-beta-24906-01", - "System.Runtime.Handles": "4.4.0-beta-24906-01", - "System.Runtime.Numerics": "4.4.0-beta-24906-01", - "System.Security.Principal": "4.4.0-beta-24906-01", - "System.Text.Encoding": "4.4.0-beta-24906-01", - "System.Text.Encoding.Extensions": "4.4.0-beta-24906-01", - "System.Threading": "4.4.0-beta-24906-01", - "System.Threading.Overlapped": "4.4.0-beta-24906-01", - "System.Threading.Tasks": "4.4.0-beta-24906-01", - "System.Threading.Thread": "4.4.0-beta-24906-01", - "System.Threading.Timer": "4.4.0-beta-24906-01", - "Microsoft.Win32.Primitives": "4.4.0-beta-24906-01", + "System.Buffers": "4.4.0-beta-24913-02", + "System.Console": "4.4.0-beta-24913-02", + "System.Collections": "4.4.0-beta-24913-02", + "System.Collections.Concurrent": "4.4.0-beta-24913-02", + "System.Collections.NonGeneric": "4.4.0-beta-24913-02", + "System.Diagnostics.Debug": "4.4.0-beta-24913-02", + "System.Diagnostics.Tracing": "4.4.0-beta-24913-02", + "System.Diagnostics.Tools": "4.4.0-beta-24913-02", + "System.Globalization": "4.4.0-beta-24913-02", + "System.Globalization.Calendars": "4.4.0-beta-24913-02", + "System.IO": "4.4.0-beta-24913-02", + "System.IO.FileSystem": "4.4.0-beta-24913-02", + "System.IO.FileSystem.Primitives": "4.4.0-beta-24913-02", + "System.Linq": "4.4.0-beta-24913-02", + "System.Reflection": "4.4.0-beta-24913-02", + "System.Reflection.Primitives": "4.4.0-beta-24913-02", + "System.Reflection.Extensions": "4.4.0-beta-24913-02", + "System.Reflection.TypeExtensions": "4.4.0-beta-24913-02", + "System.Resources.ResourceManager": "4.4.0-beta-24913-02", + "System.Runtime": "4.4.0-beta-24913-02", + "System.Runtime.CompilerServices.Unsafe": "4.4.0-beta-24913-02", + "System.Runtime.Extensions": "4.4.0-beta-24913-02", + "System.Runtime.InteropServices": "4.4.0-beta-24913-02", + "System.Runtime.InteropServices.RuntimeInformation": "4.4.0-beta-24913-02", + "System.Runtime.Handles": "4.4.0-beta-24913-02", + "System.Runtime.Numerics": "4.4.0-beta-24913-02", + "System.Security.Principal": "4.4.0-beta-24913-02", + "System.Text.Encoding": "4.4.0-beta-24913-02", + "System.Text.Encoding.Extensions": "4.4.0-beta-24913-02", + "System.Threading": "4.4.0-beta-24913-02", + "System.Threading.Overlapped": "4.4.0-beta-24913-02", + "System.Threading.Tasks": "4.4.0-beta-24913-02", + "System.Threading.Thread": "4.4.0-beta-24913-02", + "System.Threading.Timer": "4.4.0-beta-24913-02", + "Microsoft.Win32.Primitives": "4.4.0-beta-24913-02", - "runtime.native.System": "4.4.0-beta-24906-01" + "runtime.native.System": "4.4.0-beta-24913-02" } } }, diff --git a/src/packaging/packages.targets b/src/packaging/packages.targets index b3926932a..24f850056 100644 --- a/src/packaging/packages.targets +++ b/src/packaging/packages.targets @@ -22,7 +22,7 @@ lib a - 4.4.0-beta-24906-01 + 4.4.0-beta-24913-02 1.2.0-beta-24815-03 @@ -127,12 +127,20 @@ - - - - + + + + + + + + + + + + - + @@ -142,11 +150,6 @@ - - - - - diff --git a/src/packaging/uap/project.json b/src/packaging/uap/project.json index 8ed3477bd..b8ea32560 100644 --- a/src/packaging/uap/project.json +++ b/src/packaging/uap/project.json @@ -2,13 +2,8 @@ "frameworks": { "uap10.1": { "dependencies": { - "System.Reflection.Primitives": "4.4.0-beta-24906-01", - "System.Runtime.InteropServices": "4.4.0-beta-24906-01", - "System.Threading.Tasks": "4.4.0-beta-24906-01", - "System.Collections": "4.4.0-beta-24906-01", - "System.Diagnostics.Debug": "4.4.0-beta-24906-01", - "System.Diagnostics.Tracing": "4.4.0-beta-24906-01", - "System.Linq": "4.4.0-beta-24906-01", + "System.Diagnostics.Tracing": "4.4.0-beta-24913-02", + "System.Linq": "4.4.0-beta-24913-02", "Microsoft.NETCore.Portable.Compatibility": "1.0.2" } } -- cgit v1.2.3 From 85dde75bf18711139278b6eeae7641ba1ea001a9 Mon Sep 17 00:00:00 2001 From: Fadi Hanna Date: Fri, 13 Jan 2017 10:48:15 -0800 Subject: Changing the way external native layout offsets get used, to include a CoreRT optimization, avoiding an unnecessary indirection through the ExternalReferences table. We can optimize in CoreRT because all tables using data from the native layout blob are created by the same entity that creates the native layout blob (unlike in ProjectN, where one could be in NUTC and the native layout is always in the binder, therefore needing the ExternalReferencesTable lookup helper). [tfs-changeset: 1644455] --- ...utionEnvironmentImplementation.MappingTables.cs | 2 +- .../Runtime/TypeLoader/ExternalReferencesTable.cs | 25 ++++++++++++++++------ .../Internal/Runtime/TypeLoader/TemplateLocator.cs | 10 ++++----- .../TypeLoaderEnvironment.GVMResolution.cs | 10 ++++----- .../TypeLoader/TypeLoaderEnvironment.Metadata.cs | 8 +++---- 5 files changed, 34 insertions(+), 21 deletions(-) diff --git a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MappingTables.cs b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MappingTables.cs index 3d2fe2832..35e6a3e64 100644 --- a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MappingTables.cs +++ b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MappingTables.cs @@ -1075,7 +1075,7 @@ namespace Internal.Reflection.Execution } else { - uint nameAndSigOffset = externalReferences.GetNativeLayoutOffsetFromIndex(entryMethodHandleOrNameAndSigRaw); + uint nameAndSigOffset = externalReferences.GetExternalNativeLayoutOffset(entryMethodHandleOrNameAndSigRaw); MethodNameAndSignature nameAndSig; if (!TypeLoaderEnvironment.Instance.TryGetMethodNameAndSignatureFromNativeLayoutOffset(mappingTableModule, nameAndSigOffset, out nameAndSig)) { diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ExternalReferencesTable.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ExternalReferencesTable.cs index 644eac90e..99f151906 100644 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ExternalReferencesTable.cs +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ExternalReferencesTable.cs @@ -137,16 +137,29 @@ namespace Internal.Runtime.TypeLoader { return RuntimeAugments.CreateRuntimeTypeHandle(GetIntPtrFromIndex(index)); } + } - unsafe public uint GetNativeLayoutOffsetFromIndex(uint index) + public static class ExternalReferencesTableExtentions + { + public static uint GetExternalNativeLayoutOffset(this ExternalReferencesTable extRefs, uint index) { - if (index >= _elementsCount) - throw new BadImageFormatException(); - + // CoreRT is a bit more optimized than ProjectN. In ProjectN, some tables that reference data + // in the native layout are constructed at NUTC compilation time, but the native layout is only + // generated at binder time, so we use the external references table to link the nutc-built + // tables with their native layout dependencies. + // + // In ProjectN, the nutc-built tables will be emitted with indices into the external references + // table, and the entries in the external references table will contain the offsets into the + // native layout blob. + // + // In CoreRT, since all tables and native layout blob are built together at the same time, we can + // optimize by writing the native layout offsets directly into the table, without requiring the extra + // lookup in the external references table. + // #if CORERT - return *(uint*)(((IntPtr*)_elements)[index]); + return index; #else - return ((TableElement*)_elements)[index]; + return extRefs.GetRvaFromIndex(index); #endif } } diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TemplateLocator.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TemplateLocator.cs index ec5656cdc..7726b09d6 100644 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TemplateLocator.cs +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TemplateLocator.cs @@ -92,7 +92,7 @@ namespace Internal.Runtime.TypeLoader if (typeDesc == canonForm) { TypeLoaderLogger.WriteLine("Found metadata template for type " + concreteType.ToString() + ": " + typeDesc.ToString()); - nativeLayoutInfoToken = (uint)externalFixupsTable.GetNativeLayoutOffsetFromIndex(entryParser.GetUnsigned()); + nativeLayoutInfoToken = (uint)externalFixupsTable.GetExternalNativeLayoutOffset(entryParser.GetUnsigned()); if (nativeLayoutInfoToken == BadTokenFixupValue) { throw new BadImageFormatException(); @@ -150,7 +150,7 @@ namespace Internal.Runtime.TypeLoader if (methodDesc == canonForm) { TypeLoaderLogger.WriteLine("Found metadata template for method " + concreteMethod.ToString() + ": " + methodDesc.ToString()); - nativeLayoutInfoToken = (uint)externalFixupsTable.GetNativeLayoutOffsetFromIndex(entryParser.GetUnsigned()); + nativeLayoutInfoToken = (uint)externalFixupsTable.GetExternalNativeLayoutOffset(entryParser.GetUnsigned()); if (nativeLayoutInfoToken == BadTokenFixupValue) { throw new BadImageFormatException(); @@ -197,7 +197,7 @@ namespace Internal.Runtime.TypeLoader if (canonForm == candidateTemplate.ConvertToCanonForm(kind)) { TypeLoaderLogger.WriteLine("Found template for type " + concreteType.ToString() + ": " + candidateTemplate.ToString()); - nativeLayoutInfoToken = (uint)externalFixupsTable.GetNativeLayoutOffsetFromIndex(entryParser.GetUnsigned()); + nativeLayoutInfoToken = (uint)externalFixupsTable.GetExternalNativeLayoutOffset(entryParser.GetUnsigned()); if (nativeLayoutInfoToken == BadTokenFixupValue) { // TODO: once multifile gets fixed up, make this throw a BadImageFormatException @@ -269,7 +269,7 @@ namespace Internal.Runtime.TypeLoader NativeParser entryParser; while (!(entryParser = enumerator.GetNext()).IsNull) { - var methodSignatureParser = new NativeParser(nativeLayoutReader, externalFixupsTable.GetNativeLayoutOffsetFromIndex(entryParser.GetUnsigned())); + var methodSignatureParser = new NativeParser(nativeLayoutReader, externalFixupsTable.GetExternalNativeLayoutOffset(entryParser.GetUnsigned())); // Get the unified generic method holder and convert it to its canonical form var candidateTemplate = (InstantiatedMethod)context.GetMethod(ref methodSignatureParser); @@ -279,7 +279,7 @@ namespace Internal.Runtime.TypeLoader { TypeLoaderLogger.WriteLine("Found template for generic method " + concreteMethod.ToString() + ": " + candidateTemplate.ToString()); nativeLayoutInfoModule = moduleHandle; - nativeLayoutInfoToken = (uint)externalFixupsTable.GetNativeLayoutOffsetFromIndex(entryParser.GetUnsigned()); + nativeLayoutInfoToken = (uint)externalFixupsTable.GetExternalNativeLayoutOffset(entryParser.GetUnsigned()); if (nativeLayoutInfoToken == BadTokenFixupValue) { // TODO: once multifile gets fixed up, make this throw a BadImageFormatException diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.GVMResolution.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.GVMResolution.cs index c701c13a6..dd7403ba5 100644 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.GVMResolution.cs +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.GVMResolution.cs @@ -85,7 +85,7 @@ namespace Internal.Runtime.TypeLoader for (uint j = 0; j < numTargetImplementations; j++) { - uint nameAndSigToken = extRefs.GetNativeLayoutOffsetFromIndex(entryParser.GetUnsigned()); + uint nameAndSigToken = extRefs.GetExternalNativeLayoutOffset(entryParser.GetUnsigned()); MethodNameAndSignature targetMethodNameAndSignature = GetMethodNameAndSignatureFromNativeReader(nativeLayoutReader, moduleHandle, nameAndSigToken); RuntimeTypeHandle targetTypeHandle = extRefs.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); @@ -114,7 +114,7 @@ namespace Internal.Runtime.TypeLoader { RuntimeTypeHandle currentIfaceTypeHandle = default(RuntimeTypeHandle); - NativeParser ifaceSigParser = new NativeParser(nativeLayoutReader, extRefs.GetNativeLayoutOffsetFromIndex(entryParser.GetUnsigned())); + NativeParser ifaceSigParser = new NativeParser(nativeLayoutReader, extRefs.GetExternalNativeLayoutOffset(entryParser.GetUnsigned())); if (TypeLoaderEnvironment.Instance.GetTypeFromSignatureAndContext(ref ifaceSigParser, moduleHandle, targetTypeInstantiation, null, out currentIfaceTypeHandle)) { @@ -226,7 +226,7 @@ namespace Internal.Runtime.TypeLoader if (!openCallingTypeHandle.Equals(interfaceTypeHandle)) continue; - uint nameAndSigToken = extRefs.GetNativeLayoutOffsetFromIndex(entryParser.GetUnsigned()); + uint nameAndSigToken = extRefs.GetExternalNativeLayoutOffset(entryParser.GetUnsigned()); MethodNameAndSignature interfaceMethodNameAndSignature = GetMethodNameAndSignatureFromNativeReader(nativeLayoutReader, moduleHandle, nameAndSigToken); if (!interfaceMethodNameAndSignature.Equals(methodNameAndSignature)) @@ -459,13 +459,13 @@ namespace Internal.Runtime.TypeLoader if (!parsedTargetTypeHandle.Equals(openTargetTypeHandle)) continue; - uint parsedCallingNameAndSigToken = extRefs.GetNativeLayoutOffsetFromIndex(entryParser.GetUnsigned()); + uint parsedCallingNameAndSigToken = extRefs.GetExternalNativeLayoutOffset(entryParser.GetUnsigned()); MethodNameAndSignature parsedCallingNameAndSignature = GetMethodNameAndSignatureFromNativeReader(nativeLayoutReader, moduleHandle, parsedCallingNameAndSigToken); if (!parsedCallingNameAndSignature.Equals(callingMethodNameAndSignature)) continue; - uint parsedTargetMethodNameAndSigToken = extRefs.GetNativeLayoutOffsetFromIndex(entryParser.GetUnsigned()); + uint parsedTargetMethodNameAndSigToken = extRefs.GetExternalNativeLayoutOffset(entryParser.GetUnsigned()); MethodNameAndSignature targetMethodNameAndSignature = GetMethodNameAndSignatureFromNativeReader(nativeLayoutReader, moduleHandle, parsedTargetMethodNameAndSigToken); Debug.Assert(targetMethodNameAndSignature != null); diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.Metadata.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.Metadata.cs index c52479aad..22d2c7d56 100644 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.Metadata.cs +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.Metadata.cs @@ -852,7 +852,7 @@ namespace Internal.Runtime.TypeLoader if (!entryType.Equals(definitionType)) continue; - uint nameAndSigPointerToken = externalReferences.GetNativeLayoutOffsetFromIndex(entryParser.GetUnsigned()); + uint nameAndSigPointerToken = externalReferences.GetExternalNativeLayoutOffset(entryParser.GetUnsigned()); MethodNameAndSignature nameAndSig; if (!TypeLoaderEnvironment.Instance.TryGetMethodNameAndSignatureFromNativeLayoutOffset(moduleHandle, nameAndSigPointerToken, out nameAndSig)) @@ -995,7 +995,7 @@ namespace Internal.Runtime.TypeLoader if (!entryType.Equals(definitionType)) continue; - uint nameAndSigPointerToken = externalReferences.GetNativeLayoutOffsetFromIndex(entryParser.GetUnsigned()); + uint nameAndSigPointerToken = externalReferences.GetExternalNativeLayoutOffset(entryParser.GetUnsigned()); uint parentHierarchyAndFlag = entryParser.GetUnsigned(); bool isGenericVirtualMethod = ((parentHierarchyAndFlag & VirtualInvokeTableEntry.FlagsMask) == VirtualInvokeTableEntry.GenericVirtualMethod); @@ -1576,7 +1576,7 @@ namespace Internal.Runtime.TypeLoader } else { - uint nameAndSigToken = extRefTable.GetNativeLayoutOffsetFromIndex(entryParser.GetUnsigned()); + uint nameAndSigToken = extRefTable.GetExternalNativeLayoutOffset(entryParser.GetUnsigned()); MethodNameAndSignature nameAndSig; if (!TypeLoaderEnvironment.Instance.TryGetMethodNameAndSignatureFromNativeLayoutOffset(_moduleHandle, nameAndSigToken, out nameAndSig)) { @@ -1608,7 +1608,7 @@ namespace Internal.Runtime.TypeLoader { Debug.Assert((_hasEntryPoint || ((_flags & InvokeTableFlags.HasVirtualInvoke) != 0)) && ((_flags & InvokeTableFlags.RequiresInstArg) != 0)); - uint nameAndSigPointerToken = extRefTable.GetNativeLayoutOffsetFromIndex(entryParser.GetUnsigned()); + uint nameAndSigPointerToken = extRefTable.GetExternalNativeLayoutOffset(entryParser.GetUnsigned()); if (!TypeLoaderEnvironment.Instance.TryGetMethodNameAndSignatureFromNativeLayoutOffset(_moduleHandle, nameAndSigPointerToken, out _nameAndSignature)) { Debug.Assert(false); //Error -- cgit v1.2.3 From e5678962aab23ce6bac2ba53185781c4467904be Mon Sep 17 00:00:00 2001 From: dotnet-bot Date: Fri, 13 Jan 2017 12:25:19 -0800 Subject: This is the internal component of CoreRT#2485 (https://github.com/dotnet/corert/pull/2485), which ports a number of GC changes done within the last month or two from CoreCLR to CoreRT. The only changes to anything in Native/gc are: 1. Move an assert under #ifndef FEATURE_REDHAWK since it only is a useful assert on CoreCLR (https://github.com/dotnet/corert/pull/2485#issuecomment-271975502) 2. Add UNREFERENCED_PARAMETERs to things in gc.cpp and elsewhere that were producing warnings All other changes in Native/gc are directly from CoreCLR without modification. I have validated that I am able to do PerfView GC analysis with these changes (since the CoreCLR changes touched ETW eventing). [tfs-changeset: 1644465] --- src/Native/Runtime/eventtrace.h | 11 + src/Native/Runtime/gcheaputilities.cpp | 12 +- src/Native/Runtime/gcheaputilities.h | 13 + src/Native/Runtime/gcrhenv.cpp | 209 ++++++- src/Native/Runtime/profheapwalkhelper.cpp | 4 +- src/Native/Runtime/unix/PalRedhawkUnix.cpp | 5 +- src/Native/Runtime/windows/PalRedhawkMinWin.cpp | 2 +- src/Native/gc/env/gcenv.base.h | 2 +- src/Native/gc/env/gcenv.ee.h | 10 + src/Native/gc/env/gcenv.os.h | 3 +- src/Native/gc/gc.cpp | 783 ++++++++++-------------- src/Native/gc/gc.h | 16 + src/Native/gc/gccommon.cpp | 14 +- src/Native/gc/gcee.cpp | 207 +------ src/Native/gc/gcenv.ee.standalone.inl | 48 ++ src/Native/gc/gcimpl.h | 19 +- src/Native/gc/gcinterface.ee.h | 36 ++ src/Native/gc/gcinterface.h | 140 +++-- src/Native/gc/gcpriv.h | 57 +- src/Native/gc/gcrecord.h | 2 +- src/Native/gc/gcscan.cpp | 17 +- src/Native/gc/gcscan.h | 6 +- src/Native/gc/gcsvr.cpp | 1 + src/Native/gc/gcwks.cpp | 1 + src/Native/gc/handletablecache.cpp | 6 + src/Native/gc/handletablecore.cpp | 2 +- src/Native/gc/objecthandle.cpp | 99 +-- src/Native/gc/objecthandle.h | 7 +- src/Native/gc/sample/CMakeLists.txt | 4 +- src/Native/gc/sample/GCSample.cpp | 15 +- src/Native/gc/sample/GCSample.vcxproj | 7 +- src/Native/gc/sample/GCSample.vcxproj.filters | 2 +- src/Native/gc/sample/gcenv.ee.cpp | 46 +- src/Native/gc/softwarewritewatch.cpp | 12 +- src/Native/gc/softwarewritewatch.h | 40 +- 35 files changed, 967 insertions(+), 891 deletions(-) diff --git a/src/Native/Runtime/eventtrace.h b/src/Native/Runtime/eventtrace.h index 77ab912e9..ae88847ab 100644 --- a/src/Native/Runtime/eventtrace.h +++ b/src/Native/Runtime/eventtrace.h @@ -29,7 +29,18 @@ #define _VMEVENTTRACE_H_ #include "eventtracebase.h" +#include "gcinterface.h" +#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) +struct ProfilingScanContext : ScanContext +{ + BOOL fProfilerPinned; + void * pvEtwContext; + void *pHeapId; + + ProfilingScanContext(BOOL fProfilerPinnedParam); +}; +#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) namespace ETW { diff --git a/src/Native/Runtime/gcheaputilities.cpp b/src/Native/Runtime/gcheaputilities.cpp index 0ff2910fe..dd328fc46 100644 --- a/src/Native/Runtime/gcheaputilities.cpp +++ b/src/Native/Runtime/gcheaputilities.cpp @@ -7,4 +7,14 @@ #include "gcheaputilities.h" // This is the global GC heap, maintained by the VM. -GPTR_IMPL(IGCHeap, g_pGCHeap); \ No newline at end of file +GPTR_IMPL(IGCHeap, g_pGCHeap); + +// These globals are variables used within the GC and maintained +// by the EE for use in write barriers. It is the responsibility +// of the GC to communicate updates to these globals to the EE through +// GCToEEInterface::StompWriteBarrier. +GPTR_IMPL_INIT(uint32_t, g_card_table, nullptr); +GPTR_IMPL_INIT(uint8_t, g_lowest_address, nullptr); +GPTR_IMPL_INIT(uint8_t, g_highest_address, nullptr); +uint8_t* g_ephemeral_low = (uint8_t*)1; +uint8_t* g_ephemeral_high = (uint8_t*)~0; diff --git a/src/Native/Runtime/gcheaputilities.h b/src/Native/Runtime/gcheaputilities.h index 6e08472a2..5aec56bb2 100644 --- a/src/Native/Runtime/gcheaputilities.h +++ b/src/Native/Runtime/gcheaputilities.h @@ -10,6 +10,19 @@ // The singular heap instance. GPTR_DECL(IGCHeap, g_pGCHeap); +#ifndef DACCESS_COMPILE +extern "C" { +#endif // !DACCESS_COMPILE +GPTR_DECL(uint8_t,g_lowest_address); +GPTR_DECL(uint8_t,g_highest_address); +GPTR_DECL(uint32_t,g_card_table); +#ifndef DACCESS_COMPILE +} +#endif // !DACCESS_COMPILE + +extern "C" uint8_t* g_ephemeral_low; +extern "C" uint8_t* g_ephemeral_high; + // GCHeapUtilities provides a number of static methods // that operate on the global heap instance. It can't be // instantiated. diff --git a/src/Native/Runtime/gcrhenv.cpp b/src/Native/Runtime/gcrhenv.cpp index 2ff736292..c60f09f0a 100644 --- a/src/Native/Runtime/gcrhenv.cpp +++ b/src/Native/Runtime/gcrhenv.cpp @@ -51,6 +51,25 @@ #include "holder.h" +#ifdef FEATURE_ETW + #ifndef _INC_WINDOWS + typedef void* LPVOID; + typedef uint32_t UINT; + typedef void* PVOID; + typedef uint64_t ULONGLONG; + typedef uint32_t ULONG; + typedef int64_t LONGLONG; + typedef uint8_t BYTE; + typedef uint16_t UINT16; + #endif // _INC_WINDOWS + + #include "etwevents.h" + #include "eventtrace.h" +#else // FEATURE_ETW + #include "etmdummy.h" + #define ETW_EVENT_ENABLED(e,f) false +#endif // FEATURE_ETW + GPTR_IMPL(EEType, g_pFreeObjectEEType); #define USE_CLR_CACHE_SIZE_BEHAVIOR @@ -119,7 +138,7 @@ UInt32 EtwCallback(UInt32 IsEnabled, RH_ETW_CONTEXT * pContext) FireEtwGCSettings(GCHeapUtilities::GetGCHeap()->GetValidSegmentSize(FALSE), GCHeapUtilities::GetGCHeap()->GetValidSegmentSize(TRUE), GCHeapUtilities::IsServerHeap()); - GCHeapUtilities::GetGCHeap()->TraceGCSegments(); + GCHeapUtilities::GetGCHeap()->DiagTraceGCSegments(); } // Special check for the runtime provider's GCHeapCollectKeyword. Profilers @@ -686,8 +705,8 @@ void RedhawkGCInterface::ScanHeap(GcScanObjectFunction pfnScanCallback, void *pC // static void RedhawkGCInterface::ScanObject(void *pObject, GcScanObjectFunction pfnScanCallback, void *pContext) { -#if !defined(DACCESS_COMPILE) && (defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)) - GCHeapUtilities::GetGCHeap()->WalkObject((Object*)pObject, (walk_fn)pfnScanCallback, pContext); +#if !defined(DACCESS_COMPILE) && defined(FEATURE_EVENT_TRACE) + GCHeapUtilities::GetGCHeap()->DiagWalkObject((Object*)pObject, (walk_fn)pfnScanCallback, pContext); #else UNREFERENCED_PARAMETER(pObject); UNREFERENCED_PARAMETER(pfnScanCallback); @@ -759,7 +778,7 @@ void RedhawkGCInterface::ScanStaticRoots(GcScanRootFunction pfnScanCallback, voi // static void RedhawkGCInterface::ScanHandleTableRoots(GcScanRootFunction pfnScanCallback, void *pContext) { -#if !defined(DACCESS_COMPILE) && (defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)) +#if !defined(DACCESS_COMPILE) && defined(FEATURE_EVENT_TRACE) ScanRootsContext sContext; sContext.m_pfnCallback = pfnScanCallback; sContext.m_pContext = pContext; @@ -1148,6 +1167,166 @@ Thread* GCToEEInterface::CreateBackgroundThread(GCBackgroundThreadFunction threa return threadStubArgs.m_pThread; } +void GCToEEInterface::DiagGCStart(int gen, bool isInduced) +{ + UNREFERENCED_PARAMETER(gen); + UNREFERENCED_PARAMETER(isInduced); +} + +void GCToEEInterface::DiagUpdateGenerationBounds() +{ +} + +void GCToEEInterface::DiagWalkFReachableObjects(void* gcContext) +{ + UNREFERENCED_PARAMETER(gcContext); +} + +void GCToEEInterface::DiagGCEnd(size_t index, int gen, int reason, bool fConcurrent) +{ + UNREFERENCED_PARAMETER(index); + UNREFERENCED_PARAMETER(gen); + UNREFERENCED_PARAMETER(reason); + UNREFERENCED_PARAMETER(fConcurrent); +} + +// Note on last parameter: when calling this for bgc, only ETW +// should be sending these events so that existing profapi profilers +// don't get confused. +void WalkMovedReferences(uint8_t* begin, uint8_t* end, + ptrdiff_t reloc, + size_t context, + BOOL fCompacting, + BOOL fBGC) +{ + UNREFERENCED_PARAMETER(begin); + UNREFERENCED_PARAMETER(end); + UNREFERENCED_PARAMETER(reloc); + UNREFERENCED_PARAMETER(context); + UNREFERENCED_PARAMETER(fCompacting); + UNREFERENCED_PARAMETER(fBGC); +} + +// +// Diagnostics code +// + +#ifdef FEATURE_EVENT_TRACE +inline BOOL ShouldTrackMovementForProfilerOrEtw() +{ + if (ETW::GCLog::ShouldTrackMovementForEtw()) + return true; + + return false; +} +#endif // FEATURE_EVENT_TRACE + +void GCToEEInterface::DiagWalkSurvivors(void* gcContext) +{ +#ifdef FEATURE_EVENT_TRACE + if (ShouldTrackMovementForProfilerOrEtw()) + { + size_t context = 0; + ETW::GCLog::BeginMovedReferences(&context); + GCHeapUtilities::GetGCHeap()->DiagWalkSurvivorsWithType(gcContext, &WalkMovedReferences, context, walk_for_gc); + ETW::GCLog::EndMovedReferences(context); + } +#else + UNREFERENCED_PARAMETER(gcContext); +#endif // FEATURE_EVENT_TRACE +} + +void GCToEEInterface::DiagWalkLOHSurvivors(void* gcContext) +{ +#ifdef FEATURE_EVENT_TRACE + if (ShouldTrackMovementForProfilerOrEtw()) + { + size_t context = 0; + ETW::GCLog::BeginMovedReferences(&context); + GCHeapUtilities::GetGCHeap()->DiagWalkSurvivorsWithType(gcContext, &WalkMovedReferences, context, walk_for_loh); + ETW::GCLog::EndMovedReferences(context); + } +#else + UNREFERENCED_PARAMETER(gcContext); +#endif // FEATURE_EVENT_TRACE +} + +void GCToEEInterface::DiagWalkBGCSurvivors(void* gcContext) +{ +#ifdef FEATURE_EVENT_TRACE + if (ShouldTrackMovementForProfilerOrEtw()) + { + size_t context = 0; + ETW::GCLog::BeginMovedReferences(&context); + GCHeapUtilities::GetGCHeap()->DiagWalkSurvivorsWithType(gcContext, &WalkMovedReferences, context, walk_for_bgc); + ETW::GCLog::EndMovedReferences(context); + } +#else + UNREFERENCED_PARAMETER(gcContext); +#endif // FEATURE_EVENT_TRACE +} + +void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args) +{ + // CoreRT doesn't patch the write barrier like CoreCLR does, but it + // still needs to record the changes in the GC heap. + assert(args != nullptr); + switch (args->operation) + { + case WriteBarrierOp::StompResize: + // StompResize requires a new card table, a new lowest address, and + // a new highest address + assert(args->card_table != nullptr); + assert(args->lowest_address != nullptr); + assert(args->highest_address != nullptr); + g_card_table = args->card_table; + + // We need to make sure that other threads executing checked write barriers + // will see the g_card_table update before g_lowest/highest_address updates. + // Otherwise, the checked write barrier may AV accessing the old card table + // with address that it does not cover. Write barriers access card table + // without memory barriers for performance reasons, so we need to flush + // the store buffers here. + FlushProcessWriteBuffers(); + + g_lowest_address = args->lowest_address; + VolatileStore(&g_highest_address, args->highest_address); + return; + case WriteBarrierOp::StompEphemeral: + // StompEphemeral requires a new ephemeral low and a new ephemeral high + assert(args->ephemeral_low != nullptr); + assert(args->ephemeral_high != nullptr); + g_ephemeral_low = args->ephemeral_low; + g_ephemeral_high = args->ephemeral_high; + return; + case WriteBarrierOp::Initialize: + // This operation should only be invoked once, upon initialization. + assert(g_card_table == nullptr); + assert(g_lowest_address == nullptr); + assert(g_highest_address == nullptr); + assert(args->card_table != nullptr); + assert(args->lowest_address != nullptr); + assert(args->highest_address != nullptr); + assert(args->ephemeral_low != nullptr); + assert(args->ephemeral_high != nullptr); + assert(args->is_runtime_suspended && "the runtime must be suspended here!"); + + g_card_table = args->card_table; + g_lowest_address = args->lowest_address; + g_highest_address = args->highest_address; + g_ephemeral_low = args->ephemeral_low; + g_ephemeral_high = args->ephemeral_high; + return; + case WriteBarrierOp::SwitchToWriteWatch: + case WriteBarrierOp::SwitchToNonWriteWatch: + assert(!"CoreRT does not have an implementation of non-OS WriteWatch"); + return; + default: + assert(!"Unknokwn WriteBarrierOp enum"); + return; + } +} + #endif // !DACCESS_COMPILE // NOTE: this method is not in thread.cpp because it needs access to the layout of alloc_context for DAC to know the @@ -1339,14 +1518,6 @@ MethodTable * g_pFreeObjectMethodTable; int32_t g_TrapReturningThreads; bool g_fFinalizerRunOnShutDown; -void StompWriteBarrierEphemeral(bool /* isRuntimeSuspended */) -{ -} - -void StompWriteBarrierResize(bool /* isRuntimeSuspended */, bool /*bReqUpperBoundsCheck*/) -{ -} - bool IsGCThread() { return false; @@ -1425,3 +1596,17 @@ void CPUGroupInfo::GetGroupForProcessor(uint16_t /*processor_number*/, uint16_t { ASSERT_UNCONDITIONALLY("NYI: CPUGroupInfo::GetGroupForProcessor"); } + +#ifdef FEATURE_EVENT_TRACE +ProfilingScanContext::ProfilingScanContext(BOOL fProfilerPinnedParam) + : ScanContext() +{ + pHeapId = NULL; + fProfilerPinned = fProfilerPinnedParam; + pvEtwContext = NULL; +#ifdef FEATURE_CONSERVATIVE_GC + // To not confuse GCScan::GcScanRoots + promotion = g_pConfig->GetGCConservative(); +#endif +} +#endif // FEATURE_EVENT_TRACE diff --git a/src/Native/Runtime/profheapwalkhelper.cpp b/src/Native/Runtime/profheapwalkhelper.cpp index b4dddca18..038e99ee0 100644 --- a/src/Native/Runtime/profheapwalkhelper.cpp +++ b/src/Native/Runtime/profheapwalkhelper.cpp @@ -141,7 +141,7 @@ BOOL HeapWalkHelper(Object * pBO, void * pvContext) //if (pMT->ContainsPointersOrCollectible()) { // First round through calculates the number of object refs for this class - GCHeapUtilities::GetGCHeap()->WalkObject(pBO, &CountContainedObjectRef, (void *)&cNumRefs); + GCHeapUtilities::GetGCHeap()->DiagWalkObject(pBO, &CountContainedObjectRef, (void *)&cNumRefs); if (cNumRefs > 0) { @@ -166,7 +166,7 @@ BOOL HeapWalkHelper(Object * pBO, void * pvContext) // Second round saves off all of the ref values OBJECTREF * pCurObjRef = arrObjRef; - GCHeapUtilities::GetGCHeap()->WalkObject(pBO, &SaveContainedObjectRef, (void *)&pCurObjRef); + GCHeapUtilities::GetGCHeap()->DiagWalkObject(pBO, &SaveContainedObjectRef, (void *)&pCurObjRef); } } diff --git a/src/Native/Runtime/unix/PalRedhawkUnix.cpp b/src/Native/Runtime/unix/PalRedhawkUnix.cpp index 8b699c07c..91c5842cc 100644 --- a/src/Native/Runtime/unix/PalRedhawkUnix.cpp +++ b/src/Native/Runtime/unix/PalRedhawkUnix.cpp @@ -1457,13 +1457,12 @@ void GCToOSInterface::YieldThread(uint32_t switchCount) // Reserve virtual memory range. // Parameters: -// address - starting virtual address, it can be NULL to let the function choose the starting address // size - size of the virtual memory range // alignment - requested memory alignment, 0 means no specific alignment requested // flags - flags to control special settings like write watching // Return: // Starting virtual address of the reserved range -void* GCToOSInterface::VirtualReserve(void* address, size_t size, size_t alignment, uint32_t flags) +void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t flags) { ASSERT_MSG(!(flags & VirtualReserveFlags::WriteWatch), "WriteWatch not supported on Unix"); @@ -1474,7 +1473,7 @@ void* GCToOSInterface::VirtualReserve(void* address, size_t size, size_t alignme size_t alignedSize = size + (alignment - OS_PAGE_SIZE); - void * pRetVal = mmap(address, alignedSize, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0); + void * pRetVal = mmap(nullptr, alignedSize, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0); if (pRetVal != NULL) { diff --git a/src/Native/Runtime/windows/PalRedhawkMinWin.cpp b/src/Native/Runtime/windows/PalRedhawkMinWin.cpp index 49097edd3..87b52a1ec 100644 --- a/src/Native/Runtime/windows/PalRedhawkMinWin.cpp +++ b/src/Native/Runtime/windows/PalRedhawkMinWin.cpp @@ -1482,7 +1482,7 @@ void GCToOSInterface::YieldThread(uint32_t /*switchCount*/) // flags - flags to control special settings like write watching // Return: // Starting virtual address of the reserved range -void* GCToOSInterface::VirtualReserve(void* address, size_t size, size_t alignment, uint32_t flags) +void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t flags) { DWORD memFlags = (flags & VirtualReserveFlags::WriteWatch) ? (MEM_RESERVE | MEM_WRITE_WATCH) : MEM_RESERVE; return ::VirtualAlloc(0, size, memFlags, PAGE_READWRITE); diff --git a/src/Native/gc/env/gcenv.base.h b/src/Native/gc/env/gcenv.base.h index 94f73762f..0a0de73ee 100644 --- a/src/Native/gc/env/gcenv.base.h +++ b/src/Native/gc/env/gcenv.base.h @@ -96,7 +96,7 @@ inline HRESULT HRESULT_FROM_WIN32(unsigned long x) #define UNREFERENCED_PARAMETER(P) (void)(P) #ifdef PLATFORM_UNIX -#define _vsnprintf vsnprintf +#define _vsnprintf_s(string, sizeInBytes, count, format, args) vsnprintf(string, sizeInBytes, format, args) #define sprintf_s snprintf #define swprintf_s swprintf #endif diff --git a/src/Native/gc/env/gcenv.ee.h b/src/Native/gc/env/gcenv.ee.h index dc6c1d84b..beb0c1a98 100644 --- a/src/Native/gc/env/gcenv.ee.h +++ b/src/Native/gc/env/gcenv.ee.h @@ -56,6 +56,16 @@ public: static void GcEnumAllocContexts(enum_alloc_context_func* fn, void* param); static Thread* CreateBackgroundThread(GCBackgroundThreadFunction threadStart, void* arg); + + // Diagnostics methods. + static void DiagGCStart(int gen, bool isInduced); + static void DiagUpdateGenerationBounds(); + static void DiagGCEnd(size_t index, int gen, int reason, bool fConcurrent); + static void DiagWalkFReachableObjects(void* gcContext); + static void DiagWalkSurvivors(void* gcContext); + static void DiagWalkLOHSurvivors(void* gcContext); + static void DiagWalkBGCSurvivors(void* gcContext); + static void StompWriteBarrier(WriteBarrierParameters* args); }; #endif // __GCENV_EE_H__ diff --git a/src/Native/gc/env/gcenv.os.h b/src/Native/gc/env/gcenv.os.h index bb0153f11..6a126f29e 100644 --- a/src/Native/gc/env/gcenv.os.h +++ b/src/Native/gc/env/gcenv.os.h @@ -73,13 +73,12 @@ public: // Reserve virtual memory range. // Parameters: - // address - starting virtual address, it can be NULL to let the function choose the starting address // size - size of the virtual memory range // alignment - requested memory alignment // flags - flags to control special settings like write watching // Return: // Starting virtual address of the reserved range - static void* VirtualReserve(void *address, size_t size, size_t alignment, uint32_t flags); + static void* VirtualReserve(size_t size, size_t alignment, uint32_t flags); // Release virtual memory range previously reserved using VirtualReserve // Parameters: diff --git a/src/Native/gc/gc.cpp b/src/Native/gc/gc.cpp index 3ba5369a7..99e646c50 100644 --- a/src/Native/gc/gc.cpp +++ b/src/Native/gc/gc.cpp @@ -21,22 +21,6 @@ #define USE_INTROSORT -#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) -inline BOOL ShouldTrackMovementForProfilerOrEtw() -{ -#ifdef GC_PROFILING - if (CORProfilerTrackGC()) - return true; -#endif - -#ifdef FEATURE_EVENT_TRACE - if (ETW::GCLog::ShouldTrackMovementForEtw()) - return true; -#endif - - return false; -} -#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) #if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE) BOOL bgc_heap_walk_for_etw_p = FALSE; @@ -349,8 +333,8 @@ void gc_heap::add_to_history_per_heap() #endif //BACKGROUND_GC current_hist->fgc_lowest = lowest_address; current_hist->fgc_highest = highest_address; - current_hist->g_lowest = g_lowest_address; - current_hist->g_highest = g_highest_address; + current_hist->g_lowest = g_gc_lowest_address; + current_hist->g_highest = g_gc_highest_address; gchist_index_per_heap++; if (gchist_index_per_heap == max_history_count) @@ -405,7 +389,7 @@ void log_va_msg(const char *fmt, va_list args) int pid_len = sprintf_s (&pBuffer[buffer_start], BUFFERSIZE - buffer_start, "[%5d]", (uint32_t)GCToOSInterface::GetCurrentThreadIdForLogging()); buffer_start += pid_len; memset(&pBuffer[buffer_start], '-', BUFFERSIZE - buffer_start); - int msg_len = _vsnprintf(&pBuffer[buffer_start], BUFFERSIZE - buffer_start, fmt, args ); + int msg_len = _vsnprintf_s(&pBuffer[buffer_start], BUFFERSIZE - buffer_start, _TRUNCATE, fmt, args ); if (msg_len == -1) { msg_len = BUFFERSIZE - buffer_start; @@ -1418,9 +1402,6 @@ int mark_time, plan_time, sweep_time, reloc_time, compact_time; #ifndef MULTIPLE_HEAPS -#define ephemeral_low g_ephemeral_low -#define ephemeral_high g_ephemeral_high - #endif // MULTIPLE_HEAPS #ifdef TRACE_GC @@ -2192,6 +2173,52 @@ int log2(unsigned int n) return pos; } +#ifndef DACCESS_COMPILE + +void stomp_write_barrier_resize(bool is_runtime_suspended, bool requires_upper_bounds_check) +{ + WriteBarrierParameters args = {}; + args.operation = WriteBarrierOp::StompResize; + args.is_runtime_suspended = is_runtime_suspended; + args.requires_upper_bounds_check = requires_upper_bounds_check; + args.card_table = g_gc_card_table; + args.lowest_address = g_gc_lowest_address; + args.highest_address = g_gc_highest_address; +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + if (SoftwareWriteWatch::IsEnabledForGCHeap()) + { + args.write_watch_table = g_gc_sw_ww_table; + } +#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + GCToEEInterface::StompWriteBarrier(&args); +} + +void stomp_write_barrier_ephemeral(uint8_t* ephemeral_low, uint8_t* ephemeral_high) +{ + WriteBarrierParameters args = {}; + args.operation = WriteBarrierOp::StompEphemeral; + args.is_runtime_suspended = true; + args.ephemeral_low = ephemeral_low; + args.ephemeral_high = ephemeral_high; + GCToEEInterface::StompWriteBarrier(&args); +} + +void stomp_write_barrier_initialize() +{ + WriteBarrierParameters args = {}; + args.operation = WriteBarrierOp::Initialize; + args.is_runtime_suspended = true; + args.requires_upper_bounds_check = false; + args.card_table = g_gc_card_table; + args.lowest_address = g_gc_lowest_address; + args.highest_address = g_gc_highest_address; + args.ephemeral_low = reinterpret_cast(1); + args.ephemeral_high = reinterpret_cast(~0); + GCToEEInterface::StompWriteBarrier(&args); +} + +#endif // DACCESS_COMPILE + //extract the low bits [0,low[ of a uint32_t #define lowbits(wrd, bits) ((wrd) & ((1 << (bits))-1)) //extract the high bits [high, 32] of a uint32_t @@ -2397,6 +2424,10 @@ BOOL gc_heap::ro_segments_in_range; size_t gc_heap::gen0_big_free_spaces = 0; +uint8_t* gc_heap::ephemeral_low; + +uint8_t* gc_heap::ephemeral_high; + uint8_t* gc_heap::lowest_address; uint8_t* gc_heap::highest_address; @@ -3422,7 +3453,7 @@ inline size_t ro_seg_begin_index (heap_segment* seg) { size_t begin_index = (size_t)seg / gc_heap::min_segment_size; - begin_index = max (begin_index, (size_t)g_lowest_address / gc_heap::min_segment_size); + begin_index = max (begin_index, (size_t)g_gc_lowest_address / gc_heap::min_segment_size); return begin_index; } @@ -3430,14 +3461,14 @@ inline size_t ro_seg_end_index (heap_segment* seg) { size_t end_index = (size_t)(heap_segment_reserved (seg) - 1) / gc_heap::min_segment_size; - end_index = min (end_index, (size_t)g_highest_address / gc_heap::min_segment_size); + end_index = min (end_index, (size_t)g_gc_highest_address / gc_heap::min_segment_size); return end_index; } void seg_mapping_table_add_ro_segment (heap_segment* seg) { #ifdef GROWABLE_SEG_MAPPING_TABLE - if ((heap_segment_reserved (seg) <= g_lowest_address) || (heap_segment_mem (seg) >= g_highest_address)) + if ((heap_segment_reserved (seg) <= g_gc_lowest_address) || (heap_segment_mem (seg) >= g_gc_highest_address)) return; #endif //GROWABLE_SEG_MAPPING_TABLE @@ -3621,7 +3652,7 @@ gc_heap* seg_mapping_table_heap_of_worker (uint8_t* o) gc_heap* seg_mapping_table_heap_of (uint8_t* o) { #ifdef GROWABLE_SEG_MAPPING_TABLE - if ((o < g_lowest_address) || (o >= g_highest_address)) + if ((o < g_gc_lowest_address) || (o >= g_gc_highest_address)) return 0; #endif //GROWABLE_SEG_MAPPING_TABLE @@ -3631,7 +3662,7 @@ gc_heap* seg_mapping_table_heap_of (uint8_t* o) gc_heap* seg_mapping_table_heap_of_gc (uint8_t* o) { #if defined(FEATURE_BASICFREEZE) && defined(GROWABLE_SEG_MAPPING_TABLE) - if ((o < g_lowest_address) || (o >= g_highest_address)) + if ((o < g_gc_lowest_address) || (o >= g_gc_highest_address)) return 0; #endif //FEATURE_BASICFREEZE || GROWABLE_SEG_MAPPING_TABLE @@ -3643,7 +3674,7 @@ gc_heap* seg_mapping_table_heap_of_gc (uint8_t* o) heap_segment* seg_mapping_table_segment_of (uint8_t* o) { #if defined(FEATURE_BASICFREEZE) && defined(GROWABLE_SEG_MAPPING_TABLE) - if ((o < g_lowest_address) || (o >= g_highest_address)) + if ((o < g_gc_lowest_address) || (o >= g_gc_highest_address)) #ifdef FEATURE_BASICFREEZE return ro_segment_lookup (o); #else @@ -3686,7 +3717,7 @@ heap_segment* seg_mapping_table_segment_of (uint8_t* o) #ifdef FEATURE_BASICFREEZE // TODO: This was originally written assuming that the seg_mapping_table would always contain entries for ro - // segments whenever the ro segment falls into the [g_lowest_address,g_highest_address) range. I.e., it had an + // segments whenever the ro segment falls into the [g_gc_lowest_address,g_gc_highest_address) range. I.e., it had an // extra "&& (size_t)(entry->seg1) & ro_in_entry" expression. However, at the moment, grow_brick_card_table does // not correctly go through the ro segments and add them back to the seg_mapping_table when the [lowest,highest) // range changes. We should probably go ahead and modify grow_brick_card_table and put back the @@ -4086,8 +4117,8 @@ BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_h memory_details.current_block_normal = 0; memory_details.current_block_large = 0; - g_lowest_address = MAX_PTR; - g_highest_address = 0; + g_gc_lowest_address = MAX_PTR; + g_gc_highest_address = 0; if (((size_t)MAX_PTR - large_size) < normal_size) { @@ -4107,8 +4138,8 @@ BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_h uint8_t* allatonce_block = (uint8_t*)virtual_alloc (requestedMemory); if (allatonce_block) { - g_lowest_address = allatonce_block; - g_highest_address = allatonce_block + (memory_details.block_count * (large_size + normal_size)); + g_gc_lowest_address = allatonce_block; + g_gc_highest_address = allatonce_block + (memory_details.block_count * (large_size + normal_size)); memory_details.allocation_pattern = initial_memory_details::ALLATONCE; for(size_t i = 0; i < memory_details.block_count; i++) @@ -4131,8 +4162,8 @@ BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_h if (b2) { memory_details.allocation_pattern = initial_memory_details::TWO_STAGE; - g_lowest_address = min(b1,b2); - g_highest_address = max(b1 + memory_details.block_count*normal_size, + g_gc_lowest_address = min(b1,b2); + g_gc_highest_address = max(b1 + memory_details.block_count*normal_size, b2 + memory_details.block_count*large_size); for(size_t i = 0; i < memory_details.block_count; i++) { @@ -4178,10 +4209,10 @@ BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_h } else { - if (current_block->memory_base < g_lowest_address) - g_lowest_address = current_block->memory_base; - if (((uint8_t *) current_block->memory_base + block_size) > g_highest_address) - g_highest_address = (current_block->memory_base + block_size); + if (current_block->memory_base < g_gc_lowest_address) + g_gc_lowest_address = current_block->memory_base; + if (((uint8_t *) current_block->memory_base + block_size) > g_gc_highest_address) + g_gc_highest_address = (current_block->memory_base + block_size); } reserve_success = TRUE; } @@ -4288,7 +4319,7 @@ void* virtual_alloc (size_t size) flags = VirtualReserveFlags::WriteWatch; } #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP - void* prgmem = GCToOSInterface::VirtualReserve (0, requested_size, card_size * card_word_width, flags); + void* prgmem = GCToOSInterface::VirtualReserve (requested_size, card_size * card_word_width, flags); void *aligned_mem = prgmem; // We don't want (prgmem + size) to be right at the end of the address space @@ -4623,22 +4654,22 @@ gc_heap::get_segment (size_t size, BOOL loh_p) { uint8_t* start; uint8_t* end; - if (mem < g_lowest_address) + if (mem < g_gc_lowest_address) { start = (uint8_t*)mem; } else { - start = (uint8_t*)g_lowest_address; + start = (uint8_t*)g_gc_lowest_address; } - if (((uint8_t*)mem + size) > g_highest_address) + if (((uint8_t*)mem + size) > g_gc_highest_address) { end = (uint8_t*)mem + size; } else { - end = (uint8_t*)g_highest_address; + end = (uint8_t*)g_gc_highest_address; } if (gc_heap::grow_brick_card_tables (start, end, size, result, __this, loh_p) != 0) @@ -4703,10 +4734,7 @@ heap_segment* gc_heap::get_segment_for_loh (size_t size FireEtwGCCreateSegment_V1((size_t)heap_segment_mem(res), (size_t)(heap_segment_reserved (res) - heap_segment_mem(res)), ETW::GCLog::ETW_GC_INFO::LARGE_OBJECT_HEAP, GetClrInstanceId()); -#ifdef GC_PROFILING - if (CORProfilerTrackGC()) - UpdateGenerationBounds(); -#endif // GC_PROFILING + GCToEEInterface::DiagUpdateGenerationBounds(); #ifdef MULTIPLE_HEAPS hp->thread_loh_segment (res); @@ -5340,7 +5368,7 @@ heap_segment* gc_heap::segment_of (uint8_t* add, ptrdiff_t& delta, BOOL verify_p uint8_t* sadd = add; heap_segment* hs = 0; heap_segment* hs1 = 0; - if (!((add >= g_lowest_address) && (add < g_highest_address))) + if (!((add >= g_gc_lowest_address) && (add < g_gc_highest_address))) { delta = 0; return 0; @@ -5523,7 +5551,6 @@ public: saved_post_plug_reloc = temp; } -#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) void swap_pre_plug_and_saved_for_profiler() { gap_reloc_pair temp; @@ -5539,7 +5566,6 @@ public: memcpy (saved_post_plug_info_start, &saved_post_plug, sizeof (saved_post_plug)); saved_post_plug = temp; } -#endif //GC_PROFILING || //FEATURE_EVENT_TRACE // We should think about whether it's really necessary to have to copy back the pre plug // info since it was already copied during compacting plugs. But if a plug doesn't move @@ -6399,7 +6425,7 @@ void gc_heap::set_card (size_t card) inline void gset_card (size_t card) { - g_card_table [card_word (card)] |= (1 << card_bit (card)); + g_gc_card_table [card_word (card)] |= (1 << card_bit (card)); } inline @@ -6510,7 +6536,7 @@ size_t size_card_bundle_of (uint8_t* from, uint8_t* end) uint32_t* translate_card_bundle_table (uint32_t* cb) { - return (uint32_t*)((uint8_t*)cb - ((((size_t)g_lowest_address) / (card_size*card_word_width*card_bundle_size*card_bundle_word_width)) * sizeof (uint32_t))); + return (uint32_t*)((uint8_t*)cb - ((((size_t)g_gc_lowest_address) / (card_size*card_word_width*card_bundle_size*card_bundle_word_width)) * sizeof (uint32_t))); } void gc_heap::enable_card_bundles () @@ -6722,7 +6748,7 @@ size_t size_mark_array_of (uint8_t* from, uint8_t* end) // according to the lowest_address. uint32_t* translate_mark_array (uint32_t* ma) { - return (uint32_t*)((uint8_t*)ma - size_mark_array_of (0, g_lowest_address)); + return (uint32_t*)((uint8_t*)ma - size_mark_array_of (0, g_gc_lowest_address)); } // from and end must be page aligned addresses. @@ -6850,16 +6876,16 @@ void release_card_table (uint32_t* c_table) { destroy_card_table (c_table); // sever the link from the parent - if (&g_card_table[card_word (gcard_of(g_lowest_address))] == c_table) + if (&g_gc_card_table[card_word (gcard_of(g_gc_lowest_address))] == c_table) { - g_card_table = 0; + g_gc_card_table = 0; #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP SoftwareWriteWatch::StaticClose(); #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP } else { - uint32_t* p_table = &g_card_table[card_word (gcard_of(g_lowest_address))]; + uint32_t* p_table = &g_gc_card_table[card_word (gcard_of(g_gc_lowest_address))]; if (p_table) { while (p_table && (card_table_next (p_table) != c_table)) @@ -6881,8 +6907,8 @@ void destroy_card_table (uint32_t* c_table) uint32_t* gc_heap::make_card_table (uint8_t* start, uint8_t* end) { - assert (g_lowest_address == start); - assert (g_highest_address == end); + assert (g_gc_lowest_address == start); + assert (g_gc_highest_address == end); uint32_t virtual_reserve_flags = VirtualReserveFlags::None; @@ -6902,7 +6928,7 @@ uint32_t* gc_heap::make_card_table (uint8_t* start, uint8_t* end) if (can_use_write_watch_for_card_table()) { virtual_reserve_flags |= VirtualReserveFlags::WriteWatch; - cb = size_card_bundle_of (g_lowest_address, g_highest_address); + cb = size_card_bundle_of (g_gc_lowest_address, g_gc_highest_address); } #endif //CARD_BUNDLE @@ -6918,7 +6944,7 @@ uint32_t* gc_heap::make_card_table (uint8_t* start, uint8_t* end) #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP #ifdef GROWABLE_SEG_MAPPING_TABLE - size_t st = size_seg_mapping_table_of (g_lowest_address, g_highest_address); + size_t st = size_seg_mapping_table_of (g_gc_lowest_address, g_gc_highest_address); size_t st_table_offset = sizeof(card_table_info) + cs + bs + cb + wws; size_t st_table_offset_aligned = align_for_seg_mapping_table (st_table_offset); @@ -6932,7 +6958,7 @@ uint32_t* gc_heap::make_card_table (uint8_t* start, uint8_t* end) size_t alloc_size = sizeof (uint8_t)*(sizeof(card_table_info) + cs + bs + cb + wws + st + ms); size_t alloc_size_aligned = Align (alloc_size, g_SystemInfo.dwAllocationGranularity-1); - uint8_t* mem = (uint8_t*)GCToOSInterface::VirtualReserve (0, alloc_size_aligned, 0, virtual_reserve_flags); + uint8_t* mem = (uint8_t*)GCToOSInterface::VirtualReserve (alloc_size_aligned, 0, virtual_reserve_flags); if (!mem) return 0; @@ -6973,7 +6999,7 @@ uint32_t* gc_heap::make_card_table (uint8_t* start, uint8_t* end) #ifdef GROWABLE_SEG_MAPPING_TABLE seg_mapping_table = (seg_mapping*)(mem + st_table_offset_aligned); seg_mapping_table = (seg_mapping*)((uint8_t*)seg_mapping_table - - size_seg_mapping_table_of (0, (align_lower_segment (g_lowest_address)))); + size_seg_mapping_table_of (0, (align_lower_segment (g_gc_lowest_address)))); #endif //GROWABLE_SEG_MAPPING_TABLE #ifdef MARK_ARRAY @@ -7012,10 +7038,10 @@ int gc_heap::grow_brick_card_tables (uint8_t* start, gc_heap* hp, BOOL loh_p) { - uint8_t* la = g_lowest_address; - uint8_t* ha = g_highest_address; - uint8_t* saved_g_lowest_address = min (start, g_lowest_address); - uint8_t* saved_g_highest_address = max (end, g_highest_address); + uint8_t* la = g_gc_lowest_address; + uint8_t* ha = g_gc_highest_address; + uint8_t* saved_g_lowest_address = min (start, g_gc_lowest_address); + uint8_t* saved_g_highest_address = max (end, g_gc_highest_address); #ifdef BACKGROUND_GC // This value is only for logging purpose - it's not necessarily exactly what we // would commit for mark array but close enough for diagnostics purpose. @@ -7045,18 +7071,18 @@ int gc_heap::grow_brick_card_tables (uint8_t* start, #endif // BIT64 ps *= 2; - if (saved_g_lowest_address < g_lowest_address) + if (saved_g_lowest_address < g_gc_lowest_address) { - if (ps > (size_t)g_lowest_address) + if (ps > (size_t)g_gc_lowest_address) saved_g_lowest_address = (uint8_t*)OS_PAGE_SIZE; else { - assert (((size_t)g_lowest_address - ps) >= OS_PAGE_SIZE); - saved_g_lowest_address = min (saved_g_lowest_address, (g_lowest_address - ps)); + assert (((size_t)g_gc_lowest_address - ps) >= OS_PAGE_SIZE); + saved_g_lowest_address = min (saved_g_lowest_address, (g_gc_lowest_address - ps)); } } - if (saved_g_highest_address > g_highest_address) + if (saved_g_highest_address > g_gc_highest_address) { saved_g_highest_address = max ((saved_g_lowest_address + ps), saved_g_highest_address); if (saved_g_highest_address > top) @@ -7069,7 +7095,7 @@ int gc_heap::grow_brick_card_tables (uint8_t* start, bool write_barrier_updated = false; uint32_t virtual_reserve_flags = VirtualReserveFlags::None; - uint32_t* saved_g_card_table = g_card_table; + uint32_t* saved_g_card_table = g_gc_card_table; uint32_t* ct = 0; uint32_t* translated_ct = 0; short* bt = 0; @@ -7125,7 +7151,7 @@ int gc_heap::grow_brick_card_tables (uint8_t* start, dprintf (GC_TABLE_LOG, ("card table: %Id; brick table: %Id; card bundle: %Id; sw ww table: %Id; seg table: %Id; mark array: %Id", cs, bs, cb, wws, st, ms)); - uint8_t* mem = (uint8_t*)GCToOSInterface::VirtualReserve (0, alloc_size_aligned, 0, virtual_reserve_flags); + uint8_t* mem = (uint8_t*)GCToOSInterface::VirtualReserve (alloc_size_aligned, 0, virtual_reserve_flags); if (!mem) { @@ -7152,7 +7178,7 @@ int gc_heap::grow_brick_card_tables (uint8_t* start, card_table_refcount (ct) = 0; card_table_lowest_address (ct) = saved_g_lowest_address; card_table_highest_address (ct) = saved_g_highest_address; - card_table_next (ct) = &g_card_table[card_word (gcard_of (la))]; + card_table_next (ct) = &g_gc_card_table[card_word (gcard_of (la))]; //clear the card table /* @@ -7179,9 +7205,9 @@ int gc_heap::grow_brick_card_tables (uint8_t* start, seg_mapping* new_seg_mapping_table = (seg_mapping*)(mem + st_table_offset_aligned); new_seg_mapping_table = (seg_mapping*)((uint8_t*)new_seg_mapping_table - size_seg_mapping_table_of (0, (align_lower_segment (saved_g_lowest_address)))); - memcpy(&new_seg_mapping_table[seg_mapping_word_of(g_lowest_address)], - &seg_mapping_table[seg_mapping_word_of(g_lowest_address)], - size_seg_mapping_table_of(g_lowest_address, g_highest_address)); + memcpy(&new_seg_mapping_table[seg_mapping_word_of(g_gc_lowest_address)], + &seg_mapping_table[seg_mapping_word_of(g_gc_lowest_address)], + size_seg_mapping_table_of(g_gc_lowest_address, g_gc_highest_address)); seg_mapping_table = new_seg_mapping_table; } @@ -7243,13 +7269,12 @@ int gc_heap::grow_brick_card_tables (uint8_t* start, // Note on points where the runtime is suspended anywhere in this function. Upon an attempt to suspend the // runtime, a different thread may suspend first, causing this thread to block at the point of the suspend call. // So, at any suspend point, externally visible state needs to be consistent, as code that depends on that state - // may run while this thread is blocked. This includes updates to g_card_table, g_lowest_address, and - // g_highest_address. + // may run while this thread is blocked. This includes updates to g_gc_card_table, g_gc_lowest_address, and + // g_gc_highest_address. suspend_EE(); } - g_card_table = translated_ct; - + g_gc_card_table = translated_ct; SoftwareWriteWatch::SetResizedUntranslatedTable( mem + sw_ww_table_offset, saved_g_lowest_address, @@ -7260,7 +7285,9 @@ int gc_heap::grow_brick_card_tables (uint8_t* start, // grow version of the write barrier. This test tells us if the new // segment was allocated at a lower address than the old, requiring // that we start doing an upper bounds check in the write barrier. - StompWriteBarrierResize(true, la != saved_g_lowest_address); + g_gc_lowest_address = saved_g_lowest_address; + g_gc_highest_address = saved_g_highest_address; + stomp_write_barrier_resize(true, la != saved_g_lowest_address); write_barrier_updated = true; if (!is_runtime_suspended) @@ -7271,9 +7298,12 @@ int gc_heap::grow_brick_card_tables (uint8_t* start, else #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP { - g_card_table = translated_ct; + g_gc_card_table = translated_ct; } + g_gc_lowest_address = saved_g_lowest_address; + g_gc_highest_address = saved_g_highest_address; + if (!write_barrier_updated) { // This passes a bool telling whether we need to switch to the post @@ -7284,19 +7314,9 @@ int gc_heap::grow_brick_card_tables (uint8_t* start, // to be changed, so we are doing this after all global state has // been updated. See the comment above suspend_EE() above for more // info. - StompWriteBarrierResize(!!IsGCThread(), la != saved_g_lowest_address); + stomp_write_barrier_resize(!!IsGCThread(), la != saved_g_lowest_address); } - // We need to make sure that other threads executing checked write barriers - // will see the g_card_table update before g_lowest/highest_address updates. - // Otherwise, the checked write barrier may AV accessing the old card table - // with address that it does not cover. Write barriers access card table - // without memory barriers for performance reasons, so we need to flush - // the store buffers here. - GCToOSInterface::FlushProcessWriteBuffers(); - - g_lowest_address = saved_g_lowest_address; - VolatileStore(&g_highest_address, saved_g_highest_address); return 0; @@ -7305,7 +7325,7 @@ fail: if (mem) { - assert(g_card_table == saved_g_card_table); + assert(g_gc_card_table == saved_g_card_table); //delete (uint32_t*)((uint8_t*)ct - sizeof(card_table_info)); if (!GCToOSInterface::VirtualRelease (mem, alloc_size_aligned)) @@ -7463,7 +7483,7 @@ void gc_heap::copy_brick_card_table() assert (ha == card_table_highest_address (&old_card_table[card_word (card_of (la))])); /* todo: Need a global lock for this */ - uint32_t* ct = &g_card_table[card_word (gcard_of (g_lowest_address))]; + uint32_t* ct = &g_gc_card_table[card_word (gcard_of (g_gc_lowest_address))]; own_card_table (ct); card_table = translate_card_table (ct); /* End of global lock */ @@ -7476,8 +7496,8 @@ void gc_heap::copy_brick_card_table() if (gc_can_use_concurrent) { mark_array = translate_mark_array (card_table_mark_array (ct)); - assert (mark_word_of (g_highest_address) == - mark_word_of (align_on_mark_word (g_highest_address))); + assert (mark_word_of (g_gc_highest_address) == + mark_word_of (align_on_mark_word (g_gc_highest_address))); } else mark_array = NULL; @@ -7486,13 +7506,13 @@ void gc_heap::copy_brick_card_table() #ifdef CARD_BUNDLE #if defined(MARK_ARRAY) && defined(_DEBUG) #ifdef GROWABLE_SEG_MAPPING_TABLE - size_t st = size_seg_mapping_table_of (g_lowest_address, g_highest_address); + size_t st = size_seg_mapping_table_of (g_gc_lowest_address, g_gc_highest_address); #else //GROWABLE_SEG_MAPPING_TABLE size_t st = 0; #endif //GROWABLE_SEG_MAPPING_TABLE #endif //MARK_ARRAY && _DEBUG card_bundle_table = translate_card_bundle_table (card_table_card_bundle_table (ct)); - assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_lowest_address))))] == + assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_gc_lowest_address))))] == card_table_card_bundle_table (ct)); //set the card table if we are in a heap growth scenario @@ -9330,13 +9350,13 @@ void gc_heap::update_card_table_bundle() bool success = GCToOSInterface::GetWriteWatch (false /* resetState */ , base_address, region_size, (void**)g_addresses, &bcount); - assert (success); + assert (success && "GetWriteWatch failed!"); dprintf (3,("Found %d pages written", bcount)); for (unsigned i = 0; i < bcount; i++) { size_t bcardw = (uint32_t*)(max(g_addresses[i],base_address)) - &card_table[0]; size_t ecardw = (uint32_t*)(min(g_addresses[i]+OS_PAGE_SIZE, high_address)) - &card_table[0]; - assert (bcardw >= card_word (card_of (g_lowest_address))); + assert (bcardw >= card_word (card_of (g_gc_lowest_address))); card_bundles_set (cardw_card_bundle (bcardw), cardw_card_bundle (align_cardw_on_bundle (ecardw))); @@ -9639,7 +9659,7 @@ void gc_heap::make_generation (generation& gen, heap_segment* seg, uint8_t* star #endif //FREE_USAGE_STATS } -void gc_heap::adjust_ephemeral_limits (bool is_runtime_suspended) +void gc_heap::adjust_ephemeral_limits () { ephemeral_low = generation_allocation_start (generation_of (max_generation - 1)); ephemeral_high = heap_segment_reserved (ephemeral_heap_segment); @@ -9647,8 +9667,10 @@ void gc_heap::adjust_ephemeral_limits (bool is_runtime_suspended) dprintf (3, ("new ephemeral low: %Ix new ephemeral high: %Ix", (size_t)ephemeral_low, (size_t)ephemeral_high)) +#ifndef MULTIPLE_HEAPS // This updates the write barrier helpers with the new info. - StompWriteBarrierEphemeral(is_runtime_suspended); + stomp_write_barrier_ephemeral(ephemeral_low, ephemeral_high); +#endif // MULTIPLE_HEAPS } #if defined(TRACE_GC) || defined(GC_CONFIG_DRIVEN) @@ -9821,9 +9843,9 @@ HRESULT gc_heap::initialize_gc (size_t segment_size, settings.first_init(); - g_card_table = make_card_table (g_lowest_address, g_highest_address); + g_gc_card_table = make_card_table (g_gc_lowest_address, g_gc_highest_address); - if (!g_card_table) + if (!g_gc_card_table) return E_OUTOFMEMORY; gc_started = FALSE; @@ -10306,7 +10328,7 @@ gc_heap::init_gc_heap (int h_number) #endif //MULTIPLE_HEAPS /* todo: Need a global lock for this */ - uint32_t* ct = &g_card_table [card_word (card_of (g_lowest_address))]; + uint32_t* ct = &g_gc_card_table [card_word (card_of (g_gc_lowest_address))]; own_card_table (ct); card_table = translate_card_table (ct); /* End of global lock */ @@ -10317,13 +10339,13 @@ gc_heap::init_gc_heap (int h_number) #ifdef CARD_BUNDLE card_bundle_table = translate_card_bundle_table (card_table_card_bundle_table (ct)); - assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_lowest_address))))] == + assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_gc_lowest_address))))] == card_table_card_bundle_table (ct)); #endif //CARD_BUNDLE #ifdef MARK_ARRAY if (gc_can_use_concurrent) - mark_array = translate_mark_array (card_table_mark_array (&g_card_table[card_word (card_of (g_lowest_address))])); + mark_array = translate_mark_array (card_table_mark_array (&g_gc_card_table[card_word (card_of (g_gc_lowest_address))])); else mark_array = NULL; #endif //MARK_ARRAY @@ -10360,6 +10382,7 @@ gc_heap::init_gc_heap (int h_number) (size_t)(heap_segment_reserved (lseg) - heap_segment_mem(lseg)), ETW::GCLog::ETW_GC_INFO::LARGE_OBJECT_HEAP, GetClrInstanceId()); + #ifdef SEG_MAPPING_TABLE seg_mapping_table_add_segment (lseg, __this); #else //SEG_MAPPING_TABLE @@ -10442,7 +10465,7 @@ gc_heap::init_gc_heap (int h_number) make_background_mark_stack (b_arr); #endif //BACKGROUND_GC - adjust_ephemeral_limits(true); + adjust_ephemeral_limits(); #ifdef MARK_ARRAY // why would we clear the mark array for this page? it should be cleared.. @@ -13043,12 +13066,12 @@ int gc_heap::try_allocate_more_space (alloc_context* acontext, size_t size, if (can_allocate) { - //ETW trace for allocation tick size_t alloc_context_bytes = acontext->alloc_limit + Align (min_obj_size, align_const) - acontext->alloc_ptr; int etw_allocation_index = ((gen_number == 0) ? 0 : 1); etw_allocation_running_amount[etw_allocation_index] += alloc_context_bytes; + if (etw_allocation_running_amount[etw_allocation_index] > etw_allocation_tick) { #ifdef FEATURE_REDHAWK @@ -14785,6 +14808,9 @@ int gc_heap::generation_to_condemn (int n_initial, dprintf (GTC_LOG, ("h%d: alloc full - BLOCK", heap_number)); n = max_generation; *blocking_collection_p = TRUE; + if ((local_settings->reason == reason_oos_loh) || + (local_settings->reason == reason_alloc_loh)) + evaluate_elevation = FALSE; local_condemn_reasons->set_condition (gen_before_oom); } @@ -15183,7 +15209,7 @@ void gc_heap::gc1() vm_heap->GcCondemnedGeneration = settings.condemned_generation; - assert (g_card_table == card_table); + assert (g_gc_card_table == card_table); { if (n == max_generation) @@ -15337,7 +15363,11 @@ void gc_heap::gc1() if (!settings.concurrent) #endif //BACKGROUND_GC { - adjust_ephemeral_limits(!!IsGCThread()); +#ifndef FEATURE_REDHAWK + // IsGCThread() always returns false on CoreRT, but this assert is useful in CoreCLR. + assert(!!IsGCThread()); +#endif // FEATURE_REDHAWK + adjust_ephemeral_limits(); } #ifdef BACKGROUND_GC @@ -15472,7 +15502,15 @@ void gc_heap::gc1() #ifdef FEATURE_EVENT_TRACE if (bgc_heap_walk_for_etw_p && settings.concurrent) { - make_free_lists_for_profiler_for_bgc(); + GCToEEInterface::DiagWalkBGCSurvivors(__this); + +#ifdef MULTIPLE_HEAPS + bgc_t_join.join(this, gc_join_after_profiler_heap_walk); + if (bgc_t_join.joined()) + { + bgc_t_join.restart(); + } +#endif // MULTIPLE_HEAPS } #endif // FEATURE_EVENT_TRACE #endif //BACKGROUND_GC @@ -16169,7 +16207,11 @@ BOOL gc_heap::expand_soh_with_minimal_gc() dd_gc_new_allocation (dynamic_data_of (max_generation)) -= ephemeral_size; dd_new_allocation (dynamic_data_of (max_generation)) = dd_gc_new_allocation (dynamic_data_of (max_generation)); - adjust_ephemeral_limits(!!IsGCThread()); +#ifndef FEATURE_REDHAWK + // IsGCThread() always returns false on CoreRT, but this assert is useful in CoreCLR. + assert(!!IsGCThread()); +#endif // FEATURE_REDHAWK + adjust_ephemeral_limits(); return TRUE; } else @@ -16382,7 +16424,7 @@ int gc_heap::garbage_collect (int n) for (int i = 0; i < n_heaps; i++) { //copy the card and brick tables - if (g_card_table != g_heaps[i]->card_table) + if (g_gc_card_table != g_heaps[i]->card_table) { g_heaps[i]->copy_brick_card_table(); } @@ -16406,100 +16448,67 @@ int gc_heap::garbage_collect (int n) } #endif //BACKGROUND_GC // check for card table growth - if (g_card_table != card_table) + if (g_gc_card_table != card_table) copy_brick_card_table(); #endif //MULTIPLE_HEAPS - BOOL should_evaluate_elevation = FALSE; - BOOL should_do_blocking_collection = FALSE; + BOOL should_evaluate_elevation = FALSE; + BOOL should_do_blocking_collection = FALSE; #ifdef MULTIPLE_HEAPS - int gen_max = condemned_generation_num; - for (int i = 0; i < n_heaps; i++) - { - if (gen_max < g_heaps[i]->condemned_generation_num) - gen_max = g_heaps[i]->condemned_generation_num; - if ((!should_evaluate_elevation) && (g_heaps[i]->elevation_requested)) - should_evaluate_elevation = TRUE; - if ((!should_do_blocking_collection) && (g_heaps[i]->blocking_collection)) - should_do_blocking_collection = TRUE; - } + int gen_max = condemned_generation_num; + for (int i = 0; i < n_heaps; i++) + { + if (gen_max < g_heaps[i]->condemned_generation_num) + gen_max = g_heaps[i]->condemned_generation_num; + if ((!should_evaluate_elevation) && (g_heaps[i]->elevation_requested)) + should_evaluate_elevation = TRUE; + if ((!should_do_blocking_collection) && (g_heaps[i]->blocking_collection)) + should_do_blocking_collection = TRUE; + } - settings.condemned_generation = gen_max; -//logically continues after GC_PROFILING. + settings.condemned_generation = gen_max; #else //MULTIPLE_HEAPS - settings.condemned_generation = generation_to_condemn (n, - &blocking_collection, - &elevation_requested, - FALSE); - should_evaluate_elevation = elevation_requested; - should_do_blocking_collection = blocking_collection; -#endif //MULTIPLE_HEAPS - - settings.condemned_generation = joined_generation_to_condemn ( - should_evaluate_elevation, - settings.condemned_generation, - &should_do_blocking_collection - STRESS_HEAP_ARG(n) - ); + settings.condemned_generation = generation_to_condemn (n, + &blocking_collection, + &elevation_requested, + FALSE); + should_evaluate_elevation = elevation_requested; + should_do_blocking_collection = blocking_collection; +#endif //MULTIPLE_HEAPS + + settings.condemned_generation = joined_generation_to_condemn ( + should_evaluate_elevation, + settings.condemned_generation, + &should_do_blocking_collection + STRESS_HEAP_ARG(n) + ); - STRESS_LOG1(LF_GCROOTS|LF_GC|LF_GCALLOC, LL_INFO10, - "condemned generation num: %d\n", settings.condemned_generation); + STRESS_LOG1(LF_GCROOTS|LF_GC|LF_GCALLOC, LL_INFO10, + "condemned generation num: %d\n", settings.condemned_generation); - record_gcs_during_no_gc(); + record_gcs_during_no_gc(); - if (settings.condemned_generation > 1) - settings.promotion = TRUE; + if (settings.condemned_generation > 1) + settings.promotion = TRUE; #ifdef HEAP_ANALYZE - // At this point we've decided what generation is condemned - // See if we've been requested to analyze survivors after the mark phase - if (AnalyzeSurvivorsRequested(settings.condemned_generation)) - { - heap_analyze_enabled = TRUE; - } -#endif // HEAP_ANALYZE - -#ifdef GC_PROFILING - - // If we're tracking GCs, then we need to walk the first generation - // before collection to track how many items of each class has been - // allocated. - UpdateGenerationBounds(); - GarbageCollectionStartedCallback(settings.condemned_generation, settings.reason == reason_induced); + // At this point we've decided what generation is condemned + // See if we've been requested to analyze survivors after the mark phase + if (AnalyzeSurvivorsRequested(settings.condemned_generation)) { - BEGIN_PIN_PROFILER(CORProfilerTrackGC()); - size_t profiling_context = 0; - -#ifdef MULTIPLE_HEAPS - int hn = 0; - for (hn = 0; hn < gc_heap::n_heaps; hn++) - { - gc_heap* hp = gc_heap::g_heaps [hn]; - - // When we're walking objects allocated by class, then we don't want to walk the large - // object heap because then it would count things that may have been around for a while. - hp->walk_heap (&AllocByClassHelper, (void *)&profiling_context, 0, FALSE); - } -#else - // When we're walking objects allocated by class, then we don't want to walk the large - // object heap because then it would count things that may have been around for a while. - gc_heap::walk_heap (&AllocByClassHelper, (void *)&profiling_context, 0, FALSE); -#endif //MULTIPLE_HEAPS - - // Notify that we've reached the end of the Gen 0 scan - g_profControlBlock.pProfInterface->EndAllocByClass(&profiling_context); - END_PIN_PROFILER(); + heap_analyze_enabled = TRUE; } +#endif // HEAP_ANALYZE -#endif // GC_PROFILING + GCToEEInterface::DiagGCStart(settings.condemned_generation, settings.reason == reason_induced); #ifdef BACKGROUND_GC if ((settings.condemned_generation == max_generation) && (recursive_gc_sync::background_running_p())) { - //TODO BACKGROUND_GC If we just wait for the end of gc, it won't woork + //TODO BACKGROUND_GC If we just wait for the end of gc, it won't work // because we have to collect 0 and 1 properly // in particular, the allocation contexts are gone. // For now, it is simpler to collect max_generation-1 @@ -19625,12 +19634,7 @@ void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p) dprintf (3, ("Finalize marking")); finalize_queue->ScanForFinalization (GCHeap::Promote, condemned_gen_number, mark_only_p, __this); -#ifdef GC_PROFILING - if (CORProfilerTrackGC()) - { - finalize_queue->WalkFReachableObjects (__this); - } -#endif //GC_PROFILING + GCToEEInterface::DiagWalkFReachableObjects(__this); #endif // FEATURE_PREMORTEM_FINALIZATION // Scan dependent handles again to promote any secondaries associated with primaries that were promoted @@ -21105,8 +21109,7 @@ void gc_heap::relocate_in_loh_compact() generation_free_obj_space (gen))); } -#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) -void gc_heap::walk_relocation_loh (size_t profiling_context) +void gc_heap::walk_relocation_for_loh (size_t profiling_context, record_surv_fn fn) { generation* gen = large_object_generation; heap_segment* seg = heap_segment_rw (generation_start_segment (gen)); @@ -21136,14 +21139,7 @@ void gc_heap::walk_relocation_loh (size_t profiling_context) STRESS_LOG_PLUG_MOVE(o, (o + size), -reloc); - { - ETW::GCLog::MovedReference( - o, - (o + size), - reloc, - profiling_context, - settings.compaction); - } + fn (o, (o + size), reloc, profiling_context, settings.compaction, FALSE); o = o + size; if (o < heap_segment_allocated (seg)) @@ -21160,7 +21156,6 @@ void gc_heap::walk_relocation_loh (size_t profiling_context) } } } -#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) BOOL gc_heap::loh_object_p (uint8_t* o) { @@ -22318,10 +22313,7 @@ void gc_heap::plan_phase (int condemned_gen_number) if (!loh_compacted_p) #endif //FEATURE_LOH_COMPACTION { -#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) - if (ShouldTrackMovementForProfilerOrEtw()) - notify_profiler_of_surviving_large_objects(); -#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) + GCToEEInterface::DiagWalkLOHSurvivors(__this); sweep_large_objects(); } } @@ -22432,7 +22424,7 @@ void gc_heap::plan_phase (int condemned_gen_number) for (i = 0; i < n_heaps; i++) { //copy the card and brick tables - if (g_card_table!= g_heaps[i]->card_table) + if (g_gc_card_table!= g_heaps[i]->card_table) { g_heaps[i]->copy_brick_card_table(); } @@ -22523,12 +22515,7 @@ void gc_heap::plan_phase (int condemned_gen_number) assert (generation_allocation_segment (consing_gen) == ephemeral_heap_segment); -#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) - if (ShouldTrackMovementForProfilerOrEtw()) - { - record_survived_for_profiler(condemned_gen_number, first_condemned_address); - } -#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) + GCToEEInterface::DiagWalkSurvivors(__this); relocate_phase (condemned_gen_number, first_condemned_address); compact_phase (condemned_gen_number, first_condemned_address, @@ -22738,12 +22725,7 @@ void gc_heap::plan_phase (int condemned_gen_number) fix_older_allocation_area (older_gen); } -#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) - if (ShouldTrackMovementForProfilerOrEtw()) - { - record_survived_for_profiler(condemned_gen_number, first_condemned_address); - } -#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) + GCToEEInterface::DiagWalkSurvivors(__this); gen0_big_free_spaces = 0; make_free_lists (condemned_gen_number); @@ -23949,8 +23931,7 @@ void gc_heap::relocate_survivors (int condemned_gen_number, } } -#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) -void gc_heap::walk_plug (uint8_t* plug, size_t size, BOOL check_last_object_p, walk_relocate_args* args, size_t profiling_context) +void gc_heap::walk_plug (uint8_t* plug, size_t size, BOOL check_last_object_p, walk_relocate_args* args) { if (check_last_object_p) { @@ -23970,15 +23951,10 @@ void gc_heap::walk_plug (uint8_t* plug, size_t size, BOOL check_last_object_p, w } ptrdiff_t last_plug_relocation = node_relocation_distance (plug); - ptrdiff_t reloc = settings.compaction ? last_plug_relocation : 0; - STRESS_LOG_PLUG_MOVE(plug, (plug + size), -last_plug_relocation); + ptrdiff_t reloc = settings.compaction ? last_plug_relocation : 0; - ETW::GCLog::MovedReference(plug, - (plug + size), - reloc, - profiling_context, - settings.compaction); + (args->fn) (plug, (plug + size), reloc, args->profiling_context, settings.compaction, FALSE); if (check_last_object_p) { @@ -23995,12 +23971,12 @@ void gc_heap::walk_plug (uint8_t* plug, size_t size, BOOL check_last_object_p, w } } -void gc_heap::walk_relocation_in_brick (uint8_t* tree, walk_relocate_args* args, size_t profiling_context) +void gc_heap::walk_relocation_in_brick (uint8_t* tree, walk_relocate_args* args) { assert ((tree != NULL)); if (node_left_child (tree)) { - walk_relocation_in_brick (tree + node_left_child (tree), args, profiling_context); + walk_relocation_in_brick (tree + node_left_child (tree), args); } uint8_t* plug = tree; @@ -24029,7 +24005,7 @@ void gc_heap::walk_relocation_in_brick (uint8_t* tree, walk_relocate_args* args, assert (last_plug_size >= Align (min_obj_size)); } - walk_plug (args->last_plug, last_plug_size, check_last_object_p, args, profiling_context); + walk_plug (args->last_plug, last_plug_size, check_last_object_p, args); } else { @@ -24042,18 +24018,14 @@ void gc_heap::walk_relocation_in_brick (uint8_t* tree, walk_relocate_args* args, if (node_right_child (tree)) { - walk_relocation_in_brick (tree + node_right_child (tree), args, profiling_context); - + walk_relocation_in_brick (tree + node_right_child (tree), args); } } -void gc_heap::walk_relocation (int condemned_gen_number, - uint8_t* first_condemned_address, - size_t profiling_context) - +void gc_heap::walk_relocation (size_t profiling_context, record_surv_fn fn) { - generation* condemned_gen = generation_of (condemned_gen_number); - uint8_t* start_address = first_condemned_address; + generation* condemned_gen = generation_of (settings.condemned_generation); + uint8_t* start_address = generation_allocation_start (condemned_gen); size_t current_brick = brick_of (start_address); heap_segment* current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen)); @@ -24066,6 +24038,8 @@ void gc_heap::walk_relocation (int condemned_gen_number, args.is_shortened = FALSE; args.pinned_plug_entry = 0; args.last_plug = 0; + args.profiling_context = profiling_context; + args.fn = fn; while (1) { @@ -24075,8 +24049,8 @@ void gc_heap::walk_relocation (int condemned_gen_number, { walk_plug (args.last_plug, (heap_segment_allocated (current_heap_segment) - args.last_plug), - args.is_shortened, - &args, profiling_context); + args.is_shortened, + &args); args.last_plug = 0; } if (heap_segment_next_rw (current_heap_segment)) @@ -24097,16 +24071,29 @@ void gc_heap::walk_relocation (int condemned_gen_number, { walk_relocation_in_brick (brick_address (current_brick) + brick_entry - 1, - &args, - profiling_context); + &args); } } current_brick++; } } +void gc_heap::walk_survivors (record_surv_fn fn, size_t context, walk_surv_type type) +{ + if (type == walk_for_gc) + walk_survivors_relocation (context, fn); #if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE) -void gc_heap::walk_relocation_for_bgc(size_t profiling_context) + else if (type == walk_for_bgc) + walk_survivors_for_bgc (context, fn); +#endif //BACKGROUND_GC && FEATURE_EVENT_TRACE + else if (type == walk_for_loh) + walk_survivors_for_loh (context, fn); + else + assert (!"unknown type!"); +} + +#if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE) +void gc_heap::walk_survivors_for_bgc (size_t profiling_context, record_surv_fn fn) { // This should only be called for BGCs assert(settings.concurrent); @@ -24140,8 +24127,7 @@ void gc_heap::walk_relocation_for_bgc(size_t profiling_context) uint8_t* end = heap_segment_allocated (seg); while (o < end) - { - + { if (method_table(o) == g_pFreeObjectMethodTable) { o += Align (size (o), align_const); @@ -24164,51 +24150,18 @@ void gc_heap::walk_relocation_for_bgc(size_t profiling_context) uint8_t* plug_end = o; - // Note on last parameter: since this is for bgc, only ETW - // should be sending these events so that existing profapi profilers - // don't get confused. - ETW::GCLog::MovedReference( - plug_start, + fn (plug_start, plug_end, 0, // Reloc distance == 0 as this is non-compacting profiling_context, FALSE, // Non-compacting - FALSE); // fAllowProfApiNotification + TRUE); // BGC } seg = heap_segment_next (seg); } } - -void gc_heap::make_free_lists_for_profiler_for_bgc () -{ - assert(settings.concurrent); - - size_t profiling_context = 0; - ETW::GCLog::BeginMovedReferences(&profiling_context); - - // This provides the profiler with information on what blocks of - // memory are moved during a gc. - - walk_relocation_for_bgc(profiling_context); - - // Notify the EE-side profiling code that all the references have been traced for - // this heap, and that it needs to flush all cached data it hasn't sent to the - // profiler and release resources it no longer needs. Since this is for bgc, only - // ETW should be sending these events so that existing profapi profilers don't get confused. - ETW::GCLog::EndMovedReferences(profiling_context, FALSE /* fAllowProfApiNotification */); - -#ifdef MULTIPLE_HEAPS - bgc_t_join.join(this, gc_join_after_profiler_heap_walk); - if (bgc_t_join.joined()) - { - bgc_t_join.restart(); - } -#endif // MULTIPLE_HEAPS -} - #endif // defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE) -#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) void gc_heap::relocate_phase (int condemned_gen_number, uint8_t* first_condemned_address) @@ -24809,7 +24762,7 @@ void gc_heap::compact_phase (int condemned_gen_number, #pragma warning(push) #pragma warning(disable:4702) // C4702: unreachable code: gc_thread_function may not return #endif //_MSC_VER -void __stdcall gc_heap::gc_thread_stub (void* arg) +void gc_heap::gc_thread_stub (void* arg) { ClrFlsSetThreadType (ThreadType_GC); STRESS_LOG_RESERVE_MEM (GC_STRESSLOG_MULTIPLY); @@ -25177,14 +25130,14 @@ BOOL gc_heap::commit_mark_array_new_seg (gc_heap* hp, if (new_card_table == 0) { - new_card_table = g_card_table; + new_card_table = g_gc_card_table; } if (hp->card_table != new_card_table) { if (new_lowest_address == 0) { - new_lowest_address = g_lowest_address; + new_lowest_address = g_gc_lowest_address; } uint32_t* ct = &new_card_table[card_word (gcard_of (new_lowest_address))]; @@ -29174,7 +29127,7 @@ generation* gc_heap::expand_heap (int condemned_generation, return consing_gen; //copy the card and brick tables - if (g_card_table!= card_table) + if (g_gc_card_table!= card_table) copy_brick_card_table(); BOOL new_segment_p = (heap_segment_next (new_seg) == 0); @@ -30619,35 +30572,21 @@ BOOL gc_heap::large_object_marked (uint8_t* o, BOOL clearp) return m; } -#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) -void gc_heap::record_survived_for_profiler(int condemned_gen_number, uint8_t * start_address) +void gc_heap::walk_survivors_relocation (size_t profiling_context, record_surv_fn fn) { - size_t profiling_context = 0; - - ETW::GCLog::BeginMovedReferences(&profiling_context); - // Now walk the portion of memory that is actually being relocated. - walk_relocation(condemned_gen_number, start_address, profiling_context); + walk_relocation (profiling_context, fn); #ifdef FEATURE_LOH_COMPACTION if (loh_compacted_p) { - walk_relocation_loh (profiling_context); + walk_relocation_for_loh (profiling_context, fn); } #endif //FEATURE_LOH_COMPACTION - - // Notify the EE-side profiling code that all the references have been traced for - // this heap, and that it needs to flush all cached data it hasn't sent to the - // profiler and release resources it no longer needs. - ETW::GCLog::EndMovedReferences(profiling_context); } -void gc_heap::notify_profiler_of_surviving_large_objects () +void gc_heap::walk_survivors_for_loh (size_t profiling_context, record_surv_fn fn) { - size_t profiling_context = 0; - - ETW::GCLog::BeginMovedReferences(&profiling_context); - generation* gen = large_object_generation; heap_segment* seg = heap_segment_rw (generation_start_segment (gen));; @@ -30657,13 +30596,6 @@ void gc_heap::notify_profiler_of_surviving_large_objects () uint8_t* plug_end = o; uint8_t* plug_start = o; - // Generally, we can only get here if this is TRUE: - // (CORProfilerTrackGC() || ETW::GCLog::ShouldTrackMovementForEtw()) - // But we can't always assert that, as races could theoretically cause GC profiling - // or ETW to turn off just before we get here. This is harmless (we do checks later - // on, under appropriate locks, before actually calling into profilers), though it's - // a slowdown to determine these plugs for nothing. - while (1) { if (o >= heap_segment_allocated (seg)) @@ -30691,12 +30623,7 @@ void gc_heap::notify_profiler_of_surviving_large_objects () plug_end = o; - ETW::GCLog::MovedReference( - plug_start, - plug_end, - 0, - profiling_context, - FALSE); + fn (plug_start, plug_end, 0, profiling_context, FALSE, FALSE); } else { @@ -30706,9 +30633,7 @@ void gc_heap::notify_profiler_of_surviving_large_objects () } } } - ETW::GCLog::EndMovedReferences(profiling_context); } -#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) #ifdef BACKGROUND_GC @@ -31940,7 +31865,6 @@ void gc_heap::descr_card_table () void gc_heap::descr_generations_to_profiler (gen_walk_fn fn, void *context) { -#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) #ifdef MULTIPLE_HEAPS int n_heaps = g_theGCHeap->GetNumberOfHeaps (); for (int i = 0; i < n_heaps; i++) @@ -32018,7 +31942,6 @@ void gc_heap::descr_generations_to_profiler (gen_walk_fn fn, void *context) curr_gen_number0--; } } -#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) } #ifdef TRACE_GC @@ -32514,7 +32437,7 @@ void gc_heap::clear_all_mark_array() void gc_heap::verify_mark_array_cleared (heap_segment* seg) { #if defined (VERIFY_HEAP) && defined (MARK_ARRAY) - assert (card_table == g_card_table); + assert (card_table == g_gc_card_table); size_t markw = mark_word_of (heap_segment_mem (seg)); size_t markw_end = mark_word_of (heap_segment_reserved (seg)); @@ -32862,8 +32785,8 @@ gc_heap::verify_heap (BOOL begin_gc_p) #endif //BACKGROUND_GC #ifndef MULTIPLE_HEAPS - if ((g_ephemeral_low != generation_allocation_start (generation_of (max_generation - 1))) || - (g_ephemeral_high != heap_segment_reserved (ephemeral_heap_segment))) + if ((ephemeral_low != generation_allocation_start (generation_of (max_generation - 1))) || + (ephemeral_high != heap_segment_reserved (ephemeral_heap_segment))) { FATAL_GC_ERROR(); } @@ -32922,7 +32845,7 @@ gc_heap::verify_heap (BOOL begin_gc_p) for (int i = 0; i < n_heaps; i++) { //copy the card and brick tables - if (g_card_table != g_heaps[i]->card_table) + if (g_gc_card_table != g_heaps[i]->card_table) { g_heaps[i]->copy_brick_card_table(); } @@ -32931,7 +32854,7 @@ gc_heap::verify_heap (BOOL begin_gc_p) current_join->restart(); } #else - if (g_card_table != card_table) + if (g_gc_card_table != card_table) copy_brick_card_table(); #endif //MULTIPLE_HEAPS @@ -33356,11 +33279,11 @@ HRESULT GCHeap::Shutdown () //CloseHandle (WaitForGCEvent); //find out if the global card table hasn't been used yet - uint32_t* ct = &g_card_table[card_word (gcard_of (g_lowest_address))]; + uint32_t* ct = &g_gc_card_table[card_word (gcard_of (g_gc_lowest_address))]; if (card_table_refcount (ct) == 0) { destroy_card_table (ct); - g_card_table = 0; + g_gc_card_table = 0; #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP SoftwareWriteWatch::StaticClose(); #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP @@ -33520,7 +33443,7 @@ HRESULT GCHeap::Initialize () return E_FAIL; } - StompWriteBarrierResize(true, false); + stomp_write_barrier_initialize(); #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way #if defined (STRESS_HEAP) && !defined (MULTIPLE_HEAPS) @@ -33557,10 +33480,7 @@ HRESULT GCHeap::Initialize () { GCScan::GcRuntimeStructuresValid (TRUE); -#ifdef GC_PROFILING - if (CORProfilerTrackGC()) - UpdateGenerationBounds(); -#endif // GC_PROFILING + GCToEEInterface::DiagUpdateGenerationBounds(); } return hr; @@ -33644,7 +33564,7 @@ Object * GCHeap::NextObj (Object * object) uint8_t* o = (uint8_t*)object; #ifndef FEATURE_BASICFREEZE - if (!((o < g_highest_address) && (o >= g_lowest_address))) + if (!((o < g_gc_highest_address) && (o >= g_gc_lowest_address))) { return NULL; } @@ -33715,7 +33635,7 @@ BOOL GCHeap::IsHeapPointer (void* vpObject, BOOL small_heap_only) uint8_t* object = (uint8_t*) vpObject; #ifndef FEATURE_BASICFREEZE - if (!((object < g_highest_address) && (object >= g_lowest_address))) + if (!((object < g_gc_highest_address) && (object >= g_gc_lowest_address))) return FALSE; #endif //!FEATURE_BASICFREEZE @@ -34969,7 +34889,6 @@ void gc_heap::do_post_gc() { if (!settings.concurrent) { - GCProfileWalkHeap(); initGCShadow(); } @@ -34989,13 +34908,10 @@ void gc_heap::do_post_gc() GCToEEInterface::GcDone(settings.condemned_generation); -#ifdef GC_PROFILING - if (!settings.concurrent) - { - UpdateGenerationBounds(); - GarbageCollectionFinishedCallback(); - } -#endif // GC_PROFILING + GCToEEInterface::DiagGCEnd(VolatileLoad(&settings.gc_index), + (uint32_t)settings.condemned_generation, + (uint32_t)settings.reason, + !!settings.concurrent); //dprintf (1, (" ****end of Garbage Collection**** %d(gen0:%d)(%d)", dprintf (1, ("*EGC* %d(gen0:%d)(%d)(%s)", @@ -35772,85 +35688,6 @@ void GCHeap::SetFinalizationRun (Object* obj) #endif // FEATURE_PREMORTEM_FINALIZATION -//---------------------------------------------------------------------------- -// -// Write Barrier Support for bulk copy ("Clone") operations -// -// StartPoint is the target bulk copy start point -// len is the length of the bulk copy (in bytes) -// -// -// Performance Note: -// -// This is implemented somewhat "conservatively", that is we -// assume that all the contents of the bulk copy are object -// references. If they are not, and the value lies in the -// ephemeral range, we will set false positives in the card table. -// -// We could use the pointer maps and do this more accurately if necessary - -#if defined(_MSC_VER) && defined(_TARGET_X86_) -#pragma optimize("y", on) // Small critical routines, don't put in EBP frame -#endif //_MSC_VER && _TARGET_X86_ - -void -GCHeap::SetCardsAfterBulkCopy( Object **StartPoint, size_t len ) -{ - Object **rover; - Object **end; - - // Target should aligned - assert(Aligned ((size_t)StartPoint)); - - - // Don't optimize the Generation 0 case if we are checking for write barrier voilations - // since we need to update the shadow heap even in the generation 0 case. -#if defined (WRITE_BARRIER_CHECK) && !defined (SERVER_GC) - if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_BARRIERCHECK) - for(unsigned i=0; i < len / sizeof(Object*); i++) - updateGCShadow(&StartPoint[i], StartPoint[i]); -#endif //WRITE_BARRIER_CHECK && !SERVER_GC - -#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP - if (SoftwareWriteWatch::IsEnabledForGCHeap()) - { - SoftwareWriteWatch::SetDirtyRegion(StartPoint, len); - } -#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP - - // If destination is in Gen 0 don't bother - if ( -#ifdef BACKGROUND_GC - (!gc_heap::settings.concurrent) && -#endif //BACKGROUND_GC - (g_theGCHeap->WhichGeneration( (Object*) StartPoint ) == 0)) - return; - - rover = StartPoint; - end = StartPoint + (len/sizeof(Object*)); - while (rover < end) - { - if ( (((uint8_t*)*rover) >= g_ephemeral_low) && (((uint8_t*)*rover) < g_ephemeral_high) ) - { - // Set Bit For Card and advance to next card - size_t card = gcard_of ((uint8_t*)rover); - - Interlocked::Or (&g_card_table[card/card_word_width], (1U << (card % card_word_width))); - // Skip to next card for the object - rover = (Object**)align_on_card ((uint8_t*)(rover+1)); - } - else - { - rover++; - } - } -} - -#if defined(_MSC_VER) && defined(_TARGET_X86_) -#pragma optimize("", on) // Go back to command line default optimizations -#endif //_MSC_VER && _TARGET_X86_ - - #ifdef FEATURE_PREMORTEM_FINALIZATION //-------------------------------------------------------------------- @@ -36278,21 +36115,17 @@ CFinalize::GcScanRoots (promote_func* fn, int hn, ScanContext *pSC) } } -#ifdef GC_PROFILING -void CFinalize::WalkFReachableObjects (gc_heap* hp) +void CFinalize::WalkFReachableObjects (fq_walk_fn fn) { - BEGIN_PIN_PROFILER(CORProfilerPresent()); Object** startIndex = SegQueue (CriticalFinalizerListSeg); Object** stopCriticalIndex = SegQueueLimit (CriticalFinalizerListSeg); Object** stopIndex = SegQueueLimit (FinalizerListSeg); for (Object** po = startIndex; po < stopIndex; po++) { //report *po - g_profControlBlock.pProfInterface->FinalizeableObjectQueued(po < stopCriticalIndex, (ObjectID)*po); + fn(po < stopCriticalIndex, *po); } - END_PIN_PROFILER(); } -#endif //GC_PROFILING BOOL CFinalize::ScanForFinalization (promote_func* pfn, int gen, BOOL mark_only_p, @@ -36528,8 +36361,7 @@ void CFinalize::CheckFinalizerObjects() // End of VM specific support // //------------------------------------------------------------------------------ - -void gc_heap::walk_heap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p) +void gc_heap::walk_heap_per_heap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p) { generation* gen = gc_heap::generation_of (gen_number); heap_segment* seg = generation_start_segment (gen); @@ -36585,9 +36417,29 @@ void gc_heap::walk_heap (walk_fn fn, void* context, int gen_number, BOOL walk_la } } -void GCHeap::WalkObject (Object* obj, walk_fn fn, void* context) +void gc_heap::walk_finalize_queue (fq_walk_fn fn) +{ +#ifdef FEATURE_PREMORTEM_FINALIZATION + finalize_queue->WalkFReachableObjects (fn); +#endif //FEATURE_PREMORTEM_FINALIZATION +} + +void gc_heap::walk_heap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p) +{ +#ifdef MULTIPLE_HEAPS + for (int hn = 0; hn < gc_heap::n_heaps; hn++) + { + gc_heap* hp = gc_heap::g_heaps [hn]; + + hp->walk_heap_per_heap (fn, context, gen_number, walk_large_object_heap_p); + } +#else + walk_heap_per_heap(fn, context, gen_number, walk_large_object_heap_p); +#endif //MULTIPLE_HEAPS +} + +void GCHeap::DiagWalkObject (Object* obj, walk_fn fn, void* context) { -#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) uint8_t* o = (uint8_t*)obj; if (o) { @@ -36602,7 +36454,48 @@ void GCHeap::WalkObject (Object* obj, walk_fn fn, void* context) } ); } -#endif //defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) +} + +void GCHeap::DiagWalkSurvivorsWithType (void* gc_context, record_surv_fn fn, size_t diag_context, walk_surv_type type) +{ + gc_heap* hp = (gc_heap*)gc_context; + hp->walk_survivors (fn, diag_context, type); +} + +void GCHeap::DiagWalkHeap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p) +{ + gc_heap::walk_heap (fn, context, gen_number, walk_large_object_heap_p); +} + +void GCHeap::DiagWalkFinalizeQueue (void* gc_context, fq_walk_fn fn) +{ + gc_heap* hp = (gc_heap*)gc_context; + hp->walk_finalize_queue (fn); +} + +void GCHeap::DiagScanFinalizeQueue (fq_scan_fn fn, ScanContext* sc) +{ +#ifdef MULTIPLE_HEAPS + for (int hn = 0; hn < gc_heap::n_heaps; hn++) + { + gc_heap* hp = gc_heap::g_heaps [hn]; + hp->finalize_queue->GcScanRoots(fn, hn, sc); + } +#else + pGenGCHeap->finalize_queue->GcScanRoots(fn, 0, sc); +#endif //MULTIPLE_HEAPS +} + +void GCHeap::DiagScanHandles (handle_scan_fn fn, int gen_number, ScanContext* context) +{ + UNREFERENCED_PARAMETER(gen_number); + GCScan::GcScanHandlesForProfilerAndETW (max_generation, context, fn); +} + +void GCHeap::DiagScanDependentHandles (handle_scan_fn fn, int gen_number, ScanContext* context) +{ + UNREFERENCED_PARAMETER(gen_number); + GCScan::GcScanDependentHandlesForProfilerAndETW (max_generation, context, fn); } // Go through and touch (read) each page straddled by a memory block. @@ -36649,11 +36542,11 @@ void initGCShadow() if (!(g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_BARRIERCHECK)) return; - size_t len = g_highest_address - g_lowest_address; + size_t len = g_gc_highest_address - g_gc_lowest_address; if (len > (size_t)(g_GCShadowEnd - g_GCShadow)) { deleteGCShadow(); - g_GCShadowEnd = g_GCShadow = (uint8_t *)GCToOSInterface::VirtualReserve(0, len, 0, VirtualReserveFlags::None); + g_GCShadowEnd = g_GCShadow = (uint8_t *)GCToOSInterface::VirtualReserve(len, 0, VirtualReserveFlags::None); if (g_GCShadow == NULL || !GCToOSInterface::VirtualCommit(g_GCShadow, len)) { _ASSERTE(!"Not enough memory to run HeapVerify level 2"); @@ -36668,10 +36561,10 @@ void initGCShadow() g_GCShadowEnd += len; } - // save the value of g_lowest_address at this time. If this value changes before + // save the value of g_gc_lowest_address at this time. If this value changes before // the next call to checkGCWriteBarrier() it means we extended the heap (with a // large object segment most probably), and the whole shadow segment is inconsistent. - g_shadow_lowest_address = g_lowest_address; + g_shadow_lowest_address = g_gc_lowest_address; //****** Copy the whole GC heap ****** // @@ -36681,7 +36574,7 @@ void initGCShadow() generation* gen = gc_heap::generation_of (max_generation); heap_segment* seg = heap_segment_rw (generation_start_segment (gen)); - ptrdiff_t delta = g_GCShadow - g_lowest_address; + ptrdiff_t delta = g_GCShadow - g_gc_lowest_address; BOOL small_object_segments = TRUE; while(1) { @@ -36709,7 +36602,7 @@ void initGCShadow() // test to see if 'ptr' was only updated via the write barrier. inline void testGCShadow(Object** ptr) { - Object** shadow = (Object**) &g_GCShadow[((uint8_t*) ptr - g_lowest_address)]; + Object** shadow = (Object**) &g_GCShadow[((uint8_t*) ptr - g_gc_lowest_address)]; if (*ptr != 0 && (uint8_t*) shadow < g_GCShadowEnd && *ptr != *shadow) { @@ -36768,9 +36661,9 @@ void testGCShadowHelper (uint8_t* x) // Walk the whole heap, looking for pointers that were not updated with the write barrier. void checkGCWriteBarrier() { - // g_shadow_lowest_address != g_lowest_address means the GC heap was extended by a segment + // g_shadow_lowest_address != g_gc_lowest_address means the GC heap was extended by a segment // and the GC shadow segment did not track that change! - if (g_GCShadowEnd <= g_GCShadow || g_shadow_lowest_address != g_lowest_address) + if (g_GCShadowEnd <= g_GCShadow || g_shadow_lowest_address != g_gc_lowest_address) { // No shadow stack, nothing to check. return; diff --git a/src/Native/gc/gc.h b/src/Native/gc/gc.h index ca9c28d8f..6f8626a3d 100644 --- a/src/Native/gc/gc.h +++ b/src/Native/gc/gc.h @@ -14,7 +14,19 @@ Module Name: #ifndef __GC_H #define __GC_H +#ifdef Sleep +// This is a funny workaround for the fact that "common.h" defines Sleep to be +// Dont_Use_Sleep, with the hope of causing linker errors whenever someone tries to use sleep. +// +// However, GCToOSInterface defines a function called Sleep, which (due to this define) becomes +// "Dont_Use_Sleep", which the GC in turn happily uses. The symbol that GCToOSInterface actually +// exported was called "GCToOSInterface::Dont_Use_Sleep". While we progress in making the GC standalone, +// we'll need to break the dependency on common.h (the VM header) and this problem will become moot. +#undef Sleep +#endif // Sleep + #include "gcinterface.h" +#include "env/gcenv.os.h" #include "env/gcenv.ee.h" #ifdef FEATURE_STANDALONE_GC @@ -125,6 +137,10 @@ class DacHeapWalker; #define MP_LOCKS +extern "C" uint32_t* g_gc_card_table; +extern "C" uint8_t* g_gc_lowest_address; +extern "C" uint8_t* g_gc_highest_address; + namespace WKS { ::IGCHeapInternal* CreateGCHeap(); class GCHeap; diff --git a/src/Native/gc/gccommon.cpp b/src/Native/gc/gccommon.cpp index 2e6bfce83..0292705a1 100644 --- a/src/Native/gc/gccommon.cpp +++ b/src/Native/gc/gccommon.cpp @@ -26,28 +26,22 @@ IGCHeapInternal* g_theGCHeap; IGCToCLR* g_theGCToCLR; #endif // FEATURE_STANDALONE_GC -/* global versions of the card table and brick table */ -GPTR_IMPL(uint32_t,g_card_table); - -/* absolute bounds of the GC memory */ -GPTR_IMPL_INIT(uint8_t,g_lowest_address,0); -GPTR_IMPL_INIT(uint8_t,g_highest_address,0); - #ifdef GC_CONFIG_DRIVEN GARY_IMPL(size_t, gc_global_mechanisms, MAX_GLOBAL_GC_MECHANISMS_COUNT); #endif //GC_CONFIG_DRIVEN #ifndef DACCESS_COMPILE -uint8_t* g_ephemeral_low = (uint8_t*)1; -uint8_t* g_ephemeral_high = (uint8_t*)~0; - #ifdef WRITE_BARRIER_CHECK uint8_t* g_GCShadow; uint8_t* g_GCShadowEnd; uint8_t* g_shadow_lowest_address = NULL; #endif +uint32_t* g_gc_card_table; +uint8_t* g_gc_lowest_address = 0; +uint8_t* g_gc_highest_address = 0; + VOLATILE(int32_t) m_GCLock = -1; #ifdef GC_CONFIG_DRIVEN diff --git a/src/Native/gc/gcee.cpp b/src/Native/gc/gcee.cpp index 58a553661..c93cc91b5 100644 --- a/src/Native/gc/gcee.cpp +++ b/src/Native/gc/gcee.cpp @@ -381,209 +381,6 @@ size_t GCHeap::GetNow() return GetHighPrecisionTimeStamp(); } -void ProfScanRootsHelper(Object** ppObject, ScanContext *pSC, uint32_t dwFlags) -{ -#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) - Object *pObj = *ppObject; -#ifdef INTERIOR_POINTERS - if (dwFlags & GC_CALL_INTERIOR) - { - uint8_t *o = (uint8_t*)pObj; - gc_heap* hp = gc_heap::heap_of (o); - - if ((o < hp->gc_low) || (o >= hp->gc_high)) - { - return; - } - pObj = (Object*) hp->find_object(o, hp->gc_low); - } -#endif //INTERIOR_POINTERS - ScanRootsHelper(pObj, ppObject, pSC, dwFlags); -#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) -} - -// TODO - at some point we would like to completely decouple profiling -// from ETW tracing using a pattern similar to this, where the -// ProfilingScanContext has flags about whether or not certain things -// should be tracked, and each one of these ProfilerShouldXYZ functions -// will check these flags and determine what to do based upon that. -// GCProfileWalkHeapWorker can, in turn, call those methods without fear -// of things being ifdef'd out. - -// Returns TRUE if GC profiling is enabled and the profiler -// should scan dependent handles, FALSE otherwise. -BOOL ProfilerShouldTrackConditionalWeakTableElements() -{ -#if defined(GC_PROFILING) - return CORProfilerTrackConditionalWeakTableElements(); -#else - return FALSE; -#endif // defined (GC_PROFILING) -} - -// If GC profiling is enabled, informs the profiler that we are done -// tracing dependent handles. -void ProfilerEndConditionalWeakTableElementReferences(void* heapId) -{ -#if defined (GC_PROFILING) - g_profControlBlock.pProfInterface->EndConditionalWeakTableElementReferences(heapId); -#else - UNREFERENCED_PARAMETER(heapId); -#endif // defined (GC_PROFILING) -} - -// If GC profiling is enabled, informs the profiler that we are done -// tracing root references. -void ProfilerEndRootReferences2(void* heapId) -{ -#if defined (GC_PROFILING) - g_profControlBlock.pProfInterface->EndRootReferences2(heapId); -#else - UNREFERENCED_PARAMETER(heapId); -#endif // defined (GC_PROFILING) -} - -// This is called only if we've determined that either: -// a) The Profiling API wants to do a walk of the heap, and it has pinned the -// profiler in place (so it cannot be detached), and it's thus safe to call into the -// profiler, OR -// b) ETW infrastructure wants to do a walk of the heap either to log roots, -// objects, or both. -// This can also be called to do a single walk for BOTH a) and b) simultaneously. Since -// ETW can ask for roots, but not objects -#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) - -void GCProfileWalkHeapWorker(BOOL fProfilerPinned, BOOL fShouldWalkHeapRootsForEtw, BOOL fShouldWalkHeapObjectsForEtw) -{ - { - ProfilingScanContext SC(fProfilerPinned); - - // **** Scan roots: Only scan roots if profiling API wants them or ETW wants them. - if (fProfilerPinned || fShouldWalkHeapRootsForEtw) - { -#ifdef MULTIPLE_HEAPS - int hn; - - // Must emulate each GC thread number so we can hit each - // heap for enumerating the roots. - for (hn = 0; hn < gc_heap::n_heaps; hn++) - { - // Ask the vm to go over all of the roots for this specific - // heap. - gc_heap* hp = gc_heap::g_heaps [hn]; - SC.thread_number = hn; - GCScan::GcScanRoots(&ProfScanRootsHelper, max_generation, max_generation, &SC); - - // The finalizer queue is also a source of roots - SC.dwEtwRootKind = kEtwGCRootKindFinalizer; - hp->finalize_queue->GcScanRoots(&ProfScanRootsHelper, hn, &SC); - } -#else - // Ask the vm to go over all of the roots - GCScan::GcScanRoots(&ProfScanRootsHelper, max_generation, max_generation, &SC); - - // The finalizer queue is also a source of roots - SC.dwEtwRootKind = kEtwGCRootKindFinalizer; - pGenGCHeap->finalize_queue->GcScanRoots(&ProfScanRootsHelper, 0, &SC); - -#endif // MULTIPLE_HEAPS - // Handles are kept independent of wks/svr/concurrent builds - SC.dwEtwRootKind = kEtwGCRootKindHandle; - GCScan::GcScanHandlesForProfilerAndETW(max_generation, &SC); - - // indicate that regular handle scanning is over, so we can flush the buffered roots - // to the profiler. (This is for profapi only. ETW will flush after the - // entire heap was is complete, via ETW::GCLog::EndHeapDump.) - if (fProfilerPinned) - { - ProfilerEndRootReferences2(&SC.pHeapId); - } - } - - // **** Scan dependent handles: only if the profiler supports it or ETW wants roots - if ((fProfilerPinned && ProfilerShouldTrackConditionalWeakTableElements()) || - fShouldWalkHeapRootsForEtw) - { - // GcScanDependentHandlesForProfiler double-checks - // CORProfilerTrackConditionalWeakTableElements() before calling into the profiler - - GCScan::GcScanDependentHandlesForProfilerAndETW(max_generation, &SC); - - // indicate that dependent handle scanning is over, so we can flush the buffered roots - // to the profiler. (This is for profapi only. ETW will flush after the - // entire heap was is complete, via ETW::GCLog::EndHeapDump.) - if (fProfilerPinned && ProfilerShouldTrackConditionalWeakTableElements()) - { - ProfilerEndConditionalWeakTableElementReferences(&SC.pHeapId); - } - } - - ProfilerWalkHeapContext profilerWalkHeapContext(fProfilerPinned, SC.pvEtwContext); - - // **** Walk objects on heap: only if profiling API wants them or ETW wants them. - if (fProfilerPinned || fShouldWalkHeapObjectsForEtw) - { -#ifdef MULTIPLE_HEAPS - int hn; - - // Walk the heap and provide the objref to the profiler - for (hn = 0; hn < gc_heap::n_heaps; hn++) - { - gc_heap* hp = gc_heap::g_heaps [hn]; - hp->walk_heap(&HeapWalkHelper, &profilerWalkHeapContext, max_generation, TRUE /* walk the large object heap */); - } -#else - gc_heap::walk_heap(&HeapWalkHelper, &profilerWalkHeapContext, max_generation, TRUE); -#endif //MULTIPLE_HEAPS - } - -#ifdef FEATURE_EVENT_TRACE - // **** Done! Indicate to ETW helpers that the heap walk is done, so any buffers - // should be flushed into the ETW stream - if (fShouldWalkHeapObjectsForEtw || fShouldWalkHeapRootsForEtw) - { - ETW::GCLog::EndHeapDump(&profilerWalkHeapContext); - } -#endif // FEATURE_EVENT_TRACE - } -} -#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) - -void GCProfileWalkHeap() -{ - BOOL fWalkedHeapForProfiler = FALSE; - -#ifdef FEATURE_EVENT_TRACE - if (ETW::GCLog::ShouldWalkStaticsAndCOMForEtw()) - ETW::GCLog::WalkStaticsAndCOMForETW(); - - BOOL fShouldWalkHeapRootsForEtw = ETW::GCLog::ShouldWalkHeapRootsForEtw(); - BOOL fShouldWalkHeapObjectsForEtw = ETW::GCLog::ShouldWalkHeapObjectsForEtw(); -#else // !FEATURE_EVENT_TRACE - BOOL fShouldWalkHeapRootsForEtw = FALSE; - BOOL fShouldWalkHeapObjectsForEtw = FALSE; -#endif // FEATURE_EVENT_TRACE - -#if defined (GC_PROFILING) - { - BEGIN_PIN_PROFILER(CORProfilerTrackGC()); - GCProfileWalkHeapWorker(TRUE /* fProfilerPinned */, fShouldWalkHeapRootsForEtw, fShouldWalkHeapObjectsForEtw); - fWalkedHeapForProfiler = TRUE; - END_PIN_PROFILER(); - } -#endif // defined (GC_PROFILING) - -#if defined (GC_PROFILING) || defined(FEATURE_EVENT_TRACE) - // we need to walk the heap if one of GC_PROFILING or FEATURE_EVENT_TRACE - // is defined, since both of them make use of the walk heap worker. - if (!fWalkedHeapForProfiler && - (fShouldWalkHeapRootsForEtw || fShouldWalkHeapObjectsForEtw)) - { - GCProfileWalkHeapWorker(FALSE /* fProfilerPinned */, fShouldWalkHeapRootsForEtw, fShouldWalkHeapObjectsForEtw); - } -#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) -} - BOOL GCHeap::IsGCInProgressHelper (BOOL bConsiderGCStart) { return GcInProgress || (bConsiderGCStart? VolatileLoad(&gc_heap::gc_started) : FALSE); @@ -786,7 +583,7 @@ IGCHeapInternal* CreateGCHeap() { return new(nothrow) GCHeap(); // we return wks or svr } -void GCHeap::TraceGCSegments() +void GCHeap::DiagTraceGCSegments() { #ifdef FEATURE_EVENT_TRACE heap_segment* seg = 0; @@ -823,7 +620,7 @@ void GCHeap::TraceGCSegments() #endif // FEATURE_EVENT_TRACE } -void GCHeap::DescrGenerationsToProfiler (gen_walk_fn fn, void *context) +void GCHeap::DiagDescrGenerations (gen_walk_fn fn, void *context) { #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) pGenGCHeap->descr_generations_to_profiler(fn, context); diff --git a/src/Native/gc/gcenv.ee.standalone.inl b/src/Native/gc/gcenv.ee.standalone.inl index 2ecc6fc83..3b64586d7 100644 --- a/src/Native/gc/gcenv.ee.standalone.inl +++ b/src/Native/gc/gcenv.ee.standalone.inl @@ -125,4 +125,52 @@ inline Thread* GCToEEInterface::CreateBackgroundThread(GCBackgroundThreadFunctio return g_theGCToCLR->CreateBackgroundThread(threadStart, arg); } +inline void GCToEEInterface::DiagGCStart(int gen, bool isInduced) +{ + assert(g_theGCToCLR != nullptr); + g_theGCToCLR->DiagGCStart(gen, isInduced); +} + +inline void GCToEEInterface::DiagUpdateGenerationBounds() +{ + assert(g_theGCToCLR != nullptr); + g_theGCToCLR->DiagUpdateGenerationBounds(); +} + +inline void GCToEEInterface::DiagGCEnd(size_t index, int gen, int reason, bool fConcurrent) +{ + assert(g_theGCToCLR != nullptr); + g_theGCToCLR->DiagGCEnd(index, gen, reason, fConcurrent); +} + +inline void GCToEEInterface::DiagWalkFReachableObjects(void* gcContext) +{ + assert(g_theGCToCLR != nullptr); + g_theGCToCLR->DiagWalkFReachableObjects(gcContext); +} + +inline void GCToEEInterface::DiagWalkSurvivors(void* gcContext) +{ + assert(g_theGCToCLR != nullptr); + g_theGCToCLR->DiagWalkSurvivors(gcContext); +} + +inline void GCToEEInterface::DiagWalkLOHSurvivors(void* gcContext) +{ + assert(g_theGCToCLR != nullptr); + g_theGCToCLR->DiagWalkLOHSurvivors(gcContext); +} + +inline void GCToEEInterface::DiagWalkBGCSurvivors(void* gcContext) +{ + assert(g_theGCToCLR != nullptr); + return g_theGCToCLR->DiagWalkBGCSurvivors(gcContext); +} + +inline void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args) +{ + assert(g_theGCToCLR != nullptr); + g_theGCToCLR->StompWriteBarrier(args); +} + #endif // __GCTOENV_EE_STANDALONE_INL__ diff --git a/src/Native/gc/gcimpl.h b/src/Native/gc/gcimpl.h index d7393c357..cb91c4dc3 100644 --- a/src/Native/gc/gcimpl.h +++ b/src/Native/gc/gcimpl.h @@ -77,7 +77,7 @@ public: size_t GetLastGCDuration(int generation); size_t GetNow(); - void TraceGCSegments (); + void DiagTraceGCSegments (); void PublishObject(uint8_t* obj); BOOL IsGCInProgressHelper (BOOL bConsiderGCStart = FALSE); @@ -198,8 +198,7 @@ public: BOOL FinalizeAppDomain(AppDomain *pDomain, BOOL fRunFinalizers); BOOL ShouldRestartFinalizerWatchDog(); - void SetCardsAfterBulkCopy( Object**, size_t); - void WalkObject (Object* obj, walk_fn fn, void* context); + void DiagWalkObject (Object* obj, walk_fn fn, void* context); public: // FIX @@ -272,7 +271,19 @@ protected: #endif // STRESS_HEAP #endif // FEATURE_REDHAWK - virtual void DescrGenerationsToProfiler (gen_walk_fn fn, void *context); + virtual void DiagDescrGenerations (gen_walk_fn fn, void *context); + + virtual void DiagWalkSurvivorsWithType (void* gc_context, record_surv_fn fn, size_t diag_context, walk_surv_type type); + + virtual void DiagWalkFinalizeQueue (void* gc_context, fq_walk_fn fn); + + virtual void DiagScanFinalizeQueue (fq_scan_fn fn, ScanContext* context); + + virtual void DiagScanHandles (handle_scan_fn fn, int gen_number, ScanContext* context); + + virtual void DiagScanDependentHandles (handle_scan_fn fn, int gen_number, ScanContext* context); + + virtual void DiagWalkHeap(walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p); public: Object * NextObj (Object * object); diff --git a/src/Native/gc/gcinterface.ee.h b/src/Native/gc/gcinterface.ee.h index 36d20c719..c5f87ef03 100644 --- a/src/Native/gc/gcinterface.ee.h +++ b/src/Native/gc/gcinterface.ee.h @@ -92,6 +92,42 @@ public: // Creates and returns a new background thread. virtual Thread* CreateBackgroundThread(GCBackgroundThreadFunction threadStart, void* arg) = 0; + + // When a GC starts, gives the diagnostics code a chance to run. + virtual + void DiagGCStart(int gen, bool isInduced) = 0; + + // When GC heap segments change, gives the diagnostics code a chance to run. + virtual + void DiagUpdateGenerationBounds() = 0; + + // When a GC ends, gives the diagnostics code a chance to run. + virtual + void DiagGCEnd(size_t index, int gen, int reason, bool fConcurrent) = 0; + + // During a GC after we discover what objects' finalizers should run, gives the diagnostics code a chance to run. + virtual + void DiagWalkFReachableObjects(void* gcContext) = 0; + + // During a GC after we discover the survivors and the relocation info, + // gives the diagnostics code a chance to run. This includes LOH if we are + // compacting LOH. + virtual + void DiagWalkSurvivors(void* gcContext) = 0; + + // During a full GC after we discover what objects to survive on LOH, + // gives the diagnostics code a chance to run. + virtual + void DiagWalkLOHSurvivors(void* gcContext) = 0; + + // At the end of a background GC, gives the diagnostics code a chance to run. + virtual + void DiagWalkBGCSurvivors(void* gcContext) = 0; + + // Informs the EE of changes to the location of the card table, potentially updating the write + // barrier if it needs to be updated. + virtual + void StompWriteBarrier(WriteBarrierParameters* args) = 0; }; #endif // _GCINTERFACE_EE_H_ diff --git a/src/Native/gc/gcinterface.h b/src/Native/gc/gcinterface.h index ac14332dc..4ba4e0c63 100644 --- a/src/Native/gc/gcinterface.h +++ b/src/Native/gc/gcinterface.h @@ -34,6 +34,70 @@ typedef enum SUSPEND_FOR_GC_PREP = 6 } SUSPEND_REASON; +typedef enum +{ + walk_for_gc = 1, + walk_for_bgc = 2, + walk_for_loh = 3 +} walk_surv_type; + +// Different operations that can be done by GCToEEInterface::StompWriteBarrier +enum class WriteBarrierOp +{ + StompResize, + StompEphemeral, + Initialize, + SwitchToWriteWatch, + SwitchToNonWriteWatch +}; + +// Arguments to GCToEEInterface::StompWriteBarrier +struct WriteBarrierParameters +{ + // The operation that StompWriteBarrier will perform. + WriteBarrierOp operation; + + // Whether or not the runtime is currently suspended. If it is not, + // the EE will need to suspend it before bashing the write barrier. + // Used for all operations. + bool is_runtime_suspended; + + // Whether or not the GC has moved the ephemeral generation to no longer + // be at the top of the heap. When the ephemeral generation is at the top + // of the heap, and the write barrier observes that a pointer is greater than + // g_ephemeral_low, it does not need to check that the pointer is less than + // g_ephemeral_high because there is nothing in the GC heap above the ephemeral + // generation. When this is not the case, however, the GC must inform the EE + // so that the EE can switch to a write barrier that checks that a pointer + // is both greater than g_ephemeral_low and less than g_ephemeral_high. + // Used for WriteBarrierOp::StompResize. + bool requires_upper_bounds_check; + + // The new card table location. May or may not be the same as the previous + // card table. Used for WriteBarrierOp::Initialize and WriteBarrierOp::StompResize. + uint32_t* card_table; + + // The heap's new low boundary. May or may not be the same as the previous + // value. Used for WriteBarrierOp::Initialize and WriteBarrierOp::StompResize. + uint8_t* lowest_address; + + // The heap's new high boundary. May or may not be the same as the previous + // value. Used for WriteBarrierOp::Initialize and WriteBarrierOp::StompResize. + uint8_t* highest_address; + + // The new start of the ephemeral generation. + // Used for WriteBarrierOp::StompEphemeral. + uint8_t* ephemeral_low; + + // The new end of the ephemeral generation. + // Used for WriteBarrierOp::StompEphemeral. + uint8_t* ephemeral_high; + + // The new write watch table, if we are using our own write watch + // implementation. Used for WriteBarrierOp::SwitchToWriteWatch only. + uint8_t* write_watch_table; +}; + #include "gcinterface.ee.h" // The allocation context must be known to the VM for use in the allocation @@ -88,6 +152,12 @@ struct segment_info // one for the object header, and one for the first field in the object. #define min_obj_size ((sizeof(uint8_t*) + sizeof(uintptr_t) + sizeof(size_t))) +#define max_generation 2 + +// The bit shift used to convert a memory address into an index into the +// Software Write Watch table. +#define SOFTWARE_WRITE_WATCH_AddressToTableByteIndexShift 0xc + class Object; class IGCHeap; @@ -101,19 +171,6 @@ IGCHeap* InitializeGarbageCollector(IGCToCLR* clrToGC); // and the heap is actually recated. void InitializeHeapType(bool bServerHeap); -#ifndef DACCESS_COMPILE -extern "C" { -#endif // !DACCESS_COMPILE -GPTR_DECL(uint8_t,g_lowest_address); -GPTR_DECL(uint8_t,g_highest_address); -GPTR_DECL(uint32_t,g_card_table); -#ifndef DACCESS_COMPILE -} -#endif // !DACCESS_COMPILE - -extern "C" uint8_t* g_ephemeral_low; -extern "C" uint8_t* g_ephemeral_high; - #ifdef WRITE_BARRIER_CHECK //always defined, but should be 0 in Server GC extern uint8_t* g_GCShadow; @@ -174,6 +231,10 @@ enum end_no_gc_region_status typedef BOOL (* walk_fn)(Object*, void*); typedef void (* gen_walk_fn)(void* context, int generation, uint8_t* range_start, uint8_t* range_end, uint8_t* range_reserved); +typedef void (* record_surv_fn)(uint8_t* begin, uint8_t* end, ptrdiff_t reloc, size_t context, BOOL compacting_p, BOOL bgc_p); +typedef void (* fq_walk_fn)(BOOL, void*); +typedef void (* fq_scan_fn)(Object** ppObject, ScanContext *pSC, uint32_t dwFlags); +typedef void (* handle_scan_fn)(Object** pRef, Object* pSec, uint32_t flags, ScanContext* context, BOOL isDependent); // IGCHeap is the interface that the VM will use when interacting with the GC. class IGCHeap { @@ -347,9 +408,6 @@ public: // sanity checks asserting that a GC has not occured. virtual unsigned GetGcCount() = 0; - // Sets cards after an object has been memmoved. - virtual void SetCardsAfterBulkCopy(Object** obj, size_t length) = 0; - // Gets whether or not the home heap of this alloc context matches the heap // associated with this thread. virtual bool IsThreadUsingAllocationContextHeap(gc_alloc_context* acontext, int thread_number) = 0; @@ -413,8 +471,8 @@ public: // with the given size and flags. virtual Object* AllocAlign8 (gc_alloc_context* acontext, size_t size, uint32_t flags) = 0; - // If allocating on the LOH, blocks if a BGC is in a position (concurrent mark) - // where the LOH allocator can't allocate. + // This is for the allocator to indicate it's done allocating a large object during a + // background GC as the BGC threads also need to walk LOH. virtual void PublishObject(uint8_t* obj) = 0; // Gets the event that suspended threads will use to wait for the @@ -449,13 +507,31 @@ public: */ // Walks an object, invoking a callback on each member. - virtual void WalkObject(Object* obj, walk_fn fn, void* context) = 0; + virtual void DiagWalkObject(Object* obj, walk_fn fn, void* context) = 0; + + // Walk the heap object by object. + virtual void DiagWalkHeap(walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p) = 0; + + // Walks the survivors and get the relocation information if objects have moved. + virtual void DiagWalkSurvivorsWithType(void* gc_context, record_surv_fn fn, size_t diag_context, walk_surv_type type) = 0; + + // Walks the finalization queue. + virtual void DiagWalkFinalizeQueue(void* gc_context, fq_walk_fn fn) = 0; + + // Scan roots on finalizer queue. This is a generic function. + virtual void DiagScanFinalizeQueue(fq_scan_fn fn, ScanContext* context) = 0; + + // Scan handles for profiling or ETW. + virtual void DiagScanHandles(handle_scan_fn fn, int gen_number, ScanContext* context) = 0; + + // Scan dependent handles for profiling or ETW. + virtual void DiagScanDependentHandles(handle_scan_fn fn, int gen_number, ScanContext* context) = 0; // Describes all generations to the profiler, invoking a callback on each generation. - virtual void DescrGenerationsToProfiler(gen_walk_fn fn, void* context) = 0; + virtual void DiagDescrGenerations(gen_walk_fn fn, void* context) = 0; // Traces all GC segments and fires ETW events with information on them. - virtual void TraceGCSegments() = 0; + virtual void DiagTraceGCSegments() = 0; /* =========================================================================== @@ -550,26 +626,4 @@ struct ScanContext } }; -#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) -struct ProfilingScanContext : ScanContext -{ - BOOL fProfilerPinned; - void * pvEtwContext; - void *pHeapId; - - ProfilingScanContext(BOOL fProfilerPinnedParam) : ScanContext() - { - LIMITED_METHOD_CONTRACT; - - pHeapId = NULL; - fProfilerPinned = fProfilerPinnedParam; - pvEtwContext = NULL; -#ifdef FEATURE_CONSERVATIVE_GC - // To not confuse GCScan::GcScanRoots - promotion = g_pConfig->GetGCConservative(); -#endif - } -}; -#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) - #endif // _GC_INTERFACE_H_ diff --git a/src/Native/gc/gcpriv.h b/src/Native/gc/gcpriv.h index e0147c33c..1f97d7f2d 100644 --- a/src/Native/gc/gcpriv.h +++ b/src/Native/gc/gcpriv.h @@ -24,7 +24,9 @@ inline void FATAL_GC_ERROR() { +#ifndef DACCESS_COMPILE GCToOSInterface::DebugBreak(); +#endif // DACCESS_COMPILE _ASSERTE(!"Fatal Error in GC."); EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); } @@ -1073,9 +1075,6 @@ enum interesting_data_point }; //class definition of the internal class -#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) -extern void GCProfileWalkHeapWorker(BOOL fProfilerPinned, BOOL fShouldWalkHeapRootsForEtw, BOOL fShouldWalkHeapObjectsForEtw); -#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) class gc_heap { friend struct ::_DacGlobals; @@ -1225,7 +1224,7 @@ public: static gc_heap* balance_heaps_loh (alloc_context* acontext, size_t size); static - void __stdcall gc_thread_stub (void* arg); + void gc_thread_stub (void* arg); #endif //MULTIPLE_HEAPS CObjectHeader* try_fast_alloc (size_t jsize); @@ -1283,35 +1282,48 @@ public: protected: - PER_HEAP + PER_HEAP_ISOLATED void walk_heap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p); + PER_HEAP + void walk_heap_per_heap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p); + struct walk_relocate_args { uint8_t* last_plug; BOOL is_shortened; mark* pinned_plug_entry; + size_t profiling_context; + record_surv_fn fn; }; + PER_HEAP + void walk_survivors (record_surv_fn fn, size_t context, walk_surv_type type); + PER_HEAP void walk_plug (uint8_t* plug, size_t size, BOOL check_last_object_p, - walk_relocate_args* args, size_t profiling_context); + walk_relocate_args* args); PER_HEAP - void walk_relocation (int condemned_gen_number, - uint8_t* first_condemned_address, size_t profiling_context); + void walk_relocation (size_t profiling_context, record_surv_fn fn); PER_HEAP - void walk_relocation_in_brick (uint8_t* tree, walk_relocate_args* args, size_t profiling_context); + void walk_relocation_in_brick (uint8_t* tree, walk_relocate_args* args); -#if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE) PER_HEAP - void walk_relocation_for_bgc(size_t profiling_context); + void walk_finalize_queue (fq_walk_fn fn); +#if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE) PER_HEAP - void make_free_lists_for_profiler_for_bgc(); + void walk_survivors_for_bgc (size_t profiling_context, record_surv_fn fn); #endif // defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE) + // used in blocking GCs after plan phase so this walks the plugs. + PER_HEAP + void walk_survivors_relocation (size_t profiling_context, record_surv_fn fn); + PER_HEAP + void walk_survivors_for_loh (size_t profiling_context, record_surv_fn fn); + PER_HEAP int generation_to_condemn (int n, BOOL* blocking_collection_p, @@ -1659,7 +1671,7 @@ protected: PER_HEAP void reset_write_watch (BOOL concurrent_p); PER_HEAP - void adjust_ephemeral_limits (bool is_runtime_suspended); + void adjust_ephemeral_limits (); PER_HEAP void make_generation (generation& gen, heap_segment* seg, uint8_t* start, uint8_t* pointer); @@ -2148,10 +2160,8 @@ protected: PER_HEAP void relocate_in_loh_compact(); -#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) PER_HEAP - void walk_relocation_loh (size_t profiling_context); -#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) + void walk_relocation_for_loh (size_t profiling_context, record_surv_fn fn); PER_HEAP BOOL loh_enque_pinned_plug (uint8_t* plug, size_t len); @@ -2549,12 +2559,6 @@ protected: PER_HEAP_ISOLATED void descr_generations_to_profiler (gen_walk_fn fn, void *context); -#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) - PER_HEAP - void record_survived_for_profiler(int condemned_gen_number, uint8_t * first_condemned_address); - PER_HEAP - void notify_profiler_of_surviving_large_objects (); -#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) /*------------ Multiple non isolated heaps ----------------*/ #ifdef MULTIPLE_HEAPS @@ -2798,13 +2802,11 @@ public: PER_HEAP void exit_gc_done_event_lock(); -#ifdef MULTIPLE_HEAPS PER_HEAP uint8_t* ephemeral_low; //lowest ephemeral address PER_HEAP uint8_t* ephemeral_high; //highest ephemeral address -#endif //MULTIPLE_HEAPS PER_HEAP uint32_t* card_table; @@ -3763,9 +3765,7 @@ public: Object* GetNextFinalizableObject (BOOL only_non_critical=FALSE); BOOL ScanForFinalization (promote_func* fn, int gen,BOOL mark_only_p, gc_heap* hp); void RelocateFinalizationData (int gen, gc_heap* hp); -#ifdef GC_PROFILING - void WalkFReachableObjects (gc_heap* hp); -#endif //GC_PROFILING + void WalkFReachableObjects (fq_walk_fn fn); void GcScanRoots (promote_func* fn, int hn, ScanContext *pSC); void UpdatePromotedGenerations (int gen, BOOL gen_0_empty_p); size_t GetPromotedCount(); @@ -4317,9 +4317,6 @@ dynamic_data* gc_heap::dynamic_data_of (int gen_number) return &dynamic_data_table [ gen_number ]; } -extern "C" uint8_t* g_ephemeral_low; -extern "C" uint8_t* g_ephemeral_high; - #define card_word_width ((size_t)32) // diff --git a/src/Native/gc/gcrecord.h b/src/Native/gc/gcrecord.h index 8c95ad04d..fff1fc5c8 100644 --- a/src/Native/gc/gcrecord.h +++ b/src/Native/gc/gcrecord.h @@ -13,7 +13,7 @@ Module Name: #ifndef __gc_record_h__ #define __gc_record_h__ -#define max_generation 2 +//#define max_generation 2 // We pack the dynamic tuning for deciding which gen to condemn in a uint32_t. // We assume that 2 bits are enough to represent the generation. diff --git a/src/Native/gc/gcscan.cpp b/src/Native/gc/gcscan.cpp index f021554fd..b4e6352dd 100644 --- a/src/Native/gc/gcscan.cpp +++ b/src/Native/gc/gcscan.cpp @@ -192,33 +192,32 @@ void GCScan::GcScanHandles (promote_func* fn, int condemned, int max_gen, } } - -#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) - /* * Scan all handle roots in this 'namespace' for profiling */ -void GCScan::GcScanHandlesForProfilerAndETW (int max_gen, ScanContext* sc) +void GCScan::GcScanHandlesForProfilerAndETW (int max_gen, ScanContext* sc, handle_scan_fn fn) { LIMITED_METHOD_CONTRACT; +#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) LOG((LF_GC|LF_GCROOTS, LL_INFO10, "Profiler Root Scan Phase, Handles\n")); - Ref_ScanPointersForProfilerAndETW(max_gen, (uintptr_t)sc); + Ref_ScanHandlesForProfilerAndETW(max_gen, (uintptr_t)sc, fn); +#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) } /* * Scan dependent handles in this 'namespace' for profiling */ -void GCScan::GcScanDependentHandlesForProfilerAndETW (int max_gen, ProfilingScanContext* sc) +void GCScan::GcScanDependentHandlesForProfilerAndETW (int max_gen, ScanContext* sc, handle_scan_fn fn) { LIMITED_METHOD_CONTRACT; +#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) LOG((LF_GC|LF_GCROOTS, LL_INFO10, "Profiler Root Scan Phase, DependentHandles\n")); - Ref_ScanDependentHandlesForProfilerAndETW(max_gen, sc); -} - + Ref_ScanDependentHandlesForProfilerAndETW(max_gen, sc, fn); #endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) +} void GCScan::GcRuntimeStructuresValid (BOOL bValid) { diff --git a/src/Native/gc/gcscan.h b/src/Native/gc/gcscan.h index 3515b8e1b..362370fa4 100644 --- a/src/Native/gc/gcscan.h +++ b/src/Native/gc/gcscan.h @@ -52,10 +52,8 @@ class GCScan static void EnumMemoryRegions(CLRDataEnumMemoryFlags flags); #endif // DACCESS_COMPILE -#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) - static void GcScanHandlesForProfilerAndETW (int max_gen, ScanContext* sc); - static void GcScanDependentHandlesForProfilerAndETW (int max_gen, ProfilingScanContext* sc); -#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) + static void GcScanHandlesForProfilerAndETW (int max_gen, ScanContext* sc, handle_scan_fn fn); + static void GcScanDependentHandlesForProfilerAndETW (int max_gen, ScanContext* sc, handle_scan_fn fn); // scan for dead weak pointers static void GcWeakPtrScan (promote_func* fn, int condemned, int max_gen, ScanContext*sc ); diff --git a/src/Native/gc/gcsvr.cpp b/src/Native/gc/gcsvr.cpp index cf5fc9335..70801dd4e 100644 --- a/src/Native/gc/gcsvr.cpp +++ b/src/Native/gc/gcsvr.cpp @@ -13,6 +13,7 @@ #include "gc.h" #include "gcscan.h" #include "gcdesc.h" +#include "softwarewritewatch.h" #define SERVER_GC 1 diff --git a/src/Native/gc/gcwks.cpp b/src/Native/gc/gcwks.cpp index 574df8215..5c489df0e 100644 --- a/src/Native/gc/gcwks.cpp +++ b/src/Native/gc/gcwks.cpp @@ -11,6 +11,7 @@ #include "gc.h" #include "gcscan.h" #include "gcdesc.h" +#include "softwarewritewatch.h" #ifdef SERVER_GC #undef SERVER_GC diff --git a/src/Native/gc/handletablecache.cpp b/src/Native/gc/handletablecache.cpp index b2af40c82..aaf3370bd 100644 --- a/src/Native/gc/handletablecache.cpp +++ b/src/Native/gc/handletablecache.cpp @@ -15,6 +15,12 @@ #include "gcenv.h" +#ifdef Sleep // TODO(segilles) +#undef Sleep +#endif // Sleep + +#include "env/gcenv.os.h" + #include "handletablepriv.h" /**************************************************************************** diff --git a/src/Native/gc/handletablecore.cpp b/src/Native/gc/handletablecore.cpp index be65b142b..5776c26ac 100644 --- a/src/Native/gc/handletablecore.cpp +++ b/src/Native/gc/handletablecore.cpp @@ -611,7 +611,7 @@ TableSegment *SegmentAlloc(HandleTable *pTable) _ASSERTE(HANDLE_SEGMENT_ALIGNMENT >= HANDLE_SEGMENT_SIZE); _ASSERTE(HANDLE_SEGMENT_ALIGNMENT == 0x10000); - pSegment = (TableSegment *)GCToOSInterface::VirtualReserve(NULL, HANDLE_SEGMENT_SIZE, HANDLE_SEGMENT_ALIGNMENT, VirtualReserveFlags::None); + pSegment = (TableSegment *)GCToOSInterface::VirtualReserve(HANDLE_SEGMENT_SIZE, HANDLE_SEGMENT_ALIGNMENT, VirtualReserveFlags::None); _ASSERTE(((size_t)pSegment % HANDLE_SEGMENT_ALIGNMENT) == 0); // bail out if we couldn't get any memory diff --git a/src/Native/gc/objecthandle.cpp b/src/Native/gc/objecthandle.cpp index d8834b72f..e8eed9300 100644 --- a/src/Native/gc/objecthandle.cpp +++ b/src/Native/gc/objecthandle.cpp @@ -110,6 +110,21 @@ void CALLBACK PromoteRefCounted(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtra } #endif // FEATURE_COMINTEROP || FEATURE_REDHAWK + +// Only used by profiling/ETW. +//---------------------------------------------------------------------------- + +/* + * struct DIAG_DEPSCANINFO + * + * used when tracing dependent handles for profiling/ETW. + */ +struct DIAG_DEPSCANINFO +{ + HANDLESCANPROC pfnTrace; // tracing function to use + uintptr_t pfnProfilingOrETW; +}; + void CALLBACK TraceDependentHandle(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtraInfo, uintptr_t lp1, uintptr_t lp2) { WRAPPER_NO_CONTRACT; @@ -122,14 +137,15 @@ void CALLBACK TraceDependentHandle(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pEx // object should also be non-NULL. _ASSERTE(*pExtraInfo == NULL || *pObjRef != NULL); - // lp2 is a HANDLESCANPROC - HANDLESCANPROC pfnTrace = (HANDLESCANPROC) lp2; + struct DIAG_DEPSCANINFO *pInfo = (struct DIAG_DEPSCANINFO*)lp2; + + HANDLESCANPROC pfnTrace = pInfo->pfnTrace; // is the handle's secondary object non-NULL? if ((*pObjRef != NULL) && (*pExtraInfo != 0)) { // yes - call the tracing function for this handle - pfnTrace(pObjRef, NULL, lp1, *pExtraInfo); + pfnTrace(pObjRef, NULL, lp1, (uintptr_t)(pInfo->pfnProfilingOrETW)); } } @@ -414,7 +430,7 @@ void CALLBACK ScanPointerForProfilerAndETW(_UNCHECKED_OBJECTREF *pObjRef, uintpt CONTRACTL_END; #endif // FEATURE_REDHAWK UNREFERENCED_PARAMETER(pExtraInfo); - UNREFERENCED_PARAMETER(lp2); + handle_scan_fn fn = (handle_scan_fn)lp2; LOG((LF_GC | LF_CORPROF, LL_INFO100000, LOG_HANDLE_OBJECT_CLASS("Notifying profiler of ", pObjRef, "to ", *pObjRef))); @@ -422,7 +438,7 @@ void CALLBACK ScanPointerForProfilerAndETW(_UNCHECKED_OBJECTREF *pObjRef, uintpt Object **pRef = (Object **)pObjRef; // Get a hold of the heap ID that's tacked onto the end of the scancontext struct. - ProfilingScanContext *pSC = (ProfilingScanContext *)lp1; + ScanContext *pSC = (ScanContext *)lp1; uint32_t rootFlags = 0; BOOL isDependent = FALSE; @@ -487,60 +503,15 @@ void CALLBACK ScanPointerForProfilerAndETW(_UNCHECKED_OBJECTREF *pObjRef, uintpt _UNCHECKED_OBJECTREF pSec = NULL; -#ifdef GC_PROFILING - // Give the profiler the objectref. - if (pSC->fProfilerPinned) + if (isDependent) { - if (!isDependent) - { - BEGIN_PIN_PROFILER(CORProfilerTrackGC()); - g_profControlBlock.pProfInterface->RootReference2( - (uint8_t *)*pRef, - kEtwGCRootKindHandle, - (EtwGCRootFlags)rootFlags, - pRef, - &pSC->pHeapId); - END_PIN_PROFILER(); - } - else - { - BEGIN_PIN_PROFILER(CORProfilerTrackConditionalWeakTableElements()); - pSec = (_UNCHECKED_OBJECTREF)HndGetHandleExtraInfo(handle); - g_profControlBlock.pProfInterface->ConditionalWeakTableElementReference( - (uint8_t*)*pRef, - (uint8_t*)pSec, - pRef, - &pSC->pHeapId); - END_PIN_PROFILER(); - } + pSec = (_UNCHECKED_OBJECTREF)HndGetHandleExtraInfo(handle); } -#endif // GC_PROFILING - -#if defined(FEATURE_EVENT_TRACE) - // Notify ETW of the handle - if (ETW::GCLog::ShouldWalkHeapRootsForEtw()) - { - if (isDependent && (pSec == NULL)) - { - pSec = (_UNCHECKED_OBJECTREF)HndGetHandleExtraInfo(handle); - } - - ETW::GCLog::RootReference( - handle, - *pRef, // object being rooted - pSec, // pSecondaryNodeForDependentHandle - isDependent, - pSC, - 0, // dwGCFlags, - rootFlags); // ETW handle flags - } -#endif // defined(FEATURE_EVENT_TRACE) + fn(pRef, pSec, rootFlags, pSC, isDependent); } #endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) - - /* * Scan callback for updating pointers. * @@ -1417,13 +1388,15 @@ void Ref_ScanDependentHandlesForRelocation(uint32_t condemned, uint32_t maxgen, /* loop scan version of TraceVariableHandles for single-thread-managed Ref_* functions should be kept in sync with the code above + Only used by profiling/ETW. */ -void TraceDependentHandlesBySingleThread(HANDLESCANPROC pfnTrace, uintptr_t lp1, uint32_t condemned, uint32_t maxgen, uint32_t flags) +void TraceDependentHandlesBySingleThread(HANDLESCANPROC pfnTrace, uintptr_t lp1, uintptr_t lp2, uint32_t condemned, uint32_t maxgen, uint32_t flags) { WRAPPER_NO_CONTRACT; // set up to scan variable handles with the specified mask and trace function uint32_t type = HNDTYPE_DEPENDENT; + struct DIAG_DEPSCANINFO info = { pfnTrace, lp2 }; HandleTableMap *walk = &g_HandleTableMap; while (walk) { @@ -1436,14 +1409,13 @@ void TraceDependentHandlesBySingleThread(HANDLESCANPROC pfnTrace, uintptr_t lp1, HHANDLETABLE hTable = walk->pBuckets[i]->pTable[uCPUindex]; if (hTable) HndScanHandlesForGC(hTable, TraceDependentHandle, - lp1, (uintptr_t)pfnTrace, &type, 1, condemned, maxgen, HNDGCF_EXTRAINFO | flags); + lp1, (uintptr_t)&info, &type, 1, condemned, maxgen, HNDGCF_EXTRAINFO | flags); } } walk = walk->pNext; } } - // We scan handle tables by their buckets (ie, AD index). We could get into the situation where // the AD indices are not very compacted (for example if we have just unloaded ADs and their // indices haven't been reused yet) and we could be scanning them in an unbalanced fashion. @@ -1623,7 +1595,7 @@ void Ref_UpdatePointers(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Re #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) // Please update this if you change the Ref_UpdatePointers function above. -void Ref_ScanPointersForProfilerAndETW(uint32_t maxgen, uintptr_t lp1) +void Ref_ScanHandlesForProfilerAndETW(uint32_t maxgen, uintptr_t lp1, handle_scan_fn fn) { WRAPPER_NO_CONTRACT; @@ -1662,16 +1634,16 @@ void Ref_ScanPointersForProfilerAndETW(uint32_t maxgen, uintptr_t lp1) { HHANDLETABLE hTable = walk->pBuckets[i]->pTable[uCPUindex]; if (hTable) - HndScanHandlesForGC(hTable, &ScanPointerForProfilerAndETW, lp1, 0, types, _countof(types), maxgen, maxgen, flags); + HndScanHandlesForGC(hTable, &ScanPointerForProfilerAndETW, lp1, (uintptr_t)fn, types, _countof(types), maxgen, maxgen, flags); } walk = walk->pNext; } // update pointers in variable handles whose dynamic type is VHT_WEAK_SHORT, VHT_WEAK_LONG or VHT_STRONG - TraceVariableHandlesBySingleThread(&ScanPointerForProfilerAndETW, lp1, 0, VHT_WEAK_SHORT | VHT_WEAK_LONG | VHT_STRONG, maxgen, maxgen, flags); + TraceVariableHandlesBySingleThread(&ScanPointerForProfilerAndETW, lp1, (uintptr_t)fn, VHT_WEAK_SHORT | VHT_WEAK_LONG | VHT_STRONG, maxgen, maxgen, flags); } -void Ref_ScanDependentHandlesForProfilerAndETW(uint32_t maxgen, ProfilingScanContext * SC) +void Ref_ScanDependentHandlesForProfilerAndETW(uint32_t maxgen, ScanContext * SC, handle_scan_fn fn) { WRAPPER_NO_CONTRACT; @@ -1680,12 +1652,7 @@ void Ref_ScanDependentHandlesForProfilerAndETW(uint32_t maxgen, ProfilingScanCon uint32_t flags = HNDGCF_NORMAL; uintptr_t lp1 = (uintptr_t)SC; - // we'll re-use pHeapId (which was either unused (0) or freed by EndRootReferences2 - // (-1)), so reset it to NULL - _ASSERTE((*((size_t *)(&SC->pHeapId)) == (size_t)(-1)) || - (*((size_t *)(&SC->pHeapId)) == (size_t)(0))); - SC->pHeapId = NULL; - TraceDependentHandlesBySingleThread(&ScanPointerForProfilerAndETW, lp1, maxgen, maxgen, flags); + TraceDependentHandlesBySingleThread(&ScanPointerForProfilerAndETW, lp1, (uintptr_t)fn, maxgen, maxgen, flags); } #endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) diff --git a/src/Native/gc/objecthandle.h b/src/Native/gc/objecthandle.h index 89365267d..34c2a0e32 100644 --- a/src/Native/gc/objecthandle.h +++ b/src/Native/gc/objecthandle.h @@ -652,7 +652,6 @@ BOOL Ref_ContainHandle(HandleTableBucket *pBucket, OBJECTHANDLE handle); */ struct ScanContext; struct DhContext; -struct ProfilingScanContext; void Ref_BeginSynchronousGC (uint32_t uCondemnedGeneration, uint32_t uMaxGeneration); void Ref_EndSynchronousGC (uint32_t uCondemnedGeneration, uint32_t uMaxGeneration); @@ -672,10 +671,12 @@ void Ref_ScanSizedRefHandles(uint32_t condemned, uint32_t maxgen, ScanContext* s void Ref_ScanPointers(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Ref_promote_func* fn); #endif +typedef void (* handle_scan_fn)(Object** pRef, Object* pSec, uint32_t flags, ScanContext* context, BOOL isDependent); + void Ref_CheckReachable (uint32_t uCondemnedGeneration, uint32_t uMaxGeneration, uintptr_t lp1); void Ref_CheckAlive (uint32_t uCondemnedGeneration, uint32_t uMaxGeneration, uintptr_t lp1); -void Ref_ScanPointersForProfilerAndETW(uint32_t uMaxGeneration, uintptr_t lp1); -void Ref_ScanDependentHandlesForProfilerAndETW(uint32_t uMaxGeneration, ProfilingScanContext * SC); +void Ref_ScanHandlesForProfilerAndETW(uint32_t uMaxGeneration, uintptr_t lp1, handle_scan_fn fn); +void Ref_ScanDependentHandlesForProfilerAndETW(uint32_t uMaxGeneration, ScanContext * SC, handle_scan_fn fn); void Ref_AgeHandles (uint32_t uCondemnedGeneration, uint32_t uMaxGeneration, uintptr_t lp1); void Ref_RejuvenateHandles(uint32_t uCondemnedGeneration, uint32_t uMaxGeneration, uintptr_t lp1); diff --git a/src/Native/gc/sample/CMakeLists.txt b/src/Native/gc/sample/CMakeLists.txt index 572fba371..9552cc51e 100644 --- a/src/Native/gc/sample/CMakeLists.txt +++ b/src/Native/gc/sample/CMakeLists.txt @@ -22,11 +22,11 @@ set(SOURCES if(WIN32) list(APPEND SOURCES - gcenv.windows.cpp) + ../gcenv.windows.cpp) add_definitions(-DUNICODE=1) else() list(APPEND SOURCES - gcenv.unix.cpp) + ../gcenv.unix.cpp) endif() _add_executable(gcsample diff --git a/src/Native/gc/sample/GCSample.cpp b/src/Native/gc/sample/GCSample.cpp index 7f0dd8cab..112d29142 100644 --- a/src/Native/gc/sample/GCSample.cpp +++ b/src/Native/gc/sample/GCSample.cpp @@ -91,17 +91,14 @@ inline void ErectWriteBarrier(Object ** dst, Object * ref) { // if the dst is outside of the heap (unboxed value classes) then we // simply exit - if (((uint8_t*)dst < g_lowest_address) || ((uint8_t*)dst >= g_highest_address)) + if (((uint8_t*)dst < g_gc_lowest_address) || ((uint8_t*)dst >= g_gc_highest_address)) return; - if((uint8_t*)ref >= g_ephemeral_low && (uint8_t*)ref < g_ephemeral_high) - { - // volatile is used here to prevent fetch of g_card_table from being reordered - // with g_lowest/highest_address check above. See comment in code:gc_heap::grow_brick_card_tables. - uint8_t* pCardByte = (uint8_t *)*(volatile uint8_t **)(&g_card_table) + card_byte((uint8_t *)dst); - if(*pCardByte != 0xFF) - *pCardByte = 0xFF; - } + // volatile is used here to prevent fetch of g_card_table from being reordered + // with g_lowest/highest_address check above. See comment in code:gc_heap::grow_brick_card_tables. + uint8_t* pCardByte = (uint8_t *)*(volatile uint8_t **)(&g_gc_card_table) + card_byte((uint8_t *)dst); + if(*pCardByte != 0xFF) + *pCardByte = 0xFF; } void WriteBarrier(Object ** dst, Object * ref) diff --git a/src/Native/gc/sample/GCSample.vcxproj b/src/Native/gc/sample/GCSample.vcxproj index b196e1f34..1716f462e 100644 --- a/src/Native/gc/sample/GCSample.vcxproj +++ b/src/Native/gc/sample/GCSample.vcxproj @@ -84,10 +84,12 @@ - + + NotUsing + @@ -96,8 +98,7 @@ - Create - Create + Create diff --git a/src/Native/gc/sample/GCSample.vcxproj.filters b/src/Native/gc/sample/GCSample.vcxproj.filters index e46c05456..f6aacfd0c 100644 --- a/src/Native/gc/sample/GCSample.vcxproj.filters +++ b/src/Native/gc/sample/GCSample.vcxproj.filters @@ -59,7 +59,7 @@ Source Files - + Source Files diff --git a/src/Native/gc/sample/gcenv.ee.cpp b/src/Native/gc/sample/gcenv.ee.cpp index 25d829e79..ac227b482 100644 --- a/src/Native/gc/sample/gcenv.ee.cpp +++ b/src/Native/gc/sample/gcenv.ee.cpp @@ -9,6 +9,12 @@ #include "gcenv.h" #include "gc.h" +MethodTable * g_pFreeObjectMethodTable; + +int32_t g_TrapReturningThreads; + +bool g_fFinalizerRunOnShutDown; + EEConfig * g_pConfig; bool CLREventStatic::CreateManualEventNoThrow(bool bInitialState) @@ -221,6 +227,38 @@ Thread* GCToEEInterface::CreateBackgroundThread(GCBackgroundThreadFunction threa return NULL; } +void GCToEEInterface::DiagGCStart(int gen, bool isInduced) +{ +} + +void GCToEEInterface::DiagUpdateGenerationBounds() +{ +} + +void GCToEEInterface::DiagGCEnd(size_t index, int gen, int reason, bool fConcurrent) +{ +} + +void GCToEEInterface::DiagWalkFReachableObjects(void* gcContext) +{ +} + +void GCToEEInterface::DiagWalkSurvivors(void* gcContext) +{ +} + +void GCToEEInterface::DiagWalkLOHSurvivors(void* gcContext) +{ +} + +void GCToEEInterface::DiagWalkBGCSurvivors(void* gcContext) +{ +} + +void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args) +{ +} + void FinalizerThread::EnableFinalization() { // Signal to finalizer thread that there are objects to finalize @@ -238,14 +276,6 @@ bool IsGCSpecialThread() return false; } -void StompWriteBarrierEphemeral(bool /* isRuntimeSuspended */) -{ -} - -void StompWriteBarrierResize(bool /* isRuntimeSuspended */, bool /*bReqUpperBoundsCheck*/) -{ -} - bool IsGCThread() { return false; diff --git a/src/Native/gc/softwarewritewatch.cpp b/src/Native/gc/softwarewritewatch.cpp index 519744900..b85293857 100644 --- a/src/Native/gc/softwarewritewatch.cpp +++ b/src/Native/gc/softwarewritewatch.cpp @@ -3,9 +3,9 @@ // See the LICENSE file in the project root for more information. #include "common.h" -#include "softwarewritewatch.h" - #include "gcenv.h" +#include "env/gcenv.os.h" +#include "softwarewritewatch.h" #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP #ifndef DACCESS_COMPILE @@ -14,8 +14,8 @@ static_assert((static_cast(1) << SOFTWARE_WRITE_WATCH_AddressToTableByte extern "C" { - uint8_t *g_sw_ww_table = nullptr; - bool g_sw_ww_enabled_for_gc_heap = false; + uint8_t *g_gc_sw_ww_table = nullptr; + bool g_gc_sw_ww_enabled_for_gc_heap = false; } void SoftwareWriteWatch::StaticClose() @@ -25,8 +25,8 @@ void SoftwareWriteWatch::StaticClose() return; } - g_sw_ww_enabled_for_gc_heap = false; - g_sw_ww_table = nullptr; + g_gc_sw_ww_enabled_for_gc_heap = false; + g_gc_sw_ww_table = nullptr; } bool SoftwareWriteWatch::GetDirtyFromBlock( diff --git a/src/Native/gc/softwarewritewatch.h b/src/Native/gc/softwarewritewatch.h index 3c8491cec..0e6e6c819 100644 --- a/src/Native/gc/softwarewritewatch.h +++ b/src/Native/gc/softwarewritewatch.h @@ -5,25 +5,20 @@ #ifndef __SOFTWARE_WRITE_WATCH_H__ #define __SOFTWARE_WRITE_WATCH_H__ +#include "gcinterface.h" +#include "gc.h" + #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP #ifndef DACCESS_COMPILE -extern void SwitchToWriteWatchBarrier(bool isRuntimeSuspended); -extern void SwitchToNonWriteWatchBarrier(bool isRuntimeSuspended); - -#define SOFTWARE_WRITE_WATCH_AddressToTableByteIndexShift 0xc - extern "C" { // Table containing the dirty state. This table is translated to exclude the lowest address it represents, see // TranslateTableToExcludeHeapStartAddress. - extern uint8_t *g_sw_ww_table; + extern uint8_t *g_gc_sw_ww_table; // Write watch may be disabled when it is not needed (between GCs for instance). This indicates whether it is enabled. - extern bool g_sw_ww_enabled_for_gc_heap; - - extern uint8_t *g_lowest_address; // start address of the GC heap - extern uint8_t *g_highest_address; // end address of the GC heap + extern bool g_gc_sw_ww_enabled_for_gc_heap; } class SoftwareWriteWatch @@ -116,7 +111,7 @@ inline void SoftwareWriteWatch::VerifyMemoryRegion( inline uint8_t *SoftwareWriteWatch::GetTable() { - return g_sw_ww_table; + return g_gc_sw_ww_table; } inline uint8_t *SoftwareWriteWatch::GetUntranslatedTable() @@ -163,7 +158,7 @@ inline void SoftwareWriteWatch::SetUntranslatedTable(uint8_t *untranslatedTable, assert(ALIGN_DOWN(untranslatedTable, sizeof(size_t)) == untranslatedTable); assert(heapStartAddress != nullptr); - g_sw_ww_table = TranslateTableToExcludeHeapStartAddress(untranslatedTable, heapStartAddress); + g_gc_sw_ww_table = TranslateTableToExcludeHeapStartAddress(untranslatedTable, heapStartAddress); } inline void SoftwareWriteWatch::SetResizedUntranslatedTable( @@ -194,7 +189,7 @@ inline void SoftwareWriteWatch::SetResizedUntranslatedTable( inline bool SoftwareWriteWatch::IsEnabledForGCHeap() { - return g_sw_ww_enabled_for_gc_heap; + return g_gc_sw_ww_enabled_for_gc_heap; } inline void SoftwareWriteWatch::EnableForGCHeap() @@ -204,9 +199,13 @@ inline void SoftwareWriteWatch::EnableForGCHeap() VerifyCreated(); assert(!IsEnabledForGCHeap()); + g_gc_sw_ww_enabled_for_gc_heap = true; - g_sw_ww_enabled_for_gc_heap = true; - SwitchToWriteWatchBarrier(true); + WriteBarrierParameters args = {}; + args.operation = WriteBarrierOp::SwitchToWriteWatch; + args.write_watch_table = g_gc_sw_ww_table; + args.is_runtime_suspended = true; + GCToEEInterface::StompWriteBarrier(&args); } inline void SoftwareWriteWatch::DisableForGCHeap() @@ -216,19 +215,22 @@ inline void SoftwareWriteWatch::DisableForGCHeap() VerifyCreated(); assert(IsEnabledForGCHeap()); + g_gc_sw_ww_enabled_for_gc_heap = false; - g_sw_ww_enabled_for_gc_heap = false; - SwitchToNonWriteWatchBarrier(true); + WriteBarrierParameters args = {}; + args.operation = WriteBarrierOp::SwitchToNonWriteWatch; + args.is_runtime_suspended = true; + GCToEEInterface::StompWriteBarrier(&args); } inline void *SoftwareWriteWatch::GetHeapStartAddress() { - return g_lowest_address; + return g_gc_lowest_address; } inline void *SoftwareWriteWatch::GetHeapEndAddress() { - return g_highest_address; + return g_gc_highest_address; } inline size_t SoftwareWriteWatch::GetTableByteIndex(void *address) -- cgit v1.2.3 From d542b212f6b6fa4aa510b0db154c5f84ef9cdcee Mon Sep 17 00:00:00 2001 From: fadimounir Date: Fri, 13 Jan 2017 12:14:11 -0800 Subject: Fix issue 2498: Native layout signatures for nullable types. --- .../DependencyAnalysis/NativeLayoutVertexNode.cs | 37 +++++++--------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs index fd5a394d2..1283b45fe 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs @@ -237,34 +237,19 @@ namespace ILCompiler.DependencyAnalysis case Internal.TypeSystem.TypeFlags.SignatureMethodVariable: return new NativeLayoutGenericVarSignatureVertexNode(factory, type); - case Internal.TypeSystem.TypeFlags.Void: - case Internal.TypeSystem.TypeFlags.Boolean: - case Internal.TypeSystem.TypeFlags.Char: - case Internal.TypeSystem.TypeFlags.SByte: - case Internal.TypeSystem.TypeFlags.Byte: - case Internal.TypeSystem.TypeFlags.Int16: - case Internal.TypeSystem.TypeFlags.UInt16: - case Internal.TypeSystem.TypeFlags.Int32: - case Internal.TypeSystem.TypeFlags.UInt32: - case Internal.TypeSystem.TypeFlags.Int64: - case Internal.TypeSystem.TypeFlags.UInt64: - case Internal.TypeSystem.TypeFlags.Single: - case Internal.TypeSystem.TypeFlags.Double: - case Internal.TypeSystem.TypeFlags.IntPtr: - case Internal.TypeSystem.TypeFlags.UIntPtr: - case Internal.TypeSystem.TypeFlags.Enum: - case Internal.TypeSystem.TypeFlags.Class: - case Internal.TypeSystem.TypeFlags.ValueType: - case Internal.TypeSystem.TypeFlags.Interface: - if (type.HasInstantiation && !type.IsGenericDefinition) - return new NativeLayoutInstantiatedTypeSignatureVertexNode(factory, type); - else - return new NativeLayoutEETypeSignatureVertexNode(factory, type); - - // TODO case Internal.TypeSystem.TypeFlags.FunctionPointer: + // TODO Internal.TypeSystem.TypeFlags.FunctionPointer (Runtime parsing also not yet implemented) + case Internal.TypeSystem.TypeFlags.FunctionPointer: + throw new NotImplementedException("FunctionPointer signature"); default: - throw new NotImplementedException("NYI"); + { + Debug.Assert(type.IsDefType); + + if (type.HasInstantiation && !type.IsGenericDefinition) + return new NativeLayoutInstantiatedTypeSignatureVertexNode(factory, type); + else + return new NativeLayoutEETypeSignatureVertexNode(factory, type); + } } } -- cgit v1.2.3 From 8089be9159c90226af98f5fa25c6df6014605b6f Mon Sep 17 00:00:00 2001 From: Simon Nattress Date: Mon, 9 Jan 2017 18:14:43 -0800 Subject: Run ordered class constructors explicitly Previously, order-dependent class constructors were run by looking at the EagerOrderedClassConstructorAttribute and sorting them accordingly. That mechanism is not ideal for scenarios where separately compiled assembly object files are linked together since we would then have to sort all class constructors from modules on startup. Replace the attribute mechanism with a simple list of module initializers to run. The startup code injects calls to these at runtime after module tables are initialized. Internal.Runtime.TypeLoaderEnvironment..cctor is disabled for C++ code generation due to issue #2486. Remove cctors under #if CORERT from types we want to initialize in an explicit order. Add an Initialize method to those types. Introduce the `ModuleInitializers` class which has a list of assemblies with module-level initialization code to run. Each such assembly has a special type which calls the various Initialize methods in that assembly. The lookup policy here is non-fatal by design so we can compose these things in future more easily. Use a wildcard for the class library so that if a new class library is introduced, it can provide the appropriate module initializer type and automatically get initialized. Remove some old code in `CppWriter` that stubbed out the cctor in `ReflectionExecution`. Instead that is specified in the configuration for System.Private.Reflection.Execution in `ModuleInitializers`. Move the statics in `ClassConstructorRunner+Cctor` up into the nesting class so we need one fewer eager cctors. DeveloperExperience now uses the library initializer mechanism. --- .../Compiler/CompilerTypeSystemContext.TypeInit.cs | 52 +---------- .../src/Compiler/DependencyAnalysis/NodeFactory.cs | 2 +- .../src/Compiler/LibraryInitializers.cs | 103 +++++++++++++++++++++ .../src/Compiler/MainMethodRootProvider.cs | 7 +- .../src/CppCodeGen/CppWriter.cs | 14 --- .../IL/Stubs/StartupCode/StartupCodeMainMethod.cs | 18 ++-- .../src/ILCompiler.Compiler.csproj | 3 +- src/ILCompiler/src/Program.cs | 18 +--- .../Runtime/CompilerHelpers/LibraryInitializer.cs | 26 ++++++ .../src/System.Private.CoreLib.csproj | 3 +- .../CompilerServices/ClassConstructorRunner.cs | 44 ++++++--- .../src/System/Runtime/TypeLoaderExports.cs | 25 ++++- .../src/System/RuntimeExceptionHelpers.cs | 19 +++- .../Runtime/CompilerHelpers/LibraryInitializer.cs | 18 ++++ ...stem.Private.DeveloperExperience.Console.csproj | 1 + .../Reflection/Execution/ReflectionExecution.cs | 14 ++- .../Runtime/CompilerHelpers/LibraryInitializer.cs | 18 ++++ .../src/System.Private.Reflection.Execution.csproj | 1 + .../Runtime/CompilerHelpers/LibraryInitializer.cs | 18 ++++ .../TypeLoaderEnvironment.LdTokenResultLookup.cs | 2 +- .../TypeLoaderEnvironment.NamedTypeLookup.cs | 2 +- .../Runtime/TypeLoader/TypeLoaderEnvironment.cs | 16 +++- .../src/System.Private.TypeLoader.csproj | 2 +- 23 files changed, 313 insertions(+), 113 deletions(-) create mode 100644 src/ILCompiler.Compiler/src/Compiler/LibraryInitializers.cs create mode 100644 src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs create mode 100644 src/System.Private.DeveloperExperience.Console/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs create mode 100644 src/System.Private.Reflection.Execution/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs create mode 100644 src/System.Private.TypeLoader/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs diff --git a/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.TypeInit.cs b/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.TypeInit.cs index 8d96d3078..76ebf89a7 100644 --- a/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.TypeInit.cs +++ b/src/ILCompiler.Compiler/src/Compiler/CompilerTypeSystemContext.TypeInit.cs @@ -41,56 +41,8 @@ namespace ILCompiler private static bool HasEagerConstructorAttribute(TypeDesc type) { MetadataType mdType = type as MetadataType; - return mdType != null && ( - mdType.HasCustomAttribute("System.Runtime.CompilerServices", "EagerOrderedStaticConstructorAttribute") - || mdType.HasCustomAttribute("System.Runtime.CompilerServices", "EagerStaticClassConstructionAttribute")); - } - } - - public class EagerConstructorComparer : IComparer - { - private int GetConstructionOrder(MetadataType type) - { - // For EagerOrderedStaticConstructorAttribute, order is defined by an integer. - // For the other case (EagerStaticClassConstructionAttribute), order is defined - // implicitly. - - var decoded = ((EcmaType)type.GetTypeDefinition()).GetDecodedCustomAttribute( - "System.Runtime.CompilerServices", "EagerOrderedStaticConstructorAttribute"); - - if (decoded != null) - return (int)decoded.Value.FixedArguments[0].Value; - - Debug.Assert(type.HasCustomAttribute("System.Runtime.CompilerServices", "EagerStaticClassConstructionAttribute")); - // RhBind on .NET Native for UWP will sort these based on static dependencies of the .cctors. - // We could probably do the same, but this attribute is pretty much deprecated in favor of - // EagerOrderedStaticConstructorAttribute that has explicit order. The remaining uses of - // the unordered one don't appear to have dependencies, so sorting them all before the - // ordered ones should do. - return -1; - } - - public int Compare(DependencyAnalysis.IMethodNode x, DependencyAnalysis.IMethodNode y) - { - var typeX = (MetadataType)x.Method.OwningType; - var typeY = (MetadataType)y.Method.OwningType; - - int orderX = GetConstructionOrder(typeX); - int orderY = GetConstructionOrder(typeY); - - int result; - if (orderX != orderY) - { - result = Comparer.Default.Compare(orderX, orderY); - } - else - { - // Use type name as a tie breaker. We need this algorithm to produce stable - // ordering so that the sequence of eager cctors is deterministic. - result = String.Compare(typeX.GetFullName(), typeY.GetFullName(), StringComparison.Ordinal); - } - - return result; + return mdType != null && + mdType.HasCustomAttribute("System.Runtime.CompilerServices", "EagerStaticClassConstructionAttribute"); } } } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs index e9cb6ca7d..79feb94a6 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs @@ -657,7 +657,7 @@ namespace ILCompiler.DependencyAnalysis public ArrayOfEmbeddedPointersNode EagerCctorTable = new ArrayOfEmbeddedPointersNode( "__EagerCctorStart", "__EagerCctorEnd", - new EagerConstructorComparer()); + null); public ArrayOfEmbeddedPointersNode DispatchMapTable = new ArrayOfEmbeddedPointersNode( "__DispatchMapTableStart", diff --git a/src/ILCompiler.Compiler/src/Compiler/LibraryInitializers.cs b/src/ILCompiler.Compiler/src/Compiler/LibraryInitializers.cs new file mode 100644 index 000000000..eb3a3c07b --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/LibraryInitializers.cs @@ -0,0 +1,103 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; + +using Internal.TypeSystem; + +using AssemblyName = System.Reflection.AssemblyName; +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler +{ + /// + /// Encapsulates a list of class constructors that must be run in a prescribed order during start-up + /// + public sealed class LibraryInitializers + { + private const string ClassLibraryPlaceHolderString = "*ClassLibrary*"; + private const string LibraryInitializerContainerNamespaceName = "Internal.Runtime.CompilerHelpers"; + private const string LibraryInitializerContainerTypeName = "LibraryInitializer"; + private const string LibraryInitializerMethodName = "InitializeLibrary"; + + private static readonly LibraryInitializerInfo[] s_assembliesWithLibraryInitializers = + { + new LibraryInitializerInfo(ClassLibraryPlaceHolderString), + new LibraryInitializerInfo("System.Private.TypeLoader", false), + new LibraryInitializerInfo("System.Private.Reflection.Execution", false), + new LibraryInitializerInfo("System.Private.DeveloperExperience.Console"), + }; + + private List _libraryInitializerMethods; + + private readonly TypeSystemContext _context; + private readonly bool _isCppCodeGen; + + public LibraryInitializers(TypeSystemContext context, bool isCppCodeGen) + { + _context = context; + // + // We should not care which code-gen is being used, however currently CppCodeGen cannot + // handle code pulled in by all explicit cctors. + // + // See https://github.com/dotnet/corert/issues/2486 + // + _isCppCodeGen = isCppCodeGen; + } + + public IList LibraryInitializerMethods + { + get + { + if (_libraryInitializerMethods == null) + InitLibraryInitializers(); + + return _libraryInitializerMethods; + } + } + + private void InitLibraryInitializers() + { + Debug.Assert(_libraryInitializerMethods == null); + + _libraryInitializerMethods = new List(); + + foreach (var entry in s_assembliesWithLibraryInitializers) + { + if (_isCppCodeGen && !entry.UseWithCppCodeGen) + continue; + + ModuleDesc assembly = entry.Assembly == ClassLibraryPlaceHolderString + ? _context.SystemModule + : _context.ResolveAssembly(new AssemblyName(entry.Assembly), false); + + if (assembly == null) + continue; + + TypeDesc containingType = assembly.GetType(LibraryInitializerContainerNamespaceName, LibraryInitializerContainerTypeName, false); + if (containingType == null) + continue; + + MethodDesc initializerMethod = containingType.GetMethod(LibraryInitializerMethodName, null); + if (initializerMethod == null) + continue; + + _libraryInitializerMethods.Add(initializerMethod); + } + } + + private sealed class LibraryInitializerInfo + { + public string Assembly { get; } + public bool UseWithCppCodeGen { get; } + + public LibraryInitializerInfo(string assembly, bool useWithCppCodeGen = true) + { + Assembly = assembly; + UseWithCppCodeGen = useWithCppCodeGen; + } + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/MainMethodRootProvider.cs b/src/ILCompiler.Compiler/src/Compiler/MainMethodRootProvider.cs index ebcfc24e7..4df659ad8 100644 --- a/src/ILCompiler.Compiler/src/Compiler/MainMethodRootProvider.cs +++ b/src/ILCompiler.Compiler/src/Compiler/MainMethodRootProvider.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; @@ -21,10 +22,12 @@ namespace ILCompiler public const string ManagedEntryPointMethodName = "__managed__Main"; private EcmaModule _module; + private IList _libraryInitializers; - public MainMethodRootProvider(EcmaModule module) + public MainMethodRootProvider(EcmaModule module, IList libraryInitializers) { _module = module; + _libraryInitializers = libraryInitializers; } public void AddCompilationRoots(IRootingServiceProvider rootProvider) @@ -34,7 +37,7 @@ namespace ILCompiler throw new Exception("No managed entrypoint defined for executable module"); TypeDesc owningType = _module.GetGlobalModuleType(); - var startupCodeMain = new StartupCodeMainMethod(owningType, mainMethod); + var startupCodeMain = new StartupCodeMainMethod(owningType, mainMethod, _libraryInitializers); rootProvider.AddCompilationRoot(startupCodeMain, "Startup Code Main Method", ManagedEntryPointMethodName); } diff --git a/src/ILCompiler.Compiler/src/CppCodeGen/CppWriter.cs b/src/ILCompiler.Compiler/src/CppCodeGen/CppWriter.cs index c9bdff5c9..f4fc041f7 100644 --- a/src/ILCompiler.Compiler/src/CppCodeGen/CppWriter.cs +++ b/src/ILCompiler.Compiler/src/CppCodeGen/CppWriter.cs @@ -378,20 +378,6 @@ namespace ILCompiler.CppCodeGen if (methodIL == null) return; - // TODO: Remove this code once CppCodegen is able to generate code for the reflection startup path. - // The startup path runs before any user code is executed. - // For now we replace the startup path with a simple "ret". Reflection won't work, but - // programs not using reflection will. - if (method.Name == ".cctor") - { - MetadataType owningType = method.OwningType as MetadataType; - if (owningType != null && - owningType.Name == "ReflectionExecution" && owningType.Namespace == "Internal.Reflection.Execution") - { - methodIL = new Internal.IL.Stubs.ILStubMethodIL(method, new byte[] { (byte)ILOpcode.ret }, Array.Empty(), null); - } - } - try { // TODO: hacky special-case diff --git a/src/ILCompiler.Compiler/src/IL/Stubs/StartupCode/StartupCodeMainMethod.cs b/src/ILCompiler.Compiler/src/IL/Stubs/StartupCode/StartupCodeMainMethod.cs index afb38ad6c..251dd3af3 100644 --- a/src/ILCompiler.Compiler/src/IL/Stubs/StartupCode/StartupCodeMainMethod.cs +++ b/src/ILCompiler.Compiler/src/IL/Stubs/StartupCode/StartupCodeMainMethod.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using Internal.TypeSystem; @@ -20,11 +21,13 @@ namespace Internal.IL.Stubs.StartupCode private TypeDesc _owningType; private MainMethodWrapper _mainMethod; private MethodSignature _signature; + private IList _libraryInitializers; - public StartupCodeMainMethod(TypeDesc owningType, MethodDesc mainMethod) + public StartupCodeMainMethod(TypeDesc owningType, MethodDesc mainMethod, IList libraryInitializers) { _owningType = owningType; _mainMethod = new MainMethodWrapper(owningType, mainMethod); + _libraryInitializers = libraryInitializers; } public override TypeSystemContext Context @@ -56,14 +59,15 @@ namespace Internal.IL.Stubs.StartupCode ILEmitter emitter = new ILEmitter(); ILCodeStream codeStream = emitter.NewCodeStream(); - ModuleDesc developerExperience = Context.ResolveAssembly(new AssemblyName("System.Private.DeveloperExperience.Console"), false); - if (developerExperience != null) + // Allow the class library to run explicitly ordered class constructors first thing in start-up. + if (_libraryInitializers != null) { - TypeDesc connectorType = developerExperience.GetKnownType("Internal.DeveloperExperience", "DeveloperExperienceConnectorConsole"); - MethodDesc initializeMethod = connectorType.GetKnownMethod("Initialize", null); - codeStream.Emit(ILOpcode.call, emitter.NewToken(initializeMethod)); + foreach (MethodDesc method in _libraryInitializers) + { + codeStream.Emit(ILOpcode.call, emitter.NewToken(method)); + } } - + MetadataType startup = Context.GetHelperType("StartupCodeHelpers"); // Initialize command line args if the class library supports this diff --git a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj index f5f2f3f3d..acc7da1dd 100644 --- a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj +++ b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj @@ -90,6 +90,7 @@ JitInterface\JitConfigProvider.cs + @@ -266,7 +267,7 @@ TypeSystem\Interop\IL\PInvokeMethodData.cs - + IL\Stubs\UnsafeIntrinsics.cs diff --git a/src/ILCompiler/src/Program.cs b/src/ILCompiler/src/Program.cs index 04e265623..16793a5ac 100644 --- a/src/ILCompiler/src/Program.cs +++ b/src/ILCompiler/src/Program.cs @@ -209,7 +209,9 @@ namespace ILCompiler if (entrypointModule != null) { - compilationRoots.Add(new MainMethodRootProvider(entrypointModule)); + LibraryInitializers libraryInitializers = + new LibraryInitializers(typeSystemContext, _isCppCodegen); + compilationRoots.Add(new MainMethodRootProvider(entrypointModule, libraryInitializers.LibraryInitializerMethods)); } if (_multiFile) @@ -240,20 +242,6 @@ namespace ILCompiler compilationRoots.Add(new ExportedMethodsRootProvider((EcmaModule)typeSystemContext.SystemModule)); - // System.Private.Reflection.Execution needs to establish a communication channel with System.Private.CoreLib - // at process startup. This is done through an eager constructor that calls into CoreLib and passes it - // a callback object. - // - // Since CoreLib cannot reference anything, the type and it's eager constructor won't be added to the compilation - // unless we explictly add it. - - var refExec = typeSystemContext.GetModuleForSimpleName("System.Private.Reflection.Execution", false); - if (refExec != null) - { - var exec = refExec.GetType("Internal.Reflection.Execution", "ReflectionExecution"); - compilationRoots.Add(new SingleMethodRootProvider(exec.GetStaticConstructor())); - } - compilationGroup = new SingleFileCompilationModuleGroup(typeSystemContext); } diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs new file mode 100644 index 000000000..e2a2b318b --- /dev/null +++ b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime; +using System.Runtime.CompilerServices; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.Runtime.CompilerHelpers +{ + /// + /// Container class to run specific class constructors in a defined order. Since we can't + /// directly invoke class constructors in C#, they're renamed Initialize. + /// + internal class LibraryInitializer + { + public static void InitializeLibrary() + { + PreallocatedOutOfMemoryException.Initialize(); + ClassConstructorRunner.Initialize(); + TypeLoaderExports.Initialize(); + } + } +} diff --git a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj index d5b115c32..15ea8ef11 100644 --- a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -63,6 +63,7 @@ + @@ -1183,4 +1184,4 @@ - + \ No newline at end of file diff --git a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs index b0ec3c9f9..1b917fb94 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs @@ -13,7 +13,7 @@ using Internal.Runtime.CompilerHelpers; namespace System.Runtime.CompilerServices { - // Marked [EagerStaticClassConstruction] because Cctor.GetCctor + // Marked [EagerOrderedStaticConstructor] because Cctor.GetCctor // uses _cctorGlobalLock [EagerOrderedStaticConstructor(EagerStaticConstructorOrder.CompilerServicesClassConstructorRunner)] internal static partial class ClassConstructorRunner @@ -262,10 +262,8 @@ namespace System.Runtime.CompilerServices //============================================================================================================== // These structs are allocated on demand whenever the runtime tries to run a class constructor. Once the // the class constructor has been successfully initialized, we reclaim this structure. The structure is long- - // lived only if the class constructor threw an exception. This must be marked [EagerStaticClassConstruction] to - // avoid infinite mutual recursion in GetCctor. + // lived only if the class constructor threw an exception. //============================================================================================================== - [EagerOrderedStaticConstructor(EagerStaticConstructorOrder.CompilerServicesClassConstructorRunnerCctor)] private unsafe struct Cctor { public Lock Lock; @@ -273,14 +271,7 @@ namespace System.Runtime.CompilerServices public int HoldingThread; private int _refCount; private StaticClassConstructionContext* _pContext; - - // Because Cctor's are mutable structs, we have to give our callers raw references to the underlying arrays - // for this collection to be usable. This also means once we place a Cctor in an array, we can't grow or - // reallocate the array. - private static Cctor[][] s_cctorArrays = new Cctor[10][]; - private static int s_cctorArraysCount = 0; - private static int s_count; - + //========================================================================================================== // Gets the Cctor entry associated with a specific class constructor context (creating it if necessary.) //========================================================================================================== @@ -479,8 +470,35 @@ namespace System.Runtime.CompilerServices private static int s_nextBlockingRecordIndex; } - private static Lock s_cctorGlobalLock = new Lock(); + private static Lock s_cctorGlobalLock; + + // These three statics are used by ClassConstructorRunner.Cctor but moved out to avoid an unnecessary + // extra class constructor call. + // + // Because Cctor's are mutable structs, we have to give our callers raw references to the underlying arrays + // for this collection to be usable. This also means once we place a Cctor in an array, we can't grow or + // reallocate the array. + private static Cctor[][] s_cctorArrays; + private static int s_cctorArraysCount; + private static int s_count; + // + // CoreRT calls Initialize directly for all types its needs that typically have EagerOrderedStaticConstructor + // attributes. To retain compatibility, please ensure static initialization is not done inline, and instead + // added to Initialize. + // +#if !CORERT + static ClassConstructorRunner() + { + Initialize(); + } +#endif + internal static void Initialize() + { + s_cctorArrays = new Cctor[10][]; + s_cctorGlobalLock = new Lock(); + } + [Conditional("ENABLE_NOISY_CCTOR_LOG")] private static void NoisyLog(string format, IntPtr cctorMethod, int threadId) { diff --git a/src/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs b/src/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs index 7f8cf556e..57e8efd79 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs @@ -73,12 +73,31 @@ namespace System.Runtime // Initialize the cache eagerly to avoid null checks. // Use array with just single element to make this pay-for-play. The actual cache will be allocated only // once the lazy lookups are actually needed. - private static Entry[] s_cache = new Entry[1]; + private static Entry[] s_cache; private static Lock s_lock; private static GCHandle s_previousCache; - private volatile static IntPtr[] s_resolutionFunctionPointers = new IntPtr[4]; - private static int s_nextResolutionFunctionPointerIndex = (int)SignatureKind.Count; + private volatile static IntPtr[] s_resolutionFunctionPointers; + private static int s_nextResolutionFunctionPointerIndex; + + // + // CoreRT calls Initialize directly for all types its needs that typically have EagerOrderedStaticConstructor + // attributes. To retain compatibility, please ensure static initialization is not done inline, and instead + // added to Initialize. + // +#if !CORERT + static TypeLoaderExports() + { + Initialize(); + } +#endif + + internal static void Initialize() + { + s_cache = new Entry[1]; + s_resolutionFunctionPointers = new IntPtr[4]; + s_nextResolutionFunctionPointerIndex = (int)SignatureKind.Count; + } [RuntimeExport("GenericLookup")] public static IntPtr GenericLookup(IntPtr context, IntPtr signature) diff --git a/src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs b/src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs index 5c3945636..c53f1a251 100644 --- a/src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs +++ b/src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs @@ -17,7 +17,24 @@ namespace System [EagerOrderedStaticConstructor(EagerStaticConstructorOrder.SystemPreallocatedOutOfMemoryException)] internal class PreallocatedOutOfMemoryException { - public static readonly OutOfMemoryException Instance = new OutOfMemoryException(message: null); // Cannot call the nullary constructor as that triggers non-trivial resource manager logic. + public static OutOfMemoryException Instance { get; private set; } + + // + // CoreRT calls Initialize directly for all types its needs that typically have EagerOrderedStaticConstructor + // attributes. To retain compatibility, please ensure static initialization is not done inline, and instead + // added to Initialize. + // +#if !CORERT + static PreallocatedOutOfMemoryException() + { + Initialize(); + } +#endif + + internal static void Initialize() + { + Instance = new OutOfMemoryException(message: null); // Cannot call the nullary constructor as that triggers non-trivial resource manager logic. + } } public class RuntimeExceptionHelpers diff --git a/src/System.Private.DeveloperExperience.Console/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs b/src/System.Private.DeveloperExperience.Console/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs new file mode 100644 index 000000000..68af06c7f --- /dev/null +++ b/src/System.Private.DeveloperExperience.Console/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +using Internal.DeveloperExperience; + +namespace Internal.Runtime.CompilerHelpers +{ + public class LibraryInitializer + { + public static void InitializeLibrary() + { + DeveloperExperienceConnectorConsole.Initialize(); + } + } +} diff --git a/src/System.Private.DeveloperExperience.Console/src/System.Private.DeveloperExperience.Console.csproj b/src/System.Private.DeveloperExperience.Console/src/System.Private.DeveloperExperience.Console.csproj index 5cf519665..5a748e1b4 100644 --- a/src/System.Private.DeveloperExperience.Console/src/System.Private.DeveloperExperience.Console.csproj +++ b/src/System.Private.DeveloperExperience.Console/src/System.Private.DeveloperExperience.Console.csproj @@ -33,6 +33,7 @@ + diff --git a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecution.cs b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecution.cs index 453dc7637..5d287ca1c 100644 --- a/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecution.cs +++ b/src/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecution.cs @@ -38,12 +38,24 @@ namespace Internal.Reflection.Execution [EagerOrderedStaticConstructor(EagerStaticConstructorOrder.ReflectionExecution)] public static class ReflectionExecution { + // + // CoreRT calls Initialize directly for all types its needs that typically have EagerOrderedStaticConstructor + // attributes. To retain compatibility, please ensure static initialization is not done inline, and instead + // added to Initialize. + // +#if !CORERT /// /// This eager constructor initializes runtime reflection support. As part of ExecutionEnvironmentImplementation /// initialization it enumerates the modules and registers the ones containing EmbeddedMetadata reflection blobs /// in its _moduleToMetadataReader map. /// static ReflectionExecution() + { + Initialize(); + } +#endif + + internal static void Initialize() { // Initialize Reflection.Core's one and only ExecutionDomain. ExecutionEnvironmentImplementation executionEnvironment = new ExecutionEnvironmentImplementation(); @@ -85,7 +97,7 @@ namespace Internal.Reflection.Execution return ReflectionCoreExecution.ExecutionDomain.GetType(typeName, assemblyResolver, typeResolver, throwOnError, ignoreCase, defaultAssemblies); } - internal static ExecutionEnvironmentImplementation ExecutionEnvironment { get; } + internal static ExecutionEnvironmentImplementation ExecutionEnvironment { get; private set; } internal static IList DefaultAssemblyNamesForGetType; } diff --git a/src/System.Private.Reflection.Execution/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs b/src/System.Private.Reflection.Execution/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs new file mode 100644 index 000000000..9932db2d1 --- /dev/null +++ b/src/System.Private.Reflection.Execution/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +using Internal.Reflection.Execution; + +namespace Internal.Runtime.CompilerHelpers +{ + public class LibraryInitializer + { + public static void InitializeLibrary() + { + ReflectionExecution.Initialize(); + } + } +} diff --git a/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj b/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj index 4939f3853..20a4da520 100644 --- a/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj +++ b/src/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj @@ -90,6 +90,7 @@ + diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs new file mode 100644 index 000000000..5b7964f8a --- /dev/null +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/CompilerHelpers/LibraryInitializer.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +using Internal.Runtime.TypeLoader; + +namespace Internal.Runtime.CompilerHelpers +{ + public class LibraryInitializer + { + public static void InitializeLibrary() + { + TypeLoaderEnvironment.Initialize(); + } + } +} diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs index c1acd5517..777352910 100644 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs @@ -83,7 +83,7 @@ namespace Internal.Runtime.TypeLoader return Encoding.UTF8.GetString(dataStream, checked((int)stringLen)); } - private static LowLevelDictionary s_nativeFormatStrings = new LowLevelDictionary(); + private static LowLevelDictionary s_nativeFormatStrings; /// /// From a string, get a pointer to an allocated memory location that holds a NativeFormat encoded string. diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.NamedTypeLookup.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.NamedTypeLookup.cs index 64bdbed2c..9f1b828be 100644 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.NamedTypeLookup.cs +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.NamedTypeLookup.cs @@ -37,7 +37,7 @@ namespace Internal.Runtime.TypeLoader private NamedTypeRuntimeTypeHandleToMetadataHashtable _runtimeTypeHandleToMetadataHashtable = new NamedTypeRuntimeTypeHandleToMetadataHashtable(); - public static readonly IntPtr NoStaticsData = (IntPtr)1; + public static IntPtr NoStaticsData { get; private set; } private class NamedTypeRuntimeTypeHandleToMetadataHashtable : LockFreeReaderHashtable { diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs index b8d8deb11..b6309bd22 100644 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs @@ -92,7 +92,7 @@ namespace Internal.Runtime.TypeLoader [ThreadStatic] private static bool t_isReentrant; - public static readonly TypeLoaderEnvironment Instance; + public static TypeLoaderEnvironment Instance { get; private set; } /// /// List of loaded binary modules is typically used to locate / process various metadata blobs @@ -107,10 +107,24 @@ namespace Internal.Runtime.TypeLoader [ThreadStatic] private static LowLevelDictionary t_moduleNativeReaders; + // + // CoreRT calls Initialize directly for all types its needs that typically have EagerOrderedStaticConstructor + // attributes. To retain compatibility, please ensure static initialization is not done inline, and instead + // added to Initialize. + // +#if !CORERT static TypeLoaderEnvironment() + { + Initialize(); + } +#endif + + internal static void Initialize() { Instance = new TypeLoaderEnvironment(); RuntimeAugments.InitializeLookups(new Callbacks()); + s_nativeFormatStrings = new LowLevelDictionary(); + NoStaticsData = (IntPtr)1; } public TypeLoaderEnvironment() diff --git a/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj b/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj index d1c371050..58f967904 100644 --- a/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj +++ b/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj @@ -27,7 +27,6 @@ - @@ -264,6 +263,7 @@ Internal\TypeSystem\WellKnownType.cs + -- cgit v1.2.3 From b71a9f8ff40b8065f7d0d254d6b0e2fae8f65ea2 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Fri, 13 Jan 2017 15:41:19 -0800 Subject: Move around OS-specific gcenv files to match CoreCLR (#2506) --- src/Native/gc/gcenv.unix.cpp | 308 ++++++++++++++++ src/Native/gc/gcenv.windows.cpp | 625 +++++++++++++++++++++++++++++++++ src/Native/gc/sample/gcenv.unix.cpp | 14 - src/Native/gc/sample/gcenv.windows.cpp | 453 ------------------------ 4 files changed, 933 insertions(+), 467 deletions(-) create mode 100644 src/Native/gc/gcenv.unix.cpp create mode 100644 src/Native/gc/gcenv.windows.cpp delete mode 100644 src/Native/gc/sample/gcenv.unix.cpp delete mode 100644 src/Native/gc/sample/gcenv.windows.cpp diff --git a/src/Native/gc/gcenv.unix.cpp b/src/Native/gc/gcenv.unix.cpp new file mode 100644 index 000000000..0235952e2 --- /dev/null +++ b/src/Native/gc/gcenv.unix.cpp @@ -0,0 +1,308 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include "env/gcenv.structs.h" +#include "env/gcenv.base.h" +#include "env/gcenv.os.h" + +// Initialize the interface implementation +// Return: +// true if it has succeeded, false if it has failed +bool GCToOSInterface::Initialize() +{ + throw nullptr; +} + +// Shutdown the interface implementation +void GCToOSInterface::Shutdown() +{ + throw nullptr; +} + +// Get numeric id of the current thread if possible on the +// current platform. It is indended for logging purposes only. +// Return: +// Numeric id of the current thread or 0 if the +uint64_t GCToOSInterface::GetCurrentThreadIdForLogging() +{ + throw nullptr; +} + +// Get id of the process +uint32_t GCToOSInterface::GetCurrentProcessId() +{ + throw nullptr; +} + +// Set ideal affinity for the current thread +// Parameters: +// affinity - ideal processor affinity for the thread +// Return: +// true if it has succeeded, false if it has failed +bool GCToOSInterface::SetCurrentThreadIdealAffinity(GCThreadAffinity* affinity) +{ + throw nullptr; +} + +// Get the number of the current processor +uint32_t GCToOSInterface::GetCurrentProcessorNumber() +{ + throw nullptr; +} + +// Check if the OS supports getting current processor number +bool GCToOSInterface::CanGetCurrentProcessorNumber() +{ + throw nullptr; +} + +// Flush write buffers of processors that are executing threads of the current process +void GCToOSInterface::FlushProcessWriteBuffers() +{ + throw nullptr; +} + +// Break into a debugger +void GCToOSInterface::DebugBreak() +{ + throw nullptr; +} + +// Get number of logical processors +uint32_t GCToOSInterface::GetLogicalCpuCount() +{ + throw nullptr; +} + +// Causes the calling thread to sleep for the specified number of milliseconds +// Parameters: +// sleepMSec - time to sleep before switching to another thread +void GCToOSInterface::Sleep(uint32_t sleepMSec) +{ + throw nullptr; +} + +// Causes the calling thread to yield execution to another thread that is ready to run on the current processor. +// Parameters: +// switchCount - number of times the YieldThread was called in a loop +void GCToOSInterface::YieldThread(uint32_t switchCount) +{ + throw nullptr; +} + +// Reserve virtual memory range. +// Parameters: +// size - size of the virtual memory range +// alignment - requested memory alignment, 0 means no specific alignment requested +// flags - flags to control special settings like write watching +// Return: +// Starting virtual address of the reserved range +void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t flags) +{ + throw nullptr; +} + +// Release virtual memory range previously reserved using VirtualReserve +// Parameters: +// address - starting virtual address +// size - size of the virtual memory range +// Return: +// true if it has succeeded, false if it has failed +bool GCToOSInterface::VirtualRelease(void* address, size_t size) +{ + throw nullptr; +} + +// Commit virtual memory range. It must be part of a range reserved using VirtualReserve. +// Parameters: +// address - starting virtual address +// size - size of the virtual memory range +// Return: +// true if it has succeeded, false if it has failed +bool GCToOSInterface::VirtualCommit(void* address, size_t size) +{ + throw nullptr; +} + +// Decomit virtual memory range. +// Parameters: +// address - starting virtual address +// size - size of the virtual memory range +// Return: +// true if it has succeeded, false if it has failed +bool GCToOSInterface::VirtualDecommit(void* address, size_t size) +{ + throw nullptr; +} + +// Reset virtual memory range. Indicates that data in the memory range specified by address and size is no +// longer of interest, but it should not be decommitted. +// Parameters: +// address - starting virtual address +// size - size of the virtual memory range +// unlock - true if the memory range should also be unlocked +// Return: +// true if it has succeeded, false if it has failed +bool GCToOSInterface::VirtualReset(void * address, size_t size, bool unlock) +{ + throw nullptr; +} + +// Check if the OS supports write watching +bool GCToOSInterface::SupportsWriteWatch() +{ + throw nullptr; +} + +// Reset the write tracking state for the specified virtual memory range. +// Parameters: +// address - starting virtual address +// size - size of the virtual memory range +void GCToOSInterface::ResetWriteWatch(void* address, size_t size) +{ + throw nullptr; +} + +// Retrieve addresses of the pages that are written to in a region of virtual memory +// Parameters: +// resetState - true indicates to reset the write tracking state +// address - starting virtual address +// size - size of the virtual memory range +// pageAddresses - buffer that receives an array of page addresses in the memory region +// pageAddressesCount - on input, size of the lpAddresses array, in array elements +// on output, the number of page addresses that are returned in the array. +// Return: +// true if it has succeeded, false if it has failed +bool GCToOSInterface::GetWriteWatch(bool resetState, void* address, size_t size, void** pageAddresses, uintptr_t* pageAddressesCount) +{ + throw nullptr; +} + +// Get size of the largest cache on the processor die +// Parameters: +// trueSize - true to return true cache size, false to return scaled up size based on +// the processor architecture +// Return: +// Size of the cache +size_t GCToOSInterface::GetLargestOnDieCacheSize(bool trueSize) +{ + throw nullptr; +} + +// Get affinity mask of the current process +// Parameters: +// processMask - affinity mask for the specified process +// systemMask - affinity mask for the system +// Return: +// true if it has succeeded, false if it has failed +// Remarks: +// A process affinity mask is a bit vector in which each bit represents the processors that +// a process is allowed to run on. A system affinity mask is a bit vector in which each bit +// represents the processors that are configured into a system. +// A process affinity mask is a subset of the system affinity mask. A process is only allowed +// to run on the processors configured into a system. Therefore, the process affinity mask cannot +// specify a 1 bit for a processor when the system affinity mask specifies a 0 bit for that processor. +bool GCToOSInterface::GetCurrentProcessAffinityMask(uintptr_t* processMask, uintptr_t* systemMask) +{ + throw nullptr; +} + +// Get number of processors assigned to the current process +// Return: +// The number of processors +uint32_t GCToOSInterface::GetCurrentProcessCpuCount() +{ + throw nullptr; +} + +// Return the size of the user-mode portion of the virtual address space of this process. +// Return: +// non zero if it has succeeded, 0 if it has failed +size_t GCToOSInterface::GetVirtualMemoryLimit() +{ + throw nullptr; +} + +// Get the physical memory that this process can use. +// Return: +// non zero if it has succeeded, 0 if it has failed +// Remarks: +// If a process runs with a restricted memory limit, it returns the limit. If there's no limit +// specified, it returns amount of actual physical memory. +uint64_t GCToOSInterface::GetPhysicalMemoryLimit() +{ + throw nullptr; +} + +// Get memory status +// Parameters: +// memory_load - A number between 0 and 100 that specifies the approximate percentage of physical memory +// that is in use (0 indicates no memory use and 100 indicates full memory use). +// available_physical - The amount of physical memory currently available, in bytes. +// available_page_file - The maximum amount of memory the current process can commit, in bytes. +void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file) +{ + throw nullptr; +} + +// Get a high precision performance counter +// Return: +// The counter value +int64_t GCToOSInterface::QueryPerformanceCounter() +{ + throw nullptr; +} + +// Get a frequency of the high precision performance counter +// Return: +// The counter frequency +int64_t GCToOSInterface::QueryPerformanceFrequency() +{ + throw nullptr; +} + +// Get a time stamp with a low precision +// Return: +// Time stamp in milliseconds +uint32_t GCToOSInterface::GetLowPrecisionTimeStamp() +{ + throw nullptr; +} + + +// Create a new thread for GC use +// Parameters: +// function - the function to be executed by the thread +// param - parameters of the thread +// affinity - processor affinity of the thread +// Return: +// true if it has succeeded, false if it has failed +bool GCToOSInterface::CreateThread(GCThreadFunction function, void* param, GCThreadAffinity* affinity) +{ + throw nullptr; +} + +// Initialize the critical section +void CLRCriticalSection::Initialize() +{ + throw nullptr; +} + +// Destroy the critical section +void CLRCriticalSection::Destroy() +{ + throw nullptr; +} + +// Enter the critical section. Blocks until the section can be entered. +void CLRCriticalSection::Enter() +{ + throw nullptr; +} + +// Leave the critical section +void CLRCriticalSection::Leave() +{ + throw nullptr; +} \ No newline at end of file diff --git a/src/Native/gc/gcenv.windows.cpp b/src/Native/gc/gcenv.windows.cpp new file mode 100644 index 000000000..a63647824 --- /dev/null +++ b/src/Native/gc/gcenv.windows.cpp @@ -0,0 +1,625 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include +#include +#include +#include +#include "windows.h" +#include "psapi.h" +#include "env/gcenv.structs.h" +#include "env/gcenv.base.h" +#include "env/gcenv.os.h" + +GCSystemInfo g_SystemInfo; + +typedef BOOL (WINAPI *PGET_PROCESS_MEMORY_INFO)(HANDLE handle, PROCESS_MEMORY_COUNTERS* memCounters, uint32_t cb); +static PGET_PROCESS_MEMORY_INFO GCGetProcessMemoryInfo = 0; + +static size_t g_RestrictedPhysicalMemoryLimit = (size_t)UINTPTR_MAX; + +typedef BOOL (WINAPI *PIS_PROCESS_IN_JOB)(HANDLE processHandle, HANDLE jobHandle, BOOL* result); +typedef BOOL (WINAPI *PQUERY_INFORMATION_JOB_OBJECT)(HANDLE jobHandle, JOBOBJECTINFOCLASS jobObjectInfoClass, void* lpJobObjectInfo, DWORD cbJobObjectInfoLength, LPDWORD lpReturnLength); + +namespace { + +void GetProcessMemoryLoad(LPMEMORYSTATUSEX pMSEX) +{ + pMSEX->dwLength = sizeof(MEMORYSTATUSEX); + BOOL fRet = ::GlobalMemoryStatusEx(pMSEX); + assert(fRet); + + // If the machine has more RAM than virtual address limit, let us cap it. + // Our GC can never use more than virtual address limit. + if (pMSEX->ullAvailPhys > pMSEX->ullTotalVirtual) + { + pMSEX->ullAvailPhys = pMSEX->ullAvailVirtual; + } +} + +static size_t GetRestrictedPhysicalMemoryLimit() +{ + LIMITED_METHOD_CONTRACT; + + // The limit was cached already + if (g_RestrictedPhysicalMemoryLimit != (size_t)UINTPTR_MAX) + return g_RestrictedPhysicalMemoryLimit; + + size_t job_physical_memory_limit = (size_t)UINTPTR_MAX; + BOOL in_job_p = FALSE; + HINSTANCE hinstKernel32 = 0; + + PIS_PROCESS_IN_JOB GCIsProcessInJob = 0; + PQUERY_INFORMATION_JOB_OBJECT GCQueryInformationJobObject = 0; + + hinstKernel32 = LoadLibraryEx(L"kernel32.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); + if (!hinstKernel32) + goto exit; + + GCIsProcessInJob = (PIS_PROCESS_IN_JOB)GetProcAddress(hinstKernel32, "IsProcessInJob"); + if (!GCIsProcessInJob) + goto exit; + + if (!GCIsProcessInJob(GetCurrentProcess(), NULL, &in_job_p)) + goto exit; + + if (in_job_p) + { + GCGetProcessMemoryInfo = (PGET_PROCESS_MEMORY_INFO)GetProcAddress(hinstKernel32, "K32GetProcessMemoryInfo"); + + if (!GCGetProcessMemoryInfo) + goto exit; + + GCQueryInformationJobObject = (PQUERY_INFORMATION_JOB_OBJECT)GetProcAddress(hinstKernel32, "QueryInformationJobObject"); + + if (!GCQueryInformationJobObject) + goto exit; + + JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info; + if (GCQueryInformationJobObject (NULL, JobObjectExtendedLimitInformation, &limit_info, + sizeof(limit_info), NULL)) + { + size_t job_memory_limit = (size_t)UINTPTR_MAX; + size_t job_process_memory_limit = (size_t)UINTPTR_MAX; + size_t job_workingset_limit = (size_t)UINTPTR_MAX; + + // Notes on the NT job object: + // + // You can specific a bigger process commit or working set limit than + // job limit which is pointless so we use the smallest of all 3 as + // to calculate our "physical memory load" or "available physical memory" + // when running inside a job object, ie, we treat this as the amount of physical memory + // our process is allowed to use. + // + // The commit limit is already reflected by default when you run in a + // job but the physical memory load is not. + // + if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_JOB_MEMORY) != 0) + job_memory_limit = limit_info.JobMemoryLimit; + if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_PROCESS_MEMORY) != 0) + job_process_memory_limit = limit_info.ProcessMemoryLimit; + if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_WORKINGSET) != 0) + job_workingset_limit = limit_info.BasicLimitInformation.MaximumWorkingSetSize; + + job_physical_memory_limit = min (job_memory_limit, job_process_memory_limit); + job_physical_memory_limit = min (job_physical_memory_limit, job_workingset_limit); + + MEMORYSTATUSEX ms; + ::GetProcessMemoryLoad(&ms); + + // A sanity check in case someone set a larger limit than there is actual physical memory. + job_physical_memory_limit = (size_t) min (job_physical_memory_limit, ms.ullTotalPhys); + } + } + +exit: + if (job_physical_memory_limit == (size_t)UINTPTR_MAX) + { + job_physical_memory_limit = 0; + + FreeLibrary(hinstKernel32); + } + + VolatileStore(&g_RestrictedPhysicalMemoryLimit, job_physical_memory_limit); + return g_RestrictedPhysicalMemoryLimit; +} + +} // anonymous namespace + +// Initialize the interface implementation +// Return: +// true if it has succeeded, false if it has failed +bool GCToOSInterface::Initialize() +{ + SYSTEM_INFO systemInfo; + GetSystemInfo(&systemInfo); + + g_SystemInfo.dwNumberOfProcessors = systemInfo.dwNumberOfProcessors; + g_SystemInfo.dwPageSize = systemInfo.dwPageSize; + g_SystemInfo.dwAllocationGranularity = systemInfo.dwAllocationGranularity; + + return true; +} + +// Shutdown the interface implementation +void GCToOSInterface::Shutdown() +{ + // nothing to do. +} + +// Get numeric id of the current thread if possible on the +// current platform. It is indended for logging purposes only. +// Return: +// Numeric id of the current thread or 0 if the +uint64_t GCToOSInterface::GetCurrentThreadIdForLogging() +{ + return ::GetCurrentThreadId(); +} + +// Get id of the process +uint32_t GCToOSInterface::GetCurrentProcessId() +{ + return ::GetCurrentThreadId(); +} + +// Set ideal affinity for the current thread +// Parameters: +// affinity - ideal processor affinity for the thread +// Return: +// true if it has succeeded, false if it has failed +bool GCToOSInterface::SetCurrentThreadIdealAffinity(GCThreadAffinity* affinity) +{ + bool success = true; + +#if !defined(FEATURE_CORESYSTEM) + SetThreadIdealProcessor(GetCurrentThread(), (DWORD)affinity->Processor); +#else + PROCESSOR_NUMBER proc; + + if (affinity->Group != -1) + { + proc.Group = (WORD)affinity->Group; + proc.Number = (BYTE)affinity->Processor; + proc.Reserved = 0; + + success = !!SetThreadIdealProcessorEx(GetCurrentThread(), &proc, NULL); + } + else + { + if (GetThreadIdealProcessorEx(GetCurrentThread(), &proc)) + { + proc.Number = affinity->Processor; + success = !!SetThreadIdealProcessorEx(GetCurrentThread(), &proc, NULL); + } + } +#endif + + return success; +} + +// Get the number of the current processor +uint32_t GCToOSInterface::GetCurrentProcessorNumber() +{ + assert(GCToOSInterface::CanGetCurrentProcessorNumber()); + return ::GetCurrentProcessorNumber(); +} + +// Check if the OS supports getting current processor number +bool GCToOSInterface::CanGetCurrentProcessorNumber() +{ + // on all Windows platforms we support this API exists + return true; +} + +// Flush write buffers of processors that are executing threads of the current process +void GCToOSInterface::FlushProcessWriteBuffers() +{ + ::FlushProcessWriteBuffers(); +} + +// Break into a debugger +void GCToOSInterface::DebugBreak() +{ + ::DebugBreak(); +} + +// Get number of logical processors +uint32_t GCToOSInterface::GetLogicalCpuCount() +{ + // TODO(segilles) processor detection + return 1; +} + +// Causes the calling thread to sleep for the specified number of milliseconds +// Parameters: +// sleepMSec - time to sleep before switching to another thread +void GCToOSInterface::Sleep(uint32_t sleepMSec) +{ + // TODO(segilles) CLR implementation of __SwitchToThread spins for short sleep durations + // to avoid context switches - is that interesting or useful here? + if (sleepMSec > 0) + { + ::SleepEx(sleepMSec, FALSE); + } +} + +// Causes the calling thread to yield execution to another thread that is ready to run on the current processor. +// Parameters: +// switchCount - number of times the YieldThread was called in a loop +void GCToOSInterface::YieldThread(uint32_t switchCount) +{ + UNREFERENCED_PARAMETER(switchCount); + SwitchToThread(); +} + +// Reserve virtual memory range. +// Parameters: +// address - starting virtual address, it can be NULL to let the function choose the starting address +// size - size of the virtual memory range +// alignment - requested memory alignment, 0 means no specific alignment requested +// flags - flags to control special settings like write watching +// Return: +// Starting virtual address of the reserved range +void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t flags) +{ + // Windows already ensures 64kb alignment on VirtualAlloc. The current CLR + // implementation ignores it on Windows, other than making some sanity checks on it. + UNREFERENCED_PARAMETER(alignment); + assert((alignment & (alignment - 1)) == 0); + assert(alignment <= 0x10000); + DWORD memFlags = (flags & VirtualReserveFlags::WriteWatch) ? (MEM_RESERVE | MEM_WRITE_WATCH) : MEM_RESERVE; + return ::VirtualAlloc(nullptr, size, memFlags, PAGE_READWRITE); +} + +// Release virtual memory range previously reserved using VirtualReserve +// Parameters: +// address - starting virtual address +// size - size of the virtual memory range +// Return: +// true if it has succeeded, false if it has failed +bool GCToOSInterface::VirtualRelease(void* address, size_t size) +{ + return !!::VirtualFree(address, 0, MEM_RELEASE); +} + +// Commit virtual memory range. It must be part of a range reserved using VirtualReserve. +// Parameters: +// address - starting virtual address +// size - size of the virtual memory range +// Return: +// true if it has succeeded, false if it has failed +bool GCToOSInterface::VirtualCommit(void* address, size_t size) +{ + return ::VirtualAlloc(address, size, MEM_COMMIT, PAGE_READWRITE) != nullptr; +} + +// Decomit virtual memory range. +// Parameters: +// address - starting virtual address +// size - size of the virtual memory range +// Return: +// true if it has succeeded, false if it has failed +bool GCToOSInterface::VirtualDecommit(void* address, size_t size) +{ + return !!::VirtualFree(address, size, MEM_DECOMMIT); +} + +// Reset virtual memory range. Indicates that data in the memory range specified by address and size is no +// longer of interest, but it should not be decommitted. +// Parameters: +// address - starting virtual address +// size - size of the virtual memory range +// unlock - true if the memory range should also be unlocked +// Return: +// true if it has succeeded, false if it has failed. Returns false also if +// unlocking was requested but the unlock failed. +bool GCToOSInterface::VirtualReset(void * address, size_t size, bool unlock) +{ + bool success = ::VirtualAlloc(address, size, MEM_RESET, PAGE_READWRITE) != nullptr; + if (success && unlock) + { + ::VirtualUnlock(address, size); + } + + return success; +} + +// Check if the OS supports write watching +bool GCToOSInterface::SupportsWriteWatch() +{ + void* mem = GCToOSInterface::VirtualReserve(g_SystemInfo.dwAllocationGranularity, 0, VirtualReserveFlags::WriteWatch); + if (mem != nullptr) + { + GCToOSInterface::VirtualRelease(mem, g_SystemInfo.dwAllocationGranularity); + return true; + } + + return false; +} + +// Reset the write tracking state for the specified virtual memory range. +// Parameters: +// address - starting virtual address +// size - size of the virtual memory range +void GCToOSInterface::ResetWriteWatch(void* address, size_t size) +{ + ::ResetWriteWatch(address, size); +} + +// Retrieve addresses of the pages that are written to in a region of virtual memory +// Parameters: +// resetState - true indicates to reset the write tracking state +// address - starting virtual address +// size - size of the virtual memory range +// pageAddresses - buffer that receives an array of page addresses in the memory region +// pageAddressesCount - on input, size of the lpAddresses array, in array elements +// on output, the number of page addresses that are returned in the array. +// Return: +// true if it has succeeded, false if it has failed +bool GCToOSInterface::GetWriteWatch(bool resetState, void* address, size_t size, void** pageAddresses, uintptr_t* pageAddressesCount) +{ + uint32_t flags = resetState ? 1 : 0; + ULONG granularity; + + bool success = ::GetWriteWatch(flags, address, size, pageAddresses, (ULONG_PTR*)pageAddressesCount, &granularity) == 0; + if (success) + { + assert(granularity == OS_PAGE_SIZE); + } + + return success; +} + +// Get size of the largest cache on the processor die +// Parameters: +// trueSize - true to return true cache size, false to return scaled up size based on +// the processor architecture +// Return: +// Size of the cache +size_t GCToOSInterface::GetLargestOnDieCacheSize(bool trueSize) +{ + // TODO(segilles) processor detection (see src/vm/util.cpp:1935) + return 0; +} + +// Get affinity mask of the current process +// Parameters: +// processMask - affinity mask for the specified process +// systemMask - affinity mask for the system +// Return: +// true if it has succeeded, false if it has failed +// Remarks: +// A process affinity mask is a bit vector in which each bit represents the processors that +// a process is allowed to run on. A system affinity mask is a bit vector in which each bit +// represents the processors that are configured into a system. +// A process affinity mask is a subset of the system affinity mask. A process is only allowed +// to run on the processors configured into a system. Therefore, the process affinity mask cannot +// specify a 1 bit for a processor when the system affinity mask specifies a 0 bit for that processor. +bool GCToOSInterface::GetCurrentProcessAffinityMask(uintptr_t* processMask, uintptr_t* systemMask) +{ + return !!::GetProcessAffinityMask(::GetCurrentProcess(), (PDWORD_PTR)processMask, (PDWORD_PTR)systemMask); +} + +// Get number of processors assigned to the current process +// Return: +// The number of processors +uint32_t GCToOSInterface::GetCurrentProcessCpuCount() +{ + // TODO(segilles) this does not take into account process affinity + return g_SystemInfo.dwNumberOfProcessors; +} + +// Return the size of the user-mode portion of the virtual address space of this process. +// Return: +// non zero if it has succeeded, 0 if it has failed +size_t GCToOSInterface::GetVirtualMemoryLimit() +{ + MEMORYSTATUSEX memStatus; + if (::GlobalMemoryStatusEx(&memStatus)) + { + return (size_t)memStatus.ullAvailVirtual; + } + + return 0; +} + +// Get the physical memory that this process can use. +// Return: +// non zero if it has succeeded, 0 if it has failed +// Remarks: +// If a process runs with a restricted memory limit, it returns the limit. If there's no limit +// specified, it returns amount of actual physical memory. +uint64_t GCToOSInterface::GetPhysicalMemoryLimit() +{ + size_t restricted_limit = GetRestrictedPhysicalMemoryLimit(); + if (restricted_limit != 0) + return restricted_limit; + + MEMORYSTATUSEX memStatus; + if (::GlobalMemoryStatusEx(&memStatus)) + { + return memStatus.ullTotalPhys; + } + + return 0; +} + +// Get memory status +// Parameters: +// memory_load - A number between 0 and 100 that specifies the approximate percentage of physical memory +// that is in use (0 indicates no memory use and 100 indicates full memory use). +// available_physical - The amount of physical memory currently available, in bytes. +// available_page_file - The maximum amount of memory the current process can commit, in bytes. +void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file) +{ + uint64_t restricted_limit = GetRestrictedPhysicalMemoryLimit(); + if (restricted_limit != 0) + { + PROCESS_MEMORY_COUNTERS pmc; + if (GCGetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) + { + if (memory_load) + *memory_load = (uint32_t)((float)pmc.WorkingSetSize * 100.0 / (float)restricted_limit); + if (available_physical) + *available_physical = restricted_limit - pmc.WorkingSetSize; + // Available page file doesn't mean much when physical memory is restricted since + // we don't know how much of it is available to this process so we are not going to + // bother to make another OS call for it. + if (available_page_file) + *available_page_file = 0; + + return; + } + } + + MEMORYSTATUSEX ms; + ::GetProcessMemoryLoad(&ms); + + if (memory_load != nullptr) + *memory_load = ms.dwMemoryLoad; + if (available_physical != nullptr) + *available_physical = ms.ullAvailPhys; + if (available_page_file != nullptr) + *available_page_file = ms.ullAvailPageFile; +} + +// Get a high precision performance counter +// Return: +// The counter value +int64_t GCToOSInterface::QueryPerformanceCounter() +{ + LARGE_INTEGER ts; + if (!::QueryPerformanceCounter(&ts)) + { + assert(false && "Failed to query performance counter"); + } + + return ts.QuadPart; +} + +// Get a frequency of the high precision performance counter +// Return: +// The counter frequency +int64_t GCToOSInterface::QueryPerformanceFrequency() +{ + LARGE_INTEGER ts; + if (!::QueryPerformanceFrequency(&ts)) + { + assert(false && "Failed to query performance counter"); + } + + return ts.QuadPart; +} + +// Get a time stamp with a low precision +// Return: +// Time stamp in milliseconds +uint32_t GCToOSInterface::GetLowPrecisionTimeStamp() +{ + return ::GetTickCount(); +} + +// Parameters of the GC thread stub +struct GCThreadStubParam +{ + GCThreadFunction GCThreadFunction; + void* GCThreadParam; +}; + +// GC thread stub to convert GC thread function to an OS specific thread function +static DWORD GCThreadStub(void* param) +{ + GCThreadStubParam *stubParam = (GCThreadStubParam*)param; + GCThreadFunction function = stubParam->GCThreadFunction; + void* threadParam = stubParam->GCThreadParam; + + delete stubParam; + + function(threadParam); + + return 0; +} + + +// Create a new thread for GC use +// Parameters: +// function - the function to be executed by the thread +// param - parameters of the thread +// affinity - processor affinity of the thread +// Return: +// true if it has succeeded, false if it has failed +bool GCToOSInterface::CreateThread(GCThreadFunction function, void* param, GCThreadAffinity* affinity) +{ + uint32_t thread_id; + + std::unique_ptr stubParam(new (std::nothrow) GCThreadStubParam()); + if (!stubParam) + { + return false; + } + + stubParam->GCThreadFunction = function; + stubParam->GCThreadParam = param; + + HANDLE gc_thread = ::CreateThread( + nullptr, + 512 * 1024 /* Thread::StackSize_Medium */, + (LPTHREAD_START_ROUTINE)GCThreadStub, + stubParam.get(), + CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION, + (DWORD*)&thread_id); + + if (!gc_thread) + { + return false; + } + + stubParam.release(); + bool result = !!::SetThreadPriority(gc_thread, /* THREAD_PRIORITY_ABOVE_NORMAL );*/ THREAD_PRIORITY_HIGHEST ); + assert(result && "failed to set thread priority"); + + if (affinity->Group != GCThreadAffinity::None) + { + assert(affinity->Processor != GCThreadAffinity::None); + GROUP_AFFINITY ga; + ga.Group = (WORD)affinity->Group; + ga.Reserved[0] = 0; // reserve must be filled with zero + ga.Reserved[1] = 0; // otherwise call may fail + ga.Reserved[2] = 0; + ga.Mask = (size_t)1 << affinity->Processor; + + bool result = !!::SetThreadGroupAffinity(gc_thread, &ga, nullptr); + assert(result && "failed to set thread affinity"); + } + else if (affinity->Processor != GCThreadAffinity::None) + { + ::SetThreadAffinityMask(gc_thread, (DWORD_PTR)1 << affinity->Processor); + } + + return true; +} + +// Initialize the critical section +void CLRCriticalSection::Initialize() +{ + ::InitializeCriticalSection(&m_cs); +} + +// Destroy the critical section +void CLRCriticalSection::Destroy() +{ + ::DeleteCriticalSection(&m_cs); +} + +// Enter the critical section. Blocks until the section can be entered. +void CLRCriticalSection::Enter() +{ + ::EnterCriticalSection(&m_cs); +} + +// Leave the critical section +void CLRCriticalSection::Leave() +{ + ::LeaveCriticalSection(&m_cs); +} diff --git a/src/Native/gc/sample/gcenv.unix.cpp b/src/Native/gc/sample/gcenv.unix.cpp deleted file mode 100644 index a5e9e83ee..000000000 --- a/src/Native/gc/sample/gcenv.unix.cpp +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// -// Implementation of the GC environment -// - -#include "common.h" - -#include "gcenv.h" -#include "gc.h" - -// TODO: Implement diff --git a/src/Native/gc/sample/gcenv.windows.cpp b/src/Native/gc/sample/gcenv.windows.cpp deleted file mode 100644 index 76187f218..000000000 --- a/src/Native/gc/sample/gcenv.windows.cpp +++ /dev/null @@ -1,453 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// -// Implementation of the GC environment -// - -#include "common.h" - -#include "windows.h" - -#include "gcenv.h" -#include "gc.h" - -MethodTable * g_pFreeObjectMethodTable; - -int32_t g_TrapReturningThreads; - -bool g_fFinalizerRunOnShutDown; - -GCSystemInfo g_SystemInfo; - -static LARGE_INTEGER g_performanceFrequency; - -// Initialize the interface implementation -// Return: -// true if it has succeeded, false if it has failed -bool GCToOSInterface::Initialize() -{ - if (!::QueryPerformanceFrequency(&g_performanceFrequency)) - { - return false; - } - - SYSTEM_INFO systemInfo; - GetSystemInfo(&systemInfo); - - g_SystemInfo.dwNumberOfProcessors = systemInfo.dwNumberOfProcessors; - g_SystemInfo.dwPageSize = systemInfo.dwPageSize; - g_SystemInfo.dwAllocationGranularity = systemInfo.dwAllocationGranularity; - - return true; -} - -// Shutdown the interface implementation -void GCToOSInterface::Shutdown() -{ -} - -// Get numeric id of the current thread if possible on the -// current platform. It is indended for logging purposes only. -// Return: -// Numeric id of the current thread or 0 if the -uint64_t GCToOSInterface::GetCurrentThreadIdForLogging() -{ - return ::GetCurrentThreadId(); -} - -// Get id of the process -// Return: -// Id of the current process -uint32_t GCToOSInterface::GetCurrentProcessId() -{ - return ::GetCurrentProcessId(); -} - -// Set ideal affinity for the current thread -// Parameters: -// affinity - ideal processor affinity for the thread -// Return: -// true if it has succeeded, false if it has failed -bool GCToOSInterface::SetCurrentThreadIdealAffinity(GCThreadAffinity* affinity) -{ - bool success = true; - -#if !defined(FEATURE_CORESYSTEM) - SetThreadIdealProcessor(GetCurrentThread(), (DWORD)affinity->Processor); -#else - PROCESSOR_NUMBER proc; - - if (affinity->Group != -1) - { - proc.Group = (WORD)affinity->Group; - proc.Number = (BYTE)affinity->Processor; - proc.Reserved = 0; - - success = !!SetThreadIdealProcessorEx(GetCurrentThread(), &proc, NULL); - } - else - { - if (GetThreadIdealProcessorEx(GetCurrentThread(), &proc)) - { - proc.Number = affinity->Processor; - success = !!SetThreadIdealProcessorEx(GetCurrentThread(), &proc, NULL); - } - } -#endif - - return success; -} - -// Get the number of the current processor -uint32_t GCToOSInterface::GetCurrentProcessorNumber() -{ - _ASSERTE(GCToOSInterface::CanGetCurrentProcessorNumber()); - return ::GetCurrentProcessorNumber(); -} - -// Check if the OS supports getting current processor number -bool GCToOSInterface::CanGetCurrentProcessorNumber() -{ - return true; -} - -// Flush write buffers of processors that are executing threads of the current process -void GCToOSInterface::FlushProcessWriteBuffers() -{ - ::FlushProcessWriteBuffers(); -} - -// Break into a debugger -void GCToOSInterface::DebugBreak() -{ - ::DebugBreak(); -} - -// Get number of logical processors -uint32_t GCToOSInterface::GetLogicalCpuCount() -{ - return g_SystemInfo.dwNumberOfProcessors; -} - -// Causes the calling thread to sleep for the specified number of milliseconds -// Parameters: -// sleepMSec - time to sleep before switching to another thread -void GCToOSInterface::Sleep(uint32_t sleepMSec) -{ - ::Sleep(sleepMSec); -} - -// Causes the calling thread to yield execution to another thread that is ready to run on the current processor. -// Parameters: -// switchCount - number of times the YieldThread was called in a loop -void GCToOSInterface::YieldThread(uint32_t switchCount) -{ - SwitchToThread(); -} - -// Reserve virtual memory range. -// Parameters: -// address - starting virtual address, it can be NULL to let the function choose the starting address -// size - size of the virtual memory range -// alignment - requested memory alignment -// flags - flags to control special settings like write watching -// Return: -// Starting virtual address of the reserved range -void* GCToOSInterface::VirtualReserve(void* address, size_t size, size_t alignment, uint32_t flags) -{ - DWORD memFlags = (flags & VirtualReserveFlags::WriteWatch) ? (MEM_RESERVE | MEM_WRITE_WATCH) : MEM_RESERVE; - return ::VirtualAlloc(0, size, memFlags, PAGE_READWRITE); -} - -// Release virtual memory range previously reserved using VirtualReserve -// Parameters: -// address - starting virtual address -// size - size of the virtual memory range -// Return: -// true if it has succeeded, false if it has failed -bool GCToOSInterface::VirtualRelease(void* address, size_t size) -{ - UNREFERENCED_PARAMETER(size); - return !!::VirtualFree(address, 0, MEM_RELEASE); -} - -// Commit virtual memory range. It must be part of a range reserved using VirtualReserve. -// Parameters: -// address - starting virtual address -// size - size of the virtual memory range -// Return: -// true if it has succeeded, false if it has failed -bool GCToOSInterface::VirtualCommit(void* address, size_t size) -{ - return ::VirtualAlloc(address, size, MEM_COMMIT, PAGE_READWRITE) != NULL; -} - -// Decomit virtual memory range. -// Parameters: -// address - starting virtual address -// size - size of the virtual memory range -// Return: -// true if it has succeeded, false if it has failed -bool GCToOSInterface::VirtualDecommit(void* address, size_t size) -{ - return !!::VirtualFree(address, size, MEM_DECOMMIT); -} - -// Reset virtual memory range. Indicates that data in the memory range specified by address and size is no -// longer of interest, but it should not be decommitted. -// Parameters: -// address - starting virtual address -// size - size of the virtual memory range -// unlock - true if the memory range should also be unlocked -// Return: -// true if it has succeeded, false if it has failed -bool GCToOSInterface::VirtualReset(void * address, size_t size, bool unlock) -{ - bool success = ::VirtualAlloc(address, size, MEM_RESET, PAGE_READWRITE) != NULL; - if (success && unlock) - { - // Remove the page range from the working set - ::VirtualUnlock(address, size); - } - - return success; -} - -// Check if the OS supports write watching -bool GCToOSInterface::SupportsWriteWatch() -{ - return false; -} - -// Reset the write tracking state for the specified virtual memory range. -// Parameters: -// address - starting virtual address -// size - size of the virtual memory range -void GCToOSInterface::ResetWriteWatch(void* address, size_t size) -{ -} - -// Retrieve addresses of the pages that are written to in a region of virtual memory -// Parameters: -// resetState - true indicates to reset the write tracking state -// address - starting virtual address -// size - size of the virtual memory range -// pageAddresses - buffer that receives an array of page addresses in the memory region -// pageAddressesCount - on input, size of the lpAddresses array, in array elements -// on output, the number of page addresses that are returned in the array. -// Return: -// true if it has succeeded, false if it has failed -bool GCToOSInterface::GetWriteWatch(bool resetState, void* address, size_t size, void** pageAddresses, uintptr_t* pageAddressesCount) -{ - return false; -} - -// Get size of the largest cache on the processor die -// Parameters: -// trueSize - true to return true cache size, false to return scaled up size based on -// the processor architecture -// Return: -// Size of the cache -size_t GCToOSInterface::GetLargestOnDieCacheSize(bool trueSize) -{ - // TODO: implement - return 0; -} - -// Get affinity mask of the current process -// Parameters: -// processMask - affinity mask for the specified process -// systemMask - affinity mask for the system -// Return: -// true if it has succeeded, false if it has failed -// Remarks: -// A process affinity mask is a bit vector in which each bit represents the processors that -// a process is allowed to run on. A system affinity mask is a bit vector in which each bit -// represents the processors that are configured into a system. -// A process affinity mask is a subset of the system affinity mask. A process is only allowed -// to run on the processors configured into a system. Therefore, the process affinity mask cannot -// specify a 1 bit for a processor when the system affinity mask specifies a 0 bit for that processor. -bool GCToOSInterface::GetCurrentProcessAffinityMask(uintptr_t* processMask, uintptr_t* systemMask) -{ - return false; -} - -// Get number of processors assigned to the current process -// Return: -// The number of processors -uint32_t GCToOSInterface::GetCurrentProcessCpuCount() -{ - return g_SystemInfo.dwNumberOfProcessors; -} - -// Return the size of the user-mode portion of the virtual address space of this process. -// Return: -// non zero if it has succeeded, 0 if it has failed -size_t GCToOSInterface::GetVirtualMemoryLimit() -{ - MEMORYSTATUSEX memStatus; - - memStatus.dwLength = sizeof(MEMORYSTATUSEX); - BOOL fRet = GlobalMemoryStatusEx(&memStatus); - _ASSERTE(fRet); - - return (size_t)memStatus.ullTotalVirtual; -} - -// Get the physical memory that this process can use. -// Return: -// non zero if it has succeeded, 0 if it has failed -uint64_t GCToOSInterface::GetPhysicalMemoryLimit() -{ - MEMORYSTATUSEX memStatus; - - memStatus.dwLength = sizeof(MEMORYSTATUSEX); - BOOL fRet = GlobalMemoryStatusEx(&memStatus); - _ASSERTE(fRet); - - return memStatus.ullTotalPhys; -} - -// Get memory status -// Parameters: -// memory_load - A number between 0 and 100 that specifies the approximate percentage of physical memory -// that is in use (0 indicates no memory use and 100 indicates full memory use). -// available_physical - The amount of physical memory currently available, in bytes. -// available_page_file - The maximum amount of memory the current process can commit, in bytes. -void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file) -{ - MEMORYSTATUSEX memStatus; - - memStatus.dwLength = sizeof(MEMORYSTATUSEX); - BOOL fRet = GlobalMemoryStatusEx(&memStatus); - _ASSERTE (fRet); - - // If the machine has more RAM than virtual address limit, let us cap it. - // The GC can never use more than virtual address limit. - if (memStatus.ullAvailPhys > memStatus.ullTotalVirtual) - { - memStatus.ullAvailPhys = memStatus.ullAvailVirtual; - } - - if (memory_load != NULL) - *memory_load = memStatus.dwMemoryLoad; - if (available_physical != NULL) - *available_physical = memStatus.ullAvailPhys; - if (available_page_file != NULL) - *available_page_file = memStatus.ullAvailPageFile; -} - -// Get a high precision performance counter -// Return: -// The counter value -int64_t GCToOSInterface::QueryPerformanceCounter() -{ - LARGE_INTEGER ts; - if (!::QueryPerformanceCounter(&ts)) - { - _ASSERTE(!"Fatal Error - cannot query performance counter."); - abort(); - } - - return ts.QuadPart; -} - -// Get a frequency of the high precision performance counter -// Return: -// The counter frequency -int64_t GCToOSInterface::QueryPerformanceFrequency() -{ - return g_performanceFrequency.QuadPart; -} - -// Get a time stamp with a low precision -// Return: -// Time stamp in milliseconds -uint32_t GCToOSInterface::GetLowPrecisionTimeStamp() -{ - return ::GetTickCount(); -} - -// Parameters of the GC thread stub -struct GCThreadStubParam -{ - GCThreadFunction GCThreadFunction; - void* GCThreadParam; -}; - -// GC thread stub to convert GC thread function to an OS specific thread function -static DWORD __stdcall GCThreadStub(void* param) -{ - GCThreadStubParam *stubParam = (GCThreadStubParam*)param; - GCThreadFunction function = stubParam->GCThreadFunction; - void* threadParam = stubParam->GCThreadParam; - - delete stubParam; - - function(threadParam); - - return 0; -} - -// Create a new thread -// Parameters: -// function - the function to be executed by the thread -// param - parameters of the thread -// affinity - processor affinity of the thread -// Return: -// true if it has succeeded, false if it has failed -bool GCToOSInterface::CreateThread(GCThreadFunction function, void* param, GCThreadAffinity* affinity) -{ - DWORD thread_id; - - GCThreadStubParam* stubParam = new (nothrow) GCThreadStubParam(); - if (stubParam == NULL) - { - return false; - } - - stubParam->GCThreadFunction = function; - stubParam->GCThreadParam = param; - - HANDLE gc_thread = ::CreateThread(NULL, 0, GCThreadStub, stubParam, CREATE_SUSPENDED, &thread_id); - - if (!gc_thread) - { - delete stubParam; - return false; - } - - SetThreadPriority(gc_thread, /* THREAD_PRIORITY_ABOVE_NORMAL );*/ THREAD_PRIORITY_HIGHEST ); - - ResumeThread(gc_thread); - - CloseHandle(gc_thread); - - return true; -} - -// Initialize the critical section -void CLRCriticalSection::Initialize() -{ - ::InitializeCriticalSection(&m_cs); -} - -// Destroy the critical section -void CLRCriticalSection::Destroy() -{ - ::DeleteCriticalSection(&m_cs); -} - -// Enter the critical section. Blocks until the section can be entered. -void CLRCriticalSection::Enter() -{ - ::EnterCriticalSection(&m_cs); -} - -// Leave the critical section -void CLRCriticalSection::Leave() -{ - ::LeaveCriticalSection(&m_cs); -} -- cgit v1.2.3 From 7eee7431ece5603b201f2c9c1ac5b64a58ca4834 Mon Sep 17 00:00:00 2001 From: dotnet-bot Date: Fri, 13 Jan 2017 16:22:29 -0800 Subject: Repair a build break by only defining ProfilingScanContext when DACCESS_COMPILE is not defined. [tfs-changeset: 1644487] --- src/Native/Runtime/gcrhenv.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Native/Runtime/gcrhenv.cpp b/src/Native/Runtime/gcrhenv.cpp index c60f09f0a..2dfb7fcf1 100644 --- a/src/Native/Runtime/gcrhenv.cpp +++ b/src/Native/Runtime/gcrhenv.cpp @@ -1597,7 +1597,7 @@ void CPUGroupInfo::GetGroupForProcessor(uint16_t /*processor_number*/, uint16_t ASSERT_UNCONDITIONALLY("NYI: CPUGroupInfo::GetGroupForProcessor"); } -#ifdef FEATURE_EVENT_TRACE +#if defined(FEATURE_EVENT_TRACE) && !defined(DACCESS_COMPILE) ProfilingScanContext::ProfilingScanContext(BOOL fProfilerPinnedParam) : ScanContext() { @@ -1609,4 +1609,4 @@ ProfilingScanContext::ProfilingScanContext(BOOL fProfilerPinnedParam) promotion = g_pConfig->GetGCConservative(); #endif } -#endif // FEATURE_EVENT_TRACE +#endif // defined(FEATURE_EVENT_TRACE) && !defined(DACCESS_COMPILE) -- cgit v1.2.3 From cfe34d0303029908296d8d3e32ac0572c10d1db6 Mon Sep 17 00:00:00 2001 From: sandreenko Date: Fri, 13 Jan 2017 17:59:20 -0800 Subject: Encode Reverse PInvoke frame stack slot in GCInfo (#2484) Fix #2115. Set ppPreviousTransitionFrame. --- src/Native/Runtime/coreclr/gcinfodecoder.cpp | 2 +- src/Native/Runtime/unix/UnixNativeCodeManager.cpp | 18 +++++++++++++----- src/Native/Runtime/windows/CoffNativeCodeManager.cpp | 18 +++++++++++++----- src/packaging/packages.targets | 2 +- src/packaging/project.json | 2 +- 5 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/Native/Runtime/coreclr/gcinfodecoder.cpp b/src/Native/Runtime/coreclr/gcinfodecoder.cpp index fceee6662..1a8eb7eca 100644 --- a/src/Native/Runtime/coreclr/gcinfodecoder.cpp +++ b/src/Native/Runtime/coreclr/gcinfodecoder.cpp @@ -283,7 +283,7 @@ GcInfoDecoder::GcInfoDecoder( if (hasReversePInvokeFrame) { - m_ReversePInvokeFrameStackSlot = (INT32)m_Reader.DecodeVarLengthSigned(REVERSE_PINVOKE_FRAME_ENCBASE); + m_ReversePInvokeFrameStackSlot = (INT32)DENORMALIZE_STACK_SLOT(m_Reader.DecodeVarLengthSigned(REVERSE_PINVOKE_FRAME_ENCBASE)); } else { diff --git a/src/Native/Runtime/unix/UnixNativeCodeManager.cpp b/src/Native/Runtime/unix/UnixNativeCodeManager.cpp index 4138cc8b5..927755238 100644 --- a/src/Native/Runtime/unix/UnixNativeCodeManager.cpp +++ b/src/Native/Runtime/unix/UnixNativeCodeManager.cpp @@ -144,12 +144,20 @@ bool UnixNativeCodeManager::UnwindStackFrame(MethodInfo * pMethodInfo, p += sizeof(int32_t); GcInfoDecoder decoder(GCInfoToken(p), DECODE_REVERSE_PINVOKE_VAR); + INT32 slot = decoder.GetReversePInvokeFrameStackSlot(); + assert(slot != NO_REVERSE_PINVOKE_FRAME); - // @TODO: CORERT: Encode reverse PInvoke frame slot in GCInfo: https://github.com/dotnet/corert/issues/2115 - // INT32 slot = decoder.GetReversePInvokeFrameStackSlot(); - // assert(slot != NO_REVERSE_PINVOKE_FRAME); - - *ppPreviousTransitionFrame = (PTR_VOID)-1; + TADDR basePointer = NULL; + UINT32 stackBasedRegister = decoder.GetStackBaseRegister(); + if (stackBasedRegister == NO_STACK_BASE_REGISTER) + { + basePointer = dac_cast(pRegisterSet->GetSP()); + } + else + { + basePointer = dac_cast(pRegisterSet->GetFP()); + } + *ppPreviousTransitionFrame = *(void**)(basePointer + slot); return true; } diff --git a/src/Native/Runtime/windows/CoffNativeCodeManager.cpp b/src/Native/Runtime/windows/CoffNativeCodeManager.cpp index 6692d298b..076a57539 100644 --- a/src/Native/Runtime/windows/CoffNativeCodeManager.cpp +++ b/src/Native/Runtime/windows/CoffNativeCodeManager.cpp @@ -321,12 +321,20 @@ bool CoffNativeCodeManager::UnwindStackFrame(MethodInfo * pMethodInfo, p += sizeof(int32_t); GcInfoDecoder decoder(GCInfoToken(p), DECODE_REVERSE_PINVOKE_VAR); + INT32 slot = decoder.GetReversePInvokeFrameStackSlot(); + assert(slot != NO_REVERSE_PINVOKE_FRAME); - // @TODO: CORERT: Encode reverse PInvoke frame slot in GCInfo: https://github.com/dotnet/corert/issues/2115 - // INT32 slot = decoder.GetReversePInvokeFrameStackSlot(); - // assert(slot != NO_REVERSE_PINVOKE_FRAME); - - *ppPreviousTransitionFrame = (PTR_VOID)-1; + TADDR basePointer = NULL; + UINT32 stackBasedRegister = decoder.GetStackBaseRegister(); + if (stackBasedRegister == NO_STACK_BASE_REGISTER) + { + basePointer = dac_cast(pRegisterSet->GetSP()); + } + else + { + basePointer = dac_cast(pRegisterSet->GetFP()); + } + *ppPreviousTransitionFrame = *(void**)(basePointer + slot); return true; } diff --git a/src/packaging/packages.targets b/src/packaging/packages.targets index 24f850056..7b848c057 100644 --- a/src/packaging/packages.targets +++ b/src/packaging/packages.targets @@ -24,7 +24,7 @@ 4.4.0-beta-24913-02 - 1.2.0-beta-24815-03 + 1.2.0-beta-24911-02 1.0.13-prerelease-00001 ubuntu.14.04-x64 diff --git a/src/packaging/project.json b/src/packaging/project.json index df6f724ef..514f90539 100644 --- a/src/packaging/project.json +++ b/src/packaging/project.json @@ -1,6 +1,6 @@ { "dependencies": { - "Microsoft.NETCore.Jit": "1.2.0-beta-24815-03", + "Microsoft.NETCore.Jit": "1.2.0-beta-24911-02", "Microsoft.DotNet.ObjectWriter": "1.0.13-prerelease-00001" }, "frameworks": { -- cgit v1.2.3 From 896ed6bd6a01b5bd02394d4f905b679ae6acafcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Sat, 14 Jan 2017 21:23:41 -0800 Subject: Update CoreCLR.issues.targets (#2512) The AboveStackLimit test was enabled with #2467, but it's still having issuse (non-trivial marshalling this time). --- tests/CoreCLR.issues.targets | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/CoreCLR.issues.targets b/tests/CoreCLR.issues.targets index e7fc753eb..06e2d17f0 100644 --- a/tests/CoreCLR.issues.targets +++ b/tests/CoreCLR.issues.targets @@ -903,6 +903,7 @@ + -- cgit v1.2.3 From 63cc02771ecd502794340b826671b4f90d214b0f Mon Sep 17 00:00:00 2001 From: mikedn Date: Sun, 15 Jan 2017 17:05:48 +0200 Subject: Prefer using Array.Length as upper for loop limit (#2513) Port of https://github.com/dotnet/coreclr/pull/8923 --- src/System.Private.CoreLib/src/System/Delegate.cs | 2 +- src/System.Private.CoreLib/src/System/Reflection/Assembly.cs | 7 +++---- .../src/System/Runtime/CompilerServices/ConditionalWeakTable.cs | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/System.Private.CoreLib/src/System/Delegate.cs b/src/System.Private.CoreLib/src/System/Delegate.cs index 399fd783b..8263a8ac6 100644 --- a/src/System.Private.CoreLib/src/System/Delegate.cs +++ b/src/System.Private.CoreLib/src/System/Delegate.cs @@ -630,7 +630,7 @@ namespace System int invocationCount = (int)m_extraFunctionPointerOrData; del = new Delegate[invocationCount]; - for (int i = 0; i < invocationCount; i++) + for (int i = 0; i < del.Length; i++) del[i] = invocationList[i]; } return del; diff --git a/src/System.Private.CoreLib/src/System/Reflection/Assembly.cs b/src/System.Private.CoreLib/src/System/Reflection/Assembly.cs index 2405a050f..66f68e474 100644 --- a/src/System.Private.CoreLib/src/System/Reflection/Assembly.cs +++ b/src/System.Private.CoreLib/src/System/Reflection/Assembly.cs @@ -38,11 +38,10 @@ namespace System.Reflection { Module[] m = GetModules(false); - int numModules = m.Length; int finalLength = 0; - Type[][] moduleTypes = new Type[numModules][]; + Type[][] moduleTypes = new Type[m.Length][]; - for (int i = 0; i < numModules; i++) + for (int i = 0; i < moduleTypes.Length; i++) { moduleTypes[i] = m[i].GetTypes(); finalLength += moduleTypes[i].Length; @@ -50,7 +49,7 @@ namespace System.Reflection int current = 0; Type[] ret = new Type[finalLength]; - for (int i = 0; i < numModules; i++) + for (int i = 0; i < moduleTypes.Length; i++) { int length = moduleTypes[i].Length; Array.Copy(moduleTypes[i], 0, ret, current, length); diff --git a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs index b5caeb690..5bf209549 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs @@ -528,7 +528,7 @@ namespace System.Runtime.CompilerServices // Reallocate both buckets and entries and rebuild the bucket and entries from scratch. // This serves both to scrub entries with expired keys and to put the new entries in the proper bucket. int[] newBuckets = new int[newSize]; - for (int bucketIndex = 0; bucketIndex < newSize; bucketIndex++) + for (int bucketIndex = 0; bucketIndex < newBuckets.Length; bucketIndex++) { newBuckets[bucketIndex] = -1; } -- cgit v1.2.3 From 9b613d85a25da00dc8098553d3cac18f054f0434 Mon Sep 17 00:00:00 2001 From: Tomas Rylek Date: Mon, 16 Jan 2017 03:11:05 -0800 Subject: Remove about one half of EagerOrderedStaticCctor attribute values by switching .NET Native build over to the LibraryInitializer scheme used in CoreRT. The change doesn't yet fully remove all support for the attribute, that will require additional changes to MCG and serialization code that I intend to tackle in a subsequent check-in. [tfs-changeset: 1644644] --- src/System.Private.CoreLib/src/System/Environment.cs | 3 --- .../Runtime/CompilerServices/ClassConstructorRunner.cs | 15 +-------------- .../EagerOrderedStaticConstructorAttribute.cs | 13 ------------- .../src/System/Runtime/TypeLoaderExports.cs | 14 -------------- .../src/System/RuntimeExceptionHelpers.cs | 15 +-------------- .../Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs | 14 +------------- 6 files changed, 3 insertions(+), 71 deletions(-) diff --git a/src/System.Private.CoreLib/src/System/Environment.cs b/src/System.Private.CoreLib/src/System/Environment.cs index 274c4e2a4..41d5dc038 100644 --- a/src/System.Private.CoreLib/src/System/Environment.cs +++ b/src/System.Private.CoreLib/src/System/Environment.cs @@ -33,9 +33,6 @@ namespace System Machine = 2, } - // Environment is marked as Eager to allow Lock to read the current - // thread ID, since Lock is used in ClassConstructorRunner.Cctor.GetCctor - [EagerOrderedStaticConstructor(EagerStaticConstructorOrder.SystemEnvironment)] public static partial class Environment { /*==================================TickCount=================================== diff --git a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs index 1b917fb94..f2f9ff581 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs @@ -13,9 +13,6 @@ using Internal.Runtime.CompilerHelpers; namespace System.Runtime.CompilerServices { - // Marked [EagerOrderedStaticConstructor] because Cctor.GetCctor - // uses _cctorGlobalLock - [EagerOrderedStaticConstructor(EagerStaticConstructorOrder.CompilerServicesClassConstructorRunner)] internal static partial class ClassConstructorRunner { //============================================================================================================== @@ -482,17 +479,7 @@ namespace System.Runtime.CompilerServices private static int s_cctorArraysCount; private static int s_count; - // - // CoreRT calls Initialize directly for all types its needs that typically have EagerOrderedStaticConstructor - // attributes. To retain compatibility, please ensure static initialization is not done inline, and instead - // added to Initialize. - // -#if !CORERT - static ClassConstructorRunner() - { - Initialize(); - } -#endif + // Eager construction called from LibraryInitialize Cctor.GetCctor uses _cctorGlobalLock. internal static void Initialize() { s_cctorArrays = new Cctor[10][]; diff --git a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/EagerOrderedStaticConstructorAttribute.cs b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/EagerOrderedStaticConstructorAttribute.cs index ba4279c43..f3a699b17 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/EagerOrderedStaticConstructorAttribute.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/EagerOrderedStaticConstructorAttribute.cs @@ -22,19 +22,6 @@ namespace System.Runtime.CompilerServices public enum EagerStaticConstructorOrder : int { - // System.Private.CoreLib - SystemString, - SystemPreallocatedOutOfMemoryException, - SystemEnvironment, // ClassConstructorRunner.Cctor.GetCctor use Lock which inturn use current threadID , so System.Environment - // should come before CompilerServicesClassConstructorRunnerCctor - CompilerServicesClassConstructorRunnerCctor, - CompilerServicesClassConstructorRunner, - - // System.Private.TypeLoader - RuntimeTypeHandleEqualityComparer, - TypeLoaderEnvironment, - SystemRuntimeTypeLoaderExports, - // Interop InteropHeap, VtableIUnknown, diff --git a/src/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs b/src/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs index 57e8efd79..64b50165e 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs @@ -11,8 +11,6 @@ using System.Runtime.InteropServices; namespace System.Runtime { - // Initialize the cache eagerly to avoid null checks - [EagerOrderedStaticConstructor(EagerStaticConstructorOrder.SystemRuntimeTypeLoaderExports)] public static class TypeLoaderExports { [RuntimeExport("GetThreadStaticsForDynamicType")] @@ -80,18 +78,6 @@ namespace System.Runtime private volatile static IntPtr[] s_resolutionFunctionPointers; private static int s_nextResolutionFunctionPointerIndex; - // - // CoreRT calls Initialize directly for all types its needs that typically have EagerOrderedStaticConstructor - // attributes. To retain compatibility, please ensure static initialization is not done inline, and instead - // added to Initialize. - // -#if !CORERT - static TypeLoaderExports() - { - Initialize(); - } -#endif - internal static void Initialize() { s_cache = new Entry[1]; diff --git a/src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs b/src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs index c53f1a251..1a47b20a7 100644 --- a/src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs +++ b/src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs @@ -13,24 +13,11 @@ using Internal.Runtime.Augments; namespace System { - // Eagerly preallocate instance of out of memory exception to avoid infinite recursion once we run out of memory - [EagerOrderedStaticConstructor(EagerStaticConstructorOrder.SystemPreallocatedOutOfMemoryException)] internal class PreallocatedOutOfMemoryException { public static OutOfMemoryException Instance { get; private set; } - // - // CoreRT calls Initialize directly for all types its needs that typically have EagerOrderedStaticConstructor - // attributes. To retain compatibility, please ensure static initialization is not done inline, and instead - // added to Initialize. - // -#if !CORERT - static PreallocatedOutOfMemoryException() - { - Initialize(); - } -#endif - + // Eagerly preallocate instance of out of memory exception to avoid infinite recursion once we run out of memory internal static void Initialize() { Instance = new OutOfMemoryException(message: null); // Cannot call the nullary constructor as that triggers non-trivial resource manager logic. diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs index b6309bd22..addf2ed12 100644 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs @@ -86,7 +86,6 @@ namespace Internal.Runtime.TypeLoader } } - [EagerOrderedStaticConstructor(EagerStaticConstructorOrder.TypeLoaderEnvironment)] public sealed partial class TypeLoaderEnvironment { [ThreadStatic] @@ -107,18 +106,7 @@ namespace Internal.Runtime.TypeLoader [ThreadStatic] private static LowLevelDictionary t_moduleNativeReaders; - // - // CoreRT calls Initialize directly for all types its needs that typically have EagerOrderedStaticConstructor - // attributes. To retain compatibility, please ensure static initialization is not done inline, and instead - // added to Initialize. - // -#if !CORERT - static TypeLoaderEnvironment() - { - Initialize(); - } -#endif - + // Eager initialization called from LibraryInitializer for the assembly. internal static void Initialize() { Instance = new TypeLoaderEnvironment(); -- cgit v1.2.3 From 82464aaa3404fc48ed8f0a5cf6c299c5d135afe9 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Tue, 17 Jan 2017 09:52:11 -0800 Subject: Fix handling of parametrized types in CppCodeGen (#2517) - Use NameMangler.GetMangledTypeName directly to get the EEType name - Delete filtering of IsPointer and IsByRef during type emission since it works fine now - Add bigobj compiler switch to allow large auto generated .cpp files Fix #2486 --- ...ild-and-run-ilcompiler-in-visual-studio-2015.md | 2 +- .../Microsoft.NETCore.Native.Windows.props | 2 +- .../src/Compiler/LibraryInitializers.cs | 4 +-- .../src/Compiler/NameMangler.cs | 6 ++--- .../src/CppCodeGen/CppWriter.cs | 23 +++++++---------- .../src/CppCodeGen/ILToCppImporter.cs | 29 ++++++++++------------ src/ILCompiler/reproNative/reproNativeCpp.vcxproj | 2 ++ 7 files changed, 31 insertions(+), 37 deletions(-) diff --git a/Documentation/how-to-build-and-run-ilcompiler-in-visual-studio-2015.md b/Documentation/how-to-build-and-run-ilcompiler-in-visual-studio-2015.md index 108027363..e5d430f5b 100644 --- a/Documentation/how-to-build-and-run-ilcompiler-in-visual-studio-2015.md +++ b/Documentation/how-to-build-and-run-ilcompiler-in-visual-studio-2015.md @@ -52,7 +52,7 @@ _Note: The size of NuGet packages is approximately 2.75 GB, so download might ta - Set startup command line to: `@c:\corert\bin\obj\Windows_NT.x64.Debug\cpp.rsp` - - `--nolinenumbers` command line option can be used to suppress generation of line number mappings in C++ files - useful for debugging + - `--codegenopt:nolinenumbers` command line option can be used to suppress generation of line number mappings in C++ files - useful for debugging - Build & run using **F5** - This will run the compiler. The output is `c:\corert\bin\obj\Windows_NT.x64.Debug\repro\native\repro.cpp` file. diff --git a/src/BuildIntegration/Microsoft.NETCore.Native.Windows.props b/src/BuildIntegration/Microsoft.NETCore.Native.Windows.props index d36383bcc..edf33355c 100644 --- a/src/BuildIntegration/Microsoft.NETCore.Native.Windows.props +++ b/src/BuildIntegration/Microsoft.NETCore.Native.Windows.props @@ -25,7 +25,7 @@ See the LICENSE file in the project root for more information. - + diff --git a/src/ILCompiler.Compiler/src/Compiler/LibraryInitializers.cs b/src/ILCompiler.Compiler/src/Compiler/LibraryInitializers.cs index eb3a3c07b..42b942ce2 100644 --- a/src/ILCompiler.Compiler/src/Compiler/LibraryInitializers.cs +++ b/src/ILCompiler.Compiler/src/Compiler/LibraryInitializers.cs @@ -42,11 +42,11 @@ namespace ILCompiler // We should not care which code-gen is being used, however currently CppCodeGen cannot // handle code pulled in by all explicit cctors. // - // See https://github.com/dotnet/corert/issues/2486 + // See https://github.com/dotnet/corert/issues/2518 // _isCppCodeGen = isCppCodeGen; } - + public IList LibraryInitializerMethods { get diff --git a/src/ILCompiler.Compiler/src/Compiler/NameMangler.cs b/src/ILCompiler.Compiler/src/Compiler/NameMangler.cs index 4ebb2df61..c6f2e6335 100644 --- a/src/ILCompiler.Compiler/src/Compiler/NameMangler.cs +++ b/src/ILCompiler.Compiler/src/Compiler/NameMangler.cs @@ -87,14 +87,14 @@ namespace ILCompiler sb.Append("_"); } - string santizedName = (sb != null) ? sb.ToString() : s; + string sanitizedName = (sb != null) ? sb.ToString() : s; // The character sequences denoting generic instantiations, arrays, byrefs, or pointers must be // restricted to that use only. Replace them if they happened to be used in any identifiers in // the compilation input. return _mangleForCplusPlus - ? santizedName.Replace(EnterNameScopeSequence, "_AA_").Replace(ExitNameScopeSequence, "_VV_") - : santizedName; + ? sanitizedName.Replace(EnterNameScopeSequence, "_AA_").Replace(ExitNameScopeSequence, "_VV_") + : sanitizedName; } /// diff --git a/src/ILCompiler.Compiler/src/CppCodeGen/CppWriter.cs b/src/ILCompiler.Compiler/src/CppCodeGen/CppWriter.cs index f4fc041f7..413f5db33 100644 --- a/src/ILCompiler.Compiler/src/CppCodeGen/CppWriter.cs +++ b/src/ILCompiler.Compiler/src/CppCodeGen/CppWriter.cs @@ -268,7 +268,7 @@ namespace ILCompiler.CppCodeGen /// C++ declaration name for . public string GetCppMethodDeclarationName(TypeDesc owningType, string methodName) { - var s = GetCppTypeName(owningType); + var s = _compilation.NameMangler.GetMangledTypeName(owningType); if (s.StartsWith("::")) { // For a Method declaration we do not need the starting :: @@ -297,10 +297,10 @@ namespace ILCompiler.CppCodeGen public string SanitizeCppVarName(string varName) { // TODO: name mangling robustness - if (varName == "errno") // some names collide with CRT headers - varName += "_"; + if (varName == "errno" || varName == "environ" || varName == "template" || varName == "typename") // some names collide with CRT headers + return "_" + varName + "_"; - return varName; + return _compilation.NameMangler.SanitizeName(varName); } private void CompileExternMethod(CppMethodCodeNode methodCodeNodeNeedingCode, string importName) @@ -1023,15 +1023,14 @@ namespace ILCompiler.CppCodeGen { _emittedTypes = new HashSet(); } + TypeDesc nodeType = typeNode.Type; - if (nodeType.IsPointer || nodeType.IsByRef || _emittedTypes.Contains(nodeType)) + if (_emittedTypes.Contains(nodeType)) return; - _emittedTypes.Add(nodeType); - // Create Namespaces - string mangledName = GetCppTypeName(nodeType); + string mangledName = _compilation.NameMangler.GetMangledTypeName(nodeType); int nesting = 0; int current = 0; @@ -1074,7 +1073,6 @@ namespace ILCompiler.CppCodeGen // TODO: Enable once the dependencies are tracked for arrays // if (((DependencyNode)_compilation.NodeFactory.ConstructedTypeSymbol(t)).Marked) - if (!nodeType.IsPointer && !nodeType.IsByRef) { typeDefinitions.AppendLine(); typeDefinitions.Append("static MethodTable * __getMethodTable();"); @@ -1140,11 +1138,8 @@ namespace ILCompiler.CppCodeGen typeDefinitions.AppendEmptyLine(); // declare method table - if (!nodeType.IsPointer && !nodeType.IsByRef) - { - methodTable.Append(GetCodeForObjectNode(typeNode as ObjectNode, factory)); - methodTable.AppendEmptyLine(); - } + methodTable.Append(GetCodeForObjectNode(typeNode as ObjectNode, factory)); + methodTable.AppendEmptyLine(); } private String GetCodeForReadyToRunHeader(ReadyToRunHeaderNode headerNode, NodeFactory factory) diff --git a/src/ILCompiler.Compiler/src/CppCodeGen/ILToCppImporter.cs b/src/ILCompiler.Compiler/src/CppCodeGen/ILToCppImporter.cs index 78e225be6..7aa132b61 100644 --- a/src/ILCompiler.Compiler/src/CppCodeGen/ILToCppImporter.cs +++ b/src/ILCompiler.Compiler/src/CppCodeGen/ILToCppImporter.cs @@ -149,10 +149,10 @@ namespace Internal.IL var localSlotToInfoMap = new Dictionary(); foreach (var v in localVariables) { - string sanitizedName = _compilation.NameMangler.SanitizeName(v.Name); - if (!names.Add(v.Name)) + string sanitizedName = _writer.SanitizeCppVarName(v.Name); + if (!names.Add(sanitizedName)) { - sanitizedName = string.Format("{0}_local{1}", v.Name, v.Slot); + sanitizedName = string.Format("{0}_local{1}", sanitizedName, v.Slot); names.Add(sanitizedName); } @@ -172,7 +172,7 @@ namespace Internal.IL int index = 0; foreach (var p in parameters) { - parameterIndexToNameMap[index] = p; + parameterIndexToNameMap[index] = _writer.SanitizeCppVarName(p); ++index; } @@ -422,7 +422,7 @@ namespace Internal.IL if (_parameterIndexToNameMap != null && argument && _parameterIndexToNameMap.ContainsKey(index)) { - return _writer.SanitizeCppVarName(_parameterIndexToNameMap[index]); + return _parameterIndexToNameMap[index]; } return (argument ? "_a" : "_l") + index.ToStringInvariant(); @@ -2417,7 +2417,7 @@ namespace Internal.IL "::", _writer.GetCppMethodName(helper), "((intptr_t)", - _writer.GetCppTypeName((TypeDesc)ldtokenValue), + _compilation.NameMangler.GetMangledTypeName((TypeDesc)ldtokenValue), "::__getMethodTable())"); value = new LdTokenEntry(StackValueKind.ValueType, name, (TypeDesc)ldtokenValue, GetWellKnownType(ldtokenKind)); @@ -2493,7 +2493,7 @@ namespace Internal.IL GetSignatureTypeNameAndAddReference(type); - PushExpression(StackValueKind.Int32, "sizeof(" + _writer.GetCppTypeName(type) + ")"); + PushExpression(StackValueKind.Int32, "(int32_t)sizeof(" + _writer.GetCppTypeName(type) + ")"); } private void ImportRefAnyType() @@ -2575,9 +2575,12 @@ namespace Internal.IL AddTypeDependency(type, constructed); - foreach (var field in type.GetFields()) + if (!type.IsGenericDefinition) { - AddTypeDependency(field.FieldType, false); + foreach (var field in type.GetFields()) + { + AddTypeDependency(field.FieldType, false); + } } } private void AddTypeDependency(TypeDesc type, bool constructed) @@ -2586,14 +2589,8 @@ namespace Internal.IL { return; } - else if (type.IsPointer || type.IsByRef) - { - Debug.Assert(type is ParameterizedType); - AddTypeDependency((type as ParameterizedType).ParameterType, constructed); - return; - } - Object node; + Object node; if (constructed) node = _nodeFactory.ConstructedTypeSymbol(type); else diff --git a/src/ILCompiler/reproNative/reproNativeCpp.vcxproj b/src/ILCompiler/reproNative/reproNativeCpp.vcxproj index a6ff763be..eb8789560 100644 --- a/src/ILCompiler/reproNative/reproNativeCpp.vcxproj +++ b/src/ILCompiler/reproNative/reproNativeCpp.vcxproj @@ -59,6 +59,7 @@ ..\..\Native\gc;..\..\Native\gc\env;..\..\Native\Bootstrap 4477 MultiThreadedDebug + /bigobj Console @@ -79,6 +80,7 @@ ..\..\Native\gc;..\..\Native\gc\env 4477 MultiThreaded + /bigobj Console -- cgit v1.2.3 From be92d719080f191268b7c766056ec0ed826341f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Tue, 17 Jan 2017 14:26:29 -0800 Subject: Make threadstatics multifile friendly (#2508) Adding a COMDAT-foldable cell that encapsulates the type's thread static index and the associated type manager indirection. In multifile mode, threadstatic index for a generic type will potentially get generated in multiple modules, but we will end up using just one of them at runtime. If we had multiple nodes hardcoding the index, we would need to set up complex section dependencies to make sure they fold together and agree after linking. I chose not to special case generic types and use the indirection node for non-generic types as well. This removed 3 places where we needed to special case the R2R threadstatic base helper in the compiler (and there was probably one more place we needed to special case to fix a TODO I'm just deleting). I don't think the tiny overhead is worth the complexity in the compiler, especially considering we're talking about the non-optimized mode (single file with no R2R will always be the optimized one). --- .../src/Compiler/Compilation.cs | 9 +--- .../DependencyAnalysis/GenericLookupResult.cs | 9 +--- .../src/Compiler/DependencyAnalysis/NodeFactory.cs | 23 ++++++-- .../DependencyAnalysis/ReadyToRunHelperNode.cs | 6 --- .../DependencyAnalysis/RyuJitNodeFactory.cs | 14 +---- .../Target_X64/X64ReadyToRunGenericHelperNode.cs | 13 +++-- .../Target_X64/X64ReadyToRunHelperNode.cs | 21 +++----- .../TypeThreadStaticIndexNode.cs | 61 ++++++++++++++++++++++ .../src/ILCompiler.Compiler.csproj | 1 + 9 files changed, 101 insertions(+), 56 deletions(-) create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeThreadStaticIndexNode.cs diff --git a/src/ILCompiler.Compiler/src/Compiler/Compilation.cs b/src/ILCompiler.Compiler/src/Compiler/Compilation.cs index 9da9ab066..4ab222cc5 100644 --- a/src/ILCompiler.Compiler/src/Compiler/Compilation.cs +++ b/src/ILCompiler.Compiler/src/Compiler/Compilation.cs @@ -196,20 +196,13 @@ namespace ILCompiler public void AddCompilationRoot(TypeDesc type, string reason) { - if (type.IsGenericDefinition) + if (!ConstructedEETypeNode.CreationAllowed(type)) { _graph.AddRoot(_factory.NecessaryTypeSymbol(type), reason); } else { _graph.AddRoot(_factory.ConstructedTypeSymbol(type), reason); - - // If the type has a thread static field then we should eagerly create a helper - // to access such fields at runtime. This is required for multi-module compilation. - if (type.IsDefType && (((DefType)type).ThreadStaticFieldSize > 0)) - { - _graph.AddRoot(_factory.ReadyToRunHelper(ReadyToRunHelperId.GetThreadStaticBase, (MetadataType)type), reason); - } } } } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericLookupResult.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericLookupResult.cs index a8a19e172..77da6177a 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericLookupResult.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericLookupResult.cs @@ -236,14 +236,7 @@ namespace ILCompiler.DependencyAnalysis public override ISymbolNode GetTarget(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation) { var instantiatedType = (MetadataType)_type.InstantiateSignature(typeInstantiation, methodInstantiation); - return factory.TypeThreadStaticsSymbol(instantiatedType); - } - - public override void EmitDictionaryEntry(ref ObjectDataBuilder builder, NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation) - { - ThreadStaticsNode targetNode = (ThreadStaticsNode)GetTarget(factory, typeInstantiation, methodInstantiation); - int typeTlsIndex = factory.ThreadStaticsRegion.IndexOfEmbeddedObject(targetNode); - builder.EmitNaturalInt(typeTlsIndex); + return factory.TypeThreadStaticIndex(instantiatedType); } public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs index 076560532..083017c3a 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs @@ -153,6 +153,11 @@ namespace ILCompiler.DependencyAnalysis return new ThreadStaticsNode(type, this); }); + _typeThreadStaticIndices = new NodeCache(type => + { + return new TypeThreadStaticIndexNode(type); + }); + _GCStaticEETypes = new NodeCache((GCPointerMap gcMap) => { return new GCStaticEETypeNode(Target, gcMap); @@ -357,15 +362,27 @@ namespace ILCompiler.DependencyAnalysis private NodeCache _threadStatics; - public ISymbolNode TypeThreadStaticsSymbol(MetadataType type) + public ThreadStaticsNode TypeThreadStaticsSymbol(MetadataType type) + { + // This node is always used in the context of its index within the region. + // We should never ask for this if the current compilation doesn't contain the + // associated type. + Debug.Assert(_compilationModuleGroup.ContainsType(type)); + return _threadStatics.GetOrAdd(type); + } + + + private NodeCache _typeThreadStaticIndices; + + public ISymbolNode TypeThreadStaticIndex(MetadataType type) { if (_compilationModuleGroup.ContainsType(type)) { - return _threadStatics.GetOrAdd(type); + return _typeThreadStaticIndices.GetOrAdd(type); } else { - return ExternSymbol("__ThreadStaticBase_" + NodeFactory.NameMangler.GetMangledTypeName(type)); + return ExternSymbol("__TypeThreadStaticIndex_" + NameMangler.GetMangledTypeName(type)); } } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs index 249fc4114..477eef6dd 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs @@ -156,12 +156,6 @@ namespace ILCompiler.DependencyAnalysis dependencyList.Add(factory.VirtualMethodUse((MethodDesc)_target), "ReadyToRun Virtual Method Address Load"); return dependencyList; } - else if (_id == ReadyToRunHelperId.GetThreadStaticBase) - { - DependencyList dependencyList = new DependencyList(); - dependencyList.Add(factory.TypeThreadStaticsSymbol((MetadataType)_target), "ReadyToRun Thread Static Storage"); - return dependencyList; - } else { return null; diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RyuJitNodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RyuJitNodeFactory.cs index b8430f23c..cb2d74962 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RyuJitNodeFactory.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/RyuJitNodeFactory.cs @@ -49,19 +49,7 @@ namespace ILCompiler.DependencyAnalysis protected override ISymbolNode CreateReadyToRunHelperNode(Tuple helperCall) { - ReadyToRunHelperNode node = new ReadyToRunHelperNode(this, helperCall.Item1, helperCall.Item2); - - if ((node.Id != ReadyToRunHelperId.GetThreadStaticBase) || - CompilationModuleGroup.ContainsType((TypeDesc)node.Target)) - { - return node; - } - else - { - // The ReadyToRun helper for a type with thread static fields resides in the same module as the target type. - // Other modules should use an extern symbol node to access it. - return ExternSymbol(node.GetMangledName()); - } + return new ReadyToRunHelperNode(this, helperCall.Item1, helperCall.Item2); } } } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunGenericHelperNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunGenericHelperNode.cs index 95439629a..2ab092c7f 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunGenericHelperNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunGenericHelperNode.cs @@ -103,7 +103,7 @@ namespace ILCompiler.DependencyAnalysis { MetadataType target = (MetadataType)_target; - // Look up the TLS slot index + // Look up the index cell EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg1, _lookupSignature, relocsOnly); ISymbolNode helperEntrypoint; @@ -121,8 +121,15 @@ namespace ILCompiler.DependencyAnalysis helperEntrypoint = factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType); } - // Load the type manager indirection (this clobbers the generic context in Arg0) - encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeManagerIndirection); + // First arg: address of the TypeManager slot that provides the helper with + // information about module index and the type manager instance (which is used + // for initialization on first access). + AddrMode loadFromArg1 = new AddrMode(encoder.TargetRegister.Arg1, null, 0, 0, AddrModeSize.Int64); + encoder.EmitMOV(encoder.TargetRegister.Arg0, ref loadFromArg1); + + // Second arg: index of the type in the ThreadStatic section of the modules + AddrMode loadFromArg1AndDelta = new AddrMode(encoder.TargetRegister.Arg1, null, factory.Target.PointerSize, 0, AddrModeSize.Int64); + encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromArg1AndDelta); encoder.EmitJMP(helperEntrypoint); } 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 8a7bea4a1..b1f859519 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs @@ -110,27 +110,18 @@ namespace ILCompiler.DependencyAnalysis case ReadyToRunHelperId.GetThreadStaticBase: { MetadataType target = (MetadataType)Target; - ThreadStaticsNode targetNode = factory.TypeThreadStaticsSymbol(target) as ThreadStaticsNode; - int typeTlsIndex = 0; - // The GetThreadStaticBase helper should be generated only in the compilation module group - // that contains the thread static field because the helper needs the index of the type - // in Thread Static section of the containing module. - // TODO: This needs to be fixed this for the multi-module compilation - Debug.Assert(targetNode != null); - - if (!relocsOnly) - { - // Get index of the targetNode in the Thread Static region - typeTlsIndex = factory.ThreadStaticsRegion.IndexOfEmbeddedObject(targetNode); - } + encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.TypeThreadStaticIndex(target)); // First arg: address of the TypeManager slot that provides the helper with // information about module index and the type manager instance (which is used // for initialization on first access). - encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeManagerIndirection); + AddrMode loadFromArg2 = new AddrMode(encoder.TargetRegister.Arg2, null, 0, 0, AddrModeSize.Int64); + encoder.EmitMOV(encoder.TargetRegister.Arg0, ref loadFromArg2); + // Second arg: index of the type in the ThreadStatic section of the modules - encoder.EmitMOV(encoder.TargetRegister.Arg1, typeTlsIndex); + AddrMode loadFromArg2AndDelta = new AddrMode(encoder.TargetRegister.Arg2, null, factory.Target.PointerSize, 0, AddrModeSize.Int64); + encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromArg2AndDelta); if (!factory.TypeSystemContext.HasLazyStaticConstructor(target)) { diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeThreadStaticIndexNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeThreadStaticIndexNode.cs new file mode 100644 index 000000000..9949961b2 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeThreadStaticIndexNode.cs @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a node containing information necessary at runtime to locate type's thread static base. + /// + internal class TypeThreadStaticIndexNode : ObjectNode, ISymbolNode + { + private MetadataType _type; + + public TypeThreadStaticIndexNode(MetadataType type) + { + _type = type; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("__TypeThreadStaticIndex_") + .Append(NodeFactory.NameMangler.GetMangledTypeName(_type)); + } + public int Offset => 0; + protected override string GetName() => this.GetMangledName(); + public override ObjectNodeSection Section => ObjectNodeSection.ReadOnlyDataSection; + public override bool IsShareable => true; + public override bool StaticDependenciesAreComputed => true; + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + return new DependencyList + { + new DependencyListEntry(factory.TypeThreadStaticsSymbol(_type), "Thread static storage") + }; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataBuilder objData = new ObjectDataBuilder(factory); + + objData.Alignment = objData.TargetPointerSize; + objData.DefinedSymbols.Add(this); + + int typeTlsIndex = 0; + if (!relocsOnly) + { + var node = factory.TypeThreadStaticsSymbol(_type); + typeTlsIndex = factory.ThreadStaticsRegion.IndexOfEmbeddedObject(node); + } + + objData.EmitPointerReloc(factory.TypeManagerIndirection); + objData.EmitNaturalInt(typeTlsIndex); + + return objData.ToObjectData(); + } + } +} diff --git a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj index 59c6ad2d6..2330e0a02 100644 --- a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj +++ b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj @@ -176,6 +176,7 @@ + -- cgit v1.2.3 From 785a34fc3c1523baf2717e2e8ce5c059395ba07a Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Tue, 17 Jan 2017 14:53:34 -0800 Subject: Copy ByReferencefrom CoreCLR [tfs-changeset: 1644761] --- .../src/System.Private.CoreLib.csproj | 1 + .../src/System/ByReference.cs | 41 ++++++++++++++++++++++ .../CompilerServices/InternalCompilerAttributes.cs | 17 --------- 3 files changed, 42 insertions(+), 17 deletions(-) create mode 100644 src/System.Private.CoreLib/src/System/ByReference.cs diff --git a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 15ea8ef11..baf7c70b5 100644 --- a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -222,6 +222,7 @@ + diff --git a/src/System.Private.CoreLib/src/System/ByReference.cs b/src/System.Private.CoreLib/src/System/ByReference.cs new file mode 100644 index 000000000..d0129c2ee --- /dev/null +++ b/src/System.Private.CoreLib/src/System/ByReference.cs @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.CompilerServices; + +namespace System +{ + // ByReference is meant to be used to represent "ref T" fields. It is working + // around lack of first class support for byref fields in C# and IL. The JIT and + // type loader have special handling for it that turns it into a thin wrapper around ref T. + [StackOnly] + internal struct ByReference + { + // CS0169: The private field '{blah}' is never used +#pragma warning disable 169 + private IntPtr _value; +#pragma warning restore + + [Intrinsic] + public ByReference(ref T value) + { + // Implemented as a JIT intrinsic - This default implementation is for + // completeness and to provide a concrete error if called via reflection + // or if intrinsic is missed. + throw new System.PlatformNotSupportedException(); + } + + public ref T Value + { + [Intrinsic] + get + { + // Implemented as a JIT intrinsic - This default implementation is for + // completeness and to provide a concrete error if called via reflection + // or if the intrinsic is missed. + throw new System.PlatformNotSupportedException(); + } + } + } +} diff --git a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/InternalCompilerAttributes.cs b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/InternalCompilerAttributes.cs index 298b4e466..3ad095e91 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/InternalCompilerAttributes.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/InternalCompilerAttributes.cs @@ -21,21 +21,4 @@ namespace System.Runtime.CompilerServices [AttributeUsage(AttributeTargets.Struct)] public sealed class StackOnlyAttribute : Attribute { } - -#if false // Unused right now. It is likely going to be useful for Span implementation. - // This is a dummy class to be replaced by the compiler with a ref T - // It has to be a dummy class to avoid complicated type substitution - // and other complications in the compiler. - public sealed class ByReference - { - // - // Managed pointer creation - // - [Intrinsic] - public static extern ByReference FromRef(ref T source); - - [Intrinsic] - public static extern ref T ToRef(ByReference source); - } -#endif } -- cgit v1.2.3 From d06f5d39a0cf028b713d13d4bbef725db78c644b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Tue, 17 Jan 2017 15:03:38 -0800 Subject: Update CoreCLR.issues.targets (#2522) --- tests/CoreCLR.issues.targets | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/CoreCLR.issues.targets b/tests/CoreCLR.issues.targets index 06e2d17f0..7132c4749 100644 --- a/tests/CoreCLR.issues.targets +++ b/tests/CoreCLR.issues.targets @@ -1115,6 +1115,8 @@ + + -- cgit v1.2.3 From 4441ba70441a62afce2e67ab21abeb489514a213 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Tue, 17 Jan 2017 19:58:59 -0800 Subject: Undo RuntimeImport optimization for Get/SetLastError It leads to calling convention mismatch on x86 [tfs-changeset: 1644819] --- src/Common/src/Interop/Windows/mincore/Interop.GetLastError.cs | 5 +---- src/Common/src/Interop/Windows/mincore/Interop.SetLastError.cs | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/Common/src/Interop/Windows/mincore/Interop.GetLastError.cs b/src/Common/src/Interop/Windows/mincore/Interop.GetLastError.cs index fce9ba542..45dcf94c1 100644 --- a/src/Common/src/Interop/Windows/mincore/Interop.GetLastError.cs +++ b/src/Common/src/Interop/Windows/mincore/Interop.GetLastError.cs @@ -2,16 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Runtime; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; internal partial class Interop { internal partial class mincore { - [MethodImpl(MethodImplOptions.InternalCall)] - [RuntimeImport(Interop.Libraries.ErrorHandling, "GetLastError")] + [DllImport("api-ms-win-core-errorhandling-l1-1-0.dll")] internal extern static int GetLastError(); } } diff --git a/src/Common/src/Interop/Windows/mincore/Interop.SetLastError.cs b/src/Common/src/Interop/Windows/mincore/Interop.SetLastError.cs index 16e8ec8e0..583043061 100644 --- a/src/Common/src/Interop/Windows/mincore/Interop.SetLastError.cs +++ b/src/Common/src/Interop/Windows/mincore/Interop.SetLastError.cs @@ -2,16 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Runtime; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; internal partial class Interop { internal partial class mincore { - [MethodImpl(MethodImplOptions.InternalCall)] - [RuntimeImport(Interop.Libraries.ErrorHandling, "SetLastError")] + [DllImport("api-ms-win-core-errorhandling-l1-1-0.dll")] internal extern static void SetLastError(uint dwErrCode); } } -- cgit v1.2.3 From fcddeabd49c36406b561101ae25c23c48a6aa5cd Mon Sep 17 00:00:00 2001 From: Atsushi Kanamori Date: Wed, 18 Jan 2017 09:50:44 -0800 Subject: Adding ISerializable to System.Delegate/System.MulticastDelegate Doing this now because the lack of it is forcing us to spam apicompat baselines with exemptions for every delegate type in the framework. System.Delegate.GetObjectData() just throws NotSupportedException so that's easy to implement now. System.MulticastDelegate.GetObjectData() is more complicated and probably needs MethodInfo serialization to work first. So this is a stub. --- src/System.Private.CoreLib/src/System/Delegate.cs | 8 +++++++- src/System.Private.CoreLib/src/System/MulticastDelegate.cs | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/System.Private.CoreLib/src/System/Delegate.cs b/src/System.Private.CoreLib/src/System/Delegate.cs index 399fd783b..7fa5553cf 100644 --- a/src/System.Private.CoreLib/src/System/Delegate.cs +++ b/src/System.Private.CoreLib/src/System/Delegate.cs @@ -5,6 +5,7 @@ using System.Text; using System.Runtime; using System.Reflection; +using System.Runtime.Serialization; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Diagnostics; @@ -19,7 +20,7 @@ namespace System // sequential layout directive so that Bartok matches it. [StructLayout(LayoutKind.Sequential)] [DebuggerDisplay("Target method(s) = {GetTargetMethodsDescriptionForDebugger()}")] - public abstract partial class Delegate : ICloneable + public abstract partial class Delegate : ICloneable, ISerializable { // This ctor exists solely to prevent C# from generating a protected .ctor that violates the surface area. I really want this to be a // "protected-and-internal" rather than "internal" but C# has no keyword for the former. @@ -707,6 +708,11 @@ namespace System return MemberwiseClone(); } + public virtual void GetObjectData(SerializationInfo info, StreamingContext context) + { + throw new NotSupportedException(); + } + internal bool IsOpenStatic { get diff --git a/src/System.Private.CoreLib/src/System/MulticastDelegate.cs b/src/System.Private.CoreLib/src/System/MulticastDelegate.cs index 7caec691c..55b550239 100644 --- a/src/System.Private.CoreLib/src/System/MulticastDelegate.cs +++ b/src/System.Private.CoreLib/src/System/MulticastDelegate.cs @@ -3,13 +3,14 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using System.Runtime.Serialization; using System.Runtime.CompilerServices; using Internal.Runtime.CompilerServices; namespace System { - public abstract class MulticastDelegate : Delegate + public abstract class MulticastDelegate : Delegate, ISerializable { // This ctor exists solely to prevent C# from generating a protected .ctor that violates the surface area. I really want this to be a // "protected-and-internal" rather than "internal" but C# has no keyword for the former. @@ -114,5 +115,10 @@ namespace System { return base.GetInvocationList(); } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + throw new NotImplementedException(); + } } } -- cgit v1.2.3 From 4f750e89d7c8c39cd8f27ba9acc5f8fb25a6d768 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Wed, 18 Jan 2017 10:23:57 -0800 Subject: Use regular PInvoke for errno shims (#2530) --- .../Unix/System.Private.CoreLib.Native/Interop.ErrNo.cs | 10 ++++------ src/Native/System.Private.CoreLib.Native/pal_errno.cpp | 6 +++--- .../src/System/Runtime/InteropServices/PInvokeMarshal.Unix.cs | 4 ++-- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.ErrNo.cs b/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.ErrNo.cs index 911a055bc..e879fdecc 100644 --- a/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.ErrNo.cs +++ b/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.ErrNo.cs @@ -11,12 +11,10 @@ internal static partial class Interop { internal unsafe partial class Sys { - [MethodImpl(MethodImplOptions.InternalCall)] - [RuntimeImport(Interop.Libraries.CoreLibNative, "CoreLibNative_GetLastErrNo")] - internal static extern int GetLastErrNo(); + [DllImport(Interop.Libraries.CoreLibNative, EntryPoint = "CoreLibNative_GetErrNo")] + internal static extern int GetErrNo(); - [MethodImpl(MethodImplOptions.InternalCall)] - [RuntimeImport(Interop.Libraries.CoreLibNative, "CoreLibNative_SetLastErrNo")] - internal static extern void SetLastErrNo(int error); + [DllImport(Interop.Libraries.CoreLibNative, EntryPoint = "CoreLibNative_ClearErrNo")] + internal static extern void ClearErrNo(); } } diff --git a/src/Native/System.Private.CoreLib.Native/pal_errno.cpp b/src/Native/System.Private.CoreLib.Native/pal_errno.cpp index e7e7bb6ba..9a8ee708b 100644 --- a/src/Native/System.Private.CoreLib.Native/pal_errno.cpp +++ b/src/Native/System.Private.CoreLib.Native/pal_errno.cpp @@ -5,12 +5,12 @@ #include #include -extern "C" int32_t CoreLibNative_GetLastErrNo() +extern "C" int32_t CoreLibNative_GetErrNo() { return errno; } -extern "C" void CoreLibNative_SetLastErrNo(int32_t error) +extern "C" void CoreLibNative_ClearErrNo() { - errno = error; + errno = 0; } diff --git a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.Unix.cs b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.Unix.cs index 3358ddeb1..4f893e789 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.Unix.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.Unix.cs @@ -14,12 +14,12 @@ namespace System.Runtime.InteropServices { public static void SaveLastWin32Error() { - s_lastWin32Error = Interop.Sys.GetLastErrNo(); + s_lastWin32Error = Interop.Sys.GetErrNo(); } internal static void ClearLastWin32Error() { - Interop.Sys.SetLastErrNo(0); + Interop.Sys.ClearErrNo(); } public static unsafe String PtrToStringAnsi(IntPtr ptr) -- cgit v1.2.3 From 99a78afd8d2d7902c7e8b1d6ceb87dad62dbfdfc Mon Sep 17 00:00:00 2001 From: Chris Rummel Date: Wed, 18 Jan 2017 12:46:58 -0600 Subject: Split pipeline build to match CoreFX's model. (#2505) * Split pipeline build to match CoreFX's model. - OS-specific legs no longer publish to MyGet, they just upload to Azure. - New publish leg downloads from Azure and publishes to MyGet. - Only includes Microsoft.TargetingPack.Private.CoreRT for now, will add ILCompiler after we sort out what OS-specific packages should be named. * Addressing code review feedback (PR#2505). --- build.proj | 1 + buildpipeline/DotNet-CoreRT-Linux.json | 95 ++++--- buildpipeline/DotNet-CoreRT-Mac.json | 69 ++--- buildpipeline/DotNet-CoreRT-Publish.json | 456 +++++++++++++++++++++++++++++++ buildpipeline/DotNet-CoreRT-Windows.json | 53 ++-- buildpipeline/pipeline.json | 123 ++++++++- buildscripts/publish-packages.cmd | 40 +++ buildscripts/publish-packages.sh | 33 +++ buildscripts/publish.proj | 20 ++ buildscripts/syncAzure.proj | 19 ++ buildscripts/updatePublishedVersions.ps1 | 26 ++ dir.props | 6 +- 12 files changed, 820 insertions(+), 121 deletions(-) create mode 100644 buildpipeline/DotNet-CoreRT-Publish.json create mode 100644 buildscripts/publish-packages.cmd create mode 100755 buildscripts/publish-packages.sh create mode 100644 buildscripts/publish.proj create mode 100644 buildscripts/syncAzure.proj create mode 100644 buildscripts/updatePublishedVersions.ps1 diff --git a/build.proj b/build.proj index b39ae63c8..20fe45254 100644 --- a/build.proj +++ b/build.proj @@ -13,6 +13,7 @@ + diff --git a/buildpipeline/DotNet-CoreRT-Linux.json b/buildpipeline/DotNet-CoreRT-Linux.json index dd5bd631d..dde1228dd 100644 --- a/buildpipeline/DotNet-CoreRT-Linux.json +++ b/buildpipeline/DotNet-CoreRT-Linux.json @@ -449,7 +449,7 @@ } }, { - "enabled": false, + "enabled": true, "continueOnError": false, "alwaysRun": false, "displayName": "Remove container", @@ -470,23 +470,7 @@ "enabled": true, "continueOnError": false, "alwaysRun": false, - "displayName": "Remove old docker build logs", - "timeoutInMinutes": 0, - "task": { - "id": "b7e8b412-0437-4065-9371-edc5881de25b", - "versionSpec": "*", - "definitionType": "task" - }, - "inputs": { - "SourceFolder": "$(DockerCopyDest)", - "Contents": "*" - } - }, - { - "enabled": true, - "continueOnError": false, - "alwaysRun": false, - "displayName": "Run mkdir", + "displayName": "Publish packages", "timeoutInMinutes": 0, "task": { "id": "d9bafed4-0b18-4f58-968d-86655b4d2ce9", @@ -494,17 +478,17 @@ "definitionType": "task" }, "inputs": { - "filename": "mkdir", - "arguments": "$(DockerCopyDest)", - "workingFolder": "", + "filename": "docker", + "arguments": "run -w=\"$(GitDirectory)\" --name $(DockerContainerName) $(DockerModifiedImageName) $(GitDirectory)/buildscripts/publish-packages.sh -AzureAccount $(CloudDropAccountName) -AzureToken $(CloudDropAccessToken) -Container $(Label)", + "workingFolder": "$(SourceFolder)", "failOnStandardError": "false" } }, { "enabled": true, - "continueOnError": true, - "alwaysRun": true, - "displayName": "Expose docker repo for publishing", + "continueOnError": false, + "alwaysRun": false, + "displayName": "Commit changes", "timeoutInMinutes": 0, "task": { "id": "d9bafed4-0b18-4f58-968d-86655b4d2ce9", @@ -513,7 +497,7 @@ }, "inputs": { "filename": "docker", - "arguments": "cp $(DockerContainerName):$(GitDirectory) $(DockerCopyDest)/", + "arguments": "commit $(DockerContainerName) $(DockerModifiedImageName)", "workingFolder": "", "failOnStandardError": "false" } @@ -522,25 +506,23 @@ "enabled": true, "continueOnError": false, "alwaysRun": false, - "displayName": "Remove container", + "displayName": "Remove old docker build logs", "timeoutInMinutes": 0, "task": { - "id": "d9bafed4-0b18-4f58-968d-86655b4d2ce9", + "id": "b7e8b412-0437-4065-9371-edc5881de25b", "versionSpec": "*", "definitionType": "task" }, "inputs": { - "filename": "docker", - "arguments": "rm $(DockerContainerName)", - "workingFolder": "", - "failOnStandardError": "false" + "SourceFolder": "$(DockerCopyDest)", + "Contents": "*" } }, { "enabled": true, "continueOnError": false, "alwaysRun": false, - "displayName": "Download nuget", + "displayName": "Run mkdir", "timeoutInMinutes": 0, "task": { "id": "d9bafed4-0b18-4f58-968d-86655b4d2ce9", @@ -548,17 +530,17 @@ "definitionType": "task" }, "inputs": { - "filename": "curl", - "arguments": "-L -k -o $(SourceFolder)/nuget.zip https://dotnet.myget.org/F/dotnet-buildtools/api/v2/package/NuGet.CommandLine/3.5.0-rc-1256", + "filename": "mkdir", + "arguments": "$(DockerCopyDest)", "workingFolder": "", "failOnStandardError": "false" } }, { "enabled": true, - "continueOnError": false, - "alwaysRun": false, - "displayName": "Unzip nuget", + "continueOnError": true, + "alwaysRun": true, + "displayName": "Expose docker repo for publishing", "timeoutInMinutes": 0, "task": { "id": "d9bafed4-0b18-4f58-968d-86655b4d2ce9", @@ -566,17 +548,17 @@ "definitionType": "task" }, "inputs": { - "filename": "unzip", - "arguments": "$(SourceFolder)/nuget.zip -d $(SourceFolder)/nuget", + "filename": "docker", + "arguments": "cp $(DockerContainerName):$(GitDirectory) $(DockerCopyDest)/", "workingFolder": "", "failOnStandardError": "false" } }, { - "enabled": false, + "enabled": true, "continueOnError": false, "alwaysRun": false, - "displayName": "Publish packages", + "displayName": "Remove container", "timeoutInMinutes": 0, "task": { "id": "d9bafed4-0b18-4f58-968d-86655b4d2ce9", @@ -584,9 +566,9 @@ "definitionType": "task" }, "inputs": { - "filename": "mono", - "arguments": "nuget/tools/NuGet.exe push bin/Product/pkg/*.nupkg $(MyGetApiKey) -Source $(MyGetFeedUrl) -Timeout 3600", - "workingFolder": "$(SourceFolder)", + "filename": "docker", + "arguments": "rm $(DockerContainerName)", + "workingFolder": "", "failOnStandardError": "false" } }, @@ -720,8 +702,12 @@ "value": "$(Build.BuildNumber)", "allowOverride": true }, + "BuildTag": { + "value": "corert-alpha", + "allowOverride": true + }, "Label": { - "value": "$(Build.BuildNumber)", + "value": "$(BuildTag)-$(Build.BuildNumber)", "allowOverride": true }, "SourceVersion": { @@ -771,6 +757,25 @@ "value": null, "isSecret": true, "allowOverride": true + }, + "CloudDropAccountName": { + "value": "dotnetbuildoutput" + }, + "CloudDropAccessToken": { + "value": null, + "isSecret": true + }, + "UpdatePublishedVersions.AuthToken": { + "value": null, + "isSecret": true + }, + "VersionsRepoOwner": { + "value": "crummel", + "allowOverride": true + }, + "VersionsRepo": { + "value": "dotnet_versions", + "allowOverride": true } }, "demands": [ diff --git a/buildpipeline/DotNet-CoreRT-Mac.json b/buildpipeline/DotNet-CoreRT-Mac.json index a2f7c586f..2abbbb760 100644 --- a/buildpipeline/DotNet-CoreRT-Mac.json +++ b/buildpipeline/DotNet-CoreRT-Mac.json @@ -126,42 +126,6 @@ "failOnStandardError": "false" } }, - { - "enabled": true, - "continueOnError": false, - "alwaysRun": false, - "displayName": "Download nuget", - "timeoutInMinutes": 0, - "task": { - "id": "d9bafed4-0b18-4f58-968d-86655b4d2ce9", - "versionSpec": "*", - "definitionType": "task" - }, - "inputs": { - "filename": "curl", - "arguments": "-L -k -o $(SourceFolder)/nuget.zip https://dotnet.myget.org/F/dotnet-buildtools/api/v2/package/NuGet.CommandLine/3.5.0-rc-1256", - "workingFolder": "", - "failOnStandardError": "false" - } - }, - { - "enabled": true, - "continueOnError": false, - "alwaysRun": false, - "displayName": "Unzip nuget", - "timeoutInMinutes": 0, - "task": { - "id": "d9bafed4-0b18-4f58-968d-86655b4d2ce9", - "versionSpec": "*", - "definitionType": "task" - }, - "inputs": { - "filename": "unzip", - "arguments": "$(SourceFolder)/nuget.zip -d $(SourceFolder)/nuget", - "workingFolder": "", - "failOnStandardError": "false" - } - }, { "enabled": true, "continueOnError": false, @@ -170,13 +134,13 @@ "timeoutInMinutes": 0, "task": { "id": "d9bafed4-0b18-4f58-968d-86655b4d2ce9", - "versionSpec": "*", + "versionSpec": "1.*", "definitionType": "task" }, "inputs": { - "filename": "mono", - "arguments": "nuget/tools/NuGet.exe push bin/Product/pkg/*.nupkg $(MyGetApiKey) -Source $(MyGetFeedUrl) -Timeout 3600", - "workingFolder": "$(SourceFolder)", + "filename": "$(Build.SourcesDirectory)/$(SourceFolder)/buildscripts/publish-packages.sh", + "arguments": "-AzureAccount $(CloudDropAccountName) -AzureToken $(CloudDropAccessToken) -Container $(Label)", + "workingFolder": "$(Build.SourcesDirectory)/$(SourceFolder)", "failOnStandardError": "false" } }, @@ -292,8 +256,12 @@ "value": "$(Build.BuildNumber)", "allowOverride": true }, + "BuildTag": { + "value": "corert-alpha", + "allowOverride": true + }, "Label": { - "value": "$(Build.BuildNumber)", + "value": "$(BuildTag)-$(Build.BuildNumber)", "allowOverride": true }, "SourceVersion": { @@ -322,6 +290,25 @@ "value": null, "isSecret": true, "allowOverride": true + }, + "CloudDropAccountName": { + "value": "dotnetbuildoutput" + }, + "CloudDropAccessToken": { + "value": null, + "isSecret": true + }, + "UpdatePublishedVersions.AuthToken": { + "value": null, + "isSecret": true + }, + "VersionsRepoOwner": { + "value": "crummel", + "allowOverride": true + }, + "VersionsRepo": { + "value": "dotnet_versions", + "allowOverride": true } }, "demands": [ diff --git a/buildpipeline/DotNet-CoreRT-Publish.json b/buildpipeline/DotNet-CoreRT-Publish.json new file mode 100644 index 000000000..d503fce24 --- /dev/null +++ b/buildpipeline/DotNet-CoreRT-Publish.json @@ -0,0 +1,456 @@ +{ + "build": [ + { + "enabled": true, + "continueOnError": false, + "alwaysRun": false, + "displayName": "Run script $(VS140COMNTOOLS)\\VsDevCmd.bat", + "timeoutInMinutes": 0, + "task": { + "id": "bfc8bf76-e7ac-4a8c-9a55-a944a9f632fd", + "versionSpec": "*", + "definitionType": "task" + }, + "inputs": { + "filename": "$(VS140COMNTOOLS)\\VsDevCmd.bat", + "arguments": "", + "modifyEnvironment": "true", + "workingFolder": "", + "failOnStandardError": "false" + } + }, + { + "enabled": true, + "continueOnError": false, + "alwaysRun": false, + "displayName": "Fetch custom tooling (NuGet, EmbedIndex)", + "timeoutInMinutes": 0, + "task": { + "id": "e213ff0f-5d5c-4791-802d-52ea3e7be1f1", + "versionSpec": "*", + "definitionType": "task" + }, + "inputs": { + "scriptType": "filePath", + "scriptName": "scripts/DotNet-Trusted-Publish/Fetch-Tools.ps1", + "arguments": "$(Build.StagingDirectory)\\ToolingDownload", + "inlineScript": "# You can write your powershell scripts inline here. \n# You can also pass predefined and custom variables to this scripts using arguments\n\n Write-Host \"Hello World\"", + "workingFolder": "", + "failOnStandardError": "true" + } + }, + { + "enabled": true, + "continueOnError": false, + "alwaysRun": false, + "displayName": "Set up pipeline-specific git repository", + "timeoutInMinutes": 0, + "task": { + "id": "e213ff0f-5d5c-4791-802d-52ea3e7be1f1", + "versionSpec": "*", + "definitionType": "task" + }, + "inputs": { + "scriptType": "inlineScript", + "scriptName": "", + "arguments": "-gitUrl $(GitUrl) -root $(Pipeline.SourcesDirectory)", + "inlineScript": "param($gitUrl, $root)\n\nif (Test-Path $root)\n{\n Remove-Item -Recurse -Force $root\n}\ngit clone --no-checkout $gitUrl $root 2>&1 | Write-Host\ncd $root\ngit checkout $env:SourceVersion 2>&1 | Write-Host\n\nWrite-Host (\"##vso[task.setvariable variable=Pipeline.SourcesDirectory;]$root\")", + "workingFolder": "", + "failOnStandardError": "true" + } + }, + { + "enabled": true, + "continueOnError": false, + "alwaysRun": false, + "displayName": "Download packages", + "timeoutInMinutes": 0, + "task": { + "id": "e213ff0f-5d5c-4791-802d-52ea3e7be1f1", + "versionSpec": "*", + "definitionType": "task" + }, + "inputs": { + "scriptType": "inlineScript", + "scriptName": "", + "arguments": "$(CloudDropAccountName) $(CloudDropAccessToken) $(Label)", + "inlineScript": "param($account, $token, $container)\nif ($env:UseLegacyBuildScripts -eq \"true\")\n{\n .\\sync.cmd /ab /p:CloudDropAccountName=$account /p:CloudDropAccessToken=$token /p:ContainerName=$container\n}\nelse\n{\n .\\init-tools.cmd\n msbuild buildscripts\\syncAzure.proj /p:CloudDropAccountName=$account /p:CloudDropAccessToken=$token /p:ContainerName=$container /fl \"/flp:v=diag;logfile=package-download.log\"\n}", + "workingFolder": "$(Pipeline.SourcesDirectory)", + "failOnStandardError": "false" + } + }, + { + "enabled": true, + "continueOnError": false, + "alwaysRun": false, + "displayName": "Index symbol packages", + "timeoutInMinutes": 0, + "task": { + "id": "e213ff0f-5d5c-4791-802d-52ea3e7be1f1", + "versionSpec": "*", + "definitionType": "task" + }, + "inputs": { + "scriptType": "inlineScript", + "scriptName": "", + "arguments": "", + "inlineScript": "if ($env:Configuration -ne \"Release\") { exit }\n\n& $env:Build_SourcesDirectory\\scripts\\DotNet-Trusted-Publish\\Embed-Index.ps1 `\n $env:Pipeline_SourcesDirectory\\packages\\AzureTransfer\\Windows_NT.x64.$env:Configuration\\Microsoft.TargetingPack.Private.CoreRT\\$env:AzureContainerSymbolPackageGlob `\n $env:Build_StagingDirectory\\IndexedSymbolPackages", + "workingFolder": "", + "failOnStandardError": "true" + } + }, + { + "enabled": true, + "continueOnError": false, + "alwaysRun": false, + "displayName": "Generate Version Assets", + "timeoutInMinutes": 0, + "task": { + "id": "e213ff0f-5d5c-4791-802d-52ea3e7be1f1", + "versionSpec": "*", + "definitionType": "task" + }, + "inputs": { + "scriptType": "inlineScript", + "scriptName": "", + "arguments": "", + "inlineScript": "msbuild build.proj /t:CreateOrUpdateCurrentVersionFile /p:OfficialBuildId=$env:OfficialBuildId /p:BuildVersionFile=bin\\obj\\BuildVersion-$env:OfficialBuildId.props", + "workingFolder": "$(Pipeline.SourcesDirectory)", + "failOnStandardError": "true" + } + }, + { + "enabled": true, + "continueOnError": true, + "alwaysRun": false, + "displayName": "Log Native Version Assets Files", + "timeoutInMinutes": 0, + "task": { + "id": "d9bafed4-0b18-4f58-968d-86655b4d2ce9", + "versionSpec": "*", + "definitionType": "task" + }, + "inputs": { + "filename": "dir", + "arguments": "$(Pipeline.SourcesDirectory)\\bin\\obj\\BuildVersion*", + "workingFolder": "", + "failOnStandardError": "false" + } + }, + { + "enabled": true, + "continueOnError": false, + "alwaysRun": false, + "displayName": "packages -> dotnet.myget.org", + "timeoutInMinutes": 0, + "task": { + "id": "e213ff0f-5d5c-4791-802d-52ea3e7be1f1", + "versionSpec": "*", + "definitionType": "task" + }, + "inputs": { + "scriptType": "inlineScript", + "scriptName": "", + "arguments": "$(MyGetApiKey)", + "inlineScript": "param($ApiKey)\nif ($env:Configuration -ne \"Release\") { exit }\n& $env:CustomNuGetPath push $env:Pipeline_SourcesDirectory\\packages\\AzureTransfer\\Windows_NT.x64.$env:Configuration\\Microsoft.TargetingPack.Private.CoreRT\\$env:AzureContainerPackageGlob $ApiKey -Source $env:MyGetFeedUrl -Timeout 3600", + "workingFolder": "", + "failOnStandardError": "true" + } + }, + { + "enabled": true, + "continueOnError": false, + "alwaysRun": false, + "displayName": "symbol packages -> dotnet.myget.org", + "timeoutInMinutes": 0, + "task": { + "id": "e213ff0f-5d5c-4791-802d-52ea3e7be1f1", + "versionSpec": "*", + "definitionType": "task" + }, + "inputs": { + "scriptType": "inlineScript", + "scriptName": "", + "arguments": "$(MyGetApiKey)", + "inlineScript": "param($ApiKey)\nif ($env:Configuration -ne \"Release\") { exit }\n& $env:CustomNuGetPath push $env:Build_StagingDirectory\\IndexedSymbolPackages\\*.nupkg $ApiKey -Source $env:MyGetFeedUrl -Timeout 3600", + "workingFolder": "", + "failOnStandardError": "true" + } + }, + { + "enabled": true, + "continueOnError": false, + "alwaysRun": false, + "displayName": "Update versions repository", + "timeoutInMinutes": 0, + "task": { + "id": "e213ff0f-5d5c-4791-802d-52ea3e7be1f1", + "versionSpec": "*", + "definitionType": "task" + }, + "inputs": { + "scriptType": "inlineScript", + "scriptName": "", + "arguments": "-gitHubAuthToken $(UpdatePublishedVersions.AuthToken) -root $(Pipeline.SourcesDirectory)", + "inlineScript": "param($gitHubAuthToken, $root)\nif ($env:Configuration -ne \"Release\") { exit }\ncd $root\n. $root\\buildscripts\\UpdatePublishedVersions.ps1 `\n -gitHubUser dotnet-build-bot -gitHubEmail dotnet-build-bot@microsoft.com `\n -gitHubAuthToken $gitHubAuthToken `\n -versionsRepoOwner $env:VersionsRepoOwner -versionsRepo $env:VersionsRepo `\n -versionsRepoPath build-info/dotnet/$env:GitHubRepositoryName/$env:SourceBranch `\n -nupkgPath $root\\packages\\AzureTransfer\\Windows_NT.x64.$env:Configuration\\Microsoft.TargetingPack.Private.CoreRT\\$env:AzureContainerPackageGlob", + "workingFolder": "", + "failOnStandardError": "true" + } + }, + { + "enabled": true, + "continueOnError": false, + "alwaysRun": false, + "displayName": "Get Build Number", + "timeoutInMinutes": 0, + "task": { + "id": "e213ff0f-5d5c-4791-802d-52ea3e7be1f1", + "versionSpec": "*", + "definitionType": "task" + }, + "inputs": { + "scriptType": "inlineScript", + "scriptName": "", + "arguments": "$(OfficialBuildId) $(Pipeline.SourcesDirectory)", + "inlineScript": "param(\n [string]$OfficialBuildId,\n [string]$SourcesDir\n)\n$VersionPropsFile=$SourcesDir + \"\\bin\\obj\\BuildVersion-\" + $OfficialBuildId + \".props\"\n[xml]$versionXml=Get-Content $VersionPropsFile\n$env:BuildNumber=$versionXml.Project.PropertyGroup.BuildNumberMajor.InnerText + \".\" + $versionXml.Project.PropertyGroup.BuildNumberMinor.InnerText\nWrite-Host (\"##vso[task.setvariable variable=BuildNumber;]$env:BuildNumber\")", + "workingFolder": "", + "failOnStandardError": "true" + } + }, + { + "enabled": true, + "continueOnError": true, + "alwaysRun": false, + "displayName": "Publish to Artifact Services Drop (BuildNumber)", + "timeoutInMinutes": 0, + "task": { + "id": "f9d96d25-0c81-4e77-8282-1ad1f785cbb4", + "versionSpec": "*", + "definitionType": "task" + }, + "inputs": { + "dropServiceURI": "https://devdiv.artifacts.visualstudio.com/DefaultCollection", + "buildNumber": "dotnet/$(GitHubRepositoryName)/$(SourceBranch)/$(BuildNumber)/packages/$(Configuration)", + "sourcePath": "$(Pipeline.SourcesDirectory)\\packages\\AzureTransfer", + "dropExePath": "", + "toLowerCase": "true", + "detailedLog": "false", + "usePat": "false" + } + }, + { + "enabled": true, + "continueOnError": true, + "alwaysRun": false, + "displayName": "Publish to Artifact Services Drop (OfficialBuildId)", + "timeoutInMinutes": 0, + "task": { + "id": "f9d96d25-0c81-4e77-8282-1ad1f785cbb4", + "versionSpec": "*", + "definitionType": "task" + }, + "inputs": { + "dropServiceURI": "https://devdiv.artifacts.visualstudio.com/DefaultCollection", + "buildNumber": "dotnet/$(GitHubRepositoryName)/$(SourceBranch)/$(OfficialBuildId)/packages/$(Configuration)", + "sourcePath": "$(Pipeline.SourcesDirectory)\\packages\\AzureTransfer", + "dropExePath": "", + "toLowerCase": "true", + "detailedLog": "false", + "usePat": "false" + } + }, + { + "enabled": true, + "continueOnError": false, + "alwaysRun": true, + "displayName": "Copy Publish Artifact: PublishLogs", + "timeoutInMinutes": 0, + "task": { + "id": "1d341bb0-2106-458c-8422-d00bcea6512a", + "versionSpec": "*", + "definitionType": "task" + }, + "inputs": { + "CopyRoot": "", + "Contents": "**\\*.log", + "ArtifactName": "PublishLogs", + "ArtifactType": "Container", + "TargetPath": "\\\\my\\share\\$(Build.DefinitionName)\\$(Build.BuildNumber)" + } + } + ], + "options": [ + { + "enabled": false, + "definition": { + "id": "7c555368-ca64-4199-add6-9ebaf0b0137d" + }, + "inputs": { + "multipliers": "[]", + "parallel": "false", + "continueOnError": "true", + "additionalFields": "{}" + } + }, + { + "enabled": false, + "definition": { + "id": "a9db38f9-9fdc-478c-b0f9-464221e58316" + }, + "inputs": { + "workItemType": "234347", + "assignToRequestor": "true", + "additionalFields": "{}" + } + }, + { + "enabled": false, + "definition": { + "id": "57578776-4c22-4526-aeb0-86b6da17ee9c" + }, + "inputs": { + "additionalFields": "{}" + } + } + ], + "variables": { + "system.debug": { + "value": "false", + "allowOverride": true + }, + "Configuration": { + "value": "Debug", + "allowOverride": true + }, + "TeamName": { + "value": "DotNetCore" + }, + "CloudDropAccountName": { + "value": "dotnetbuildoutput" + }, + "CloudDropAccessToken": { + "value": null, + "isSecret": true + }, + "OfficialBuildId": { + "value": "$(Build.BuildNumber)", + "allowOverride": true + }, + "BuildTag": { + "value": "corert-alpha", + "allowOverride": true + }, + "Label": { + "value": "$(BuildTag)-$(Build.BuildNumber)", + "allowOverride": true + }, + "MyGetFeedUrl": { + "value": "https://dotnet.myget.org/F/dotnet-core-test/api/v2/package", + "allowOverride": true + }, + "MyGetApiKey": { + "value": null, + "isSecret": true + }, + "VstsPat": { + "value": null, + "isSecret": true + }, + "DevDivPat": { + "value": null, + "isSecret": true + }, + "UpdatePublishedVersions.AuthToken": { + "value": null, + "isSecret": true + }, + "VersionsRepoOwner": { + "value": "crummel", + "allowOverride": true + }, + "VersionsRepo": { + "value": "dotnet_versions", + "allowOverride": true + }, + "Pipeline.SourcesDirectory": { + "value": "$(Build.BinariesDirectory)\\pipelineRepository" + }, + "SourceVersion": { + "value": "master", + "allowOverride": true + }, + "SourceBranch": { + "value": "master", + "allowOverride": true + }, + "AzureContainerPackageGlob": { + "value": "*.nupkg", + "allowOverride": true + }, + "AzureContainerSymbolPackageGlob": { + "value": "symbols\\*.nupkg", + "allowOverride": true + }, + "GitHubRepositoryName": { + "value": "corert" + }, + "UseLegacyBuildScripts": { + "value": "false", + "allowOverride": true + } + }, + "retentionRules": [ + { + "branches": [ + "+refs/heads/*" + ], + "artifacts": [], + "artifactTypesToDelete": [ + "FilePath", + "SymbolStore" + ], + "daysToKeep": 10, + "minimumToKeep": 1, + "deleteBuildRecord": true, + "deleteTestResults": true + } + ], + "buildNumberFormat": "$(date:yyyyMMdd)$(rev:-rr)", + "jobAuthorizationScope": "projectCollection", + "jobTimeoutInMinutes": 180, + "repository": { + "properties": { + "labelSources": "0", + "reportBuildStatus": "false" + }, + "id": "0a2b2664-c1be-429c-9b40-8a24dee27a4a", + "type": "TfsGit", + "name": "DotNet-BuildPipeline", + "url": "https://devdiv.visualstudio.com/DevDiv/_git/DotNet-BuildPipeline", + "defaultBranch": "refs/heads/master", + "clean": "true", + "checkoutSubmodules": false + }, + "quality": "definition", + "defaultBranch": "refs/heads/master", + "queue": { + "pool": { + "id": 39, + "name": "DotNet-Build" + }, + "id": 36, + "name": "DotNet-Build" + }, + "path": "\\", + "type": "build", + "id": 2943, + "name": "DotNet-CoreRT-Publish", + "project": { + "id": "0bdbc590-a062-4c3f-b0f6-9383f67865ee", + "name": "DevDiv", + "description": "Visual Studio and DevDiv team project for git source code repositories. Work items will be added for Adams, Dev14 work items are tracked in vstfdevdiv. ", + "url": "https://devdiv.visualstudio.com/DefaultCollection/_apis/projects/0bdbc590-a062-4c3f-b0f6-9383f67865ee", + "state": "wellFormed", + "revision": 418097399 + } +} \ No newline at end of file diff --git a/buildpipeline/DotNet-CoreRT-Windows.json b/buildpipeline/DotNet-CoreRT-Windows.json index 7453b6cfb..dcb89273b 100644 --- a/buildpipeline/DotNet-CoreRT-Windows.json +++ b/buildpipeline/DotNet-CoreRT-Windows.json @@ -148,27 +148,7 @@ "enabled": true, "continueOnError": false, "alwaysRun": false, - "displayName": "Download nuget", - "timeoutInMinutes": 0, - "task": { - "id": "e213ff0f-5d5c-4791-802d-52ea3e7be1f1", - "versionSpec": "*", - "definitionType": "task" - }, - "inputs": { - "scriptType": "inlineScript", - "scriptName": "", - "arguments": "https://dotnet.myget.org/F/dotnet-buildtools/api/v2/package/NuGet.CommandLine/3.5.0-rc-1256 $(CustomNuGetDownloadPath) $(CustomNuGetPath)", - "inlineScript": "param($packageUrl, $packageDestDir, $nugetDestPath)\n$downloadPath=\"$packageDestDir\\NuGet.CommandLine.nupkg\"\n$extract = \"$packageDestDir\\extracted\"\n$t = mkdir $packageDestDir -ea Ignore\n(New-Object Net.WebClient).DownloadFile($packageUrl, $downloadPath)\nAdd-Type -Assembly 'System.IO.Compression.FileSystem'\nRemove-Item $extract -Recurse -ea Ignore\n[System.IO.Compression.ZipFile]::ExtractToDirectory($downloadPath, $extract)\nCopy-Item $packageDestDir\\extracted\\tools\\NuGet.exe $nugetDestPath", - "workingFolder": "", - "failOnStandardError": "true" - } - }, - { - "enabled": true, - "continueOnError": false, - "alwaysRun": false, - "displayName": "Publish packages to MyGet", + "displayName": "Publish packages", "timeoutInMinutes": 0, "task": { "id": "d9bafed4-0b18-4f58-968d-86655b4d2ce9", @@ -176,8 +156,8 @@ "definitionType": "task" }, "inputs": { - "filename": "$(CustomNuGetPath)", - "arguments": "push bin\\Product\\pkg\\*.nupkg $(MyGetApiKey) -Source $(MyGetFeedUrl) -Timeout 3600", + "filename": "$(Build.SourcesDirectory)\\$(SourceFolder)\\buildscripts\\publish-packages.cmd", + "arguments": "-AzureAccount=$(CloudDropAccountName) -AzureToken=\"$(CloudDropAccessToken)\" -Container=$(Label)", "workingFolder": "$(SourceFolder)", "failOnStandardError": "false" } @@ -236,7 +216,7 @@ "inputs": { "symbolServiceURI": "https://devdiv.artifacts.visualstudio.com/DefaultCollection", "requestName": "$(system.teamProject)/$(Build.BuildNumber)/$(Build.BuildId)", - "sourcePath": "$(Build.SourcesDirectory)\\corefx\\bin", + "sourcePath": "$(Build.SourcesDirectory)\\corert\\bin", "assemblyPath": "", "toLowerCase": "true", "detailedLog": "true", @@ -408,8 +388,12 @@ "value": "$(Build.BuildNumber)", "allowOverride": true }, + "BuildTag": { + "value": "corert-alpha", + "allowOverride": true + }, "Label": { - "value": "$(Build.BuildNumber)", + "value": "$(BuildTag)-$(Build.BuildNumber)", "allowOverride": true }, "SourceVersion": { @@ -444,6 +428,25 @@ "SourceFolder": { "value": "corert_$(Build.BuildId)", "allowOverride": false + }, + "CloudDropAccountName": { + "value": "dotnetbuildoutput" + }, + "CloudDropAccessToken": { + "value": null, + "isSecret": true + }, + "UpdatePublishedVersions.AuthToken": { + "value": null, + "isSecret": true + }, + "VersionsRepoOwner": { + "value": "crummel", + "allowOverride": true + }, + "VersionsRepo": { + "value": "dotnet_versions", + "allowOverride": true } }, "demands": [ diff --git a/buildpipeline/pipeline.json b/buildpipeline/pipeline.json index 837259ebb..ef9acfed2 100644 --- a/buildpipeline/pipeline.json +++ b/buildpipeline/pipeline.json @@ -7,43 +7,148 @@ }, "Pipelines": [ { - "Name": "All-Release-x64", + "Name": "All-Release", "Parameters": { "TreatWarningsAsErrors": "false" }, "BuildParameters": { - "Platform": "x64", "Configuration": "Release" }, "Definitions": [ { "Name": "DotNet-CoreRT-Linux", + "Parameters": { + "Platform": "x64" + }, "ReportingParameters": { "OperatingSystem": "Debian 8.2", - "SubType": "native", "Type": "build/product/", - "ConfigurationGroup": "Release" + "ConfigurationGroup": "Release", + "Platform": "x64" } }, { "Name": "DotNet-CoreRT-Mac", + "Parameters": { + "Platform": "x64" + }, "ReportingParameters": { - "SubType": "native", "OperatingSystem": "OSX", "Type": "build/product/", - "ConfigurationGroup": "Release" + "ConfigurationGroup": "Release", + "Platform": "x64" } }, { "Name": "DotNet-CoreRT-Windows", + "Parameters": { + "Platform": "x64" + }, "ReportingParameters": { - "SubType": "managed", - "OperatingSystem": "All (Managed)", + "OperatingSystem": "Windows", "Type": "build/product/", - "ConfigurationGroup": "Release" + "ConfigurationGroup": "Release", + "Platform": "x64" } } ] + }, + { + "Name": "All-Debug", + "Parameters": { + "TreatWarningsAsErrors": "false" + }, + "BuildParameters": { + "Configuration": "Debug" + }, + "Definitions": [ + { + "Name": "DotNet-CoreRT-Linux", + "Parameters": { + "Platform": "x64" + }, + "ReportingParameters": { + "OperatingSystem": "Debian 8.2", + "Type": "build/product/", + "ConfigurationGroup": "Debug", + "Platform": "x64" + } + }, + { + "Name": "DotNet-CoreRT-Mac", + "Parameters": { + "Platform": "x64" + }, + "ReportingParameters": { + "OperatingSystem": "OSX", + "Type": "build/product/", + "ConfigurationGroup": "Debug", + "Platform": "x64" + } + }, + { + "Name": "DotNet-CoreRT-Windows", + "Parameters": { + "Platform": "x64" + }, + "ReportingParameters": { + "OperatingSystem": "Windows", + "Type": "build/product/", + "ConfigurationGroup": "Debug", + "Platform": "x64" + } + } + ] + }, + { + "Name": "Publish-Release", + "Parameters": { + "TreatWarningsAsErrors": "false" + }, + "BuildParameters": { + "Configuration": "Release" + }, + "Definitions": [ + { + "Name": "DotNet-CoreRT-Publish", + "Parameters": { + "GitHubRepositoryName": "corert" + }, + "ReportingParameters": { + "TaskName": "Package Publish", + "Type": "build/publish/", + "ConfigurationGroup": "Release - Push to MyGet Feed" + } + } + ], + "DependsOn": [ + "All-Release" + ] + }, + { + "Name": "Publish-Debug", + "Parameters": { + "TreatWarningsAsErrors": "false" + }, + "BuildParameters": { + "ConfigurationGroup": "Debug" + }, + "Definitions": [ + { + "Name": "DotNet-CoreRT-Publish", + "Parameters": { + "GitHubRepositoryName": "corert" + }, + "ReportingParameters": { + "TaskName": "Package Publish", + "Type": "build/publish/", + "ConfigurationGroup": "Debug - Push to Azure Storage" + } + } + ], + "DependsOn": [ + "All-Debug" + ] } ] } diff --git a/buildscripts/publish-packages.cmd b/buildscripts/publish-packages.cmd new file mode 100644 index 000000000..633ff7199 --- /dev/null +++ b/buildscripts/publish-packages.cmd @@ -0,0 +1,40 @@ +@echo off +REM don't pass args to buildvars-setup, just get defaults +call %~dp0buildvars-setup.cmd + +set _msbuildexe="%ProgramFiles(x86)%\MSBuild\14.0\Bin\MSBuild.exe" +if not exist %_msbuildexe% (set _msbuildexe="%ProgramFiles%\MSBuild\14.0\Bin\MSBuild.exe") +REM hopefully it's on the path +if not exist %_msbuildexe% set _msbuildexe=msbuild + +set AzureAccount= +set AzureToken= +set Container= + +:Arg_Loop +if "%1" == "" goto ArgsDone + +if /i "%1" == "-AzureAccount" (set AzureAccount=%2&shift&shift&goto Arg_Loop) +if /i "%1" == "-AzureToken" (set AzureToken=%2&shift&shift&goto Arg_Loop) +if /i "%1" == "-Container" (set Container=%2&shift&shift&goto Arg_Loop) + +echo Invalid command line argument: %1 +exit /b 1 +:ArgsDone + +set AzureToken=%AzureToken:"=% + +if "%AzureAccount%" == "" ( + echo Azure account not specified. + exit /b 1 +) +if "%AzureToken%" == "" ( + echo Azure token not specified. + exit /b 1 +) +if "%Container%" == "" ( + echo Azure container not specified. + exit /b 1 +) + +%_msbuildexe% %__ProjectDir%\buildscripts\publish.proj /p:CloudDropAccountName=%AzureAccount% /p:CloudDropAccessToken=%AzureToken% /p:ContainerName=%Container% /flp:v=diag;LogFile=publish-packages.log \ No newline at end of file diff --git a/buildscripts/publish-packages.sh b/buildscripts/publish-packages.sh new file mode 100755 index 000000000..a1632ed15 --- /dev/null +++ b/buildscripts/publish-packages.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +export AzureAccount= +export AzureToken= +export Container= + +while [ "$1" != "" ]; do + lowerI="$(echo $1 | awk '{print tolower($0)}')" + case $lowerI in + -azureaccount) + shift + export AzureAccount=$1 + ;; + -azuretoken) + shift + export AzureToken=$1 + ;; + -container) + shift + export Container=$1 + ;; + *) + echo Bad argument $1 + exit 1 + esac + shift +done + +# don't pass args to buildvars-setup, just get defaults +scriptRoot="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +. $scriptRoot/buildvars-setup.sh + +$__ProjectRoot/Tools/msbuild.sh $scriptRoot/publish.proj /p:CloudDropAccountName=$AzureAccount /p:CloudDropAccessToken=$AzureToken /p:ContainerName=$Container "/flp:v=diag;LogFile=publish-packages.log" \ No newline at end of file diff --git a/buildscripts/publish.proj b/buildscripts/publish.proj new file mode 100644 index 000000000..25731c681 --- /dev/null +++ b/buildscripts/publish.proj @@ -0,0 +1,20 @@ + + + + + + + + $(PackageOutputRoot)**\*.nupkg + + + + + corert-$(PreReleaseLabel)-$(BuildNumberMajor)-$(BuildNumberMinor) + + + + + \ No newline at end of file diff --git a/buildscripts/syncAzure.proj b/buildscripts/syncAzure.proj new file mode 100644 index 000000000..5e5c3bcc8 --- /dev/null +++ b/buildscripts/syncAzure.proj @@ -0,0 +1,19 @@ + + + + + + corert-$(PreReleaseLabel) + $(ContainerNamePrefix)-$(BuildNumberMajor)-$(BuildNumberMinor) + $(PackagesDir)AzureTransfer + + + + + + + + + + + \ No newline at end of file diff --git a/buildscripts/updatePublishedVersions.ps1 b/buildscripts/updatePublishedVersions.ps1 new file mode 100644 index 000000000..465bb4e70 --- /dev/null +++ b/buildscripts/updatePublishedVersions.ps1 @@ -0,0 +1,26 @@ +# +# Copyright (c) .NET Foundation and contributors. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. +# + +# This script updates the dotnet/versions repository based on a set of packages. It directly +# commits the changes using GitHub APIs. + +param( + [Parameter(Mandatory=$true)][string]$gitHubUser, + [Parameter(Mandatory=$true)][string]$gitHubEmail, + [Parameter(Mandatory=$true)][string]$gitHubAuthToken, + [Parameter(Mandatory=$true)][string]$versionsRepoOwner, + [Parameter(Mandatory=$true)][string]$versionsRepo, + [Parameter(Mandatory=$true)][string]$versionsRepoPath, + # A pattern matching all packages in the set that the versions repository should be set to. + [Parameter(Mandatory=$true)][string]$nupkgPath) + +msbuild /t:UpdatePublishedVersions ` + /p:GitHubUser="$gitHubUser" ` + /p:GitHubEmail="$gitHubEmail" ` + /p:GitHubAuthToken="$gitHubAuthToken" ` + /p:VersionsRepoOwner="$versionsRepoOwner" ` + /p:VersionsRepo="$versionsRepo" ` + /p:VersionsRepoPath="$versionsRepoPath" ` + /p:ShippedNuGetPackageGlobPath="$nupkgPath" \ No newline at end of file diff --git a/dir.props b/dir.props index 8180ce78f..f9010d1aa 100644 --- a/dir.props +++ b/dir.props @@ -68,6 +68,8 @@ $(BinDir)obj/ $(BinDir)Product/ $(BinDir)tests/ + $(BinDir)packages_noship/ + $(ProductBinDir)pkg/ $(BinDir)packages/ @@ -92,7 +94,9 @@ $(BinDirOSGroup).$(BinDirPlatform).$(BinDirConfiguration) $(ProductBinDir) - $(BaseOutputPath)$(OSPlatformConfig)/$(MSBuildProjectName)/ + $(PackageOutputRoot)$(OSPlatformConfig)/$(MSBuildProjectName)/ + $(PackageOutputPath)symbols/ + $(BaseOutputPath)$(OSPlatformConfig)/$(MSBuildProjectName) $(BaseOutputPath)$(OSPlatformConfig)/packaging/ -- cgit v1.2.3 From d36cff726565d312e3c48381a15a54f7b54d02e8 Mon Sep 17 00:00:00 2001 From: Daniel Harvey Date: Wed, 18 Jan 2017 11:35:19 -0800 Subject: Brings over some Assembly and AssemblyName APIs [tfs-changeset: 1644899] --- .../src/System.Private.CoreLib.csproj | 1 + .../src/System/Reflection/Assembly.cs | 32 ++++++++++ .../src/System/Reflection/AssemblyName.cs | 14 +++++ .../src/System/Reflection/StrongNameKeyPair.cs | 73 ++++++++++++++++++++++ .../src/System/Security/Attributes.cs | 7 +++ 5 files changed, 127 insertions(+) create mode 100644 src/System.Private.CoreLib/src/System/Reflection/StrongNameKeyPair.cs diff --git a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj index baf7c70b5..4998a45c6 100644 --- a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -194,6 +194,7 @@ + diff --git a/src/System.Private.CoreLib/src/System/Reflection/Assembly.cs b/src/System.Private.CoreLib/src/System/Reflection/Assembly.cs index 2405a050f..84ff35d6b 100644 --- a/src/System.Private.CoreLib/src/System/Reflection/Assembly.cs +++ b/src/System.Private.CoreLib/src/System/Reflection/Assembly.cs @@ -5,7 +5,9 @@ using System.IO; using System.Globalization; using System.Collections.Generic; +using System.Configuration.Assemblies; using System.Runtime.Serialization; +using System.Security; using Internal.Reflection.Augments; @@ -76,6 +78,8 @@ namespace System.Reflection public virtual Stream GetManifestResourceStream(string name) { throw NotImplemented.ByDesign; } public virtual Stream GetManifestResourceStream(Type type, string name) { throw NotImplemented.ByDesign; } + public bool IsFullyTrusted => true; + public virtual AssemblyName GetName() => GetName(copiedName: false); public virtual AssemblyName GetName(bool copiedName) { throw NotImplemented.ByDesign; } @@ -91,6 +95,8 @@ namespace System.Reflection public virtual object[] GetCustomAttributes(bool inherit) { throw NotImplemented.ByDesign; } public virtual object[] GetCustomAttributes(Type attributeType, bool inherit) { throw NotImplemented.ByDesign; } + public virtual String EscapedCodeBase => AssemblyName.EscapeCodeBase(CodeBase); + public object CreateInstance(string typeName) => CreateInstance(typeName, false, BindingFlags.Public | BindingFlags.Instance, binder: null, args: null, culture: null, activationAttributes: null); public object CreateInstance(string typeName, bool ignoreCase) => CreateInstance(typeName, ignoreCase, BindingFlags.Public | BindingFlags.Instance, binder: null, args: null, culture: null, activationAttributes: null); public virtual object CreateInstance(string typeName, bool ignoreCase, BindingFlags bindingAttr, Binder binder, object[] args, CultureInfo culture, object[] activationAttributes) @@ -130,6 +136,12 @@ namespace System.Reflection return displayName; } + /* + Returns true if the assembly was loaded from the global assembly cache. + */ + public virtual bool GlobalAssemblyCache { get { throw NotImplemented.ByDesign; } } + public virtual Int64 HostContext { get { throw NotImplemented.ByDesign; } } + public override bool Equals(object o) => base.Equals(o); public override int GetHashCode() => base.GetHashCode(); @@ -180,8 +192,28 @@ namespace System.Reflection return Load(name); } + public static Assembly LoadFile(String path) { throw new PlatformNotSupportedException(); } + public static Assembly LoadFrom(String assemblyFile) { throw new PlatformNotSupportedException(); } + public static Assembly LoadFrom(String assemblyFile, byte[] hashValue, AssemblyHashAlgorithm hashAlgorithm) { throw new PlatformNotSupportedException(); } + + [Obsolete("This method has been deprecated. Please use Assembly.Load() instead. http://go.microsoft.com/fwlink/?linkid=14202")] + public static Assembly LoadWithPartialName(String partialName) + { + if (partialName == null) + throw new ArgumentNullException(nameof(partialName)); + + return Load(partialName); + } + + public static Assembly UnsafeLoadFrom(string assemblyFile) => LoadFrom(assemblyFile); + + public Module LoadModule(String moduleName, byte[] rawModule) => LoadModule(moduleName, rawModule, null); + public virtual Module LoadModule(String moduleName, byte[] rawModule, byte[] rawSymbolStore) { throw NotImplemented.ByDesign; } + public static Assembly ReflectionOnlyLoad(byte[] rawAssembly) { throw new PlatformNotSupportedException(SR.PlatformNotSupported_ReflectionOnly); } public static Assembly ReflectionOnlyLoad(string assemblyString) { throw new PlatformNotSupportedException(SR.PlatformNotSupported_ReflectionOnly); } public static Assembly ReflectionOnlyLoadFrom(string assemblyFile) { throw new PlatformNotSupportedException(SR.PlatformNotSupported_ReflectionOnly); } + + public virtual SecurityRuleSet SecurityRuleSet => SecurityRuleSet.None; } } diff --git a/src/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs b/src/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs index 0eed90c2a..d4b8cebef 100644 --- a/src/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs +++ b/src/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs @@ -129,6 +129,18 @@ namespace System.Reflection public string CodeBase { get; set; } public AssemblyHashAlgorithm HashAlgorithm { get; set; } public AssemblyVersionCompatibility VersionCompatibility { get; set; } + public StrongNameKeyPair KeyPair { get; set; } + + public String EscapedCodeBase + { + get + { + if (CodeBase == null) + return null; + else + return EscapeCodeBase(CodeBase); + } + } public byte[] GetPublicKey() { @@ -172,6 +184,8 @@ namespace System.Reflection public static AssemblyName GetAssemblyName(String assemblyFile) { throw new NotImplementedException(); } public static bool ReferenceMatchesDefinition(AssemblyName reference, AssemblyName definition) { throw new NotImplementedException(); } + internal static String EscapeCodeBase(String codebase) { throw new PlatformNotSupportedException(); } + private AssemblyNameFlags _flags; private byte[] _publicKey; private byte[] _publicKeyToken; diff --git a/src/System.Private.CoreLib/src/System/Reflection/StrongNameKeyPair.cs b/src/System.Private.CoreLib/src/System/Reflection/StrongNameKeyPair.cs new file mode 100644 index 000000000..db3e92423 --- /dev/null +++ b/src/System.Private.CoreLib/src/System/Reflection/StrongNameKeyPair.cs @@ -0,0 +1,73 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** +** +** +** Purpose: Encapsulate access to a public/private key pair +** used to sign strong name assemblies. +** +** +===========================================================*/ + +using System; +using System.Runtime.Serialization; + +namespace System.Reflection +{ + [Serializable] + public class StrongNameKeyPair : IDeserializationCallback, ISerializable + { + private bool _keyPairExported; + private byte[] _keyPairArray; + private String _keyPairContainer; + private byte[] _publicKey; + + // Build key pair from byte array in memory. + public StrongNameKeyPair(byte[] keyPairArray) + { + if (keyPairArray == null) + throw new ArgumentNullException(nameof(keyPairArray)); + + _keyPairArray = new byte[keyPairArray.Length]; + Array.Copy(keyPairArray, _keyPairArray, keyPairArray.Length); + + _keyPairExported = true; + } + + protected StrongNameKeyPair(SerializationInfo info, StreamingContext context) + { + _keyPairExported = (bool)info.GetValue("_keyPairExported", typeof(bool)); + _keyPairArray = (byte[])info.GetValue("_keyPairArray", typeof(byte[])); + _keyPairContainer = (string)info.GetValue("_keyPairContainer", typeof(string)); + _publicKey = (byte[])info.GetValue("_publicKey", typeof(byte[])); + } + + public StrongNameKeyPair(String keyPairContainer) + { + throw new PlatformNotSupportedException(); + } + + public byte[] PublicKey + { + get + { + throw new PlatformNotSupportedException(); + } + } + + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("_keyPairExported", _keyPairExported); + info.AddValue("_keyPairArray", _keyPairArray); + info.AddValue("_keyPairContainer", _keyPairContainer); + info.AddValue("_publicKey", _publicKey); + } + + void IDeserializationCallback.OnDeserialization(Object sender) { } + } +} diff --git a/src/System.Private.CoreLib/src/System/Security/Attributes.cs b/src/System.Private.CoreLib/src/System/Security/Attributes.cs index 538fbf15d..fcb292cd2 100644 --- a/src/System.Private.CoreLib/src/System/Security/Attributes.cs +++ b/src/System.Private.CoreLib/src/System/Security/Attributes.cs @@ -71,4 +71,11 @@ namespace System.Security { public SecurityTransparentAttribute() { } } + + public enum SecurityRuleSet : byte + { + None = 0, + Level1 = 1, // v2.0 transparency model + Level2 = 2, // v4.0 transparency model + } } -- cgit v1.2.3 From afebdb8894887ec8195c1d5a09db012cdb1b2b2c Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Wed, 18 Jan 2017 20:06:16 +0100 Subject: Update default clang version to 3.9 and require xcode 8 This change updates the default clang version used to build CoreRT to the latest stable version 3.9. It also removes the version 3.5 from the supported ones. The 3.5 has a bug preventing us from using thread_local C++ feature. Due to the same reason, the minimum supported xcode version on OSX is 8, so the prerequisities document is updated accordingly. --- Documentation/prerequisites-for-building.md | 13 ++++++++++--- buildscripts/buildvars-setup.sh | 10 +++++++++- src/BuildIntegration/Microsoft.NETCore.Native.Unix.props | 2 +- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/Documentation/prerequisites-for-building.md b/Documentation/prerequisites-for-building.md index 802edc565..7f6970ce8 100644 --- a/Documentation/prerequisites-for-building.md +++ b/Documentation/prerequisites-for-building.md @@ -12,12 +12,19 @@ PowerShell also needs to be available from the PATH environment variable (it's t Install basic dependency packages: +First add a new package source to be able to install clang-3.9: +```sh +echo "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.9 main" | sudo tee /etc/apt/sources.list.d/llvm.list +wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add - +sudo apt-get update ``` -sudo apt-get install cmake llvm-3.5 clang-3.5 lldb-3.6 lldb-3.6-dev liblttng-ust liblttng-ust-dev uuid uuid-dev + +```sh +sudo apt-get install cmake clang-3.9 libicu52 libunwind8 ``` -# Mac OSX (10.10+) +# Mac OSX (10.11.5+) -1. Install [Command Line Tools for XCode 6.3.1](https://developer.apple.com/xcode/download/) or higher. +1. Install [Command Line Tools for XCode 8](https://developer.apple.com/xcode/download/) or higher. 2. Install [CMake](https://cmake.org/download/). Launch `/Applications/CMake.app/Contents/MacOS/CMake` GUI. Goto "OSX App Menu -> Tools -> Install For Command Line Use" and follow the steps. CMake < 3.6 has [a bug](https://cmake.org/Bug/view.php?id=16064) that can make `--install` fail. Do `sudo mkdir /usr/local/bin` to work around. 3. Install OpenSSL. Build tools have a dependency on OpenSSL. You can use [Homebrew](http://brew.sh/) to install it: with Homebrew installed, run `brew install openssl` followed by `ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/`, followed by `ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/`. diff --git a/buildscripts/buildvars-setup.sh b/buildscripts/buildvars-setup.sh index 7e259b407..ead93b646 100755 --- a/buildscripts/buildvars-setup.sh +++ b/buildscripts/buildvars-setup.sh @@ -149,7 +149,7 @@ export __UnprocessedBuildArgs= export __CleanBuild=0 export __VerboseBuild=0 export __ClangMajorVersion=3 -export __ClangMinorVersion=5 +export __ClangMinorVersion=9 export __CrossBuild=0 @@ -202,6 +202,14 @@ while [ "$1" != "" ]; do export __ClangMajorVersion=3 export __ClangMinorVersion=7 ;; + clang3.8) + export __ClangMajorVersion=3 + export __ClangMinorVersion=8 + ;; + clang3.9) + export __ClangMajorVersion=3 + export __ClangMinorVersion=9 + ;; cross) export __CrossBuild=1 ;; diff --git a/src/BuildIntegration/Microsoft.NETCore.Native.Unix.props b/src/BuildIntegration/Microsoft.NETCore.Native.Unix.props index 832339c6f..b82b5f8e9 100644 --- a/src/BuildIntegration/Microsoft.NETCore.Native.Unix.props +++ b/src/BuildIntegration/Microsoft.NETCore.Native.Unix.props @@ -17,7 +17,7 @@ See the LICENSE file in the project root for more information. clang - clang-3.5 + clang-3.9 $(CppCompilerAndLinker) $(CppCompilerAndLinker) -- cgit v1.2.3 From 4a25645832e00012868844c0a5d6b72ce4509309 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Wed, 18 Jan 2017 14:14:41 -0800 Subject: Disable tests against issue 2535 (#2537) --- tests/CoreCLR.issues.targets | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/CoreCLR.issues.targets b/tests/CoreCLR.issues.targets index 7132c4749..6caf9e45c 100644 --- a/tests/CoreCLR.issues.targets +++ b/tests/CoreCLR.issues.targets @@ -929,6 +929,11 @@ + + + + + -- cgit v1.2.3 From 45d23dd984987a73874346475ca6b7aadca2b73c Mon Sep 17 00:00:00 2001 From: fadimounir Date: Tue, 17 Jan 2017 17:16:11 -0800 Subject: Concrete generic constraints validation helpers --- .../Common/TypeSystemConstaintsHelpers.cs | 83 +++++++ .../src/ILCompiler.TypeSystem.csproj | 3 + .../tests/ConstraintsValidationTest.cs | 254 +++++++++++++++++++++ .../tests/CoreTestAssembly/CoreTestAssembly.csproj | 1 + .../tests/CoreTestAssembly/GenericConstraints.cs | 53 +++++ .../tests/TypeSystem.Tests.csproj | 1 + 6 files changed, 395 insertions(+) create mode 100644 src/Common/src/TypeSystem/Common/TypeSystemConstaintsHelpers.cs create mode 100644 src/ILCompiler.TypeSystem/tests/ConstraintsValidationTest.cs create mode 100644 src/ILCompiler.TypeSystem/tests/CoreTestAssembly/GenericConstraints.cs diff --git a/src/Common/src/TypeSystem/Common/TypeSystemConstaintsHelpers.cs b/src/Common/src/TypeSystem/Common/TypeSystemConstaintsHelpers.cs new file mode 100644 index 000000000..31d1d293c --- /dev/null +++ b/src/Common/src/TypeSystem/Common/TypeSystemConstaintsHelpers.cs @@ -0,0 +1,83 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +using System.Diagnostics; + +namespace Internal.TypeSystem +{ + public static class TypeSystemConstraintsHelpers + { + private static bool VerifyGenericParamConstraint(Instantiation typeInstantiation, Instantiation methodInstantiation, GenericParameterDesc genericParam, TypeDesc instantiationParam) + { + // Check class constraint + if (genericParam.HasReferenceTypeConstraint && !instantiationParam.IsGCPointer) + return false; + + // Check default constructor constraint + if (genericParam.HasDefaultConstructorConstraint) + { + if (!instantiationParam.IsDefType) + return false; + + if (!instantiationParam.IsValueType && instantiationParam.GetDefaultConstructor() == null) + return false; + } + + // Check struct constraint + if (genericParam.HasNotNullableValueTypeConstraint) + { + if (!instantiationParam.IsValueType) + return false; + + if (instantiationParam.IsNullable) + return false; + } + + foreach (var constraintType in genericParam.TypeConstraints) + { + var instantiatedType = constraintType.InstantiateSignature(typeInstantiation, methodInstantiation); + if (!instantiationParam.CanCastTo(instantiatedType)) + return false; + } + + return true; + } + + public static bool CheckConstraints(this TypeDesc type) + { + // Non-generic types always pass constraints check + if (!type.HasInstantiation) + return true; + + TypeDesc uninstantiatedType = type.GetTypeDefinition(); + for (int i = 0; i < uninstantiatedType.Instantiation.Length; i++) + { + if (!VerifyGenericParamConstraint(type.Instantiation, default(Instantiation), (GenericParameterDesc)uninstantiatedType.Instantiation[i], type.Instantiation[i])) + return false; + } + + return true; + } + + public static bool CheckConstraints(this MethodDesc method) + { + if (!method.OwningType.CheckConstraints()) + return false; + + // Non-generic methods always pass constraints check + if (!method.HasInstantiation) + return true; + + MethodDesc uninstantiatedMethod = method.GetMethodDefinition(); + for (int i = 0; i < uninstantiatedMethod.Instantiation.Length; i++) + { + if (!VerifyGenericParamConstraint(method.OwningType.Instantiation, method.Instantiation, (GenericParameterDesc)uninstantiatedMethod.Instantiation[i], method.Instantiation[i])) + return false; + } + + return true; + } + } +} diff --git a/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj b/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj index aac62c84e..e2ea5f3cd 100644 --- a/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj +++ b/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj @@ -212,6 +212,9 @@ TypeSystem\Common\TypeSystemHelpers.cs + + TypeSystem\Common\TypeSystemConstaintsHelpers.cs + Utilities\TypeNameFormatter.cs diff --git a/src/ILCompiler.TypeSystem/tests/ConstraintsValidationTest.cs b/src/ILCompiler.TypeSystem/tests/ConstraintsValidationTest.cs new file mode 100644 index 000000000..3b4d12395 --- /dev/null +++ b/src/ILCompiler.TypeSystem/tests/ConstraintsValidationTest.cs @@ -0,0 +1,254 @@ +// Licensed to the.NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Internal.TypeSystem; + +using Xunit; + +namespace TypeSystemTests +{ + public class ConstraintsValidationTest + { + private TestTypeSystemContext _context; + private ModuleDesc _testModule; + + private MetadataType _iNonGenType; + private MetadataType _iGenType; + private MetadataType _arg1Type; + private MetadataType _arg2Type; + private MetadataType _arg3Type; + private MetadataType _structArgWithDefaultCtorType; + private MetadataType _structArgWithoutDefaultCtorType; + private MetadataType _classArgWithDefaultCtorType; + private MetadataType _classArgWithoutDefaultCtorType; + private MetadataType _referenceTypeConstraintType; + private MetadataType _defaultConstructorConstraintType; + private MetadataType _notNullableValueTypeConstraintType; + private MetadataType _simpleTypeConstraintType; + private MetadataType _doubleSimpleTypeConstraintType; + private MetadataType _simpleGenericConstraintType; + private MetadataType _complexGenericConstraint1Type; + private MetadataType _complexGenericConstraint2Type; + private MetadataType _complexGenericConstraint3Type; + private MetadataType _multipleConstraintsType; + + public ConstraintsValidationTest() + { + _context = new TestTypeSystemContext(TargetArchitecture.Unknown); + var systemModule = _context.CreateModuleForSimpleName("CoreTestAssembly"); + _context.SetSystemModule(systemModule); + + _testModule = systemModule; + + _iNonGenType = _testModule.GetType("GenericConstraints", "INonGen"); + _iGenType = _testModule.GetType("GenericConstraints", "IGen`1"); + _arg1Type = _testModule.GetType("GenericConstraints", "Arg1"); + _arg2Type = _testModule.GetType("GenericConstraints", "Arg2`1"); + _arg3Type = _testModule.GetType("GenericConstraints", "Arg3`1"); + _structArgWithDefaultCtorType = _testModule.GetType("GenericConstraints", "StructArgWithDefaultCtor"); + _structArgWithoutDefaultCtorType = _testModule.GetType("GenericConstraints", "StructArgWithoutDefaultCtor"); + _classArgWithDefaultCtorType = _testModule.GetType("GenericConstraints", "ClassArgWithDefaultCtor"); + _classArgWithoutDefaultCtorType = _testModule.GetType("GenericConstraints", "ClassArgWithoutDefaultCtor"); + + _referenceTypeConstraintType = _testModule.GetType("GenericConstraints", "ReferenceTypeConstraint`1"); + _defaultConstructorConstraintType = _testModule.GetType("GenericConstraints", "DefaultConstructorConstraint`1"); + _notNullableValueTypeConstraintType = _testModule.GetType("GenericConstraints", "NotNullableValueTypeConstraint`1"); + _simpleTypeConstraintType = _testModule.GetType("GenericConstraints", "SimpleTypeConstraint`1"); + _doubleSimpleTypeConstraintType = _testModule.GetType("GenericConstraints", "DoubleSimpleTypeConstraint`1"); + _simpleGenericConstraintType = _testModule.GetType("GenericConstraints", "SimpleGenericConstraint`2"); + _complexGenericConstraint1Type = _testModule.GetType("GenericConstraints", "ComplexGenericConstraint1`2"); + _complexGenericConstraint2Type = _testModule.GetType("GenericConstraints", "ComplexGenericConstraint2`2"); + _complexGenericConstraint3Type = _testModule.GetType("GenericConstraints", "ComplexGenericConstraint3`2"); + _multipleConstraintsType = _testModule.GetType("GenericConstraints", "MultipleConstraints`2"); + } + + [Fact] + public void TestTypeConstraints() + { + MetadataType instantiatedType; + + MetadataType arg2OfInt = _arg2Type.MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.Int32)); + MetadataType arg2OfBool = _arg2Type.MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.Boolean)); + MetadataType arg2OfObject = _arg2Type.MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.Object)); + + // ReferenceTypeConstraint + { + instantiatedType = _referenceTypeConstraintType.MakeInstantiatedType(_arg1Type); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _referenceTypeConstraintType.MakeInstantiatedType(_iNonGenType); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _referenceTypeConstraintType.MakeInstantiatedType(_structArgWithDefaultCtorType); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = _referenceTypeConstraintType.MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.Int32)); + Assert.False(instantiatedType.CheckConstraints()); + } + + // DefaultConstructorConstraint + { + instantiatedType = _defaultConstructorConstraintType.MakeInstantiatedType(_arg1Type); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _defaultConstructorConstraintType.MakeInstantiatedType(_classArgWithDefaultCtorType); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _defaultConstructorConstraintType.MakeInstantiatedType(_classArgWithoutDefaultCtorType); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = _defaultConstructorConstraintType.MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.Int32)); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _defaultConstructorConstraintType.MakeInstantiatedType(_structArgWithDefaultCtorType); + Assert.True(instantiatedType.CheckConstraints()); + + // Structs always have implicit default constructors + instantiatedType = _defaultConstructorConstraintType.MakeInstantiatedType(_structArgWithoutDefaultCtorType); + Assert.True(instantiatedType.CheckConstraints()); + } + + // NotNullableValueTypeConstraint + { + instantiatedType = _notNullableValueTypeConstraintType.MakeInstantiatedType(_arg1Type); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = _notNullableValueTypeConstraintType.MakeInstantiatedType(_structArgWithDefaultCtorType); + Assert.True(instantiatedType.CheckConstraints()); + + MetadataType nullable = (MetadataType)_context.GetWellKnownType(WellKnownType.Nullable); + MetadataType nullableOfInt = nullable.MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.Int32)); + + instantiatedType = _notNullableValueTypeConstraintType.MakeInstantiatedType(nullableOfInt); + Assert.False(instantiatedType.CheckConstraints()); + } + + // SimpleTypeConstraint and DoubleSimpleTypeConstraint + foreach(var genType in new MetadataType[] { _simpleTypeConstraintType , _doubleSimpleTypeConstraintType }) + { + instantiatedType = genType.MakeInstantiatedType(_arg1Type); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = genType.MakeInstantiatedType(_iNonGenType); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = genType.MakeInstantiatedType(_classArgWithDefaultCtorType); + Assert.False(instantiatedType.CheckConstraints()); + } + + // SimpleGenericConstraint + { + instantiatedType = _simpleGenericConstraintType.MakeInstantiatedType(_arg1Type, _arg1Type); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _simpleGenericConstraintType.MakeInstantiatedType(_arg1Type, _iNonGenType); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _simpleGenericConstraintType.MakeInstantiatedType(_classArgWithDefaultCtorType, _classArgWithoutDefaultCtorType); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = _simpleGenericConstraintType.MakeInstantiatedType(_arg1Type, _context.GetWellKnownType(WellKnownType.Object)); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _simpleGenericConstraintType.MakeInstantiatedType(_structArgWithDefaultCtorType, _context.GetWellKnownType(WellKnownType.ValueType)); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _simpleGenericConstraintType.MakeInstantiatedType(_arg1Type, _context.GetWellKnownType(WellKnownType.ValueType)); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = _simpleGenericConstraintType.MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.UInt16), _context.GetWellKnownType(WellKnownType.UInt32)); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = _simpleGenericConstraintType.MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.UInt16), _context.GetWellKnownType(WellKnownType.ValueType)); + Assert.True(instantiatedType.CheckConstraints()); + } + + // ComplexGenericConstraint1 + { + instantiatedType = _complexGenericConstraint1Type.MakeInstantiatedType(_arg1Type, _arg1Type /* uninteresting */); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = _complexGenericConstraint1Type.MakeInstantiatedType(arg2OfInt, _arg1Type /* uninteresting */); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _complexGenericConstraint1Type.MakeInstantiatedType(arg2OfBool, _arg1Type /* uninteresting */); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = _complexGenericConstraint1Type.MakeInstantiatedType(arg2OfObject, _arg1Type /* uninteresting */); + Assert.False(instantiatedType.CheckConstraints()); + } + + // ComplexGenericConstraint2 + { + MetadataType arg2OfArg2OfInt = _arg2Type.MakeInstantiatedType(arg2OfInt); + MetadataType arg2OfArg2OfBool = _arg2Type.MakeInstantiatedType(arg2OfBool); + MetadataType arg2OfArg2OfObject = _arg2Type.MakeInstantiatedType(arg2OfObject); + + instantiatedType = _complexGenericConstraint2Type.MakeInstantiatedType(_arg1Type, _context.GetWellKnownType(WellKnownType.Int32)); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = _complexGenericConstraint2Type.MakeInstantiatedType(arg2OfArg2OfInt, _context.GetWellKnownType(WellKnownType.Int32)); + Assert.True(instantiatedType.CheckConstraints()); + instantiatedType = _complexGenericConstraint2Type.MakeInstantiatedType(arg2OfArg2OfBool, _context.GetWellKnownType(WellKnownType.Int32)); + Assert.False(instantiatedType.CheckConstraints()); + instantiatedType = _complexGenericConstraint2Type.MakeInstantiatedType(arg2OfArg2OfObject, _context.GetWellKnownType(WellKnownType.Int32)); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = _complexGenericConstraint2Type.MakeInstantiatedType(arg2OfArg2OfInt, _context.GetWellKnownType(WellKnownType.Object)); + Assert.False(instantiatedType.CheckConstraints()); + instantiatedType = _complexGenericConstraint2Type.MakeInstantiatedType(arg2OfArg2OfBool, _context.GetWellKnownType(WellKnownType.Object)); + Assert.False(instantiatedType.CheckConstraints()); + instantiatedType = _complexGenericConstraint2Type.MakeInstantiatedType(arg2OfArg2OfObject, _context.GetWellKnownType(WellKnownType.Object)); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _complexGenericConstraint2Type.MakeInstantiatedType(arg2OfArg2OfInt, _context.GetWellKnownType(WellKnownType.Boolean)); + Assert.False(instantiatedType.CheckConstraints()); + instantiatedType = _complexGenericConstraint2Type.MakeInstantiatedType(arg2OfArg2OfBool, _context.GetWellKnownType(WellKnownType.Boolean)); + Assert.True(instantiatedType.CheckConstraints()); + instantiatedType = _complexGenericConstraint2Type.MakeInstantiatedType(arg2OfArg2OfObject, _context.GetWellKnownType(WellKnownType.Boolean)); + Assert.False(instantiatedType.CheckConstraints()); + } + + // ComplexGenericConstraint3 + { + MetadataType igenOfObject = _iGenType.MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.Object)); + + instantiatedType = _complexGenericConstraint3Type.MakeInstantiatedType(igenOfObject, _context.GetWellKnownType(WellKnownType.Object)); + Assert.True(instantiatedType.CheckConstraints()); + + // Variance-compatible instantiation argument + instantiatedType = _complexGenericConstraint3Type.MakeInstantiatedType(igenOfObject, _context.GetWellKnownType(WellKnownType.String)); + Assert.True(instantiatedType.CheckConstraints()); + + // Type that implements the interface + var arg3OfObject = _arg3Type.MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.Object)); + instantiatedType = _complexGenericConstraint3Type.MakeInstantiatedType(arg3OfObject, _context.GetWellKnownType(WellKnownType.Object)); + Assert.True(instantiatedType.CheckConstraints()); + + // Type that implements a variant compatible interface + instantiatedType = _complexGenericConstraint3Type.MakeInstantiatedType(arg3OfObject, _context.GetWellKnownType(WellKnownType.String)); + Assert.True(instantiatedType.CheckConstraints()); + } + + // MultipleConstraints + { + // Violate the class constraint + instantiatedType = _multipleConstraintsType.MakeInstantiatedType(_structArgWithDefaultCtorType, _context.GetWellKnownType(WellKnownType.Object)); + Assert.False(instantiatedType.CheckConstraints()); + + // Violate the new() constraint + instantiatedType = _multipleConstraintsType.MakeInstantiatedType(_classArgWithoutDefaultCtorType, _context.GetWellKnownType(WellKnownType.Object)); + Assert.False(instantiatedType.CheckConstraints()); + + // Violate the IGen constraint + instantiatedType = _multipleConstraintsType.MakeInstantiatedType(_arg1Type, _context.GetWellKnownType(WellKnownType.Object)); + Assert.False(instantiatedType.CheckConstraints()); + + // Satisfy all constraints + instantiatedType = _multipleConstraintsType.MakeInstantiatedType(_classArgWithDefaultCtorType, _context.GetWellKnownType(WellKnownType.Object)); + Assert.True(instantiatedType.CheckConstraints()); + } + } + } +} diff --git a/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/CoreTestAssembly.csproj b/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/CoreTestAssembly.csproj index 056b0534a..74380612a 100644 --- a/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/CoreTestAssembly.csproj +++ b/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/CoreTestAssembly.csproj @@ -29,6 +29,7 @@ + diff --git a/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/GenericConstraints.cs b/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/GenericConstraints.cs new file mode 100644 index 000000000..8a9f7d394 --- /dev/null +++ b/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/GenericConstraints.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace GenericConstraints +{ + public interface INonGen { } + + public interface IGen { } + + public class Arg1 : INonGen { } + + public class Arg2 { } + + public class Arg3 : IGen { } + + public struct StructArgWithDefaultCtor { } + + public struct StructArgWithoutDefaultCtor + { + public StructArgWithoutDefaultCtor(int argument) { } + } + + public class ClassArgWithDefaultCtor : IGen + { + public ClassArgWithDefaultCtor() { } + } + + public class ClassArgWithoutDefaultCtor : IGen + { + public ClassArgWithoutDefaultCtor(int argument) { } + } + + public class ReferenceTypeConstraint where T : class { } + + public class DefaultConstructorConstraint where T : new() { } + + public class NotNullableValueTypeConstraint where T : struct { } + + public class SimpleTypeConstraint where T : Arg1 { } + + public class DoubleSimpleTypeConstraint where T : Arg1, INonGen { } + + public class SimpleGenericConstraint where T : U { } + + public class ComplexGenericConstraint1 where T : Arg2 { } + + public class ComplexGenericConstraint2 where T : Arg2> { } + + public class ComplexGenericConstraint3 where T : IGen { } + + public class MultipleConstraints where T : class, IGen, new() { } +} diff --git a/src/ILCompiler.TypeSystem/tests/TypeSystem.Tests.csproj b/src/ILCompiler.TypeSystem/tests/TypeSystem.Tests.csproj index dc84f7306..ec379925b 100644 --- a/src/ILCompiler.TypeSystem/tests/TypeSystem.Tests.csproj +++ b/src/ILCompiler.TypeSystem/tests/TypeSystem.Tests.csproj @@ -38,6 +38,7 @@ + -- cgit v1.2.3 From 097568046b152e5954c8a1b8454191c7f69d97a5 Mon Sep 17 00:00:00 2001 From: Simon Nattress Date: Wed, 18 Jan 2017 15:46:25 -0800 Subject: Allow tests to run in internal branches (#2539) `dir.props` loads the parent folder `dirs.props`. This is used when CoreRT is built as a part of another source tree. When building CoreRT with its own build system, any MSBuild invocation needs to set `RepoLocalBuild` to `true` to avoid dragging in an unexpected set of MSBuild configuration. Update the the standard and CoreCLR test harnesses to pass in `RepoLocalBuild=true` to MSBuild as a command-line property. --- tests/CoreCLR/build-and-run-test.cmd | 4 ++-- tests/runtest.cmd | 6 +++--- tests/runtest.sh | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/CoreCLR/build-and-run-test.cmd b/tests/CoreCLR/build-and-run-test.cmd index 511094311..8fd7ed145 100644 --- a/tests/CoreCLR/build-and-run-test.cmd +++ b/tests/CoreCLR/build-and-run-test.cmd @@ -37,8 +37,8 @@ if "%CoreRT_BuildArch%" == "x64" ( call "%VS140COMNTOOLS%\..\..\VC\bin\amd64\vcvars64.bat" ) -echo msbuild /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%CoreRT_ToolchainDir%" "/p:Configuration=%CoreRT_BuildType%" %TestFolder%\Test.csproj -msbuild /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%CoreRT_ToolchainDir%" "/p:Configuration=%CoreRT_BuildType%" %TestFolder%\Test.csproj +echo msbuild /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%CoreRT_ToolchainDir%" "/p:Configuration=%CoreRT_BuildType%" "/p:RepoLocalBuild=true" %TestFolder%\Test.csproj +msbuild /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%CoreRT_ToolchainDir%" "/p:Configuration=%CoreRT_BuildType%" "/p:RepoLocalBuild=true" %TestFolder%\Test.csproj if errorlevel 1 ( set TestExitCode=!ERRORLEVEL! goto :Cleanup diff --git a/tests/runtest.cmd b/tests/runtest.cmd index ed8dc33e2..1a43f5230 100644 --- a/tests/runtest.cmd +++ b/tests/runtest.cmd @@ -181,9 +181,9 @@ goto :eof ) ) - echo msbuild /m /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%CoreRT_ToolchainDir%" "/p:Configuration=%CoreRT_BuildType%" !extraArgs! !__SourceFile!.csproj + echo msbuild /m /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%CoreRT_ToolchainDir%" "/p:Configuration=%CoreRT_BuildType%" "/p:RepoLocalBuild=true" !extraArgs! !__SourceFile!.csproj echo. - msbuild /m /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%CoreRT_ToolchainDir%" "/p:Configuration=%CoreRT_BuildType%" !extraArgs! !__SourceFile!.csproj + msbuild /m /ConsoleLoggerParameters:ForceNoAlign "/p:IlcPath=%CoreRT_ToolchainDir%" "/p:Configuration=%CoreRT_BuildType%" "/p:RepoLocalBuild=true" !extraArgs! !__SourceFile!.csproj endlocal set __SavedErrorLevel=%ErrorLevel% @@ -271,7 +271,7 @@ goto :eof echo CORE_ROOT IS NOW %CORE_ROOT% pushd %CoreRT_TestRoot%\CoreCLR\runtest - msbuild src\TestWrappersConfig\XUnitTooling.depproj + msbuild "/p:RepoLocalBuild=true" src\TestWrappersConfig\XUnitTooling.depproj if errorlevel 1 ( exit /b 1 ) diff --git a/tests/runtest.sh b/tests/runtest.sh index 0cc8eb8a3..c786b5c0c 100755 --- a/tests/runtest.sh +++ b/tests/runtest.sh @@ -34,8 +34,8 @@ run_test_dir() rm -rf ${__dir_path}/bin ${__dir_path}/obj local __msbuild_dir=${CoreRT_TestRoot}/../Tools - echo ${__msbuild_dir}/msbuild.sh /m /p:IlcPath=${CoreRT_ToolchainDir} /p:Configuration=${CoreRT_BuildType} ${__extra_args} ${__dir_path}/${__filename}.csproj - ${__msbuild_dir}/msbuild.sh /m /p:IlcPath=${CoreRT_ToolchainDir} /p:Configuration=${CoreRT_BuildType} ${__extra_args} ${__dir_path}/${__filename}.csproj + echo ${__msbuild_dir}/msbuild.sh /m /p:IlcPath=${CoreRT_ToolchainDir} /p:Configuration=${CoreRT_BuildType} /p:RepoLocalBuild=true ${__extra_args} ${__dir_path}/${__filename}.csproj + ${__msbuild_dir}/msbuild.sh /m /p:IlcPath=${CoreRT_ToolchainDir} /p:Configuration=${CoreRT_BuildType} /p:RepoLocalBuild=true ${__extra_args} ${__dir_path}/${__filename}.csproj runtest ${__dir_path} ${__filename} local __exitcode=$? -- cgit v1.2.3 From 0a845a64d40bf8ec433183a550b62e1f8fefa996 Mon Sep 17 00:00:00 2001 From: Matt Mitchell Date: Wed, 18 Jan 2017 15:47:42 -0800 Subject: Change the Ubuntu image to use one that has clang 3.9 (#2538) --- netci.groovy | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/netci.groovy b/netci.groovy index 48367b641..20bef1863 100644 --- a/netci.groovy +++ b/netci.groovy @@ -8,6 +8,10 @@ def project = GithubProject // The input branch name (e.g. master) def branch = GithubBranchName +def imageVersionMap = ['Windows_NT':'latest-or-auto', + 'OSX':'latest-or-auto', + 'Ubuntu':'20170118'] + // Innerloop build OS's def osList = ['Ubuntu', 'OSX', 'Windows_NT'] @@ -65,7 +69,7 @@ def osList = ['Ubuntu', 'OSX', 'Windows_NT'] // This call performs test run checks for the CI. Utilities.addXUnitDotNETResults(newJob, '**/testResults.xml') - Utilities.setMachineAffinity(newJob, os, 'latest-or-auto') + Utilities.setMachineAffinity(newJob, os, imageVersionMap[os]) Utilities.standardJobSetup(newJob, project, isPR, "*/${branch}") if (isPR) { Utilities.addGithubPRTriggerForBranch(newJob, branch, prJobDescription) -- cgit v1.2.3 From 3e29cbe31a0ed5b6eee72fe579a3f220fa530eff Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Wed, 18 Jan 2017 17:06:14 -0800 Subject: Switch to the new CoreCLR package for framework (#2532) --- src/packaging/netcoreapp/project.json | 38 +++------------------------ src/packaging/packages.targets | 48 ++++++++++++++--------------------- src/packaging/uap/project.json | 4 +-- 3 files changed, 24 insertions(+), 66 deletions(-) diff --git a/src/packaging/netcoreapp/project.json b/src/packaging/netcoreapp/project.json index 6c036cd40..5d366a435 100644 --- a/src/packaging/netcoreapp/project.json +++ b/src/packaging/netcoreapp/project.json @@ -1,42 +1,12 @@ { "frameworks": { - "netcoreapp1.2": { + "netcoreapp2.0": { "dependencies": { - "System.Buffers": "4.4.0-beta-24913-02", - "System.Console": "4.4.0-beta-24913-02", - "System.Collections": "4.4.0-beta-24913-02", - "System.Collections.Concurrent": "4.4.0-beta-24913-02", - "System.Collections.NonGeneric": "4.4.0-beta-24913-02", - "System.Diagnostics.Debug": "4.4.0-beta-24913-02", - "System.Diagnostics.Tracing": "4.4.0-beta-24913-02", - "System.Diagnostics.Tools": "4.4.0-beta-24913-02", - "System.Globalization": "4.4.0-beta-24913-02", - "System.Globalization.Calendars": "4.4.0-beta-24913-02", - "System.IO": "4.4.0-beta-24913-02", - "System.IO.FileSystem": "4.4.0-beta-24913-02", - "System.IO.FileSystem.Primitives": "4.4.0-beta-24913-02", + "Microsoft.NETCore.App": "2.0.0-beta-001368-00", + + "System.Threading": "4.4.0-beta-24913-02", "System.Linq": "4.4.0-beta-24913-02", - "System.Reflection": "4.4.0-beta-24913-02", - "System.Reflection.Primitives": "4.4.0-beta-24913-02", - "System.Reflection.Extensions": "4.4.0-beta-24913-02", - "System.Reflection.TypeExtensions": "4.4.0-beta-24913-02", - "System.Resources.ResourceManager": "4.4.0-beta-24913-02", - "System.Runtime": "4.4.0-beta-24913-02", "System.Runtime.CompilerServices.Unsafe": "4.4.0-beta-24913-02", - "System.Runtime.Extensions": "4.4.0-beta-24913-02", - "System.Runtime.InteropServices": "4.4.0-beta-24913-02", - "System.Runtime.InteropServices.RuntimeInformation": "4.4.0-beta-24913-02", - "System.Runtime.Handles": "4.4.0-beta-24913-02", - "System.Runtime.Numerics": "4.4.0-beta-24913-02", - "System.Security.Principal": "4.4.0-beta-24913-02", - "System.Text.Encoding": "4.4.0-beta-24913-02", - "System.Text.Encoding.Extensions": "4.4.0-beta-24913-02", - "System.Threading": "4.4.0-beta-24913-02", - "System.Threading.Overlapped": "4.4.0-beta-24913-02", - "System.Threading.Tasks": "4.4.0-beta-24913-02", - "System.Threading.Thread": "4.4.0-beta-24913-02", - "System.Threading.Timer": "4.4.0-beta-24913-02", - "Microsoft.Win32.Primitives": "4.4.0-beta-24913-02", "runtime.native.System": "4.4.0-beta-24913-02" } diff --git a/src/packaging/packages.targets b/src/packaging/packages.targets index 7b848c057..f362e52cd 100644 --- a/src/packaging/packages.targets +++ b/src/packaging/packages.targets @@ -23,9 +23,10 @@ a 4.4.0-beta-24913-02 + 2.0.0-beta-001368-00 1.2.0-beta-24911-02 - + 1.0.13-prerelease-00001 ubuntu.14.04-x64 $(NuPkgRid) @@ -112,21 +113,35 @@ + + + + + + + + + + + - + + + + + - ]]> + ]]> - @@ -148,32 +163,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/packaging/uap/project.json b/src/packaging/uap/project.json index b8ea32560..b92a42b19 100644 --- a/src/packaging/uap/project.json +++ b/src/packaging/uap/project.json @@ -2,9 +2,7 @@ "frameworks": { "uap10.1": { "dependencies": { - "System.Diagnostics.Tracing": "4.4.0-beta-24913-02", - "System.Linq": "4.4.0-beta-24913-02", - "Microsoft.NETCore.Portable.Compatibility": "1.0.2" + "System.Diagnostics.Tracing": "4.4.0-beta-24913-02" } } }, -- cgit v1.2.3 From 6eedad45bcb621c6d26aafa4ec47d37cdbc1204e Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Wed, 11 Jan 2017 21:55:35 +0100 Subject: Fix Unix thread shutdown There was a problem with the secondary thread shutdown on Unix. First, when the TlsObjectDestructor is called, it is passed the key that was set using the pthread_setspecific before. But I was missing the fact that before the TlsObjectDestructor is called, the key is set to NULL. So we cannot use pthread_getspecific to get the key anymore and we can only use the value passed as the TlsObjectDestructor parameter. The second part of the problem was that the at the time the TlsObjectDestructor is called, the tls_CurrentThread to which the key points was already removed from the TLS and so an attempt to access tls_CurrentThread results in creation of a fresh new tls_CurrentThread. And our checks that we have at a few places that verify that the argument passed to the TlsObjectDestructor was actually the current thread pointer cannot be done. The fix is to use __cxa_thread_atexit to register a destruction callback for the ThreadStore object and use it instead of the pthread callback functionality. --- buildscripts/buildvars-setup.sh | 4 -- src/Native/Runtime/unix/PalRedhawkUnix.cpp | 72 +++++++++++------------------- 2 files changed, 25 insertions(+), 51 deletions(-) diff --git a/buildscripts/buildvars-setup.sh b/buildscripts/buildvars-setup.sh index ead93b646..588bcd626 100755 --- a/buildscripts/buildvars-setup.sh +++ b/buildscripts/buildvars-setup.sh @@ -190,10 +190,6 @@ while [ "$1" != "" ]; do verbose) export __VerboseBuild=1 ;; - clang3.5) - export __ClangMajorVersion=3 - export __ClangMinorVersion=5 - ;; clang3.6) export __ClangMajorVersion=3 export __ClangMinorVersion=6 diff --git a/src/Native/Runtime/unix/PalRedhawkUnix.cpp b/src/Native/Runtime/unix/PalRedhawkUnix.cpp index 91c5842cc..47df6e2d9 100644 --- a/src/Native/Runtime/unix/PalRedhawkUnix.cpp +++ b/src/Native/Runtime/unix/PalRedhawkUnix.cpp @@ -158,9 +158,6 @@ static uint8_t g_helperPage[OS_PAGE_SIZE] __attribute__((aligned(OS_PAGE_SIZE))) // Mutex to make the FlushProcessWriteBuffersMutex thread safe pthread_mutex_t g_flushProcessWriteBuffersMutex; -// Key for the thread local storage of the attached thread pointer -static pthread_key_t g_threadKey; - extern bool PalQueryProcessorTopology(); bool InitializeFlushProcessWriteBuffers(); @@ -413,15 +410,6 @@ public: typedef UnixHandle ThreadUnixHandle; -// Destructor of the thread local object represented by the g_threadKey, -// called when a thread is shut down -void TlsObjectDestructor(void* data) -{ - ASSERT(data == pthread_getspecific(g_threadKey)); - - RuntimeThreadShutdown(data); -} - // The Redhawk PAL must be initialized before any of its exports can be called. Returns true for a successful // initialization and false on failure. REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalInit() @@ -449,11 +437,6 @@ REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalInit() return false; } #endif // !USE_PORTABLE_HELPERS - int status = pthread_key_create(&g_threadKey, TlsObjectDestructor); - if (status != 0) - { - return false; - } return true; } @@ -464,6 +447,28 @@ REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalHasCapability(PalCapability capability) return (g_dwPALCapabilities & (uint32_t)capability) == (uint32_t)capability; } +struct TlsDestructionMonitor +{ + void* m_thread = nullptr; + + void SetThread(void* thread) + { + m_thread = thread; + } + + ~TlsDestructionMonitor() + { + if (m_thread != nullptr) + { + RuntimeThreadShutdown(m_thread); + } + } +}; + +// This thread local object is used to detect thread shutdown. Its destructor +// is called when a thread is being shut down. +thread_local TlsDestructionMonitor tls_destructionMonitor; + // Attach thread to PAL. // It can be called multiple times for the same thread. // It fails fast if a different thread was already registered. @@ -471,16 +476,7 @@ REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalHasCapability(PalCapability capability) // thread - thread to attach extern "C" void PalAttachThread(void* thread) { - void* attachedThread = pthread_getspecific(g_threadKey); - - ASSERT_MSG(attachedThread == NULL, "PalAttachThread called multiple times for the same thread"); - - int status = pthread_setspecific(g_threadKey, thread); - if (status != 0) - { - ASSERT_UNCONDITIONALLY("PalAttachThread failed to store thread pointer in thread local storage"); - RhFailFast(); - } + tls_destructionMonitor.SetThread(thread); } // Detach thread from PAL. @@ -491,26 +487,8 @@ extern "C" void PalAttachThread(void* thread) // true if the thread was detached, false if there was no attached thread extern "C" bool PalDetachThread(void* thread) { - void* attachedThread = pthread_getspecific(g_threadKey); - - if (attachedThread == thread) - { - int status = pthread_setspecific(g_threadKey, NULL); - if (status != 0) - { - ASSERT_UNCONDITIONALLY("PalDetachThread failed to clear thread pointer in thread local storage"); - RhFailFast(); - } - return true; - } - - if (attachedThread != NULL) - { - ASSERT_UNCONDITIONALLY("PalDetachThread called with different thread pointer than PalAttachThread"); - RhFailFast(); - } - - return false; + UNREFERENCED_PARAMETER(thread); + return true; } REDHAWK_PALEXPORT unsigned int REDHAWK_PALAPI PalGetCurrentProcessorNumber() -- cgit v1.2.3 From ae6d335f7211e222cf24b103d060cb4dfba7470b Mon Sep 17 00:00:00 2001 From: Faizur Rahman Date: Wed, 18 Jan 2017 21:59:43 -0800 Subject: Implement out SafeHandle marshaller --- src/Common/src/TypeSystem/Interop/IL/Marshaller.cs | 94 +++++++++++++++++----- tests/src/Simple/PInvoke/PInvoke.cs | 9 +++ tests/src/Simple/PInvoke/PInvokeNative.cpp | 9 +++ 3 files changed, 94 insertions(+), 18 deletions(-) diff --git a/src/Common/src/TypeSystem/Interop/IL/Marshaller.cs b/src/Common/src/TypeSystem/Interop/IL/Marshaller.cs index abfec6ab7..7b6f5cb7d 100644 --- a/src/Common/src/TypeSystem/Interop/IL/Marshaller.cs +++ b/src/Common/src/TypeSystem/Interop/IL/Marshaller.cs @@ -55,7 +55,10 @@ namespace Internal.TypeSystem.Interop /// The created Marshaller public static Marshaller CreateMarshaller(TypeDesc parameterType, PInvokeMethodData pInvokeMethodData, ParameterMetadata pInvokeParameterdata) { - MarshallerKind marshallerKind = GetMarshallerKind(parameterType, pInvokeParameterdata.MarshalAsDescriptor, pInvokeMethodData, pInvokeParameterdata.Return); + MarshallerKind marshallerKind = GetMarshallerKind(parameterType, + pInvokeParameterdata.MarshalAsDescriptor, + pInvokeMethodData, + pInvokeParameterdata); // Create the marshaller based on MarshallerKind Marshaller marshaller = Marshaller.CreateMarshallerInternal(marshallerKind); @@ -92,9 +95,9 @@ namespace Internal.TypeSystem.Interop throw new NotSupportedException(); } } - private static MarshallerKind GetMarshallerKind(TypeDesc type, MarshalAsDescriptor marshalAs, PInvokeMethodData PInvokeMethodData, bool isReturn) + private static MarshallerKind GetMarshallerKind(TypeDesc type, MarshalAsDescriptor marshalAs, PInvokeMethodData PInvokeMethodData, ParameterMetadata paramMetadata) { - if (isReturn) + if (paramMetadata.Return) { if (type.IsVoid) { @@ -184,6 +187,21 @@ namespace Internal.TypeSystem.Interop { return MarshallerKind.SafeHandle; } + + // Temporary fix for out SafeHandle scenario + // TODO: handle in,out,ref properly + if (paramMetadata.Out && !paramMetadata.In) + { + ByRefType byRefType = type as ByRefType; + if (byRefType != null) + { + if (PInvokeMethodData.IsSafeHandle(byRefType.ParameterType)) + { + return MarshallerKind.SafeHandle; + } + } + } + throw new NotSupportedException(); } #endregion @@ -366,28 +384,68 @@ namespace Internal.TypeSystem.Interop { protected override TypeDesc MarshalArgument(TypeDesc managedType, ILEmitter emitter, ILCodeStream marshallingCodeStream, ILCodeStream unmarshallingCodeStream) { + // we don't support [IN,OUT] together yet, either IN or OUT + Debug.Assert(!(PInvokeParameterMetadata.Out && PInvokeParameterMetadata.In)); + var safeHandleType = PInvokeMethodData.SafeHandleType; - var vAddRefed = emitter.NewLocal(PInvokeMethodData.Context.GetWellKnownType(WellKnownType.Boolean)); - var vSafeHandle = emitter.NewLocal(managedType); + if (PInvokeParameterMetadata.Out) + { + // 1) If this is an output parameter we need to preallocate a SafeHandle to wrap the new native handle value. We + // must allocate this before the native call to avoid a failure point when we already have a native resource + // allocated. We must allocate a new SafeHandle even if we have one on input since both input and output native + // handles need to be tracked and released by a SafeHandle. + // 2) Initialize a local IntPtr that will be passed to the native call. + // 3) After the native call, the new handle value is written into the output SafeHandle and that SafeHandle + // is propagated back to the caller. + + Debug.Assert(managedType is ByRefType); + var vOutArg = emitter.NewLocal(managedType); + marshallingCodeStream.EmitStLoc(vOutArg); + + TypeDesc resolvedType = ((ByRefType)managedType).ParameterType; + + var nativeType = PInvokeMethodData.Context.GetWellKnownType(WellKnownType.IntPtr).MakeByRefType(); + var vPinnedOutValue = emitter.NewLocal(nativeType, true); + var vSafeHandle = emitter.NewLocal(resolvedType); + marshallingCodeStream.Emit(ILOpcode.newobj, emitter.NewToken(resolvedType.GetDefaultConstructor())); + marshallingCodeStream.EmitStLoc(vSafeHandle); + marshallingCodeStream.EmitLdLoca(vPinnedOutValue); + + unmarshallingCodeStream.EmitLdLoc(vSafeHandle); + unmarshallingCodeStream.EmitLdLoc(vPinnedOutValue); + unmarshallingCodeStream.Emit(ILOpcode.call, emitter.NewToken( + PInvokeMethodData.SafeHandleType.GetKnownMethod("SetHandle", null))); - marshallingCodeStream.EmitStLoc(vSafeHandle); + unmarshallingCodeStream.EmitLdLoc(vOutArg); + unmarshallingCodeStream.EmitLdLoc(vSafeHandle); + unmarshallingCodeStream.Emit(ILOpcode.stind_i); - marshallingCodeStream.EmitLdLoc(vSafeHandle); - marshallingCodeStream.EmitLdLoca(vAddRefed); - marshallingCodeStream.Emit(ILOpcode.call, emitter.NewToken( - safeHandleType.GetKnownMethod("DangerousAddRef", null))); + return nativeType; + } + else + { + var vAddRefed = emitter.NewLocal(PInvokeMethodData.Context.GetWellKnownType(WellKnownType.Boolean)); + var vSafeHandle = emitter.NewLocal(managedType); - marshallingCodeStream.EmitLdLoc(vSafeHandle); - marshallingCodeStream.Emit(ILOpcode.call, emitter.NewToken( - safeHandleType.GetKnownMethod("DangerousGetHandle", null))); + marshallingCodeStream.EmitStLoc(vSafeHandle); - // TODO: This should be inside finally block and only executed it the handle was addrefed - unmarshallingCodeStream.EmitLdLoc(vSafeHandle); - unmarshallingCodeStream.Emit(ILOpcode.call, emitter.NewToken( - safeHandleType.GetKnownMethod("DangerousRelease", null))); + marshallingCodeStream.EmitLdLoc(vSafeHandle); + marshallingCodeStream.EmitLdLoca(vAddRefed); + marshallingCodeStream.Emit(ILOpcode.call, emitter.NewToken( + safeHandleType.GetKnownMethod("DangerousAddRef", null))); - return PInvokeMethodData.Context.GetWellKnownType(WellKnownType.IntPtr); + marshallingCodeStream.EmitLdLoc(vSafeHandle); + marshallingCodeStream.Emit(ILOpcode.call, emitter.NewToken( + safeHandleType.GetKnownMethod("DangerousGetHandle", null))); + + // TODO: This should be inside finally block and only executed it the handle was addrefed + unmarshallingCodeStream.EmitLdLoc(vSafeHandle); + unmarshallingCodeStream.Emit(ILOpcode.call, emitter.NewToken( + safeHandleType.GetKnownMethod("DangerousRelease", null))); + + return PInvokeMethodData.Context.GetWellKnownType(WellKnownType.IntPtr); + } } protected override TypeDesc MarshalReturn(TypeDesc managedType, ILEmitter emitter, ILCodeStream marshallingCodeStream, ILCodeStream returnValueMarshallingCodeStream ) diff --git a/tests/src/Simple/PInvoke/PInvoke.cs b/tests/src/Simple/PInvoke/PInvoke.cs index 1e7a6fa99..ed7f53fb1 100644 --- a/tests/src/Simple/PInvoke/PInvoke.cs +++ b/tests/src/Simple/PInvoke/PInvoke.cs @@ -36,6 +36,9 @@ namespace PInvoke [DllImport("*", CallingConvention = CallingConvention.StdCall)] public static extern bool SafeHandleTest(SafeMemoryHandle sh1, Int64 sh1Value); + [DllImport("*", CallingConvention = CallingConvention.StdCall)] + public static extern int SafeHandleOutTest(out SafeMemoryHandle sh1); + [DllImport("*", CallingConvention = CallingConvention.StdCall, SetLastError = true)] public static extern bool LastErrorTest(); @@ -117,6 +120,12 @@ namespace PInvoke long val = hndIntPtr.ToInt64(); //return the 64-bit value associated with hnd ThrowIfNotEquals(true, SafeHandleTest(hnd, val), "SafeHandle marshalling failed."); + + Console.WriteLine("Testing marshalling out SafeHandle"); + SafeMemoryHandle hnd2; + val = SafeHandleOutTest(out hnd2); + int val2 = unchecked((int)hnd2.DangerousGetHandle().ToInt64()); + ThrowIfNotEquals(val, val2, "SafeHandle out marshalling failed"); } } diff --git a/tests/src/Simple/PInvoke/PInvokeNative.cpp b/tests/src/Simple/PInvoke/PInvokeNative.cpp index ca66eafb9..8c70cbb6e 100644 --- a/tests/src/Simple/PInvoke/PInvokeNative.cpp +++ b/tests/src/Simple/PInvoke/PInvokeNative.cpp @@ -116,3 +116,12 @@ DLL_EXPORT bool __stdcall SafeHandleTest(HANDLE sh, long shValue) { return (long)((size_t)(sh)) == shValue; } + +DLL_EXPORT long __stdcall SafeHandleOutTest(HANDLE **sh) +{ + if (sh == NULL) + return -1; + + *sh = (HANDLE *)malloc(100); + return (long)((size_t)(*sh)); +} -- cgit v1.2.3 From 8b7a2b519420bbc4529ed96c44612651bd9cb086 Mon Sep 17 00:00:00 2001 From: Atsushi Kanamori Date: Thu, 19 Jan 2017 08:16:20 -0800 Subject: Make two System.Type methods virtual to conform with 4.2.0.0. System.Runtime 4.2.0.0 finally brings IsEnum in line with the other "Is" predicates.... --- src/System.Private.CoreLib/src/System/Type.cs | 4 ++-- .../System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/System.Private.CoreLib/src/System/Type.cs b/src/System.Private.CoreLib/src/System/Type.cs index 8a9197464..4c54744a6 100644 --- a/src/System.Private.CoreLib/src/System/Type.cs +++ b/src/System.Private.CoreLib/src/System/Type.cs @@ -105,7 +105,7 @@ namespace System public bool IsContextful => IsContextfulImpl(); protected virtual bool IsContextfulImpl() => typeof(ContextBoundObject).IsAssignableFrom(this); - public bool IsEnum => IsSubclassOf(typeof(Enum)); + public virtual bool IsEnum => IsSubclassOf(typeof(Enum)); public bool IsMarshalByRef => IsMarshalByRefImpl(); protected virtual bool IsMarshalByRefImpl() => typeof(MarshalByRefObject).IsAssignableFrom(this); public bool IsPrimitive => IsPrimitiveImpl(); @@ -351,7 +351,7 @@ namespace System return systemType.GetHashCode(); return base.GetHashCode(); } - public bool Equals(Type o) => o == null ? false : object.ReferenceEquals(this.UnderlyingSystemType, o.UnderlyingSystemType); + public virtual bool Equals(Type o) => o == null ? false : object.ReferenceEquals(this.UnderlyingSystemType, o.UnderlyingSystemType); public static bool operator ==(Type left, Type right) { diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs index 83cd7a4fc..58635acca 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs @@ -141,6 +141,11 @@ namespace System.Reflection.Runtime.TypeInfos return object.ReferenceEquals(this, obj); } + public sealed override bool Equals(Type o) + { + return object.ReferenceEquals(this, o); + } + public sealed override int GetHashCode() { return InternalGetHashCode(); @@ -302,6 +307,14 @@ namespace System.Reflection.Runtime.TypeInfos } } + public sealed override bool IsEnum + { + get + { + return 0 != (Classification & TypeClassification.IsEnum); + } + } + // // Left unsealed as generic parameter types must override. // -- cgit v1.2.3 From 0893676f65845428cce02e71871b34ad7b676fd8 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Thu, 19 Jan 2017 22:22:57 +0100 Subject: Enable workaround for build tools that don't support thread_local Apple started to support thread_local in xcode starting from version 8.0, this workaround enables the thread shutdown detection to compile even on devices that don't have the latest xcode. --- src/Native/Runtime/startup.cpp | 5 +++++ src/Native/Runtime/unix/PalRedhawkUnix.cpp | 15 +++++++++++++++ src/Native/Runtime/unix/config.h.in | 2 ++ src/Native/Runtime/unix/configure.cmake | 9 +++++++++ 4 files changed, 31 insertions(+) diff --git a/src/Native/Runtime/startup.cpp b/src/Native/Runtime/startup.cpp index 8a7b0c56c..3a368191b 100644 --- a/src/Native/Runtime/startup.cpp +++ b/src/Native/Runtime/startup.cpp @@ -268,8 +268,13 @@ void __stdcall RuntimeThreadShutdown(void* thread) // that is made for the single thread that runs the final stages of orderly process // shutdown (i.e., the thread that delivers the DLL_PROCESS_DETACH notifications when the // process is being torn down via an ExitProcess call). +#if HAVE_THREAD_LOCAL + // If the current Unix platform doesn't support thread_local, we don't get the thread pointer + // as the parameter, we just get NULL, so we can check the thread validity only if the + // thread_local is supported UNREFERENCED_PARAMETER(thread); ASSERT((Thread*)thread == ThreadStore::GetCurrentThread()); +#endif if (!g_processShutdownHasStarted) { diff --git a/src/Native/Runtime/unix/PalRedhawkUnix.cpp b/src/Native/Runtime/unix/PalRedhawkUnix.cpp index 47df6e2d9..c9f8ea4a8 100644 --- a/src/Native/Runtime/unix/PalRedhawkUnix.cpp +++ b/src/Native/Runtime/unix/PalRedhawkUnix.cpp @@ -410,6 +410,11 @@ public: typedef UnixHandle ThreadUnixHandle; +#if !HAVE_THREAD_LOCAL +extern "C" int __cxa_thread_atexit(void (*)(void*), void*, void *); +extern "C" void *__dso_handle; +#endif + // The Redhawk PAL must be initialized before any of its exports can be called. Returns true for a successful // initialization and false on failure. REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalInit() @@ -438,6 +443,10 @@ REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalInit() } #endif // !USE_PORTABLE_HELPERS +#if !HAVE_THREAD_LOCAL + __cxa_thread_atexit(RuntimeThreadShutdown, NULL, &__dso_handle); +#endif + return true; } @@ -447,6 +456,8 @@ REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalHasCapability(PalCapability capability) return (g_dwPALCapabilities & (uint32_t)capability) == (uint32_t)capability; } +#if HAVE_THREAD_LOCAL + struct TlsDestructionMonitor { void* m_thread = nullptr; @@ -469,6 +480,8 @@ struct TlsDestructionMonitor // is called when a thread is being shut down. thread_local TlsDestructionMonitor tls_destructionMonitor; +#endif // HAVE_THREAD_LOCAL + // Attach thread to PAL. // It can be called multiple times for the same thread. // It fails fast if a different thread was already registered. @@ -476,7 +489,9 @@ thread_local TlsDestructionMonitor tls_destructionMonitor; // thread - thread to attach extern "C" void PalAttachThread(void* thread) { +#if HAVE_THREAD_LOCAL tls_destructionMonitor.SetThread(thread); +#endif } // Detach thread from PAL. diff --git a/src/Native/Runtime/unix/config.h.in b/src/Native/Runtime/unix/config.h.in index 085ceaa3a..ca5f5aa71 100644 --- a/src/Native/Runtime/unix/config.h.in +++ b/src/Native/Runtime/unix/config.h.in @@ -29,4 +29,6 @@ #cmakedefine01 HAVE_CLOCK_MONOTONIC_COARSE #cmakedefine01 HAVE_MACH_ABSOLUTE_TIME +#cmakedefine01 HAVE_THREAD_LOCAL + #endif diff --git a/src/Native/Runtime/unix/configure.cmake b/src/Native/Runtime/unix/configure.cmake index d58e1219e..a8fd29d18 100644 --- a/src/Native/Runtime/unix/configure.cmake +++ b/src/Native/Runtime/unix/configure.cmake @@ -105,4 +105,13 @@ int main() exit(ret); }" HAVE_MACH_ABSOLUTE_TIME) +check_cxx_source_compiles(" +thread_local int x; + +int main(int argc, char **argv) +{ + x = 1; + return 0; +}" HAVE_THREAD_LOCAL) + configure_file(${CMAKE_CURRENT_LIST_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) -- cgit v1.2.3 From 7f4560bdec0c33ff4f5805cb2fe6f2814b0a7e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 19 Jan 2017 13:36:23 -0800 Subject: Fix handling of constrained method calls from shared generic code (#2527) Don't track runtime determined dependencies if we're generating a call to a constrained method. The more complicated case is tracked in #2526. --- src/JitInterface/src/CorInfoImpl.cs | 7 ++++++- tests/src/Simple/Generics/Generics.cs | 37 +++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/JitInterface/src/CorInfoImpl.cs b/src/JitInterface/src/CorInfoImpl.cs index 2064ed4de..89b8d6a7a 100644 --- a/src/JitInterface/src/CorInfoImpl.cs +++ b/src/JitInterface/src/CorInfoImpl.cs @@ -2659,7 +2659,11 @@ namespace Internal.JitInterface // to abort the inlining attempt if the inlinee does any generic lookups. bool inlining = contextMethod != MethodBeingCompiled; - if (targetMethod.IsSharedByGenericInstantiations && !inlining) + // If we resolved a constrained call, calling GetRuntimeDeterminedObjectForToken below would + // result in getting back the unresolved target. Don't capture runtime determined dependencies + // in that case and rely on the dependency analysis computing them based on seeing a call to the + // canonical method body. + if (targetMethod.IsSharedByGenericInstantiations && !inlining && !resolvedConstraint) { MethodDesc runtimeDeterminedMethod = (MethodDesc)GetRuntimeDeterminedObjectForToken(ref pResolvedToken); pResult.codePointerOrStubLookup.constLookup.addr = (void*)ObjectToHandle( @@ -2667,6 +2671,7 @@ namespace Internal.JitInterface } else { + Debug.Assert(!forceUseRuntimeLookup); pResult.codePointerOrStubLookup.constLookup.addr = (void*)ObjectToHandle( _compilation.NodeFactory.MethodEntrypoint(targetMethod)); } diff --git a/tests/src/Simple/Generics/Generics.cs b/tests/src/Simple/Generics/Generics.cs index e4fc32932..603d5b792 100644 --- a/tests/src/Simple/Generics/Generics.cs +++ b/tests/src/Simple/Generics/Generics.cs @@ -18,6 +18,7 @@ class Program TestDelegateVirtualMethod.Run(); TestDelegateInterfaceMethod.Run(); TestThreadStaticFieldAccess.Run(); + TestConstrainedMethodCalls.Run(); TestNameManglingCollisionRegression.Run(); TestUnusedGVMsDoNotCrashCompiler.Run(); @@ -383,6 +384,42 @@ class Program } } + class TestConstrainedMethodCalls + { + interface IFoo + { + void Frob(); + } + + struct Foo : IFoo + { + public int FrobbedValue; + + public void Frob() + { + FrobbedValue = 12345; + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void DoFrob(ref T t) where T : IFoo + { + // Perform a constrained interface call from shared code. + // This should have been resolved to a direct call at compile time. + t.Frob(); + } + + public static void Run() + { + var foo = new Foo(); + DoFrob, object>(ref foo); + + // If the FrobbedValue doesn't change when we frob, we must have done box+interface call. + if (foo.FrobbedValue != 12345) + throw new Exception(); + } + } + // // Regression test for issue https://github.com/dotnet/corert/issues/1964 // -- cgit v1.2.3 From 70874488d0b71ae376c302827288f8c4806c9b26 Mon Sep 17 00:00:00 2001 From: Anton Lapounov Date: Thu, 19 Jan 2017 14:11:48 -0800 Subject: Use native thread id in the Lock class so it may be implemented as a compiler intrinsic on Windows. CR: SergeyK, EJan [tfs-changeset: 1645128] --- .../src/Interop/Windows/Interop.Libraries.cs | 1 + .../Windows/mincore/Interop.GetCurrentThreadId.cs | 14 ++++++++++++++ .../src/Interop/Interop.manual.cs | 3 --- .../src/System.Private.CoreLib.csproj | 3 +++ .../src/System/Environment.Unix.cs | 2 ++ .../src/System/Environment.Windows.cs | 2 ++ .../src/System/Threading/Lock.cs | 22 ++++++++++++---------- 7 files changed, 34 insertions(+), 13 deletions(-) create mode 100644 src/Common/src/Interop/Windows/mincore/Interop.GetCurrentThreadId.cs diff --git a/src/Common/src/Interop/Windows/Interop.Libraries.cs b/src/Common/src/Interop/Windows/Interop.Libraries.cs index 6740c2cb2..0c1679ed2 100644 --- a/src/Common/src/Interop/Windows/Interop.Libraries.cs +++ b/src/Common/src/Interop/Windows/Interop.Libraries.cs @@ -15,6 +15,7 @@ internal static partial class Interop internal const string IO = "api-ms-win-core-io-l1-1-0.dll"; internal const string Memory = "api-ms-win-core-memory-l1-1-0.dll"; internal const string ProcessEnvironment = "api-ms-win-core-processenvironment-l1-1-0.dll"; + internal const string ProcessThreads = "api-ms-win-core-processthreads-l1-1-0.dll"; internal const string RealTime = "api-ms-win-core-realtime-l1-1-0.dll"; internal const string SysInfo = "api-ms-win-core-sysinfo-l1-2-0.dll"; internal const string Kernel32 = "api-ms-win-core-kernel32-legacy-l1-1-0.dll"; diff --git a/src/Common/src/Interop/Windows/mincore/Interop.GetCurrentThreadId.cs b/src/Common/src/Interop/Windows/mincore/Interop.GetCurrentThreadId.cs new file mode 100644 index 000000000..10e850f03 --- /dev/null +++ b/src/Common/src/Interop/Windows/mincore/Interop.GetCurrentThreadId.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class mincore + { + [DllImport(Libraries.ProcessThreads)] + internal extern static uint GetCurrentThreadId(); + } +} diff --git a/src/System.Private.CoreLib/src/Interop/Interop.manual.cs b/src/System.Private.CoreLib/src/Interop/Interop.manual.cs index 08b9a91e5..ffc17b236 100644 --- a/src/System.Private.CoreLib/src/Interop/Interop.manual.cs +++ b/src/System.Private.CoreLib/src/Interop/Interop.manual.cs @@ -86,9 +86,6 @@ internal partial class Interop [DllImport("api-ms-win-core-synch-l1-1-0.dll", EntryPoint = "CreateSemaphoreExW", CharSet = CharSet.Unicode)] internal static extern IntPtr CreateSemaphoreEx(IntPtr lpSemaphoreAttributes, int lInitialCount, int lMaximumCount, string lpName, uint dwFlags, uint dwDesiredAccess); - [DllImport("api-ms-win-core-processthreads-l1-1-0.dll")] - internal extern static uint GetCurrentThreadId(); - [DllImport("api-ms-win-core-debug-l1-1-0.dll", EntryPoint = "IsDebuggerPresent", CharSet = CharSet.Unicode)] internal extern static bool IsDebuggerPresent(); diff --git a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 4998a45c6..41cce6086 100644 --- a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -752,6 +752,9 @@ Interop\Windows\Interop.BOOL.cs + + Interop\Windows\mincore\Interop.GetCurrentThreadId.cs + Interop\Windows\mincore\Interop.SetLastError.cs diff --git a/src/System.Private.CoreLib/src/System/Environment.Unix.cs b/src/System.Private.CoreLib/src/System/Environment.Unix.cs index bcfdbe4f6..7cf60c683 100644 --- a/src/System.Private.CoreLib/src/System/Environment.Unix.cs +++ b/src/System.Private.CoreLib/src/System/Environment.Unix.cs @@ -9,6 +9,8 @@ namespace System { public static partial class Environment { + internal static int CurrentNativeThreadId => ManagedThreadId.Current; + internal static long TickCount64 { get diff --git a/src/System.Private.CoreLib/src/System/Environment.Windows.cs b/src/System.Private.CoreLib/src/System/Environment.Windows.cs index 7576857a9..3c35f0f7a 100644 --- a/src/System.Private.CoreLib/src/System/Environment.Windows.cs +++ b/src/System.Private.CoreLib/src/System/Environment.Windows.cs @@ -6,6 +6,8 @@ namespace System { public static partial class Environment { + internal static int CurrentNativeThreadId => unchecked((int)Interop.mincore.GetCurrentThreadId()); + internal static long TickCount64 => (long)Interop.mincore.GetTickCount64(); public static int ProcessorCount diff --git a/src/System.Private.CoreLib/src/System/Threading/Lock.cs b/src/System.Private.CoreLib/src/System/Threading/Lock.cs index e81d5c3cf..1a122c21b 100644 --- a/src/System.Private.CoreLib/src/System/Threading/Lock.cs +++ b/src/System.Private.CoreLib/src/System/Threading/Lock.cs @@ -2,9 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#pragma warning disable 0420 //passing volatile field by reference - - using System.Diagnostics; using System.Runtime.CompilerServices; @@ -57,12 +54,14 @@ namespace System.Threading } } - /// Inlined version of Lock.Acquire has CurrentManagedThreadId not inlined, non-inlined version has it inlined. - /// So it saves code to keep this function non inlining while keep the same runtime cost + // On platforms where CurrentNativeThreadId redirects to ManagedThreadId.Current the inlined + // version of Lock.Acquire has the ManagedThreadId.Current call not inlined, while the non-inlined + // version has it inlined. So it saves code to keep this function not inlined while having + // the same runtime cost. [MethodImpl(MethodImplOptions.NoInlining)] public void Acquire() { - int currentThreadId = Environment.CurrentManagedThreadId; + int currentThreadId = Environment.CurrentNativeThreadId; // // Make one quick attempt to acquire an uncontended lock @@ -95,7 +94,7 @@ namespace System.Threading if (millisecondsTimeout < -1) throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); - int currentThreadId = Environment.CurrentManagedThreadId; + int currentThreadId = Environment.CurrentNativeThreadId; // // Make one quick attempt to acquire an uncontended lock @@ -230,6 +229,9 @@ namespace System.Threading { get { + // + // The comment below is for platforms where CurrentNativeThreadId redirects to + // ManagedThreadId.Current instead of being a compiler intrinsic. // // Compare the current owning thread ID with the current thread ID. We need // to read the current thread's ID before we read m_owningThreadId. Otherwise, @@ -237,7 +239,7 @@ namespace System.Threading // // 1) We read m_owningThreadId, and get, say 42, which belongs to another thread. // 2) Thread 42 releases the lock, and exits. - // 3) We call CurrentManagedThreadId. If this is the first time it's been called + // 3) We call ManagedThreadId.Current. If this is the first time it's been called // on this thread, we'll go get a new ID. We may reuse thread 42's ID, since // that thread is dead. // 4) Now we're thread 42, and it looks like we own the lock, even though we don't. @@ -246,8 +248,8 @@ namespace System.Threading // because while we're doing this check the current thread is definitely still // alive. // - int currentManagedThreadId = Environment.CurrentManagedThreadId; - bool acquired = (currentManagedThreadId == _owningThreadId); + int currentThreadId = Environment.CurrentNativeThreadId; + bool acquired = (currentThreadId == _owningThreadId); if (acquired) Debug.Assert((_state & Locked) != 0); return acquired; -- cgit v1.2.3 From d9459c0e691dbfba6f2f6fecc530c1e9ff9d5704 Mon Sep 17 00:00:00 2001 From: Simon Nattress Date: Thu, 19 Jan 2017 14:54:36 -0800 Subject: Unix CoreCLR tests CI authoring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Author the CI netci.groovy changes along with a stubbed out `runtest.sh -coreclr top200` implementation so that the actual PR containing the test changes will run. This is needed to ensure we don’t break the build since netci.groovy changes aren’t picked up in a PR (CI takes netci.groovy from HEAD). --- netci.groovy | 21 +++++++++++++++++++-- tests/runtest.sh | 5 +++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/netci.groovy b/netci.groovy index 20bef1863..e6d1b990a 100644 --- a/netci.groovy +++ b/netci.groovy @@ -30,7 +30,10 @@ def osList = ['Ubuntu', 'OSX', 'Windows_NT'] def newJobName = Utilities.getFullJobName(project, lowercaseConfiguration + '_' + os.toLowerCase(), isPR) def buildString = ""; def prJobDescription = "${os} ${configuration}"; - + if (configuration == 'Debug') { + prJobDescription += " and CoreCLR tests" + } + // Calculate the build commands if (os == 'Windows_NT') { buildString = "build.cmd ${lowercaseConfiguration}" @@ -38,6 +41,7 @@ def osList = ['Ubuntu', 'OSX', 'Windows_NT'] } else { buildString = "./build.sh ${lowercaseConfiguration}" + testScriptString = "tests/runtest.sh -coreclr " } // Create a new job with the specified name. The brace opens a new closure @@ -51,7 +55,6 @@ def osList = ['Ubuntu', 'OSX', 'Windows_NT'] if (configuration == 'Debug') { if (isPR) { - prJobDescription += " and CoreCLR tests" // Run a small set of BVTs during PR validation batchFile(testScriptString + "Top200") } @@ -63,6 +66,20 @@ def osList = ['Ubuntu', 'OSX', 'Windows_NT'] } else { shell(buildString) + + if (configuration == 'Debug') { + if (isPR) { + prJobDescription += " and CoreCLR tests" + // Run a small set of BVTs during PR validation + shell(testScriptString + "top200") + } + else { + // Run the full set of known passing tests in the post-commit job + + // Todo: Enable push test jobs once we establish a reasonable passing set of tests + // shell(testScriptString + "KnownGood") + } + } } } } diff --git a/tests/runtest.sh b/tests/runtest.sh index c786b5c0c..2593515fb 100755 --- a/tests/runtest.sh +++ b/tests/runtest.sh @@ -105,6 +105,11 @@ while [ "$1" != "" ]; do shift CoreRT_CliBinDir=$1 ;; + -coreclr) + shift + echo "Tests will run here. For now, early out." + exit 0 + ;; *) ;; esac -- cgit v1.2.3 From dbf52630bae18e0ef551cd08aa734996b085b724 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Thu, 19 Jan 2017 18:54:55 -0800 Subject: Fix Unix build break --- src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs b/src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs index 1a47b20a7..ca62a64b6 100644 --- a/src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs +++ b/src/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs @@ -402,7 +402,7 @@ namespace System LowLevelList exceptions = new LowLevelList(curThreadExceptions); LowLevelList nonThrownInnerExceptions = new LowLevelList(); - uint currentThreadId = Interop.mincore.GetCurrentThreadId(); + uint currentThreadId = (uint)Environment.CurrentNativeThreadId; // Reset nesting levels for exceptions on this thread that might not be currently in flight foreach (ExceptionData exceptionData in s_exceptionDataTable.GetValues()) -- cgit v1.2.3 From 1c6f01ae270eb26b4bcfee8e86f7bd9a4c463813 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Thu, 19 Jan 2017 19:06:34 -0800 Subject: Fix another Unix build break --- src/System.Private.CoreLib/src/System/Environment.Unix.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/System.Private.CoreLib/src/System/Environment.Unix.cs b/src/System.Private.CoreLib/src/System/Environment.Unix.cs index 7cf60c683..84ce40165 100644 --- a/src/System.Private.CoreLib/src/System/Environment.Unix.cs +++ b/src/System.Private.CoreLib/src/System/Environment.Unix.cs @@ -4,6 +4,7 @@ using System.Text; using System.Runtime.InteropServices; +using System.Threading; namespace System { -- cgit v1.2.3 From abf07c287cb033ebbeb0f11625857ab175cd8652 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Thu, 19 Jan 2017 20:30:05 -0800 Subject: Fold System.Private.Reflection.rdxml into CoreLib MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move System.Private.Reflection.rdxml to CoreLib because of that’s where the types referenced by it are now [tfs-changeset: 1645193] --- .../src/Resources/System.Private.CoreLib.rd.xml | 8 +++++++- .../src/Resources/System.Private.Reflection.rd.xml | 14 -------------- .../src/System.Private.Reflection.csproj | 3 --- 3 files changed, 7 insertions(+), 18 deletions(-) delete mode 100644 src/System.Private.Reflection/src/Resources/System.Private.Reflection.rd.xml diff --git a/src/System.Private.CoreLib/src/Resources/System.Private.CoreLib.rd.xml b/src/System.Private.CoreLib/src/Resources/System.Private.CoreLib.rd.xml index f33a654d6..8e7f7ead1 100644 --- a/src/System.Private.CoreLib/src/Resources/System.Private.CoreLib.rd.xml +++ b/src/System.Private.CoreLib/src/Resources/System.Private.CoreLib.rd.xml @@ -87,7 +87,13 @@ + + + + + + + - diff --git a/src/System.Private.Reflection/src/Resources/System.Private.Reflection.rd.xml b/src/System.Private.Reflection/src/Resources/System.Private.Reflection.rd.xml deleted file mode 100644 index 7d964d890..000000000 --- a/src/System.Private.Reflection/src/Resources/System.Private.Reflection.rd.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/src/System.Private.Reflection/src/System.Private.Reflection.csproj b/src/System.Private.Reflection/src/System.Private.Reflection.csproj index 645c7e84c..e2e74005a 100644 --- a/src/System.Private.Reflection/src/System.Private.Reflection.csproj +++ b/src/System.Private.Reflection/src/System.Private.Reflection.csproj @@ -24,8 +24,5 @@ - - - -- cgit v1.2.3 From c0f65c82ed00359964c83bd5faa0563deb20d99a Mon Sep 17 00:00:00 2001 From: Justin Van Patten Date: Fri, 20 Jan 2017 00:27:06 -0500 Subject: Cleanup uses of string.Split (#2548) --- .../src/System/Globalization/JapaneseCalendar.Win32.cs | 2 +- src/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs | 2 +- src/System.Private.CoreLib/src/System/Version.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.Win32.cs b/src/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.Win32.cs index 55e78f5d5..a83c4fad9 100644 --- a/src/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.Win32.cs +++ b/src/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.Win32.cs @@ -171,7 +171,7 @@ namespace System.Globalization // Get Strings // // Needs to be a certain length e_a_E_A at least (7 chars, exactly 4 groups) - String[] names = data.Split(new char[] { '_' }); + String[] names = data.Split('_'); // Should have exactly 4 parts // 0 - Era Name diff --git a/src/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs b/src/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs index 7c7711906..326f1718d 100644 --- a/src/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs +++ b/src/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs @@ -830,7 +830,7 @@ namespace System // filePath = "C:\Windows\System32\tzres.dll" // resourceId = -100 // - string[] resources = resource.Split(new char[] { ',' }, StringSplitOptions.None); + string[] resources = resource.Split(','); if (resources.Length != 2) { return String.Empty; diff --git a/src/System.Private.CoreLib/src/System/Version.cs b/src/System.Private.CoreLib/src/System/Version.cs index 31f062c82..13404bfd7 100644 --- a/src/System.Private.CoreLib/src/System/Version.cs +++ b/src/System.Private.CoreLib/src/System/Version.cs @@ -256,7 +256,7 @@ namespace System return false; } - String[] parsedComponents = version.Split(new char[] { '.' }); + String[] parsedComponents = version.Split('.'); int parsedComponentsLength = parsedComponents.Length; if ((parsedComponentsLength < 2) || (parsedComponentsLength > 4)) { -- cgit v1.2.3 From b3fba48529ea068b0ecd753c1283d33a0098b87d Mon Sep 17 00:00:00 2001 From: "Yi Zhang (CLR)" Date: Thu, 19 Jan 2017 23:25:58 -0800 Subject: =?UTF-8?q?Update=20CoreRT=20doc=20to=20include=20how=20to=20debug?= =?UTF-8?q?=20ILC=20using=20VSCode=20in=20non-win=E2=80=A6=20(#2514)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update CoreRT doc to include how to debug ILC using VSCode in non-windows * Automate some of the tasks and check in vscode .json files * Simplify instructions * Explains the magic and added a bit more coding annotations to make it looks better * Change ILCompiler.csproj to output ILC.dll directly to be VS-code friendly --- .vscode/launch.json | 27 +++++++++++ .vscode/tasks.json | 18 ++++++++ Documentation/README.md | 1 + .../how-to-build-and-run-ilcompiler-in-vscode.md | 54 ++++++++++++++++++++++ .../Microsoft.NETCore.Native.targets | 2 +- src/ILCompiler/src/ILCompiler.csproj | 8 ++++ src/ILCompiler/src/ilc.runtimeconfig.json | 8 ++++ src/Test.CoreLib/readme.md | 2 +- src/packaging/packages.targets | 8 +++- 9 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json create mode 100644 Documentation/how-to-build-and-run-ilcompiler-in-vscode.md create mode 100755 src/ILCompiler/src/ilc.runtimeconfig.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..46d40ef7b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,27 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": ".NET Core Launch (console)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "", + "osx": { + "program": "${workspaceRoot}/bin/Product/OSX.x64.Debug/packaging/publish1/ilc.dll" + }, + "linux": { + "program": "${workspaceRoot}/bin/Product/Linux.x64.Debug/packaging/publish1/ilc.dll" + }, + "args": [], + "cwd": "${workspaceRoot}", + "stopAtEntry": false, + "externalConsole": false + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach", + "processId": "${command.pickProcess}" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000..3420cc476 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,18 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "0.1.0", + "command": "${workspaceRoot}/build.sh", + "isShellCommand": false, + "args": [], + "tasks": [ + { + "taskName": "build", + "args": [ ], + "isBuildCommand": true, + "showOutput": "silent", + "suppressTaskName" : true, + "problemMatcher": "$msCompile" + } + ] +} diff --git a/Documentation/README.md b/Documentation/README.md index bcca1faeb..1f099ff2c 100644 --- a/Documentation/README.md +++ b/Documentation/README.md @@ -12,5 +12,6 @@ This is the repo for CoreRT, the .NET Core runtime optimized for AOT (Ahead of T - [Prerequisites for building](prerequisites-for-building.md) - [How to build and run from the Command Line](how-to-build-and-run-ilcompiler-in-console-shell-prompt.md) - [How to build and run from Visual Studio](how-to-build-and-run-ilcompiler-in-visual-studio-2015.md) +- [How to build and run from VSCode](how-to-build-and-run-ilcompiler-in-vscode.md) - [How to run tests](how-to-run-tests.md) - [Cross Compilation for ARM on Linux](cross-building.md) diff --git a/Documentation/how-to-build-and-run-ilcompiler-in-vscode.md b/Documentation/how-to-build-and-run-ilcompiler-in-vscode.md new file mode 100644 index 000000000..df1f3fe9b --- /dev/null +++ b/Documentation/how-to-build-and-run-ilcompiler-in-vscode.md @@ -0,0 +1,54 @@ +_Please ensure that [pre-requisites](prerequisites-for-building.md) are installed for a successful build of the repo._ + +_Note_: + +* Instructions below assume ```~/corert``` is the repo root. + +# Setting up # + +Please make sure you have latest VS Code, C# extension, and .NET Core available. This guide is tested under C# 1.6.2 + VS Code 1.8.1 + CLI 1.0.0-preview4-004233. + +This guide assumes that your VS code workspace is set to the root of the repo. + +# Running VS Code + +We've checked-in reasonable default ```launch.json``` and ```tasks.json``` under ```corert/.vscode``` directory. You only need to run vscode form corert root: + +``` +code ~/corert +``` + +And then press SHIFT+COMMAND+B to start the build. + +# Debugging ILC.exe using .NET Core Debugger # + +Go to the debug pane and click Debug, choose .NET Core as the environment. If needed, you can change program property in launch.json (the gear button) to point to a different flavor of ilc: + +```json + "osx": { + "program": "${workspaceRoot}/bin/Product/OSX.x64.Debug/packaging/publish1/ilc.dll" + }, + "linux": { + "program": "${workspaceRoot}/bin/Product/Linux.x64.Debug/packaging/publish1/ilc.dll" + }, +``` + +By default we've disabled automatic build before debug. If you want to change that, you can change the ```preLaunchTask``` property to ```"build"```. But this is not currently recommended. + +# Getting ILC response files + +A ```.ilc.rsp``` file path can be easily obtained from a .NET core project that you want to debug by following command: + +``` +dotnet build /t:LinkNative /t:Rebuild /v:Detailed | grep ".ilc.rsp" +``` + +Once you have the ilc path, you can change ```launch.json``` accordingly: + +```json + "args": ["@obj/Debug/netcoreapp1.0/native/.ilc.rsp"], + "cwd": "", +``` + +* ```args``` - the argument to ILC +* ```cwd``` - the current directory where ILC is running. You can set it to the .NET Core project root. diff --git a/src/BuildIntegration/Microsoft.NETCore.Native.targets b/src/BuildIntegration/Microsoft.NETCore.Native.targets index 9d1325eff..c45f6636f 100644 --- a/src/BuildIntegration/Microsoft.NETCore.Native.targets +++ b/src/BuildIntegration/Microsoft.NETCore.Native.targets @@ -91,7 +91,7 @@ See the LICENSE file in the project root for more information. corerun - + diff --git a/src/ILCompiler/src/ILCompiler.csproj b/src/ILCompiler/src/ILCompiler.csproj index 1121a50ee..3ffc7651a 100644 --- a/src/ILCompiler/src/ILCompiler.csproj +++ b/src/ILCompiler/src/ILCompiler.csproj @@ -7,6 +7,10 @@ {DD5B6BAA-D41A-4A6E-9E7D-83060F394B10} Exe ILCompiler + + + + .dll ilc en-US 512 @@ -44,6 +48,10 @@ + + + PreserveNewest + diff --git a/src/ILCompiler/src/ilc.runtimeconfig.json b/src/ILCompiler/src/ilc.runtimeconfig.json new file mode 100755 index 000000000..d0437745b --- /dev/null +++ b/src/ILCompiler/src/ilc.runtimeconfig.json @@ -0,0 +1,8 @@ +{ + "runtimeOptions": { + "framework": { + "name": "Microsoft.NETCore.App", + "version": "1.0.1" + } + } +} \ No newline at end of file diff --git a/src/Test.CoreLib/readme.md b/src/Test.CoreLib/readme.md index d05eca4ff..878302f80 100644 --- a/src/Test.CoreLib/readme.md +++ b/src/Test.CoreLib/readme.md @@ -14,7 +14,7 @@ csc /noconfig /nostdlib Program.cs /r:\bin\Product\Windows_NT.x64.Deb 2. Compile the IL with ILC -Use ilc.exe that was built with the repo to compile the program. +Use ilc.dll that was built with the repo to compile the program. ``` ilc repro.exe -o:repro.obj -r:\bin\Product\Windows_NT.x64.Debug\Test.CoreLib\Test.CoreLib.dll --systemmodule Test.CoreLib diff --git a/src/packaging/packages.targets b/src/packaging/packages.targets index f362e52cd..00b505047 100644 --- a/src/packaging/packages.targets +++ b/src/packaging/packages.targets @@ -33,7 +33,7 @@ - + @@ -74,6 +74,12 @@ ]]> + + + + + ]]> + -- cgit v1.2.3 From e1f2b951e65c9639694ab1d8144ebe122729da77 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Fri, 20 Jan 2017 08:55:55 -0800 Subject: Make System.Tuple as serializable --- src/System.Private.CoreLib/src/System/Tuple.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/System.Private.CoreLib/src/System/Tuple.cs b/src/System.Private.CoreLib/src/System/Tuple.cs index e22c5aea7..d3b7d4f61 100644 --- a/src/System.Private.CoreLib/src/System/Tuple.cs +++ b/src/System.Private.CoreLib/src/System/Tuple.cs @@ -102,6 +102,7 @@ namespace System } } + [Serializable] public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITupleInternal, ITuple { private readonly T1 m_Item1; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. @@ -200,6 +201,7 @@ namespace System } } + [Serializable] public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITupleInternal, ITuple { private readonly T1 m_Item1; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. @@ -313,6 +315,7 @@ namespace System } } + [Serializable] public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITupleInternal, ITuple { private readonly T1 m_Item1; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. @@ -437,6 +440,7 @@ namespace System } } + [Serializable] public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITupleInternal, ITuple { private readonly T1 m_Item1; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. @@ -572,6 +576,7 @@ namespace System } } + [Serializable] public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITupleInternal, ITuple { private readonly T1 m_Item1; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. @@ -718,6 +723,7 @@ namespace System } } + [Serializable] public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITupleInternal, ITuple { private readonly T1 m_Item1; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. @@ -875,6 +881,7 @@ namespace System } } + [Serializable] public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITupleInternal, ITuple { private readonly T1 m_Item1; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. @@ -1043,6 +1050,7 @@ namespace System } } + [Serializable] public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITupleInternal, ITuple { private readonly T1 m_Item1; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. -- cgit v1.2.3 From 7648852bc304e7e8d862b49696d5eca048720892 Mon Sep 17 00:00:00 2001 From: Daniel Harvey Date: Fri, 20 Jan 2017 10:39:41 -0800 Subject: Fills in overrides for RuntimeAssembly. [tfs-changeset: 1645243] --- .../Runtime/Assemblies/RuntimeAssembly.cs | 30 ++++++++++++++++++++++ .../Runtime/General/NonOverriddenApis.cs | 1 + 2 files changed, 31 insertions(+) diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/RuntimeAssembly.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/RuntimeAssembly.cs index 40991424c..09b31f967 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/RuntimeAssembly.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/Assemblies/RuntimeAssembly.cs @@ -19,6 +19,7 @@ using Internal.Reflection.Core; using Internal.Reflection.Core.Execution; using Internal.Reflection.Tracing; +using System.Security; namespace System.Reflection.Runtime.Assemblies { @@ -181,6 +182,35 @@ namespace System.Reflection.Runtime.Assemblies } } + public sealed override bool GlobalAssemblyCache + { + get + { + return false; + } + } + + public sealed override long HostContext + { + get + { + return 0; + } + } + + public sealed override Module LoadModule(string moduleName, byte[] rawModule, byte[] rawSymbolStore) + { + throw new PlatformNotSupportedException(); + } + + public sealed override SecurityRuleSet SecurityRuleSet + { + get + { + return SecurityRuleSet.None; + } + } + private volatile CaseSensitiveTypeCache _lazyCaseSensitiveTypeTable; private sealed class CaseSensitiveTypeCache : ConcurrentUnifier diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/NonOverriddenApis.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/NonOverriddenApis.cs index a747fab31..fb38ee000 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/NonOverriddenApis.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/NonOverriddenApis.cs @@ -36,6 +36,7 @@ namespace System.Reflection.Runtime.Assemblies public sealed override Type GetType(string name, bool throwOnError) => base.GetType(name, throwOnError); public sealed override bool IsDynamic => base.IsDynamic; public sealed override string ToString() => base.ToString(); + public sealed override string EscapedCodeBase => base.EscapedCodeBase; #endif //DEBUG } } -- cgit v1.2.3 From f7061a317a0f4b206f76c034e30a28deddb64992 Mon Sep 17 00:00:00 2001 From: Simon Nattress Date: Thu, 19 Jan 2017 12:47:47 -0800 Subject: Run CoreCLR tests on Unix Add scripting for Linux / OSX to download the pre-compiled CoreCLR tests and run them. Top200.unix.txt contains the set of top 200 tests run on Windows that also pass on Unix systems. --- netci.groovy | 1 - tests/CoreCLR/Test.csproj | 2 +- tests/CoreCLR/corerun | 30 ++++ tests/CoreCLR/runtest/runtest.sh | 11 +- .../CoreCLR/runtest/testsFailingOutsideWindows.txt | 0 .../runtest/testsUnsupportedOutsideWindows.txt | 0 tests/Top200.unix.txt | 195 +++++++++++++++++++++ tests/runtest.sh | 89 ++++++++-- 8 files changed, 314 insertions(+), 14 deletions(-) create mode 100755 tests/CoreCLR/corerun mode change 100644 => 100755 tests/CoreCLR/runtest/runtest.sh create mode 100644 tests/CoreCLR/runtest/testsFailingOutsideWindows.txt create mode 100644 tests/CoreCLR/runtest/testsUnsupportedOutsideWindows.txt create mode 100644 tests/Top200.unix.txt diff --git a/netci.groovy b/netci.groovy index e6d1b990a..af7627fae 100644 --- a/netci.groovy +++ b/netci.groovy @@ -69,7 +69,6 @@ def osList = ['Ubuntu', 'OSX', 'Windows_NT'] if (configuration == 'Debug') { if (isPR) { - prJobDescription += " and CoreCLR tests" // Run a small set of BVTs during PR validation shell(testScriptString + "top200") } diff --git a/tests/CoreCLR/Test.csproj b/tests/CoreCLR/Test.csproj index 7d527b0f0..12021e49d 100644 --- a/tests/CoreCLR/Test.csproj +++ b/tests/CoreCLR/Test.csproj @@ -2,7 +2,7 @@ $(TestFileName) - .Exe + .exe Exe $(MSBuildProjectDirectory)\ $(MSBuildProjectDirectory)\ diff --git a/tests/CoreCLR/corerun b/tests/CoreCLR/corerun new file mode 100755 index 000000000..88a5eeb0c --- /dev/null +++ b/tests/CoreCLR/corerun @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +# This is the Unix equivalent of build-and-run-test.cmd +# It is invoked by each test's bash script. The reason it's called corerun is that +# the unix CoreCLR tests don't have a custom runner override environment variable. +# See issue https://github.com/dotnet/coreclr/issues/9007 + +export TestExecutable=$1 +export TestFileName=${TestExecutable%.*} + +cp $CoreRT_TestRoot/CoreCLR/Test.csproj . + +__msbuild_dir=${CoreRT_TestRoot}/../Tools +echo ${__msbuild_dir}/msbuild.sh /m /p:IlcPath=${CoreRT_ToolchainDir} /p:Configuration=${CoreRT_BuildType} Test.csproj +${__msbuild_dir}/msbuild.sh /m /p:IlcPath=${CoreRT_ToolchainDir} /p:Configuration=${CoreRT_BuildType} Test.csproj + +# Some tests (interop) have native artifacts they depend on. Copy all DLLs to be sure we have them. +cp *.dll native/ 2>/dev/null + +# Remove the test executable from the arg list so it isn't passed to test execution +shift + +native/${TestFileName} $* + +testScriptExitCode=$? + +# Clean up test binary artifacts to save space +rm -r native 2>/dev/null + +exit $testScriptExitCode diff --git a/tests/CoreCLR/runtest/runtest.sh b/tests/CoreCLR/runtest/runtest.sh old mode 100644 new mode 100755 index 0c1124f31..5b4072704 --- a/tests/CoreCLR/runtest/runtest.sh +++ b/tests/CoreCLR/runtest/runtest.sh @@ -792,6 +792,12 @@ function finish_remaining_tests { function prep_test { local scriptFilePath=$1 + # Skip any test that's not in the current playlist, if a playlist was + # given to us. + if [ -n "$playlistFile" ] && ! is_playlist_test "$scriptFilePath"; then + return + fi + test "$verbose" == 1 && echo "Preparing $scriptFilePath" if [ ! "$noLFConversion" == "ON" ]; then @@ -1178,7 +1184,10 @@ fi if [ "$ARCH" == "x64" ] then scriptPath=$(dirname $0) - ${scriptPath}/setup-runtime-dependencies.sh --outputDir=$coreOverlayDir + # Disabled for CoreRT + # This is how CoreCLR sets up GCStress. We will probably go the .NET Native route + # when we get to GC Stress though. + #${scriptPath}/setup-runtime-dependencies.sh --outputDir=$coreOverlayDir else echo "Skip preparing for GC stress test. Dependent package is not supported on this architecture." fi diff --git a/tests/CoreCLR/runtest/testsFailingOutsideWindows.txt b/tests/CoreCLR/runtest/testsFailingOutsideWindows.txt new file mode 100644 index 000000000..e69de29bb diff --git a/tests/CoreCLR/runtest/testsUnsupportedOutsideWindows.txt b/tests/CoreCLR/runtest/testsUnsupportedOutsideWindows.txt new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Top200.unix.txt b/tests/Top200.unix.txt new file mode 100644 index 000000000..9215d296b --- /dev/null +++ b/tests/Top200.unix.txt @@ -0,0 +1,195 @@ +GC/Features/PartialCompaction/eco1/eco1.sh +GC/Features/PartialCompaction/partialcompactiontest/partialcompactiontest.sh +GC/Features/PartialCompaction/partialcompactionwloh/partialcompactionwloh.sh +GC/Scenarios/Boxing/vararystress/vararystress.sh +GC/Scenarios/Boxing/variantint/variantint.sh +GC/Scenarios/Boxing/variantlinklist/variantlinklist.sh +GC/Scenarios/GCBase1/gc_base1/gc_base1.sh +GC/Scenarios/GCBase1/gc_base1_1/gc_base1_1.sh +GC/Scenarios/GCBench/gcbench/gcbench.sh +GC/Scenarios/GCSimulator/GCSimulator_287/GCSimulator_287.sh +GC/Scenarios/GCSimulator/GCSimulator_288/GCSimulator_288.sh +GC/Scenarios/GCSimulator/GCSimulator_289/GCSimulator_289.sh +GC/Scenarios/GCSimulator/GCSimulator_29/GCSimulator_29.sh +GC/Scenarios/GCSimulator/GCSimulator_290/GCSimulator_290.sh +GC/Scenarios/GCSimulator/GCSimulator_291/GCSimulator_291.sh +GC/Scenarios/GCSimulator/GCSimulator_292/GCSimulator_292.sh +GC/Scenarios/GCSimulator/GCSimulator_293/GCSimulator_293.sh +GC/Scenarios/GCSimulator/GCSimulator_294/GCSimulator_294.sh +GC/Scenarios/SingLinkList/singlinkstay/singlinkstay.sh +JIT/CodeGenBringUpTests/addref/addref.sh +JIT/CodeGenBringUpTests/And1/And1.sh +JIT/CodeGenBringUpTests/AndRef/AndRef.sh +JIT/CodeGenBringUpTests/Args4/Args4.sh +JIT/CodeGenBringUpTests/Args5/Args5.sh +JIT/CodeGenBringUpTests/AsgAdd1/AsgAdd1.sh +JIT/CodeGenBringUpTests/AsgAnd1/AsgAnd1.sh +JIT/CodeGenBringUpTests/AsgOr1/AsgOr1.sh +JIT/CodeGenBringUpTests/AsgSub1/AsgSub1.sh +JIT/CodeGenBringUpTests/AsgXor1/AsgXor1.sh +JIT/CodeGenBringUpTests/BinaryRMW/BinaryRMW.sh +JIT/CodeGenBringUpTests/Call1/Call1.sh +JIT/CodeGenBringUpTests/CnsBool/CnsBool.sh +JIT/CodeGenBringUpTests/CnsLng1/CnsLng1.sh +JIT/CodeGenBringUpTests/DblAdd/DblAdd.sh +JIT/CodeGenBringUpTests/DblAddConst/DblAddConst.sh +JIT/CodeGenBringUpTests/DblArea/DblArea.sh +JIT/CodeGenBringUpTests/DblArray/DblArray.sh +JIT/CodeGenBringUpTests/DblAvg2/DblAvg2.sh +JIT/CodeGenBringUpTests/DblAvg6/DblAvg6.sh +JIT/CodeGenBringUpTests/DblCall1/DblCall1.sh +JIT/CodeGenBringUpTests/DblCall2/DblCall2.sh +JIT/CodeGenBringUpTests/DblDist/DblDist.sh +JIT/CodeGenBringUpTests/DblDiv/DblDiv.sh +JIT/CodeGenBringUpTests/DblDivConst/DblDivConst.sh +JIT/CodeGenBringUpTests/DblFillArray/DblFillArray.sh +JIT/CodeGenBringUpTests/DblMul/DblMul.sh +JIT/CodeGenBringUpTests/DblMulConst/DblMulConst.sh +JIT/CodeGenBringUpTests/DblNeg/DblNeg.sh +JIT/CodeGenBringUpTests/DblRem/DblRem.sh +JIT/CodeGenBringUpTests/DblRoots/DblRoots.sh +JIT/CodeGenBringUpTests/DblSub/DblSub.sh +JIT/CodeGenBringUpTests/DblSubConst/DblSubConst.sh +JIT/CodeGenBringUpTests/DblVar/DblVar.sh +JIT/CodeGenBringUpTests/div1/div1.sh +JIT/CodeGenBringUpTests/divref/divref.sh +JIT/CodeGenBringUpTests/Eq1/Eq1.sh +JIT/CodeGenBringUpTests/FactorialRec/FactorialRec.sh +JIT/CodeGenBringUpTests/FibLoop/FibLoop.sh +JIT/CodeGenBringUpTests/FiboRec/FiboRec.sh +JIT/CodeGenBringUpTests/FPAdd/FPAdd.sh +JIT/CodeGenBringUpTests/FPAddConst/FPAddConst.sh +JIT/CodeGenBringUpTests/FPArea/FPArea.sh +JIT/CodeGenBringUpTests/FPArray/FPArray.sh +JIT/CodeGenBringUpTests/FPAvg2/FPAvg2.sh +JIT/CodeGenBringUpTests/FPAvg6/FPAvg6.sh +JIT/CodeGenBringUpTests/FPCall1/FPCall1.sh +JIT/CodeGenBringUpTests/FPCall2/FPCall2.sh +JIT/CodeGenBringUpTests/FPConvDbl2Lng/FPConvDbl2Lng.sh +JIT/CodeGenBringUpTests/FPConvF2F/FPConvF2F.sh +JIT/CodeGenBringUpTests/FPConvF2I/FPConvF2I.sh +JIT/CodeGenBringUpTests/FPConvF2Lng/FPConvF2Lng.sh +JIT/CodeGenBringUpTests/FPConvI2F/FPConvI2F.sh +JIT/CodeGenBringUpTests/FPDist/FPDist.sh +JIT/CodeGenBringUpTests/FPDiv/FPDiv.sh +JIT/CodeGenBringUpTests/FPDivConst/FPDivConst.sh +JIT/CodeGenBringUpTests/FPError/FPError.sh +JIT/CodeGenBringUpTests/FPFillArray/FPFillArray.sh +JIT/CodeGenBringUpTests/FPMath/FPMath.sh +JIT/CodeGenBringUpTests/FPMul/FPMul.sh +JIT/CodeGenBringUpTests/FPMulConst/FPMulConst.sh +JIT/CodeGenBringUpTests/FPNeg/FPNeg.sh +JIT/CodeGenBringUpTests/FPRem/FPRem.sh +JIT/CodeGenBringUpTests/FPRoots/FPRoots.sh +JIT/CodeGenBringUpTests/FPSmall/FPSmall.sh +JIT/CodeGenBringUpTests/FPSub/FPSub.sh +JIT/CodeGenBringUpTests/FPSubConst/FPSubConst.sh +JIT/CodeGenBringUpTests/FPVar/FPVar.sh +JIT/CodeGenBringUpTests/Gcd/Gcd.sh +JIT/CodeGenBringUpTests/Ge1/Ge1.sh +JIT/CodeGenBringUpTests/Gt1/Gt1.sh +JIT/CodeGenBringUpTests/Ind1/Ind1.sh +JIT/CodeGenBringUpTests/InitObj/InitObj.sh +JIT/CodeGenBringUpTests/InstanceCalls/InstanceCalls.sh +JIT/CodeGenBringUpTests/IntArraySum/IntArraySum.sh +JIT/CodeGenBringUpTests/IntConv/IntConv.sh +JIT/CodeGenBringUpTests/Jmp1/Jmp1.sh +JIT/CodeGenBringUpTests/JTrue1/JTrue1.sh +JIT/CodeGenBringUpTests/JTrueEqDbl/JTrueEqDbl.sh +JIT/CodeGenBringUpTests/JTrueEqFP/JTrueEqFP.sh +JIT/CodeGenBringUpTests/JTrueEqInt1/JTrueEqInt1.sh +JIT/CodeGenBringUpTests/JTrueGeDbl/JTrueGeDbl.sh +JIT/CodeGenBringUpTests/JTrueGeFP/JTrueGeFP.sh +JIT/CodeGenBringUpTests/JTrueGeInt1/JTrueGeInt1.sh +JIT/CodeGenBringUpTests/JTrueGtDbl/JTrueGtDbl.sh +JIT/CodeGenBringUpTests/JTrueGtFP/JTrueGtFP.sh +JIT/CodeGenBringUpTests/JTrueGtInt1/JTrueGtInt1.sh +JIT/CodeGenBringUpTests/JTrueLeDbl/JTrueLeDbl.sh +JIT/CodeGenBringUpTests/JTrueLeFP/JTrueLeFP.sh +JIT/CodeGenBringUpTests/JTrueLeInt1/JTrueLeInt1.sh +JIT/CodeGenBringUpTests/JTrueLtDbl/JTrueLtDbl.sh +JIT/CodeGenBringUpTests/JTrueLtFP/JTrueLtFP.sh +JIT/CodeGenBringUpTests/JTrueLtInt1/JTrueLtInt1.sh +JIT/CodeGenBringUpTests/JTrueNeDbl/JTrueNeDbl.sh +JIT/CodeGenBringUpTests/JTrueNeFP/JTrueNeFP.sh +JIT/CodeGenBringUpTests/JTrueNeInt1/JTrueNeInt1.sh +JIT/CodeGenBringUpTests/Le1/Le1.sh +JIT/CodeGenBringUpTests/LeftShift/LeftShift.sh +JIT/CodeGenBringUpTests/LngConv/LngConv.sh +JIT/CodeGenBringUpTests/LongArgsAndReturn/LongArgsAndReturn.sh +JIT/CodeGenBringUpTests/Lt1/Lt1.sh +JIT/CodeGenBringUpTests/mul1/mul1.sh +JIT/CodeGenBringUpTests/mul2/mul2.sh +JIT/CodeGenBringUpTests/mul3/mul3.sh +JIT/CodeGenBringUpTests/mul4/mul4.sh +JIT/CodeGenBringUpTests/Ne1/Ne1.sh +JIT/CodeGenBringUpTests/NegRMW/NegRMW.sh +JIT/CodeGenBringUpTests/NestedCall/NestedCall.sh +JIT/CodeGenBringUpTests/NotAndNeg/NotAndNeg.sh +JIT/CodeGenBringUpTests/NotRMW/NotRMW.sh +JIT/CodeGenBringUpTests/ObjAlloc/ObjAlloc.sh +JIT/CodeGenBringUpTests/OpMembersOfStructLocal/OpMembersOfStructLocal.sh +JIT/CodeGenBringUpTests/Or1/Or1.sh +JIT/CodeGenBringUpTests/OrRef/OrRef.sh +JIT/CodeGenBringUpTests/rem1/rem1.sh +JIT/CodeGenBringUpTests/RightShiftRef/RightShiftRef.sh +JIT/CodeGenBringUpTests/Rotate/Rotate.sh +JIT/CodeGenBringUpTests/StaticCalls/StaticCalls.sh +JIT/CodeGenBringUpTests/StaticValueField/StaticValueField.sh +JIT/CodeGenBringUpTests/struct16args/struct16args.sh +JIT/CodeGenBringUpTests/StructFldAddr/StructFldAddr.sh +JIT/CodeGenBringUpTests/StructInstMethod/StructInstMethod.sh +JIT/CodeGenBringUpTests/Sub1/Sub1.sh +JIT/CodeGenBringUpTests/SubRef/SubRef.sh +JIT/CodeGenBringUpTests/Swap/Swap.sh +JIT/CodeGenBringUpTests/Switch/Switch.sh +JIT/CodeGenBringUpTests/Unbox/Unbox.sh +JIT/CodeGenBringUpTests/Xor1/Xor1.sh +JIT/CodeGenBringUpTests/XorRef/XorRef.sh +JIT/Directed/ExcepFilters/excepobj/excepobj/excepobj.sh +JIT/Directed/ExcepFilters/fault/fault/fault.sh +JIT/Directed/ExcepFilters/mixed/mixed/mixed.sh +JIT/Directed/nullabletypes/isinst_d/isinst_d.sh +JIT/Directed/nullabletypes/isinst_do/isinst_do.sh +Loader/classloader/generics/Layout/General/Base01a_auto/Base01a_auto.sh +Loader/classloader/generics/Layout/General/Base01a_auto_ser/Base01a_auto_ser.sh +Loader/classloader/generics/Layout/General/Base01a_seq/Base01a_seq.sh +Loader/classloader/generics/Layout/General/struct01_auto/struct01_auto.sh +Loader/classloader/generics/Layout/Specific/Positive007/Positive007.sh +Loader/classloader/generics/Layout/Specific/Positive008/Positive008.sh +Loader/classloader/generics/Layout/Specific/Positive009/Positive009.sh +Loader/classloader/generics/Layout/Specific/Positive010/Positive010.sh +Loader/classloader/generics/Misc/ConstraintsAndInheritance/ConstraintsAndInheritance.sh +Loader/classloader/generics/Misc/TestWithManyParams/TestWithManyParams.sh +Loader/classloader/generics/Statics/Regressions/524571/StaticsProblem1/StaticsProblem1.sh +Loader/classloader/generics/Statics/Regressions/524571/StaticsProblem2/StaticsProblem2.sh +Loader/classloader/generics/Statics/Regressions/524571/StaticsProblem3/StaticsProblem3.sh +Loader/classloader/InterfaceFolding/TestCase0/TestCase0.sh +Loader/classloader/InterfaceFolding/TestCase0_Nested_I/TestCase0_Nested_I.sh +Loader/classloader/InterfaceFolding/TestCase0_Nested_I_Nested_J/TestCase0_Nested_I_Nested_J.sh +Loader/classloader/InterfaceFolding/TestCase0_Nested_J/TestCase0_Nested_J.sh +Loader/classloader/InterfaceFolding/TestCase0_Nested_J_Nested_I/TestCase0_Nested_J_Nested_I.sh +Loader/classloader/InterfaceFolding/TestCase1/TestCase1.sh +Loader/classloader/InterfaceFolding/TestCase1_Nested_I/TestCase1_Nested_I.sh +Loader/classloader/InterfaceFolding/TestCase1_Nested_I_Nested_J/TestCase1_Nested_I_Nested_J.sh +Loader/classloader/InterfaceFolding/TestCase1_Nested_J/TestCase1_Nested_J.sh +Loader/classloader/InterfaceFolding/TestCase2/TestCase2.sh +Loader/classloader/InterfaceFolding/TestCase2_Nested_I/TestCase2_Nested_I.sh +Loader/classloader/InterfaceFolding/TestCase2_Nested_I_Nested_J/TestCase2_Nested_I_Nested_J.sh +Loader/classloader/InterfaceFolding/TestCase2_Nested_J/TestCase2_Nested_J.sh +Loader/classloader/InterfaceFolding/TestCase2_Nested_J_Nested_I/TestCase2_Nested_J_Nested_I.sh +Loader/classloader/regressions/dev10_568786/4_Misc/ConstrainedMethods/ConstrainedMethods.sh +Loader/classloader/regressions/dev10_568786/4_Misc/RecursiveGen/RecursiveGen.sh +Loader/classloader/TSAmbiguities/Variance/Covariant_CollapsedInterfaces/HelloWorld/HelloWorld.sh +Loader/classloader/TSAmbiguities/Variance/Covariant_InherittedCollision/HelloWorld/HelloWorld.sh +Loader/classloader/TSAmbiguities/Variance/Variant_CollapsedInterfaces/HelloWorld/HelloWorld.sh +Loader/classloader/TSAmbiguities/Variance/Variant_InherittedCollision/HelloWorld/HelloWorld.sh +Loader/classloader/v1/Beta1/Layout/Matrix/cs/L-1-2-1/L-1-2-1.sh +Regressions/expl_double/expl_double/expl_double.sh +JIT/CodeGenBringUpTests/Localloc/Localloc.sh +JIT/CodeGenBringUpTests/UDivConst/UDivConst.sh +JIT/CodeGenBringUpTests/UModConst/UModConst.sh +JIT/Directed/FaultHandlers/Nesting/Nesting/Nesting.sh +Loader/classloader/TypeInitialization/CctorsWithSideEffects/CctorThrowStaticField/CctorThrowStaticField.sh +JIT/jit64/localloc/eh/eh01_dynamic/eh01_dynamic.sh +JIT/Regression/JitBlue/DevDiv_142976/DevDiv_142976/DevDiv_142976.sh \ No newline at end of file diff --git a/tests/runtest.sh b/tests/runtest.sh index 2593515fb..ce7e7ab4a 100755 --- a/tests/runtest.sh +++ b/tests/runtest.sh @@ -2,11 +2,15 @@ usage() { - echo "Usage: $0 [OS] [arch] [flavor] [-extrepo] [-buildextrepo] [-mode] [-runtest]" + echo "Usage: $0 [OS] [arch] [flavor] [-mode] [-runtest] [-coreclr ]" echo " -mode : Compilation mode. Specify cpp/ryujit. Default: ryujit" echo " -runtest : Should just compile or run compiled binary? Specify: true/false. Default: true." - echo " -extrepo : Path to external repo, currently supports: GitHub: dotnet/coreclr. Specify full path. If unspecified, runs corert tests" - echo " -buildextrepo : Should build at root level of external repo? Specify: true/false. Default: true" + echo " -coreclr : Download and run the CoreCLR repo tests" + echo "" + echo " --- CoreCLR Subset ---" + echo " top200 : Runs broad coverage / CI validation (~200 tests)." + echo " knowngood : Runs tests known to pass on CoreRT (~6000 tests)." + echo " all : Runs all tests. There will be many failures (~7000 tests)." exit 1 } @@ -55,14 +59,69 @@ run_test_dir() return $? } +restore_coreclr_tests() +{ + CoreRT_Test_Download_Semaphore=${CoreRT_TestExtRepo}/init-tests.completed + + if [ -e ${CoreRT_Test_Download_Semaphore} ]; then + echo "Tests are already initialized." + return 0 + fi + + if [ -d ${CoreRT_TestExtRepo} ]; then + rm -r ${CoreRT_TestExtRepo} + fi + mkdir -p ${CoreRT_TestExtRepo} + + echo "Restoring tests (this may take a few minutes).." + TESTS_REMOTE_URL=$(<${CoreRT_TestRoot}/../CoreCLRTestsURL.txt) + TESTS_LOCAL_ZIP=${CoreRT_TestExtRepo}/tests.zip + curl --retry 10 --retry-delay 5 -sSL -o ${TESTS_LOCAL_ZIP} ${TESTS_REMOTE_URL} + + unzip -q ${TESTS_LOCAL_ZIP} -d ${CoreRT_TestExtRepo} + + echo "CoreCLR tests restored from ${TESTS_REMOTE_URL}" >> ${CoreRT_Test_Download_Semaphore} +} + +run_coreclr_tests() +{ + if [ -z ${CoreRT_TestExtRepo} ]; then + CoreRT_TestExtRepo=${CoreRT_TestRoot}/../tests_downloaded/CoreCLR + fi + + restore_coreclr_tests + + if [ ! -d ${CoreRT_TestExtRepo} ]; then + echo "Error: ${CoreRT_TestExtRepo} does not exist." + exit -1 + fi + + XunitTestBinBase=${CoreRT_TestExtRepo} + CORE_ROOT=${CoreRT_TestRoot}/CoreCLR/runtest + pushd ${CoreRT_TestRoot}/CoreCLR/runtest + + export CoreRT_TestRoot + + CoreRT_TestSelectionArg= + if [ "$SelectedTests" = "top200" ]; then + CoreRT_TestSelectionArg="--playlist=${CoreRT_TestRoot}/Top200.unix.txt" + elif [ "$SelectedTests" = "knowngood" ]; then + # Todo: Build the list of tests that pass + CoreRT_TestSelectionArg= + elif [ "$SelectedTests" = "all" ]; then + CoreRT_TestSelectionArg= + fi + + echo ./runtest.sh --testRootDir=${CoreRT_TestExtRepo} --coreOverlayDir=${CoreRT_TestRoot}/CoreCLR ${CoreRT_TestSelectionArg} + ./runtest.sh --testRootDir=${CoreRT_TestExtRepo} --coreOverlayDir=${CoreRT_TestRoot}/CoreCLR ${CoreRT_TestSelectionArg} +} + CoreRT_TestRoot="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" CoreRT_CliBinDir=${CoreRT_TestRoot}/../Tools/dotnetcli CoreRT_BuildArch=x64 CoreRT_BuildType=Debug CoreRT_TestRun=true CoreRT_TestCompileMode=ryujit -CoreRT_TestExtRepo= -CoreRT_BuildExtRepo= while [ "$1" != "" ]; do lowerI="$(echo $1 | awk '{print tolower($0)}')" @@ -89,10 +148,6 @@ while [ "$1" != "" ]; do release) CoreRT_BuildType=Release ;; - -extrepo) - shift - CoreRT_TestExtRepo=$1 - ;; -mode) shift CoreRT_TestCompileMode=$1 @@ -106,9 +161,16 @@ while [ "$1" != "" ]; do CoreRT_CliBinDir=$1 ;; -coreclr) + CoreRT_RunCoreCLRTests=true; shift - echo "Tests will run here. For now, early out." - exit 0 + SelectedTests=$1 + + if [ -z ${SelectedTests} ]; then + SelectedTests=top200 + elif [ "${SelectedTests}" != "all" ] && [ "${SelectedTests}" != "top200" ] && [ "${SelectedTests}" != "knowngood" ]; then + echo "Error: Invalid CoreCLR test selection." + exit -1 + fi ;; *) ;; @@ -118,6 +180,11 @@ done source "$CoreRT_TestRoot/testenv.sh" +if [ ${CoreRT_RunCoreCLRTests} ]; then + run_coreclr_tests + exit $? +fi + __BuildStr=${CoreRT_BuildOS}.${CoreRT_BuildArch}.${CoreRT_BuildType} __CoreRTTestBinDir=${CoreRT_TestRoot}/../bin/tests __LogDir=${CoreRT_TestRoot}/../bin/Logs/${__BuildStr}/tests -- cgit v1.2.3 From 14e3ad3e69a77f925fd4d40b52be42c622eabcf3 Mon Sep 17 00:00:00 2001 From: Simon Nattress Date: Fri, 20 Jan 2017 13:44:53 -0800 Subject: Emit correct log file name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename test results log to `testResults.xml` to be picked up by CI. Place log file under bin/Logs so it’s in a standard place for this repo. --- tests/CoreCLR/runtest/runtest.sh | 10 +++++++++- tests/runtest.sh | 18 +++++++++++------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/tests/CoreCLR/runtest/runtest.sh b/tests/CoreCLR/runtest/runtest.sh index 5b4072704..6e227ee42 100755 --- a/tests/CoreCLR/runtest/runtest.sh +++ b/tests/CoreCLR/runtest/runtest.sh @@ -61,6 +61,7 @@ function print_usage { echo ' --build-overlay-only : Exit after overlay directory is populated' echo ' --limitedDumpGeneration : Enables the generation of a limited number of core dumps if test(s) crash, even if ulimit' echo ' is zero when launching this script. This option is intended for use in CI.' + echo ' --logdir= : Specifies a folder to emit logs to. Default is test root folder.' echo '' echo 'Runtime Code Coverage options:' echo ' --coreclr-coverage : Optional argument to get coreclr code coverage reports' @@ -126,7 +127,7 @@ fi find . -type f -name "local_dumplings.txt" -exec rm {} \; function xunit_output_begin { - xunitOutputPath=$testRootDir/coreclrtests.xml + xunitOutputPath=$__LogDir/testResults.xml xunitTestOutputPath=${xunitOutputPath}.test if [ -e "$xunitOutputPath" ]; then rm -f -r "$xunitOutputPath" @@ -1082,6 +1083,9 @@ do --limitedDumpGeneration) limitedCoreDumps=ON ;; + --logdir=*) + __LogDir=${i#*=} + ;; *) echo "Unknown switch: $i" print_usage @@ -1111,6 +1115,10 @@ if [ ! -d "$testRootDir" ]; then exit $EXIT_CODE_EXCEPTION fi +if [ -z "$__LogDir" ]; then + __LogDir=$testRootDir +fi + # Copy native interop test libraries over to the mscorlib path in # order for interop tests to run on linux. if [ -z "$mscorlibDir" ]; then diff --git a/tests/runtest.sh b/tests/runtest.sh index ce7e7ab4a..09a44946f 100755 --- a/tests/runtest.sh +++ b/tests/runtest.sh @@ -112,8 +112,8 @@ run_coreclr_tests() CoreRT_TestSelectionArg= fi - echo ./runtest.sh --testRootDir=${CoreRT_TestExtRepo} --coreOverlayDir=${CoreRT_TestRoot}/CoreCLR ${CoreRT_TestSelectionArg} - ./runtest.sh --testRootDir=${CoreRT_TestExtRepo} --coreOverlayDir=${CoreRT_TestRoot}/CoreCLR ${CoreRT_TestSelectionArg} + echo ./runtest.sh --testRootDir=${CoreRT_TestExtRepo} --coreOverlayDir=${CoreRT_TestRoot}/CoreCLR ${CoreRT_TestSelectionArg} --logdir=$__LogDir + ./runtest.sh --testRootDir=${CoreRT_TestExtRepo} --coreOverlayDir=${CoreRT_TestRoot}/CoreCLR ${CoreRT_TestSelectionArg} --logdir=$__LogDir } CoreRT_TestRoot="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -180,21 +180,25 @@ done source "$CoreRT_TestRoot/testenv.sh" -if [ ${CoreRT_RunCoreCLRTests} ]; then - run_coreclr_tests - exit $? -fi - __BuildStr=${CoreRT_BuildOS}.${CoreRT_BuildArch}.${CoreRT_BuildType} __CoreRTTestBinDir=${CoreRT_TestRoot}/../bin/tests __LogDir=${CoreRT_TestRoot}/../bin/Logs/${__BuildStr}/tests __build_os_lowcase=$(echo "${CoreRT_BuildOS}" | tr '[:upper:]' '[:lower:]') +if [ ! -d $__LogDir ]; then + mkdir -p $__LogDir +fi + if [ ! -d ${CoreRT_ToolchainDir} ]; then echo "Toolchain not found in ${CoreRT_ToolchainDir}" exit -1 fi +if [ ${CoreRT_RunCoreCLRTests} ]; then + run_coreclr_tests + exit $? +fi + __CppTotalTests=0 __CppPassedTests=0 __JitTotalTests=0 -- cgit v1.2.3 From 9428dfa773914adac20bd2c7c59de2f3e32349ad Mon Sep 17 00:00:00 2001 From: Faizur Rahman Date: Fri, 20 Jan 2017 13:28:05 -0800 Subject: Fix PInvoke CPPCodegen This change contains the following: 1. Fixed out SafeHandleMarshaller with CPP codegen as Sedar pointed out. 2. Also enable PInvoke test with CPP Codegen. The generic method ThrowIfNotEquals doesn't work with CPPCodegen. So temporary created overrides to address this. --- src/Common/src/TypeSystem/Interop/IL/Marshaller.cs | 6 +++--- .../src/CppCodeGen/ILToCppImporter.cs | 2 +- tests/src/Simple/PInvoke/PInvoke.cs | 17 +++++++++++------ tests/src/Simple/PInvoke/no_cpp | 1 - 4 files changed, 15 insertions(+), 11 deletions(-) delete mode 100644 tests/src/Simple/PInvoke/no_cpp diff --git a/src/Common/src/TypeSystem/Interop/IL/Marshaller.cs b/src/Common/src/TypeSystem/Interop/IL/Marshaller.cs index 7b6f5cb7d..7701bbbbb 100644 --- a/src/Common/src/TypeSystem/Interop/IL/Marshaller.cs +++ b/src/Common/src/TypeSystem/Interop/IL/Marshaller.cs @@ -406,14 +406,14 @@ namespace Internal.TypeSystem.Interop TypeDesc resolvedType = ((ByRefType)managedType).ParameterType; var nativeType = PInvokeMethodData.Context.GetWellKnownType(WellKnownType.IntPtr).MakeByRefType(); - var vPinnedOutValue = emitter.NewLocal(nativeType, true); + var vOutValue = emitter.NewLocal(PInvokeMethodData.Context.GetWellKnownType(WellKnownType.IntPtr)); var vSafeHandle = emitter.NewLocal(resolvedType); marshallingCodeStream.Emit(ILOpcode.newobj, emitter.NewToken(resolvedType.GetDefaultConstructor())); marshallingCodeStream.EmitStLoc(vSafeHandle); - marshallingCodeStream.EmitLdLoca(vPinnedOutValue); + marshallingCodeStream.EmitLdLoca(vOutValue); unmarshallingCodeStream.EmitLdLoc(vSafeHandle); - unmarshallingCodeStream.EmitLdLoc(vPinnedOutValue); + unmarshallingCodeStream.EmitLdLoc(vOutValue); unmarshallingCodeStream.Emit(ILOpcode.call, emitter.NewToken( PInvokeMethodData.SafeHandleType.GetKnownMethod("SetHandle", null))); diff --git a/src/ILCompiler.Compiler/src/CppCodeGen/ILToCppImporter.cs b/src/ILCompiler.Compiler/src/CppCodeGen/ILToCppImporter.cs index 7aa132b61..b2b5d0bda 100644 --- a/src/ILCompiler.Compiler/src/CppCodeGen/ILToCppImporter.cs +++ b/src/ILCompiler.Compiler/src/CppCodeGen/ILToCppImporter.cs @@ -292,7 +292,7 @@ namespace Internal.IL private void AppendCastIfNecessary(TypeDesc destType, StackEntry srcEntry) { ConstantEntry constant = srcEntry as ConstantEntry; - if ((constant != null) && (constant.IsCastNecessary(destType)) || !destType.IsValueType) + if ((constant != null) && (constant.IsCastNecessary(destType)) || !destType.IsValueType || destType != srcEntry.Type) { Append("("); Append(GetSignatureTypeNameAndAddReference(destType)); diff --git a/tests/src/Simple/PInvoke/PInvoke.cs b/tests/src/Simple/PInvoke/PInvoke.cs index ed7f53fb1..2804cd614 100644 --- a/tests/src/Simple/PInvoke/PInvoke.cs +++ b/tests/src/Simple/PInvoke/PInvoke.cs @@ -8,7 +8,7 @@ using System.Text; // Name of namespace matches the name of the assembly on purpose to // ensure that we can handle this (mostly an issue for C++ code generation). -namespace PInvoke +namespace PInvokeTests { internal class Program { @@ -54,9 +54,9 @@ namespace PInvoke return 100; } - public static void ThrowIfNotEquals(T expected, T actual, string message) + public static void ThrowIfNotEquals(int expected, int actual, string message) { - if (!Object.Equals(expected, actual)) + if (expected != actual) { message += "\nExpected: " + expected + "\n"; message += "Actual: " + actual + "\n"; @@ -64,6 +64,11 @@ namespace PInvoke } } + public static void ThrowIfNotEquals(bool expected, bool actual, string message) + { + ThrowIfNotEquals(expected ? 1 : 0, actual ? 1 : 0, message); + } + private static void TestBlittableType() { Console.WriteLine("Testing marshalling blittable types"); @@ -123,9 +128,9 @@ namespace PInvoke Console.WriteLine("Testing marshalling out SafeHandle"); SafeMemoryHandle hnd2; - val = SafeHandleOutTest(out hnd2); - int val2 = unchecked((int)hnd2.DangerousGetHandle().ToInt64()); - ThrowIfNotEquals(val, val2, "SafeHandle out marshalling failed"); + int actual = SafeHandleOutTest(out hnd2); + int expected = unchecked((int)hnd2.DangerousGetHandle().ToInt64()); + ThrowIfNotEquals(actual, expected, "SafeHandle out marshalling failed"); } } diff --git a/tests/src/Simple/PInvoke/no_cpp b/tests/src/Simple/PInvoke/no_cpp deleted file mode 100644 index 639bcaf8b..000000000 --- a/tests/src/Simple/PInvoke/no_cpp +++ /dev/null @@ -1 +0,0 @@ -Skip this test for cpp codegen mode -- cgit v1.2.3 From 354d0fae2a78ac8b183f8ac4662618918e8d57a3 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Fri, 20 Jan 2017 23:02:40 +0100 Subject: Fix build of native components on Alpine (#2550) This tiny change enables successful build of the native components on x64 Alpine Linux. --- src/Native/System.Private.CoreLib.Native/pal_datetime.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Native/System.Private.CoreLib.Native/pal_datetime.cpp b/src/Native/System.Private.CoreLib.Native/pal_datetime.cpp index e38f40321..19a97e5fd 100644 --- a/src/Native/System.Private.CoreLib.Native/pal_datetime.cpp +++ b/src/Native/System.Private.CoreLib.Native/pal_datetime.cpp @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. #include +#include #include static const int64_t SECS_TO_100NS = 10000000; /* 10^7 */ -- cgit v1.2.3 From 08b78f8c9c8195637ae16a41ec96343f7092d90b Mon Sep 17 00:00:00 2001 From: dotnet-bot Date: Fri, 20 Jan 2017 17:23:21 -0800 Subject: Add sources for Dictionary, BinaryReader and MemoryStream from corefx --- .../src/System/Collections/Generic/Dictionary.cs | 1382 ++++++++++++++++++++ .../Collections/Generic/IDictionaryDebugView.cs | 80 ++ .../src/System/IO/BinaryReader.cs | 681 ++++++++++ .../src/System/IO/MemoryStream.cs | 802 ++++++++++++ 4 files changed, 2945 insertions(+) create mode 100644 src/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs create mode 100644 src/System.Private.CoreLib/src/System/Collections/Generic/IDictionaryDebugView.cs create mode 100644 src/System.Private.CoreLib/src/System/IO/BinaryReader.cs create mode 100644 src/System.Private.CoreLib/src/System/IO/MemoryStream.cs diff --git a/src/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs b/src/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs new file mode 100644 index 000000000..8dd5d845b --- /dev/null +++ b/src/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs @@ -0,0 +1,1382 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; + +namespace System.Collections.Generic +{ + [DebuggerTypeProxy(typeof(IDictionaryDebugView<,>))] + [DebuggerDisplay("Count = {Count}")] + [Serializable] + public class Dictionary : IDictionary, IDictionary, IReadOnlyDictionary, ISerializable, IDeserializationCallback + { + private struct Entry + { + public int hashCode; // Lower 31 bits of hash code, -1 if unused + public int next; // Index of next entry, -1 if last + public TKey key; // Key of entry + public TValue value; // Value of entry + } + + private int[] buckets; + private Entry[] entries; + private int count; + private int version; + + private int freeList; + private int freeCount; + private IEqualityComparer comparer; + private KeyCollection keys; + private ValueCollection values; + private object _syncRoot; + + // constants for serialization + private const string VersionName = "Version"; + private const string HashSizeName = "HashSize"; // Must save buckets.Length + private const string KeyValuePairsName = "KeyValuePairs"; + private const string ComparerName = "Comparer"; + + public Dictionary() : this(0, null) { } + + public Dictionary(int capacity) : this(capacity, null) { } + + public Dictionary(IEqualityComparer comparer) : this(0, comparer) { } + + public Dictionary(int capacity, IEqualityComparer comparer) + { + if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity), capacity, SR.ArgumentOutOfRange_NeedNonNegNum); + if (capacity > 0) Initialize(capacity); + this.comparer = comparer ?? EqualityComparer.Default; + } + + public Dictionary(IDictionary dictionary) : this(dictionary, null) { } + + public Dictionary(IDictionary dictionary, IEqualityComparer comparer) : + this(dictionary != null ? dictionary.Count : 0, comparer) + { + if (dictionary == null) + { + throw new ArgumentNullException(nameof(dictionary)); + } + + // It is likely that the passed-in dictionary is Dictionary. When this is the case, + // avoid the enumerator allocation and overhead by looping through the entries array directly. + // We only do this when dictionary is Dictionary and not a subclass, to maintain + // back-compat with subclasses that may have overridden the enumerator behavior. + if (dictionary.GetType() == typeof(Dictionary)) + { + Dictionary d = (Dictionary)dictionary; + int count = d.count; + Entry[] entries = d.entries; + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0) + { + Add(entries[i].key, entries[i].value); + } + } + return; + } + + foreach (KeyValuePair pair in dictionary) + { + Add(pair.Key, pair.Value); + } + } + + public Dictionary(IEnumerable> collection) : this(collection, null) { } + + public Dictionary(IEnumerable> collection, IEqualityComparer comparer) : + this((collection as ICollection>)?.Count ?? 0, comparer) + { + if (collection == null) + { + throw new ArgumentNullException(nameof(collection)); + } + + foreach (KeyValuePair pair in collection) + { + Add(pair.Key, pair.Value); + } + } + + protected Dictionary(SerializationInfo info, StreamingContext context) + { + // We can't do anything with the keys and values until the entire graph has been deserialized + // and we have a resonable estimate that GetHashCode is not going to fail. For the time being, + // we'll just cache this. The graph is not valid until OnDeserialization has been called. + HashHelpers.SerializationInfoTable.Add(this, info); + } + + public IEqualityComparer Comparer + { + get + { + return comparer; + } + } + + public int Count + { + get { return count - freeCount; } + } + + public KeyCollection Keys + { + get + { + Contract.Ensures(Contract.Result() != null); + if (keys == null) keys = new KeyCollection(this); + return keys; + } + } + + ICollection IDictionary.Keys + { + get + { + if (keys == null) keys = new KeyCollection(this); + return keys; + } + } + + IEnumerable IReadOnlyDictionary.Keys + { + get + { + if (keys == null) keys = new KeyCollection(this); + return keys; + } + } + + public ValueCollection Values + { + get + { + Contract.Ensures(Contract.Result() != null); + if (values == null) values = new ValueCollection(this); + return values; + } + } + + ICollection IDictionary.Values + { + get + { + if (values == null) values = new ValueCollection(this); + return values; + } + } + + IEnumerable IReadOnlyDictionary.Values + { + get + { + if (values == null) values = new ValueCollection(this); + return values; + } + } + + public TValue this[TKey key] + { + get + { + int i = FindEntry(key); + if (i >= 0) return entries[i].value; + throw new KeyNotFoundException(); + } + set + { + Insert(key, value, false); + } + } + + public void Add(TKey key, TValue value) + { + Insert(key, value, true); + } + + void ICollection>.Add(KeyValuePair keyValuePair) + { + Add(keyValuePair.Key, keyValuePair.Value); + } + + bool ICollection>.Contains(KeyValuePair keyValuePair) + { + int i = FindEntry(keyValuePair.Key); + if (i >= 0 && EqualityComparer.Default.Equals(entries[i].value, keyValuePair.Value)) + { + return true; + } + return false; + } + + bool ICollection>.Remove(KeyValuePair keyValuePair) + { + int i = FindEntry(keyValuePair.Key); + if (i >= 0 && EqualityComparer.Default.Equals(entries[i].value, keyValuePair.Value)) + { + Remove(keyValuePair.Key); + return true; + } + return false; + } + + public void Clear() + { + if (count > 0) + { + for (int i = 0; i < buckets.Length; i++) buckets[i] = -1; + Array.Clear(entries, 0, count); + freeList = -1; + count = 0; + freeCount = 0; + version++; + } + } + + public bool ContainsKey(TKey key) + { + return FindEntry(key) >= 0; + } + + public bool ContainsValue(TValue value) + { + if (value == null) + { + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0 && entries[i].value == null) return true; + } + } + else + { + EqualityComparer c = EqualityComparer.Default; + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0 && c.Equals(entries[i].value, value)) return true; + } + } + return false; + } + + private void CopyTo(KeyValuePair[] array, int index) + { + if (array == null) + { + throw new ArgumentNullException(nameof(array)); + } + + if (index < 0 || index > array.Length) + { + throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); + } + + if (array.Length - index < Count) + { + throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); + } + + int count = this.count; + Entry[] entries = this.entries; + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0) + { + array[index++] = new KeyValuePair(entries[i].key, entries[i].value); + } + } + } + + public Enumerator GetEnumerator() + { + return new Enumerator(this, Enumerator.KeyValuePair); + } + + IEnumerator> IEnumerable>.GetEnumerator() + { + return new Enumerator(this, Enumerator.KeyValuePair); + } + + public virtual void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + info.AddValue(VersionName, version); + info.AddValue(ComparerName, HashHelpers.GetEqualityComparerForSerialization(comparer), typeof(IEqualityComparer)); + info.AddValue(HashSizeName, buckets == null ? 0 : buckets.Length); // This is the length of the bucket array + + if (buckets != null) + { + var array = new KeyValuePair[Count]; + CopyTo(array, 0); + info.AddValue(KeyValuePairsName, array, typeof(KeyValuePair[])); + } + } + + private int FindEntry(TKey key) + { + if (key == null) + { + throw new ArgumentNullException(nameof(key)); + } + + if (buckets != null) + { + int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF; + for (int i = buckets[hashCode % buckets.Length]; i >= 0; i = entries[i].next) + { + if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) return i; + } + } + return -1; + } + + private void Initialize(int capacity) + { + int size = HashHelpers.GetPrime(capacity); + buckets = new int[size]; + for (int i = 0; i < buckets.Length; i++) buckets[i] = -1; + entries = new Entry[size]; + freeList = -1; + } + + private void Insert(TKey key, TValue value, bool add) + { + if (key == null) + { + throw new ArgumentNullException(nameof(key)); + } + + if (buckets == null) Initialize(0); + int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF; + int targetBucket = hashCode % buckets.Length; + +#if FEATURE_RANDOMIZED_STRING_HASHING + int collisionCount = 0; +#endif + + for (int i = buckets[targetBucket]; i >= 0; i = entries[i].next) + { + if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) + { + if (add) + { + throw new ArgumentException(SR.Format(SR.Argument_AddingDuplicate, key)); + } + entries[i].value = value; + version++; + return; + } +#if FEATURE_RANDOMIZED_STRING_HASHING + collisionCount++; +#endif + } + + int index; + + if (freeCount > 0) + { + index = freeList; + freeList = entries[index].next; + freeCount--; + } + else + { + if (count == entries.Length) + { + Resize(); + targetBucket = hashCode % buckets.Length; + } + index = count; + count++; + } + + entries[index].hashCode = hashCode; + entries[index].next = buckets[targetBucket]; + entries[index].key = key; + entries[index].value = value; + buckets[targetBucket] = index; + version++; +#if FEATURE_RANDOMIZED_STRING_HASHING + if (collisionCount > HashHelpers.HashCollisionThreshold && HashHelpers.IsWellKnownEqualityComparer(comparer)) + { + comparer = (IEqualityComparer)HashHelpers.GetRandomizedEqualityComparer(comparer); + Resize(entries.Length, true); + } +#endif + } + + public virtual void OnDeserialization(object sender) + { + SerializationInfo siInfo; + HashHelpers.SerializationInfoTable.TryGetValue(this, out siInfo); + if (siInfo == null) + { + // We can return immediately if this function is called twice. + // Note we remove the serialization info from the table at the end of this method. + return; + } + + int realVersion = siInfo.GetInt32(VersionName); + int hashsize = siInfo.GetInt32(HashSizeName); + comparer = (IEqualityComparer)siInfo.GetValue(ComparerName, typeof(IEqualityComparer)); + + if (hashsize != 0) + { + buckets = new int[hashsize]; + for (int i = 0; i < buckets.Length; i++) buckets[i] = -1; + entries = new Entry[hashsize]; + freeList = -1; + + KeyValuePair[] array = + (KeyValuePair[])siInfo.GetValue(KeyValuePairsName, typeof(KeyValuePair[])); + + if (array == null) + { + throw new SerializationException(SR.Serialization_MissingKeys); + } + + for (int i = 0; i < array.Length; i++) + { + if (array[i].Key == null) + { + throw new SerializationException(SR.Serialization_NullKey); + } + Insert(array[i].Key, array[i].Value, true); + } + } + else + { + buckets = null; + } + + version = realVersion; + HashHelpers.SerializationInfoTable.Remove(this); + } + + private void Resize() + { + Resize(HashHelpers.ExpandPrime(count), false); + } + + private void Resize(int newSize, bool forceNewHashCodes) + { + Debug.Assert(newSize >= entries.Length); + int[] newBuckets = new int[newSize]; + for (int i = 0; i < newBuckets.Length; i++) newBuckets[i] = -1; + + Entry[] newEntries = new Entry[newSize]; + Array.Copy(entries, 0, newEntries, 0, count); + + if (forceNewHashCodes) + { + for (int i = 0; i < count; i++) + { + if (newEntries[i].hashCode != -1) + { + newEntries[i].hashCode = (comparer.GetHashCode(newEntries[i].key) & 0x7FFFFFFF); + } + } + } + + for (int i = 0; i < count; i++) + { + if (newEntries[i].hashCode >= 0) + { + int bucket = newEntries[i].hashCode % newSize; + newEntries[i].next = newBuckets[bucket]; + newBuckets[bucket] = i; + } + } + + buckets = newBuckets; + entries = newEntries; + } + + public bool Remove(TKey key) + { + if (key == null) + { + throw new ArgumentNullException(nameof(key)); + } + + if (buckets != null) + { + int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF; + int bucket = hashCode % buckets.Length; + int last = -1; + for (int i = buckets[bucket]; i >= 0; last = i, i = entries[i].next) + { + if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) + { + if (last < 0) + { + buckets[bucket] = entries[i].next; + } + else + { + entries[last].next = entries[i].next; + } + entries[i].hashCode = -1; + entries[i].next = freeList; + entries[i].key = default(TKey); + entries[i].value = default(TValue); + freeList = i; + freeCount++; + version++; + return true; + } + } + } + return false; + } + + public bool TryGetValue(TKey key, out TValue value) + { + int i = FindEntry(key); + if (i >= 0) + { + value = entries[i].value; + return true; + } + value = default(TValue); + return false; + } + + // This is a convenience method for the internal callers that were converted from using Hashtable. + // Many were combining key doesn't exist and key exists but null value (for non-value types) checks. + // This allows them to continue getting that behavior with minimal code delta. This is basically + // TryGetValue without the out param + internal TValue GetValueOrDefault(TKey key) + { + int i = FindEntry(key); + if (i >= 0) + { + return entries[i].value; + } + return default(TValue); + } + + bool ICollection>.IsReadOnly + { + get { return false; } + } + + void ICollection>.CopyTo(KeyValuePair[] array, int index) + { + CopyTo(array, index); + } + + void ICollection.CopyTo(Array array, int index) + { + if (array == null) + { + throw new ArgumentNullException(nameof(array)); + } + + if (array.Rank != 1) + { + throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array)); + } + + if (array.GetLowerBound(0) != 0) + { + throw new ArgumentException(SR.Arg_NonZeroLowerBound, nameof(array)); + } + + if (index < 0 || index > array.Length) + { + throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); + } + + if (array.Length - index < Count) + { + throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); + } + + KeyValuePair[] pairs = array as KeyValuePair[]; + if (pairs != null) + { + CopyTo(pairs, index); + } + else if (array is DictionaryEntry[]) + { + DictionaryEntry[] dictEntryArray = array as DictionaryEntry[]; + Entry[] entries = this.entries; + + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0) + { + dictEntryArray[index++] = new DictionaryEntry(entries[i].key, entries[i].value); + } + } + } + else + { + object[] objects = array as object[]; + if (objects == null) + { + throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); + } + + try + { + int count = this.count; + Entry[] entries = this.entries; + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0) + { + objects[index++] = new KeyValuePair(entries[i].key, entries[i].value); + } + } + } + catch (ArrayTypeMismatchException) + { + throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); + } + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(this, Enumerator.KeyValuePair); + } + + bool ICollection.IsSynchronized + { + get { return false; } + } + + object ICollection.SyncRoot + { + get + { + if (_syncRoot == null) + { + System.Threading.Interlocked.CompareExchange(ref _syncRoot, new object(), null); + } + return _syncRoot; + } + } + + bool IDictionary.IsFixedSize + { + get { return false; } + } + + bool IDictionary.IsReadOnly + { + get { return false; } + } + + ICollection IDictionary.Keys + { + get { return (ICollection)Keys; } + } + + ICollection IDictionary.Values + { + get { return (ICollection)Values; } + } + + object IDictionary.this[object key] + { + get + { + if (IsCompatibleKey(key)) + { + int i = FindEntry((TKey)key); + if (i >= 0) + { + return entries[i].value; + } + } + return null; + } + set + { + if (key == null) + { + throw new ArgumentNullException(nameof(key)); + } + if (value == null && !(default(TValue) == null)) + throw new ArgumentNullException(nameof(value)); + + try + { + TKey tempKey = (TKey)key; + try + { + this[tempKey] = (TValue)value; + } + catch (InvalidCastException) + { + throw new ArgumentException(SR.Format(SR.Arg_WrongType, value, typeof(TValue)), nameof(value)); + } + } + catch (InvalidCastException) + { + throw new ArgumentException(SR.Format(SR.Arg_WrongType, key, typeof(TKey)), nameof(key)); + } + } + } + + private static bool IsCompatibleKey(object key) + { + if (key == null) + { + throw new ArgumentNullException(nameof(key)); + } + return (key is TKey); + } + + void IDictionary.Add(object key, object value) + { + if (key == null) + { + throw new ArgumentNullException(nameof(key)); + } + + if (value == null && !(default(TValue) == null)) + throw new ArgumentNullException(nameof(value)); + + try + { + TKey tempKey = (TKey)key; + + try + { + Add(tempKey, (TValue)value); + } + catch (InvalidCastException) + { + throw new ArgumentException(SR.Format(SR.Arg_WrongType, value, typeof(TValue)), nameof(value)); + } + } + catch (InvalidCastException) + { + throw new ArgumentException(SR.Format(SR.Arg_WrongType, key, typeof(TKey)), nameof(key)); + } + } + + bool IDictionary.Contains(object key) + { + if (IsCompatibleKey(key)) + { + return ContainsKey((TKey)key); + } + + return false; + } + + IDictionaryEnumerator IDictionary.GetEnumerator() + { + return new Enumerator(this, Enumerator.DictEntry); + } + + void IDictionary.Remove(object key) + { + if (IsCompatibleKey(key)) + { + Remove((TKey)key); + } + } + + [Serializable] + public struct Enumerator : IEnumerator>, + IDictionaryEnumerator + { + private Dictionary dictionary; + private int version; + private int index; + private KeyValuePair current; + private int getEnumeratorRetType; // What should Enumerator.Current return? + + internal const int DictEntry = 1; + internal const int KeyValuePair = 2; + + internal Enumerator(Dictionary dictionary, int getEnumeratorRetType) + { + this.dictionary = dictionary; + version = dictionary.version; + index = 0; + this.getEnumeratorRetType = getEnumeratorRetType; + current = new KeyValuePair(); + } + + public bool MoveNext() + { + if (version != dictionary.version) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + } + + // Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends. + // dictionary.count+1 could be negative if dictionary.count is Int32.MaxValue + while ((uint)index < (uint)dictionary.count) + { + if (dictionary.entries[index].hashCode >= 0) + { + current = new KeyValuePair(dictionary.entries[index].key, dictionary.entries[index].value); + index++; + return true; + } + index++; + } + + index = dictionary.count + 1; + current = new KeyValuePair(); + return false; + } + + public KeyValuePair Current + { + get { return current; } + } + + public void Dispose() + { + } + + object IEnumerator.Current + { + get + { + if (index == 0 || (index == dictionary.count + 1)) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + } + + if (getEnumeratorRetType == DictEntry) + { + return new System.Collections.DictionaryEntry(current.Key, current.Value); + } + else + { + return new KeyValuePair(current.Key, current.Value); + } + } + } + + void IEnumerator.Reset() + { + if (version != dictionary.version) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + } + + index = 0; + current = new KeyValuePair(); + } + + DictionaryEntry IDictionaryEnumerator.Entry + { + get + { + if (index == 0 || (index == dictionary.count + 1)) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + } + + return new DictionaryEntry(current.Key, current.Value); + } + } + + object IDictionaryEnumerator.Key + { + get + { + if (index == 0 || (index == dictionary.count + 1)) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + } + + return current.Key; + } + } + + object IDictionaryEnumerator.Value + { + get + { + if (index == 0 || (index == dictionary.count + 1)) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + } + + return current.Value; + } + } + } + + [DebuggerTypeProxy(typeof(DictionaryKeyCollectionDebugView<,>))] + [DebuggerDisplay("Count = {Count}")] + [Serializable] + public sealed class KeyCollection : ICollection, ICollection, IReadOnlyCollection + { + private Dictionary dictionary; + + public KeyCollection(Dictionary dictionary) + { + if (dictionary == null) + { + throw new ArgumentNullException(nameof(dictionary)); + } + this.dictionary = dictionary; + } + + public Enumerator GetEnumerator() + { + return new Enumerator(dictionary); + } + + public void CopyTo(TKey[] array, int index) + { + if (array == null) + { + throw new ArgumentNullException(nameof(array)); + } + + if (index < 0 || index > array.Length) + { + throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); + } + + if (array.Length - index < dictionary.Count) + { + throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); + } + + int count = dictionary.count; + Entry[] entries = dictionary.entries; + + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0) array[index++] = entries[i].key; + } + } + + public int Count + { + get { return dictionary.Count; } + } + + bool ICollection.IsReadOnly + { + get { return true; } + } + + void ICollection.Add(TKey item) + { + throw new NotSupportedException(SR.NotSupported_KeyCollectionSet); + } + + void ICollection.Clear() + { + throw new NotSupportedException(SR.NotSupported_KeyCollectionSet); + } + + bool ICollection.Contains(TKey item) + { + return dictionary.ContainsKey(item); + } + + bool ICollection.Remove(TKey item) + { + throw new NotSupportedException(SR.NotSupported_KeyCollectionSet); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(dictionary); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(dictionary); + } + + void ICollection.CopyTo(Array array, int index) + { + if (array == null) + { + throw new ArgumentNullException(nameof(array)); + } + + if (array.Rank != 1) + { + throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array)); + } + + if (array.GetLowerBound(0) != 0) + { + throw new ArgumentException(SR.Arg_NonZeroLowerBound, nameof(array)); + } + + if (index < 0 || index > array.Length) + { + throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); + } + + if (array.Length - index < dictionary.Count) + { + throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); + } + + TKey[] keys = array as TKey[]; + if (keys != null) + { + CopyTo(keys, index); + } + else + { + object[] objects = array as object[]; + if (objects == null) + { + throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); + } + + int count = dictionary.count; + Entry[] entries = dictionary.entries; + + try + { + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0) objects[index++] = entries[i].key; + } + } + catch (ArrayTypeMismatchException) + { + throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); + } + } + } + + bool ICollection.IsSynchronized + { + get { return false; } + } + + object ICollection.SyncRoot + { + get { return ((ICollection)dictionary).SyncRoot; } + } + + [Serializable] + public struct Enumerator : IEnumerator, System.Collections.IEnumerator + { + private Dictionary dictionary; + private int index; + private int version; + private TKey currentKey; + + internal Enumerator(Dictionary dictionary) + { + this.dictionary = dictionary; + version = dictionary.version; + index = 0; + currentKey = default(TKey); + } + + public void Dispose() + { + } + + public bool MoveNext() + { + if (version != dictionary.version) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + } + + while ((uint)index < (uint)dictionary.count) + { + if (dictionary.entries[index].hashCode >= 0) + { + currentKey = dictionary.entries[index].key; + index++; + return true; + } + index++; + } + + index = dictionary.count + 1; + currentKey = default(TKey); + return false; + } + + public TKey Current + { + get + { + return currentKey; + } + } + + object System.Collections.IEnumerator.Current + { + get + { + if (index == 0 || (index == dictionary.count + 1)) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + } + + return currentKey; + } + } + + void System.Collections.IEnumerator.Reset() + { + if (version != dictionary.version) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + } + + index = 0; + currentKey = default(TKey); + } + } + } + + [DebuggerTypeProxy(typeof(DictionaryValueCollectionDebugView<,>))] + [DebuggerDisplay("Count = {Count}")] + [Serializable] + public sealed class ValueCollection : ICollection, ICollection, IReadOnlyCollection + { + private Dictionary dictionary; + + public ValueCollection(Dictionary dictionary) + { + if (dictionary == null) + { + throw new ArgumentNullException(nameof(dictionary)); + } + this.dictionary = dictionary; + } + + public Enumerator GetEnumerator() + { + return new Enumerator(dictionary); + } + + public void CopyTo(TValue[] array, int index) + { + if (array == null) + { + throw new ArgumentNullException(nameof(array)); + } + + if (index < 0 || index > array.Length) + { + throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); + } + + if (array.Length - index < dictionary.Count) + { + throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); + } + + int count = dictionary.count; + Entry[] entries = dictionary.entries; + + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0) array[index++] = entries[i].value; + } + } + + public int Count + { + get { return dictionary.Count; } + } + + bool ICollection.IsReadOnly + { + get { return true; } + } + + void ICollection.Add(TValue item) + { + throw new NotSupportedException(SR.NotSupported_ValueCollectionSet); + } + + bool ICollection.Remove(TValue item) + { + throw new NotSupportedException(SR.NotSupported_ValueCollectionSet); + } + + void ICollection.Clear() + { + throw new NotSupportedException(SR.NotSupported_ValueCollectionSet); + } + + bool ICollection.Contains(TValue item) + { + return dictionary.ContainsValue(item); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(dictionary); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(dictionary); + } + + void ICollection.CopyTo(Array array, int index) + { + if (array == null) + { + throw new ArgumentNullException(nameof(array)); + } + + if (array.Rank != 1) + { + throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array)); + } + + if (array.GetLowerBound(0) != 0) + { + throw new ArgumentException(SR.Arg_NonZeroLowerBound, nameof(array)); + } + + if (index < 0 || index > array.Length) + { + throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); + } + + if (array.Length - index < dictionary.Count) + throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); + + TValue[] values = array as TValue[]; + if (values != null) + { + CopyTo(values, index); + } + else + { + object[] objects = array as object[]; + if (objects == null) + { + throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); + } + + int count = dictionary.count; + Entry[] entries = dictionary.entries; + + try + { + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0) objects[index++] = entries[i].value; + } + } + catch (ArrayTypeMismatchException) + { + throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); + } + } + } + + bool ICollection.IsSynchronized + { + get { return false; } + } + + object ICollection.SyncRoot + { + get { return ((ICollection)dictionary).SyncRoot; } + } + + [Serializable] + public struct Enumerator : IEnumerator, System.Collections.IEnumerator + { + private Dictionary dictionary; + private int index; + private int version; + private TValue currentValue; + + internal Enumerator(Dictionary dictionary) + { + this.dictionary = dictionary; + version = dictionary.version; + index = 0; + currentValue = default(TValue); + } + + public void Dispose() + { + } + + public bool MoveNext() + { + if (version != dictionary.version) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + } + + while ((uint)index < (uint)dictionary.count) + { + if (dictionary.entries[index].hashCode >= 0) + { + currentValue = dictionary.entries[index].value; + index++; + return true; + } + index++; + } + index = dictionary.count + 1; + currentValue = default(TValue); + return false; + } + + public TValue Current + { + get + { + return currentValue; + } + } + + object System.Collections.IEnumerator.Current + { + get + { + if (index == 0 || (index == dictionary.count + 1)) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + } + + return currentValue; + } + } + + void System.Collections.IEnumerator.Reset() + { + if (version != dictionary.version) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + } + index = 0; + currentValue = default(TValue); + } + } + } + } +} diff --git a/src/System.Private.CoreLib/src/System/Collections/Generic/IDictionaryDebugView.cs b/src/System.Private.CoreLib/src/System/Collections/Generic/IDictionaryDebugView.cs new file mode 100644 index 000000000..4721642fe --- /dev/null +++ b/src/System.Private.CoreLib/src/System/Collections/Generic/IDictionaryDebugView.cs @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; + +namespace System.Collections.Generic +{ + internal sealed class IDictionaryDebugView + { + private readonly IDictionary _dict; + + public IDictionaryDebugView(IDictionary dictionary) + { + if (dictionary == null) + throw new ArgumentNullException(nameof(dictionary)); + + _dict = dictionary; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public KeyValuePair[] Items + { + get + { + KeyValuePair[] items = new KeyValuePair[_dict.Count]; + _dict.CopyTo(items, 0); + return items; + } + } + } + + internal sealed class DictionaryKeyCollectionDebugView + { + private readonly ICollection _collection; + + public DictionaryKeyCollectionDebugView(ICollection collection) + { + if (collection == null) + throw new ArgumentNullException(nameof(collection)); + + _collection = collection; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public TKey[] Items + { + get + { + TKey[] items = new TKey[_collection.Count]; + _collection.CopyTo(items, 0); + return items; + } + } + } + + internal sealed class DictionaryValueCollectionDebugView + { + private readonly ICollection _collection; + + public DictionaryValueCollectionDebugView(ICollection collection) + { + if (collection == null) + throw new ArgumentNullException(nameof(collection)); + + _collection = collection; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public TValue[] Items + { + get + { + TValue[] items = new TValue[_collection.Count]; + _collection.CopyTo(items, 0); + return items; + } + } + } +} diff --git a/src/System.Private.CoreLib/src/System/IO/BinaryReader.cs b/src/System.Private.CoreLib/src/System/IO/BinaryReader.cs new file mode 100644 index 000000000..4ca3a7648 --- /dev/null +++ b/src/System.Private.CoreLib/src/System/IO/BinaryReader.cs @@ -0,0 +1,681 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Text; +using System.Diagnostics; + +namespace System.IO +{ + public class BinaryReader : IDisposable + { + private const int MaxCharBytesSize = 128; + + private Stream _stream; + private byte[] _buffer; + private Decoder _decoder; + private byte[] _charBytes; + private char[] _singleChar; + private char[] _charBuffer; + private int _maxCharsSize; // From MaxCharBytesSize & Encoding + + // Performance optimization for Read() w/ Unicode. Speeds us up by ~40% + private bool _2BytesPerChar; + private bool _isMemoryStream; // "do we sit on MemoryStream?" for Read/ReadInt32 perf + private bool _leaveOpen; + + public BinaryReader(Stream input) : this(input, Encoding.UTF8, false) + { + } + + public BinaryReader(Stream input, Encoding encoding) : this(input, encoding, false) + { + } + + public BinaryReader(Stream input, Encoding encoding, bool leaveOpen) + { + if (input == null) + { + throw new ArgumentNullException(nameof(input)); + } + if (encoding == null) + { + throw new ArgumentNullException(nameof(encoding)); + } + if (!input.CanRead) + { + throw new ArgumentException(SR.Argument_StreamNotReadable); + } + + _stream = input; + _decoder = encoding.GetDecoder(); + _maxCharsSize = encoding.GetMaxCharCount(MaxCharBytesSize); + int minBufferSize = encoding.GetMaxByteCount(1); // max bytes per one char + if (minBufferSize < 16) + { + minBufferSize = 16; + } + + _buffer = new byte[minBufferSize]; + // _charBuffer and _charBytes will be left null. + + // For Encodings that always use 2 bytes per char (or more), + // special case them here to make Read() & Peek() faster. + _2BytesPerChar = encoding is UnicodeEncoding; + // check if BinaryReader is based on MemoryStream, and keep this for it's life + // we cannot use "as" operator, since derived classes are not allowed + _isMemoryStream = (_stream.GetType() == typeof(MemoryStream)); + _leaveOpen = leaveOpen; + + Debug.Assert(_decoder != null, "[BinaryReader.ctor]_decoder!=null"); + } + + public virtual Stream BaseStream + { + get + { + return _stream; + } + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + Stream copyOfStream = _stream; + _stream = null; + if (copyOfStream != null && !_leaveOpen) + { + copyOfStream.Dispose(); + } + } + _stream = null; + _buffer = null; + _decoder = null; + _charBytes = null; + _singleChar = null; + _charBuffer = null; + } + + public void Dispose() + { + Dispose(true); + } + + /// + /// Override Dispose(bool) instead of Close(). This API exists for compatibility purposes. + /// + public virtual void Close() + { + Dispose(true); + } + + public virtual int PeekChar() + { + if (_stream == null) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_FileClosed); + } + + if (!_stream.CanSeek) + { + return -1; + } + + long origPos = _stream.Position; + int ch = Read(); + _stream.Position = origPos; + return ch; + } + + public virtual int Read() + { + if (_stream == null) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_FileClosed); + } + return InternalReadOneChar(); + } + + public virtual bool ReadBoolean() + { + FillBuffer(1); + return (_buffer[0] != 0); + } + + public virtual byte ReadByte() + { + // Inlined to avoid some method call overhead with FillBuffer. + if (_stream == null) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_FileClosed); + } + + int b = _stream.ReadByte(); + if (b == -1) + { + throw new EndOfStreamException(SR.IO_EOF_ReadBeyondEOF); + } + + return (byte)b; + } + + [CLSCompliant(false)] + public virtual sbyte ReadSByte() + { + FillBuffer(1); + return (sbyte)(_buffer[0]); + } + + public virtual char ReadChar() + { + int value = Read(); + if (value == -1) + { + throw new EndOfStreamException(SR.IO_EOF_ReadBeyondEOF); + } + return (char)value; + } + + public virtual short ReadInt16() + { + FillBuffer(2); + return (short)(_buffer[0] | _buffer[1] << 8); + } + + [CLSCompliant(false)] + public virtual ushort ReadUInt16() + { + FillBuffer(2); + return (ushort)(_buffer[0] | _buffer[1] << 8); + } + + public virtual int ReadInt32() + { + if (_isMemoryStream) + { + if (_stream == null) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_FileClosed); + } + + // read directly from MemoryStream buffer + MemoryStream mStream = _stream as MemoryStream; + Debug.Assert(mStream != null, "_stream as MemoryStream != null"); + + return mStream.InternalReadInt32(); + } + else + { + FillBuffer(4); + return (int)(_buffer[0] | _buffer[1] << 8 | _buffer[2] << 16 | _buffer[3] << 24); + } + } + + [CLSCompliant(false)] + public virtual uint ReadUInt32() + { + FillBuffer(4); + return (uint)(_buffer[0] | _buffer[1] << 8 | _buffer[2] << 16 | _buffer[3] << 24); + } + + public virtual long ReadInt64() + { + FillBuffer(8); + uint lo = (uint)(_buffer[0] | _buffer[1] << 8 | + _buffer[2] << 16 | _buffer[3] << 24); + uint hi = (uint)(_buffer[4] | _buffer[5] << 8 | + _buffer[6] << 16 | _buffer[7] << 24); + return (long)((ulong)hi) << 32 | lo; + } + + [CLSCompliant(false)] + public virtual ulong ReadUInt64() + { + FillBuffer(8); + uint lo = (uint)(_buffer[0] | _buffer[1] << 8 | + _buffer[2] << 16 | _buffer[3] << 24); + uint hi = (uint)(_buffer[4] | _buffer[5] << 8 | + _buffer[6] << 16 | _buffer[7] << 24); + return ((ulong)hi) << 32 | lo; + } + + public virtual unsafe float ReadSingle() + { + FillBuffer(4); + uint tmpBuffer = (uint)(_buffer[0] | _buffer[1] << 8 | _buffer[2] << 16 | _buffer[3] << 24); + return *((float*)&tmpBuffer); + } + + public virtual unsafe double ReadDouble() + { + FillBuffer(8); + uint lo = (uint)(_buffer[0] | _buffer[1] << 8 | + _buffer[2] << 16 | _buffer[3] << 24); + uint hi = (uint)(_buffer[4] | _buffer[5] << 8 | + _buffer[6] << 16 | _buffer[7] << 24); + + ulong tmpBuffer = ((ulong)hi) << 32 | lo; + return *((double*)&tmpBuffer); + } + + public virtual decimal ReadDecimal() + { + FillBuffer(16); + int[] ints = new int[4]; + Buffer.BlockCopy(_buffer, 0, ints, 0, 16); + try + { + return new decimal(ints); + } + catch (ArgumentException e) + { + // ReadDecimal cannot leak out ArgumentException + throw new IOException(SR.Arg_DecBitCtor, e); + } + } + + public virtual string ReadString() + { + if (_stream == null) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_FileClosed); + } + + int currPos = 0; + int n; + int stringLength; + int readLength; + int charsRead; + + // Length of the string in bytes, not chars + stringLength = Read7BitEncodedInt(); + if (stringLength < 0) + { + throw new IOException(SR.Format(SR.IO_IO_InvalidStringLen_Len, stringLength)); + } + + if (stringLength == 0) + { + return string.Empty; + } + + if (_charBytes == null) + { + _charBytes = new byte[MaxCharBytesSize]; + } + + if (_charBuffer == null) + { + _charBuffer = new char[_maxCharsSize]; + } + + StringBuilder sb = null; + do + { + readLength = ((stringLength - currPos) > MaxCharBytesSize) ? MaxCharBytesSize : (stringLength - currPos); + + n = _stream.Read(_charBytes, 0, readLength); + if (n == 0) + { + throw new EndOfStreamException(SR.IO_EOF_ReadBeyondEOF); + } + + charsRead = _decoder.GetChars(_charBytes, 0, n, _charBuffer, 0); + + if (currPos == 0 && n == stringLength) + { + return new string(_charBuffer, 0, charsRead); + } + + if (sb == null) + { + sb = StringBuilderCache.Acquire(stringLength); // Actual string length in chars may be smaller. + } + + sb.Append(_charBuffer, 0, charsRead); + currPos += n; + } while (currPos < stringLength); + + return StringBuilderCache.GetStringAndRelease(sb); + } + + public virtual int Read(char[] buffer, int index, int count) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); + } + if (index < 0) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (buffer.Length - index < count) + { + throw new ArgumentException(SR.Argument_InvalidOffLen); + } + if (_stream == null) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_FileClosed); + } + + // SafeCritical: index and count have already been verified to be a valid range for the buffer + return InternalReadChars(buffer, index, count); + } + + private int InternalReadChars(char[] buffer, int index, int count) + { + Debug.Assert(buffer != null); + Debug.Assert(index >= 0 && count >= 0); + Debug.Assert(_stream != null); + + int numBytes = 0; + int charsRemaining = count; + + if (_charBytes == null) + { + _charBytes = new byte[MaxCharBytesSize]; + } + + while (charsRemaining > 0) + { + int charsRead = 0; + // We really want to know what the minimum number of bytes per char + // is for our encoding. Otherwise for UnicodeEncoding we'd have to + // do ~1+log(n) reads to read n characters. + numBytes = charsRemaining; + + if (_2BytesPerChar) + { + numBytes <<= 1; + } + if (numBytes > MaxCharBytesSize) + { + numBytes = MaxCharBytesSize; + } + + int position = 0; + byte[] byteBuffer = null; + if (_isMemoryStream) + { + MemoryStream mStream = _stream as MemoryStream; + Debug.Assert(mStream != null, "_stream as MemoryStream != null"); + + position = mStream.InternalGetPosition(); + numBytes = mStream.InternalEmulateRead(numBytes); + byteBuffer = mStream.InternalGetBuffer(); + } + else + { + numBytes = _stream.Read(_charBytes, 0, numBytes); + byteBuffer = _charBytes; + } + + if (numBytes == 0) + { + return (count - charsRemaining); + } + + Debug.Assert(byteBuffer != null, "expected byteBuffer to be non-null"); + charsRead = _decoder.GetChars(byteBuffer, position, numBytes, buffer, index, flush: false); + + charsRemaining -= charsRead; + index += charsRead; + } + + // this should never fail + Debug.Assert(charsRemaining >= 0, "We read too many characters."); + + // we may have read fewer than the number of characters requested if end of stream reached + // or if the encoding makes the char count too big for the buffer (e.g. fallback sequence) + return (count - charsRemaining); + } + + private int InternalReadOneChar() + { + // I know having a separate InternalReadOneChar method seems a little + // redundant, but this makes a scenario like the security parser code + // 20% faster, in addition to the optimizations for UnicodeEncoding I + // put in InternalReadChars. + int charsRead = 0; + int numBytes = 0; + long posSav = posSav = 0; + + if (_stream.CanSeek) + { + posSav = _stream.Position; + } + + if (_charBytes == null) + { + _charBytes = new byte[MaxCharBytesSize]; //REVIEW: We need at most 2 bytes/char here? + } + if (_singleChar == null) + { + _singleChar = new char[1]; + } + + while (charsRead == 0) + { + // We really want to know what the minimum number of bytes per char + // is for our encoding. Otherwise for UnicodeEncoding we'd have to + // do ~1+log(n) reads to read n characters. + // Assume 1 byte can be 1 char unless _2BytesPerChar is true. + numBytes = _2BytesPerChar ? 2 : 1; + + int r = _stream.ReadByte(); + _charBytes[0] = (byte)r; + if (r == -1) + { + numBytes = 0; + } + if (numBytes == 2) + { + r = _stream.ReadByte(); + _charBytes[1] = (byte)r; + if (r == -1) + { + numBytes = 1; + } + } + + if (numBytes == 0) + { + // Console.WriteLine("Found no bytes. We're outta here."); + return -1; + } + + Debug.Assert(numBytes == 1 || numBytes == 2, "BinaryReader::InternalReadOneChar assumes it's reading one or 2 bytes only."); + + try + { + charsRead = _decoder.GetChars(_charBytes, 0, numBytes, _singleChar, 0); + } + catch + { + // Handle surrogate char + + if (_stream.CanSeek) + { + _stream.Seek((posSav - _stream.Position), SeekOrigin.Current); + } + // else - we can't do much here + + throw; + } + + Debug.Assert(charsRead < 2, "InternalReadOneChar - assuming we only got 0 or 1 char, not 2!"); + // Console.WriteLine("That became: " + charsRead + " characters."); + } + + Debug.Assert(charsRead != 0); + + return _singleChar[0]; + } + + public virtual char[] ReadChars(int count) + { + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (_stream == null) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_FileClosed); + } + + if (count == 0) + { + return Array.Empty(); + } + + // SafeCritical: we own the chars buffer, and therefore can guarantee that the index and count are valid + char[] chars = new char[count]; + int n = InternalReadChars(chars, 0, count); + if (n != count) + { + char[] copy = new char[n]; + Buffer.BlockCopy(chars, 0, copy, 0, 2 * n); // sizeof(char) + chars = copy; + } + + return chars; + } + + public virtual int Read(byte[] buffer, int index, int count) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); + } + if (index < 0) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (buffer.Length - index < count) + { + throw new ArgumentException(SR.Argument_InvalidOffLen); + } + if (_stream == null) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_FileClosed); + } + + return _stream.Read(buffer, index, count); + } + + public virtual byte[] ReadBytes(int count) + { + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (_stream == null) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_FileClosed); + } + + if (count == 0) + { + return Array.Empty(); + } + + byte[] result = new byte[count]; + int numRead = 0; + do + { + int n = _stream.Read(result, numRead, count); + if (n == 0) + { + break; + } + + numRead += n; + count -= n; + } while (count > 0); + + if (numRead != result.Length) + { + // Trim array. This should happen on EOF & possibly net streams. + byte[] copy = new byte[numRead]; + Buffer.BlockCopy(result, 0, copy, 0, numRead); + result = copy; + } + + return result; + } + + protected virtual void FillBuffer(int numBytes) + { + if (_buffer != null && (numBytes < 0 || numBytes > _buffer.Length)) + { + throw new ArgumentOutOfRangeException(nameof(numBytes), SR.ArgumentOutOfRange_BinaryReaderFillBuffer); + } + + int bytesRead = 0; + int n = 0; + + if (_stream == null) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_FileClosed); + } + + // Need to find a good threshold for calling ReadByte() repeatedly + // vs. calling Read(byte[], int, int) for both buffered & unbuffered + // streams. + if (numBytes == 1) + { + n = _stream.ReadByte(); + if (n == -1) + { + throw new EndOfStreamException(SR.IO_EOF_ReadBeyondEOF); + } + + _buffer[0] = (byte)n; + return; + } + + do + { + n = _stream.Read(_buffer, bytesRead, numBytes - bytesRead); + if (n == 0) + { + throw new EndOfStreamException(SR.IO_EOF_ReadBeyondEOF); + } + bytesRead += n; + } while (bytesRead < numBytes); + } + + protected internal int Read7BitEncodedInt() + { + // Read out an Int32 7 bits at a time. The high bit + // of the byte when on means to continue reading more bytes. + int count = 0; + int shift = 0; + byte b; + do + { + // Check for a corrupted stream. Read a max of 5 bytes. + // In a future version, add a DataFormatException. + if (shift == 5 * 7) // 5 bytes max per Int32, shift += 7 + { + throw new FormatException(SR.Format_Bad7BitInt32); + } + + // ReadByte handles end of stream cases for us. + b = ReadByte(); + count |= (b & 0x7F) << shift; + shift += 7; + } while ((b & 0x80) != 0); + return count; + } + } +} diff --git a/src/System.Private.CoreLib/src/System/IO/MemoryStream.cs b/src/System.Private.CoreLib/src/System/IO/MemoryStream.cs new file mode 100644 index 000000000..f073ed242 --- /dev/null +++ b/src/System.Private.CoreLib/src/System/IO/MemoryStream.cs @@ -0,0 +1,802 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; +using System.Diagnostics; +using System.Diagnostics.Contracts; + +namespace System.IO +{ + // A MemoryStream represents a Stream in memory (i.e, it has no backing store). + // This stream may reduce the need for temporary buffers and files in + // an application. + // + // There are two ways to create a MemoryStream. You can initialize one + // from an unsigned byte array, or you can create an empty one. Empty + // memory streams are resizable, while ones created with a byte array provide + // a stream "view" of the data. + [Serializable] + public class MemoryStream : Stream + { + private byte[] _buffer; // Either allocated internally or externally. + private int _origin; // For user-provided arrays, start at this origin + private int _position; // read/write head. + [ContractPublicPropertyName("Length")] + private int _length; // Number of bytes within the memory stream + private int _capacity; // length of usable portion of buffer for stream + // Note that _capacity == _buffer.Length for non-user-provided byte[]'s + + private bool _expandable; // User-provided buffers aren't expandable. + private bool _writable; // Can user write to this stream? + private bool _exposable; // Whether the array can be returned to the user. + private bool _isOpen; // Is this stream open or closed? + + // In V2, if we get support for arrays of more than 2 GB worth of elements, + // consider removing this constraint, or setting it to Int64.MaxValue. + private const int MemStreamMaxLength = int.MaxValue; + + public MemoryStream() + : this(0) + { + } + + public MemoryStream(int capacity) + { + if (capacity < 0) + { + throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_NegativeCapacity); + } + + _buffer = capacity != 0 ? new byte[capacity] : Array.Empty(); + _capacity = capacity; + _expandable = true; + _writable = true; + _exposable = true; + _origin = 0; // Must be 0 for byte[]'s created by MemoryStream + _isOpen = true; + } + + public MemoryStream(byte[] buffer) + : this(buffer, true) + { + } + + public MemoryStream(byte[] buffer, bool writable) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); + } + + _buffer = buffer; + _length = _capacity = buffer.Length; + _writable = writable; + _exposable = false; + _origin = 0; + _isOpen = true; + } + + public MemoryStream(byte[] buffer, int index, int count) + : this(buffer, index, count, true, false) + { + } + + public MemoryStream(byte[] buffer, int index, int count, bool writable) + : this(buffer, index, count, writable, false) + { + } + + public MemoryStream(byte[] buffer, int index, int count, bool writable, bool publiclyVisible) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); + } + if (index < 0) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (buffer.Length - index < count) + { + throw new ArgumentException(SR.Argument_InvalidOffLen); + } + + _buffer = buffer; + _origin = _position = index; + _length = _capacity = index + count; + _writable = writable; + _exposable = publiclyVisible; // Can TryGetBuffer return the array? + _expandable = false; + _isOpen = true; + } + + public override bool CanRead + { + [Pure] + get + { return _isOpen; } + } + + public override bool CanSeek + { + [Pure] + get + { return _isOpen; } + } + + public override bool CanWrite + { + [Pure] + get + { return _writable; } + } + + private void EnsureWriteable() + { + if (!CanWrite) + { + throw new NotSupportedException(SR.NotSupported_UnwritableStream); + } + } + + protected override void Dispose(bool disposing) + { + try + { + if (disposing) + { + _isOpen = false; + _writable = false; + _expandable = false; + // Don't set buffer to null - allow TryGetBuffer & ToArray to work. + } + } + finally + { + // Call base.Close() to cleanup async IO resources + base.Dispose(disposing); + } + } + + // returns a bool saying whether we allocated a new array. + private bool EnsureCapacity(int value) + { + // Check for overflow + if (value < 0) + { + throw new IOException(SR.IO_IO_StreamTooLong); + } + if (value > _capacity) + { + int newCapacity = value; + if (newCapacity < 256) + { + newCapacity = 256; + } + if (newCapacity < _capacity * 2) + { + newCapacity = _capacity * 2; + } + + Capacity = newCapacity; + return true; + } + return false; + } + + public override void Flush() + { + } + +#pragma warning disable 1998 //async method with no await operators + public override async Task FlushAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + Flush(); + } +#pragma warning restore 1998 + + public virtual bool TryGetBuffer(out ArraySegment buffer) + { + if (!_exposable) + { + buffer = default(ArraySegment); + return false; + } + + buffer = new ArraySegment(_buffer, offset: _origin, count: (_length - _origin)); + return true; + } + + public virtual byte[] GetBuffer() + { + if (!_exposable) + throw new UnauthorizedAccessException(SR.UnauthorizedAccess_MemStreamBuffer); + return _buffer; + } + + // PERF: Internal sibling of GetBuffer, always returns a buffer (cf. GetBuffer()) + internal byte[] InternalGetBuffer() + { + return _buffer; + } + + // PERF: True cursor position, we don't need _origin for direct access + internal int InternalGetPosition() + { + if (!_isOpen) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); + } + return _position; + } + + // PERF: Takes out Int32 as fast as possible + internal int InternalReadInt32() + { + if (!_isOpen) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); + } + + int pos = (_position += 4); // use temp to avoid race + if (pos > _length) + { + _position = _length; + throw new EndOfStreamException(SR.IO_EOF_ReadBeyondEOF); + } + return (int)(_buffer[pos - 4] | _buffer[pos - 3] << 8 | _buffer[pos - 2] << 16 | _buffer[pos - 1] << 24); + } + + // PERF: Get actual length of bytes available for read; do sanity checks; shift position - i.e. everything except actual copying bytes + internal int InternalEmulateRead(int count) + { + if (!_isOpen) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); + } + + int n = _length - _position; + if (n > count) + { + n = count; + } + if (n < 0) + { + n = 0; + } + + Debug.Assert(_position + n >= 0, "_position + n >= 0"); // len is less than 2^31 -1. + _position += n; + return n; + } + + // Gets & sets the capacity (number of bytes allocated) for this stream. + // The capacity cannot be set to a value less than the current length + // of the stream. + // + public virtual int Capacity + { + get + { + if (!_isOpen) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); + } + + return _capacity - _origin; + } + set + { + // Only update the capacity if the MS is expandable and the value is different than the current capacity. + // Special behavior if the MS isn't expandable: we don't throw if value is the same as the current capacity + if (value < Length) + { + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_SmallCapacity); + } + if (!_isOpen) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); + } + if (!_expandable && (value != Capacity)) + { + throw new NotSupportedException(SR.NotSupported_MemStreamNotExpandable); + } + + // MemoryStream has this invariant: _origin > 0 => !expandable (see ctors) + if (_expandable && value != _capacity) + { + if (value > 0) + { + byte[] newBuffer = new byte[value]; + if (_length > 0) + { + Buffer.BlockCopy(_buffer, 0, newBuffer, 0, _length); + } + + _buffer = newBuffer; + } + else + { + _buffer = null; + } + _capacity = value; + } + } + } + + public override long Length + { + get + { + if (!_isOpen) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); + } + + return _length - _origin; + } + } + + public override long Position + { + get + { + if (!_isOpen) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); + } + + return _position - _origin; + } + set + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (!_isOpen) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); + } + if (value > MemStreamMaxLength) + { + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_StreamLength); + } + + _position = _origin + (int)value; + } + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); + } + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (buffer.Length - offset < count) + { + throw new ArgumentException(SR.Argument_InvalidOffLen); + } + if (!_isOpen) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); + } + + int n = _length - _position; + if (n > count) + { + n = count; + } + if (n <= 0) + { + return 0; + } + + Debug.Assert(_position + n >= 0, "_position + n >= 0"); // len is less than 2^31 -1. + + if (n <= 8) + { + int byteCount = n; + while (--byteCount >= 0) + buffer[offset + byteCount] = _buffer[_position + byteCount]; + } + else + Buffer.BlockCopy(_buffer, _position, buffer, offset, n); + _position += n; + + return n; + } + + public override Task ReadAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); + } + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (buffer.Length - offset < count) + { + throw new ArgumentException(SR.Argument_InvalidOffLen); + } + + return ReadAsyncImpl(buffer, offset, count, cancellationToken); + } + +#pragma warning disable 1998 //async method with no await operators + private async Task ReadAsyncImpl(Byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + return Read(buffer, offset, count); + } +#pragma warning restore 1998 + + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) => + TaskToApm.Begin(ReadAsync(buffer, offset, count, CancellationToken.None), callback, state); + + public override int EndRead(IAsyncResult asyncResult) => + TaskToApm.End(asyncResult); + + public override int ReadByte() + { + if (!_isOpen) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); + } + + if (_position >= _length) + { + return -1; + } + + return _buffer[_position++]; + } + + public override void CopyTo(Stream destination, int bufferSize) + { + // Since we did not originally override this method, validate the arguments + // the same way Stream does for back-compat. + StreamHelpers.ValidateCopyToArgs(this, destination, bufferSize); + + // If we have been inherited into a subclass, the following implementation could be incorrect + // since it does not call through to Read() which a subclass might have overridden. + // To be safe we will only use this implementation in cases where we know it is safe to do so, + // and delegate to our base class (which will call into Read) when we are not sure. + if (GetType() != typeof(MemoryStream)) + { + base.CopyTo(destination, bufferSize); + return; + } + + int originalPosition = _position; + + // Seek to the end of the MemoryStream. + int remaining = InternalEmulateRead(_length - originalPosition); + + // If we were already at or past the end, there's no copying to do so just quit. + if (remaining > 0) + { + // Call Write() on the other Stream, using our internal buffer and avoiding any + // intermediary allocations. + destination.Write(_buffer, originalPosition, remaining); + } + } + + public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + { + // This implementation offers better performance compared to the base class version. + + StreamHelpers.ValidateCopyToArgs(this, destination, bufferSize); + + // If we have been inherited into a subclass, the following implementation could be incorrect + // since it does not call through to ReadAsync() which a subclass might have overridden. + // To be safe we will only use this implementation in cases where we know it is safe to do so, + // and delegate to our base class (which will call into ReadAsync) when we are not sure. + if (GetType() != typeof(MemoryStream)) + { + return base.CopyToAsync(destination, bufferSize, cancellationToken); + } + + return CopyToAsyncImpl(destination, bufferSize, cancellationToken); + } + + private async Task CopyToAsyncImpl(Stream destination, int bufferSize, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + // Avoid copying data from this buffer into a temp buffer: + // (require that InternalEmulateRead does not throw, + // otherwise it needs to be wrapped into try-catch-Task.FromException like memStrDest.Write below) + + int pos = _position; + int n = InternalEmulateRead(_length - _position); + + // If destination is not a memory stream, write there asynchronously: + MemoryStream memStrDest = destination as MemoryStream; + if (memStrDest == null) + { + await destination.WriteAsync(_buffer, pos, n, cancellationToken).ConfigureAwait(false); + } + else + { + memStrDest.Write(_buffer, pos, n); + } + } + + + public override long Seek(long offset, SeekOrigin loc) + { + if (!_isOpen) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); + } + if (offset > MemStreamMaxLength) + { + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_StreamLength); + } + + switch (loc) + { + case SeekOrigin.Begin: + { + int tempPosition = unchecked(_origin + (int)offset); + if (offset < 0 || tempPosition < _origin) + { + throw new IOException(SR.IO_IO_SeekBeforeBegin); + } + + _position = tempPosition; + break; + } + case SeekOrigin.Current: + { + int tempPosition = unchecked(_position + (int)offset); + if (unchecked(_position + offset) < _origin || tempPosition < _origin) + { + throw new IOException(SR.IO_IO_SeekBeforeBegin); + } + + _position = tempPosition; + break; + } + case SeekOrigin.End: + { + int tempPosition = unchecked(_length + (int)offset); + if (unchecked(_length + offset) < _origin || tempPosition < _origin) + { + throw new IOException(SR.IO_IO_SeekBeforeBegin); + } + + _position = tempPosition; + break; + } + default: + throw new ArgumentException(SR.Argument_InvalidSeekOrigin); + } + + Debug.Assert(_position >= 0, "_position >= 0"); + return _position; + } + + // Sets the length of the stream to a given value. The new + // value must be nonnegative and less than the space remaining in + // the array, Int32.MaxValue - origin + // Origin is 0 in all cases other than a MemoryStream created on + // top of an existing array and a specific starting offset was passed + // into the MemoryStream constructor. The upper bounds prevents any + // situations where a stream may be created on top of an array then + // the stream is made longer than the maximum possible length of the + // array (Int32.MaxValue). + // + public override void SetLength(long value) + { + if (value < 0 || value > int.MaxValue) + { + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_StreamLength); + } + EnsureWriteable(); + + // Origin wasn't publicly exposed above. + Debug.Assert(MemStreamMaxLength == int.MaxValue); // Check parameter validation logic in this method if this fails. + if (value > (int.MaxValue - _origin)) + { + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_StreamLength); + } + + int newLength = _origin + (int)value; + bool allocatedNewArray = EnsureCapacity(newLength); + if (!allocatedNewArray && newLength > _length) + { + Array.Clear(_buffer, _length, newLength - _length); + } + + _length = newLength; + if (_position > newLength) + { + _position = newLength; + } + } + + public virtual byte[] ToArray() + { + //BCLDebug.Perf(_exposable, "MemoryStream::GetBuffer will let you avoid a copy."); + int count = _length - _origin; + if (count == 0) + { + return Array.Empty(); + } + + byte[] copy = new byte[count]; + Buffer.BlockCopy(_buffer, _origin, copy, 0, _length - _origin); + return copy; + } + + public override void Write(byte[] buffer, int offset, int count) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); + } + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (buffer.Length - offset < count) + { + throw new ArgumentException(SR.Argument_InvalidOffLen); + } + + if (!_isOpen) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); + } + EnsureWriteable(); + + int i = _position + count; + // Check for overflow + if (i < 0) + { + throw new IOException(SR.IO_IO_StreamTooLong); + } + + if (i > _length) + { + bool mustZero = _position > _length; + if (i > _capacity) + { + bool allocatedNewArray = EnsureCapacity(i); + if (allocatedNewArray) + { + mustZero = false; + } + } + if (mustZero) + { + Array.Clear(_buffer, _length, i - _length); + } + _length = i; + } + if ((count <= 8) && (buffer != _buffer)) + { + int byteCount = count; + while (--byteCount >= 0) + { + _buffer[_position + byteCount] = buffer[offset + byteCount]; + } + } + else + { + Buffer.BlockCopy(buffer, offset, _buffer, _position, count); + } + _position = i; + } + + public override Task WriteAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer); + } + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); + } + if (buffer.Length - offset < count) + { + throw new ArgumentException(SR.Argument_InvalidOffLen); + } + + return WriteAsyncImpl(buffer, offset, count, cancellationToken); + } + +#pragma warning disable 1998 //async method with no await operators + private async Task WriteAsyncImpl(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + Write(buffer, offset, count); + } +#pragma warning restore 1998 + + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) => + TaskToApm.Begin(WriteAsync(buffer, offset, count, CancellationToken.None), callback, state); + + public override void EndWrite(IAsyncResult asyncResult) => + TaskToApm.End(asyncResult); + + public override void WriteByte(byte value) + { + if (!_isOpen) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); + } + EnsureWriteable(); + + if (_position >= _length) + { + int newLength = _position + 1; + bool mustZero = _position > _length; + if (newLength >= _capacity) + { + bool allocatedNewArray = EnsureCapacity(newLength); + if (allocatedNewArray) + { + mustZero = false; + } + } + if (mustZero) + { + Array.Clear(_buffer, _length, _position - _length); + } + _length = newLength; + } + _buffer[_position++] = value; + } + + // Writes this MemoryStream to another stream. + public virtual void WriteTo(Stream stream) + { + if (stream == null) + { + throw new ArgumentNullException(nameof(stream), SR.ArgumentNull_Stream); + } + if (!_isOpen) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); + } + + stream.Write(_buffer, _origin, _length - _origin); + } + } +} -- cgit v1.2.3 From 9a72a0965cab4e5910b39890d575bdef262f4481 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Fri, 20 Jan 2017 17:32:48 -0800 Subject: Add Dictionary, BinaryReader and MemoryStream to the build --- .../src/Resources/Strings.resx | 33 ++++++++++++++++++++++ .../src/System.Private.CoreLib.csproj | 4 +++ .../src/System/Collections/Generic/Dictionary.cs | 13 ++++++--- src/packaging/packages.targets | 2 +- 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/System.Private.CoreLib/src/Resources/Strings.resx b/src/System.Private.CoreLib/src/Resources/Strings.resx index 24edf287c..c85ef7119 100644 --- a/src/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/System.Private.CoreLib/src/Resources/Strings.resx @@ -2098,4 +2098,37 @@ Unable to find manifest resource. + + The keys for this dictionary are missing. + + + One of the serialized keys is null. + + + Mutating a key collection derived from a dictionary is not allowed. + + + Mutating a value collection derived from a dictionary is not allowed. + + + Stream was too long. + + + MemoryStream's internal buffer cannot be accessed. + + + Memory stream is not expandable. + + + An attempt was made to move the position before the beginning of the stream. + + + Stream cannot be null. + + + BinaryReader encountered an invalid string length of {0} characters. + + + The number of bytes requested does not fit into BinaryReader's internal buffer. + \ No newline at end of file diff --git a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 41cce6086..f9122722a 100644 --- a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -254,6 +254,8 @@ + + @@ -301,6 +303,7 @@ + @@ -312,6 +315,7 @@ + diff --git a/src/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs b/src/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs index 8dd5d845b..fee5393d6 100644 --- a/src/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs +++ b/src/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs @@ -111,7 +111,7 @@ namespace System.Collections.Generic // We can't do anything with the keys and values until the entire graph has been deserialized // and we have a resonable estimate that GetHashCode is not going to fail. For the time being, // we'll just cache this. The graph is not valid until OnDeserialization has been called. - HashHelpers.SerializationInfoTable.Add(this, info); + DictionaryHashHelpers.SerializationInfoTable.Add(this, info); } public IEqualityComparer Comparer @@ -312,7 +312,7 @@ namespace System.Collections.Generic } info.AddValue(VersionName, version); - info.AddValue(ComparerName, HashHelpers.GetEqualityComparerForSerialization(comparer), typeof(IEqualityComparer)); + info.AddValue(ComparerName, comparer, typeof(IEqualityComparer)); info.AddValue(HashSizeName, buckets == null ? 0 : buckets.Length); // This is the length of the bucket array if (buckets != null) @@ -419,7 +419,7 @@ namespace System.Collections.Generic public virtual void OnDeserialization(object sender) { SerializationInfo siInfo; - HashHelpers.SerializationInfoTable.TryGetValue(this, out siInfo); + DictionaryHashHelpers.SerializationInfoTable.TryGetValue(this, out siInfo); if (siInfo == null) { // We can return immediately if this function is called twice. @@ -461,7 +461,7 @@ namespace System.Collections.Generic } version = realVersion; - HashHelpers.SerializationInfoTable.Remove(this); + DictionaryHashHelpers.SerializationInfoTable.Remove(this); } private void Resize() @@ -1379,4 +1379,9 @@ namespace System.Collections.Generic } } } + + internal class DictionaryHashHelpers + { + internal static ConditionalWeakTable SerializationInfoTable { get; } = new ConditionalWeakTable(); + } } diff --git a/src/packaging/packages.targets b/src/packaging/packages.targets index 00b505047..9fa966871 100644 --- a/src/packaging/packages.targets +++ b/src/packaging/packages.targets @@ -122,6 +122,7 @@ + @@ -157,7 +158,6 @@ - -- cgit v1.2.3 From 972e86c243974d49047328b0dccd72eb44ca493a Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Fri, 20 Jan 2017 21:21:08 -0800 Subject: Fix build break with strict warning settings (#2558) --- src/Native/Runtime/PalRedhawkCommon.h | 2 +- src/Native/Runtime/startup.cpp | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Native/Runtime/PalRedhawkCommon.h b/src/Native/Runtime/PalRedhawkCommon.h index 882d5a3f6..2b0008888 100644 --- a/src/Native/Runtime/PalRedhawkCommon.h +++ b/src/Native/Runtime/PalRedhawkCommon.h @@ -118,7 +118,7 @@ struct PAL_LIMITED_CONTEXT #endif // _TARGET_ARM_ }; -void __stdcall RuntimeThreadShutdown(void* thread); +void RuntimeThreadShutdown(void* thread); #ifdef PLATFORM_UNIX typedef Int32 (*PHARDWARE_EXCEPTION_HANDLER)(UIntNative faultCode, UIntNative faultAddress, PAL_LIMITED_CONTEXT* palContext, UIntNative* arg0Reg, UIntNative* arg1Reg); diff --git a/src/Native/Runtime/startup.cpp b/src/Native/Runtime/startup.cpp index 3a368191b..41e5c994f 100644 --- a/src/Native/Runtime/startup.cpp +++ b/src/Native/Runtime/startup.cpp @@ -261,18 +261,20 @@ void DllThreadDetach() } } -void __stdcall RuntimeThreadShutdown(void* thread) +void RuntimeThreadShutdown(void* thread) { // Note: loader lock is normally *not* held here! // The one exception is that the loader lock may be held during the thread shutdown callback // that is made for the single thread that runs the final stages of orderly process // shutdown (i.e., the thread that delivers the DLL_PROCESS_DETACH notifications when the // process is being torn down via an ExitProcess call). -#if HAVE_THREAD_LOCAL + + UNREFERENCED_PARAMETER(thread); + +#if _WIN32 || HAVE_THREAD_LOCAL // If the current Unix platform doesn't support thread_local, we don't get the thread pointer // as the parameter, we just get NULL, so we can check the thread validity only if the // thread_local is supported - UNREFERENCED_PARAMETER(thread); ASSERT((Thread*)thread == ThreadStore::GetCurrentThread()); #endif -- cgit v1.2.3 From b580dd08744ee43d22b1adb59b674dafbb2f0e30 Mon Sep 17 00:00:00 2001 From: Simon Nattress Date: Sat, 21 Jan 2017 00:54:07 -0800 Subject: X86 and Arm assembly emitters (#2555) Port the emitter code for X86 and Arm, allowing unbox stub generation for those architectures. --- .../DependencyAnalysis/AssemblyStubNode.cs | 17 +++ .../InterfaceDispatchCellNode.cs | 13 +- .../DependencyAnalysis/ObjectDataBuilder.cs | 8 ++ .../src/Compiler/DependencyAnalysis/Relocation.cs | 10 ++ .../DependencyAnalysis/Target_ARM/ARMEmitter.cs | 62 +++++++++ .../ARMInitialInterfaceDispatchStubNode.cs | 49 +++++++ .../Target_ARM/ARMNodeFactory.cs | 27 ++++ .../Target_ARM/ARMReadyToRunGenericHelperNode.cs | 22 +++ .../Target_ARM/ARMReadyToRunHelperNode.cs | 22 +++ .../Target_ARM/ARMUnboxingStubNode.cs | 17 +++ .../DependencyAnalysis/Target_ARM/Register.cs | 37 +++++ .../Target_ARM/TargetRegisterMap.cs | 35 +++++ .../DependencyAnalysis/Target_X86/AddrMode.cs | 33 +++++ .../DependencyAnalysis/Target_X86/Register.cs | 29 ++++ .../Target_X86/TargetRegisterMap.cs | 35 +++++ .../DependencyAnalysis/Target_X86/X86Emitter.cs | 155 +++++++++++++++++++++ .../Target_X86/X86ReadyToRunGenericHelperNode.cs | 22 +++ .../Target_X86/X86ReadyToRunHelperNode.cs | 24 ++++ .../Target_X86/X86UnboxingStubNode.cs | 19 +++ .../src/ILCompiler.Compiler.csproj | 15 ++ 20 files changed, 648 insertions(+), 3 deletions(-) create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMEmitter.cs create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMInitialInterfaceDispatchStubNode.cs create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMNodeFactory.cs create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunGenericHelperNode.cs create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunHelperNode.cs create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMUnboxingStubNode.cs create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/Register.cs create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/TargetRegisterMap.cs create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/AddrMode.cs create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/Register.cs create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/TargetRegisterMap.cs create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86Emitter.cs create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunGenericHelperNode.cs create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunHelperNode.cs create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86UnboxingStubNode.cs diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/AssemblyStubNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/AssemblyStubNode.cs index df7322c46..930dfaff1 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/AssemblyStubNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/AssemblyStubNode.cs @@ -34,11 +34,28 @@ namespace ILCompiler.DependencyAnalysis x64Emitter.Builder.Alignment = factory.Target.MinimumFunctionAlignment; x64Emitter.Builder.DefinedSymbols.Add(this); return x64Emitter.Builder.ToObjectData(); + + case TargetArchitecture.X86: + X86.X86Emitter x86Emitter = new X86.X86Emitter(factory); + EmitCode(factory, ref x86Emitter, relocsOnly); + x86Emitter.Builder.Alignment = factory.Target.MinimumFunctionAlignment; + x86Emitter.Builder.DefinedSymbols.Add(this); + return x86Emitter.Builder.ToObjectData(); + + case TargetArchitecture.ARM: + ARM.ARMEmitter armEmitter = new ARM.ARMEmitter(factory); + EmitCode(factory, ref armEmitter, relocsOnly); + armEmitter.Builder.Alignment = factory.Target.MinimumFunctionAlignment; + armEmitter.Builder.DefinedSymbols.Add(this); + return armEmitter.Builder.ToObjectData(); + default: throw new NotImplementedException(); } } protected abstract void EmitCode(NodeFactory factory, ref X64.X64Emitter instructionEncoder, bool relocsOnly); + protected abstract void EmitCode(NodeFactory factory, ref X86.X86Emitter instructionEncoder, bool relocsOnly); + protected abstract void EmitCode(NodeFactory factory, ref ARM.ARMEmitter instructionEncoder, bool relocsOnly); } } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs index 9ca9e4415..aad579a3b 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs @@ -42,9 +42,16 @@ namespace ILCompiler.DependencyAnalysis // synchronization mechanism of the two values in the runtime. objData.Alignment = _targetMethod.Context.Target.PointerSize * 2; objData.DefinedSymbols.Add(this); - - objData.EmitPointerReloc(factory.ExternSymbol("RhpInitialDynamicInterfaceDispatch")); - + + if (factory.Target.Architecture == TargetArchitecture.ARM) + { + objData.EmitPointerReloc(factory.InitialInterfaceDispatchStub); + } + else + { + objData.EmitPointerReloc(factory.ExternSymbol("RhpInitialDynamicInterfaceDispatch")); + } + // The second cell field uses the two lower-order bits to communicate the contents. // We add 1 to signal IDC_CachePointerIsInterfacePointer. See src\Native\Runtime\inc\rhbinder.h. objData.EmitPointerReloc(factory.NecessaryTypeSymbol(_targetMethod.OwningType), 1); diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectDataBuilder.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectDataBuilder.cs index dcf5396b6..93abe65f5 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectDataBuilder.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectDataBuilder.cs @@ -227,12 +227,20 @@ namespace ILCompiler.DependencyAnalysis switch (relocType) { case RelocType.IMAGE_REL_BASED_REL32: + case RelocType.IMAGE_REL_BASED_RELPTR32: case RelocType.IMAGE_REL_BASED_ABSOLUTE: + case RelocType.IMAGE_REL_BASED_HIGHLOW: + case RelocType.IMAGE_REL_SECREL: EmitInt(delta); break; case RelocType.IMAGE_REL_BASED_DIR64: EmitLong(delta); break; + case RelocType.IMAGE_REL_THUMB_BRANCH24: + case RelocType.IMAGE_REL_THUMB_MOV32: + // Do not vacate space for this kind of relocation, because + // the space is embedded in the instruction. + break; default: throw new NotImplementedException(); } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Relocation.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Relocation.cs index dfe96310a..645b7f505 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Relocation.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Relocation.cs @@ -12,6 +12,14 @@ namespace ILCompiler.DependencyAnalysis IMAGE_REL_BASED_HIGHLOW = 0x03, IMAGE_REL_BASED_DIR64 = 0x0A, IMAGE_REL_BASED_REL32 = 0x10, + IMAGE_REL_THUMB_MOV32 = 0x11, // Thumb2: MOVW/MOVT + IMAGE_REL_THUMB_BRANCH24 = 0x14, // Thumb2: B, BL + + IMAGE_REL_BASED_RELPTR32 = 0x7C, // 32-bit relative address from byte starting reloc + // This is a special NGEN-specific relocation type + // for relative pointer (used to make NGen relocation + // section smaller) + IMAGE_REL_SECREL = 0x80, // 32 bit offset from base of section containing target } public struct Relocation { @@ -51,6 +59,8 @@ namespace ILCompiler.DependencyAnalysis case RelocType.IMAGE_REL_BASED_ABSOLUTE: case RelocType.IMAGE_REL_BASED_HIGHLOW: case RelocType.IMAGE_REL_BASED_REL32: + case RelocType.IMAGE_REL_BASED_RELPTR32: + case RelocType.IMAGE_REL_SECREL: return *(int*)location; case RelocType.IMAGE_REL_BASED_DIR64: return *(long*)location; diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMEmitter.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMEmitter.cs new file mode 100644 index 000000000..0f59d1603 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMEmitter.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; + +namespace ILCompiler.DependencyAnalysis.ARM +{ + public struct ARMEmitter + { + public ARMEmitter(NodeFactory factory) + { + Builder = new ObjectDataBuilder(factory); + TargetRegister = new TargetRegisterMap(factory.Target.OperatingSystem); + } + + public ObjectDataBuilder Builder; + public TargetRegisterMap TargetRegister; + + // add reg, immediate + public void EmitADD(Register reg, byte immediate) + { + Builder.EmitShort((short)(0x3000 + ((byte)reg << 8) + immediate)); + } + + // push reg + public void EmitPUSH(Register reg) + { + Builder.EmitByte(0x4d); + Builder.EmitByte(0xf8); + Builder.EmitShort((short)(0x0d04 + ((byte)reg << 12))); + } + + // mov reg, reg + public void EmitMOV(Register destination, Register source) + { + Builder.EmitShort((short)(0x4600 + (((byte)destination & 0x8) << 4) + (((byte)source & 0x8) << 3) + (((byte)source & 0x7) << 3) + ((byte)destination & 0x7))); + } + + // mov reg, [reloc] & 0x0000FFFF + // movt reg, [reloc] & 0xFFFF0000 + public void EmitMOV(Register destination, ISymbolNode symbol) + { + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_THUMB_MOV32); + Builder.EmitShort(unchecked((short)0xf240)); + Builder.EmitShort((short)((byte)destination << 8)); + Builder.EmitShort(unchecked((short)0xf2c0)); + Builder.EmitShort((short)((byte)destination << 8)); + } + + // b symbol + public void EmitJMP(ISymbolNode symbol) + { + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_THUMB_BRANCH24); + Builder.EmitByte(0); + Builder.EmitByte(0xF0); + Builder.EmitByte(0); + Builder.EmitByte(0xB8); + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMInitialInterfaceDispatchStubNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMInitialInterfaceDispatchStubNode.cs new file mode 100644 index 000000000..c34dfe9ec --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMInitialInterfaceDispatchStubNode.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Internal.Text; +using System; + +using ILCompiler.DependencyAnalysis.ARM; +using ILCompiler.DependencyAnalysis.X64; +using ILCompiler.DependencyAnalysis.X86; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// On ARM, we use R12 to store the interface dispatch cell. However, the jump through the import address + /// table to call the runtime interface dispatch helper trashes R12. This stub pushes R12 before making + /// the runtime call. The ARM runtime interface dispatch code expects this and pops R12 to get the dispatch + /// cell. + /// + public partial class InitialInterfaceDispatchStubNode : AssemblyStubNode + { + protected override string GetName() => this.GetMangledName(); + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("_InitialInterfaceDispatchStub"); + } + + public override bool IsShareable => false; + + protected override void EmitCode(NodeFactory factory, ref ARMEmitter instructionEncoder, bool relocsOnly) + { + instructionEncoder.EmitPUSH(ARM.Register.R12); + instructionEncoder.EmitMOV(ARM.Register.R12, factory.ExternSymbol("RhpInitialInterfaceDispatch")); + instructionEncoder.EmitMOV(ARM.Register.R15, ARM.Register.R12); + } + + // Only ARM requires a stub + protected override void EmitCode(NodeFactory factory, ref X86Emitter instructionEncoder, bool relocsOnly) + { + throw new NotImplementedException(); + } + + protected override void EmitCode(NodeFactory factory, ref X64Emitter instructionEncoder, bool relocsOnly) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMNodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMNodeFactory.cs new file mode 100644 index 000000000..4bf25f08b --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMNodeFactory.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class NodeFactory + { + private InitialInterfaceDispatchStubNode _initialInterfaceDispatchStubNode; + + public InitialInterfaceDispatchStubNode InitialInterfaceDispatchStub + { + get + { + if (_initialInterfaceDispatchStubNode == null) + { + _initialInterfaceDispatchStubNode = new InitialInterfaceDispatchStubNode(); + } + + return _initialInterfaceDispatchStubNode; + } + } + + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunGenericHelperNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunGenericHelperNode.cs new file mode 100644 index 000000000..df0916b44 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunGenericHelperNode.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +using ILCompiler.DependencyAnalysis.ARM; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + partial class ReadyToRunGenericHelperNode + { + protected sealed override void EmitCode(NodeFactory factory, ref ARMEmitter encoder, bool relocsOnly) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunHelperNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunHelperNode.cs new file mode 100644 index 000000000..afdf9bdeb --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunHelperNode.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +using Internal.TypeSystem; + +using ILCompiler; +using ILCompiler.DependencyAnalysis.ARM; + +namespace ILCompiler.DependencyAnalysis +{ + // ARM specific portions of ReadyToRunHelperNode + partial class ReadyToRunHelperNode + { + protected override void EmitCode(NodeFactory factory, ref ARMEmitter encoder, bool relocsOnly) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMUnboxingStubNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMUnboxingStubNode.cs new file mode 100644 index 000000000..99fd0dec3 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMUnboxingStubNode.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using ILCompiler.DependencyAnalysis.ARM; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class UnboxingStubNode + { + protected override void EmitCode(NodeFactory factory, ref ARMEmitter encoder, bool relocsOnly) + { + encoder.EmitADD(encoder.TargetRegister.Arg0, (byte)factory.Target.PointerSize); // add r0, sizeof(void*); + encoder.EmitJMP(factory.MethodEntrypoint(_target)); // b methodEntryPoint + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/Register.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/Register.cs new file mode 100644 index 000000000..15213c752 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/Register.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ILCompiler.DependencyAnalysis.ARM +{ + public enum Register + { + R0 = 0, + R1 = 1, + R2 = 2, + R3 = 3, + R4 = 4, + R5 = 5, + R6 = 6, + R7 = 7, + R8 = 8, + R9 = 9, + R10 = 10, + R11 = 11, + R12 = 12, + R13 = 13, + R14 = 14, + R15 = 15, + + None = 16, + RegDirect = 24, + + NoIndex = 128, + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/TargetRegisterMap.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/TargetRegisterMap.cs new file mode 100644 index 000000000..93bbdd57e --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/TargetRegisterMap.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis.ARM +{ + /// + /// Maps logical registers to physical registers on a specified OS. + /// + public struct TargetRegisterMap + { + public readonly Register Arg0; + public readonly Register Arg1; + public readonly Register Result; + + public TargetRegisterMap(TargetOS os) + { + switch (os) + { + case TargetOS.Windows: + Arg0 = Register.R0; + Arg1 = Register.R1; + Result = Register.R0; + break; + + default: + throw new NotImplementedException(); + } + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/AddrMode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/AddrMode.cs new file mode 100644 index 000000000..75a5a7eda --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/AddrMode.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace ILCompiler.DependencyAnalysis.X86 +{ + public enum AddrModeSize + { + Int8 = 1, + Int16 = 2, + Int32 = 4 + } + + public struct AddrMode + { + public readonly Register BaseReg; + public readonly Register? IndexReg; + public readonly int Offset; + public readonly byte Scale; + public readonly AddrModeSize Size; + + public AddrMode(Register baseRegister, Register? indexRegister, int offset, byte scale, AddrModeSize size) + { + BaseReg = baseRegister; + IndexReg = indexRegister; + Offset = offset; + Scale = scale; + Size = size; + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/Register.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/Register.cs new file mode 100644 index 000000000..ab938c828 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/Register.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ILCompiler.DependencyAnalysis.X86 +{ + public enum Register + { + EAX = 0, + ECX = 1, + EDX = 2, + EBX = 3, + ESP = 4, + EBP = 5, + ESI = 6, + EDI = 7, + + None = 8, + RegDirect = 24, + + NoIndex = 128, + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/TargetRegisterMap.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/TargetRegisterMap.cs new file mode 100644 index 000000000..3b81e43ba --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/TargetRegisterMap.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis.X86 +{ + /// + /// Maps logical registers to physical registers on a specified OS. + /// + public struct TargetRegisterMap + { + public readonly Register Arg0; + public readonly Register Arg1; + public readonly Register Result; + + public TargetRegisterMap(TargetOS os) + { + switch (os) + { + case TargetOS.Windows: + Arg0 = Register.ECX; + Arg1 = Register.EDX; + Result = Register.EAX; + break; + + default: + throw new NotImplementedException(); + } + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86Emitter.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86Emitter.cs new file mode 100644 index 000000000..3ecc4078c --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86Emitter.cs @@ -0,0 +1,155 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; + +namespace ILCompiler.DependencyAnalysis.X86 +{ + public struct X86Emitter + { + public X86Emitter(NodeFactory factory) + { + Builder = new ObjectDataBuilder(factory); + TargetRegister = new TargetRegisterMap(factory.Target.OperatingSystem); + } + + public ObjectDataBuilder Builder; + public TargetRegisterMap TargetRegister; + + public void EmitADD(ref AddrMode addrMode, sbyte immediate) + { + if (addrMode.Size == AddrModeSize.Int16) + Builder.EmitByte(0x66); + EmitIndirInstruction((byte)((addrMode.Size != AddrModeSize.Int8) ? 0x83 : 0x80), (byte)0, ref addrMode); + Builder.EmitByte((byte)immediate); + } + + public void EmitJMP(ISymbolNode symbol) + { + Builder.EmitByte(0xE9); + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_REL32); + } + + private bool InSignedByteRange(int i) + { + return i == (int)(sbyte)i; + } + + private void EmitImmediate(int immediate, int size) + { + switch (size) + { + case 0: + break; + case 1: + Builder.EmitByte((byte)immediate); + break; + case 2: + Builder.EmitShort((short)immediate); + break; + case 4: + Builder.EmitInt(immediate); + break; + default: + throw new NotImplementedException(); + } + } + + private void EmitModRM(byte subOpcode, ref AddrMode addrMode) + { + byte modRM = (byte)((subOpcode & 0x07) << 3); + if (addrMode.BaseReg > Register.None) + { + Debug.Assert(addrMode.BaseReg >= Register.RegDirect); + + Register reg = (Register)(addrMode.BaseReg - Register.RegDirect); + Builder.EmitByte((byte)(0xC0 | modRM | ((int)reg & 0x07))); + } + else + { + byte lowOrderBitsOfBaseReg = (byte)((int)addrMode.BaseReg & 0x07); + modRM |= lowOrderBitsOfBaseReg; + int offsetSize = 0; + + if (addrMode.Offset == 0 && (lowOrderBitsOfBaseReg != (byte)Register.EBP)) + { + offsetSize = 0; + } + else if (InSignedByteRange(addrMode.Offset)) + { + offsetSize = 1; + modRM |= 0x40; + } + else + { + offsetSize = 4; + modRM |= 0x80; + } + + bool emitSibByte = false; + Register sibByteBaseRegister = addrMode.BaseReg; + + if (addrMode.BaseReg == Register.None) + { + emitSibByte = (addrMode.IndexReg != Register.NoIndex); + modRM &= 0x38; // set Mod bits to 00 and clear out base reg + offsetSize = 4; // this forces 32-bit displacement + + if (emitSibByte) + { + // EBP in SIB byte means no base + // ModRM base register forced to ESP in SIB code below + sibByteBaseRegister = Register.EBP; + } + else + { + // EBP in ModRM means no base + modRM |= (byte)(Register.EBP); + } + } + else if (lowOrderBitsOfBaseReg == (byte)Register.ESP || addrMode.IndexReg.HasValue) + { + emitSibByte = true; + } + + if (!emitSibByte) + { + Builder.EmitByte(modRM); + } + else + { + modRM = (byte)((modRM & 0xF8) | (int)Register.ESP); + Builder.EmitByte(modRM); + int indexRegAsInt = (int)(addrMode.IndexReg.HasValue ? addrMode.IndexReg.Value : Register.ESP); + Builder.EmitByte((byte)((addrMode.Scale << 6) + ((indexRegAsInt & 0x07) << 3) + ((int)sibByteBaseRegister & 0x07))); + } + EmitImmediate(addrMode.Offset, offsetSize); + } + } + + private void EmitExtendedOpcode(int opcode) + { + if ((opcode >> 16) != 0) + { + if ((opcode >> 24) != 0) + { + Builder.EmitByte((byte)(opcode >> 24)); + } + Builder.EmitByte((byte)(opcode >> 16)); + } + Builder.EmitByte((byte)(opcode >> 8)); + } + + private void EmitIndirInstruction(int opcode, byte subOpcode, ref AddrMode addrMode) + { + if ((opcode >> 8) != 0) + { + EmitExtendedOpcode(opcode); + } + Builder.EmitByte((byte)opcode); + EmitModRM(subOpcode, ref addrMode); + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunGenericHelperNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunGenericHelperNode.cs new file mode 100644 index 000000000..8f856ae5e --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunGenericHelperNode.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +using ILCompiler.DependencyAnalysis.X86; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + partial class ReadyToRunGenericHelperNode + { + protected sealed override void EmitCode(NodeFactory factory, ref X86Emitter encoder, bool relocsOnly) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunHelperNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunHelperNode.cs new file mode 100644 index 000000000..0c8347625 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunHelperNode.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using ILCompiler.DependencyAnalysis.X86; +using Internal.TypeSystem; +using ILCompiler; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// X64 specific portions of ReadyToRunHelperNode + /// + public partial class ReadyToRunHelperNode + { + protected override void EmitCode(NodeFactory factory, ref X86Emitter encoder, bool relocsOnly) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86UnboxingStubNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86UnboxingStubNode.cs new file mode 100644 index 000000000..ea6009ce7 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X86/X86UnboxingStubNode.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using ILCompiler.DependencyAnalysis.X86; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class UnboxingStubNode + { + protected override void EmitCode(NodeFactory factory, ref X86Emitter encoder, bool relocsOnly) + { + AddrMode thisPtr = new AddrMode( + Register.RegDirect | encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int32); + encoder.EmitADD(ref thisPtr, (sbyte)factory.Target.PointerSize); + encoder.EmitJMP(factory.MethodEntrypoint(_target)); + } + } +} diff --git a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj index 2330e0a02..ed9a96cb5 100644 --- a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj +++ b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj @@ -139,6 +139,8 @@ + + @@ -184,6 +186,19 @@ + + + + + + + + + + + + + -- cgit v1.2.3 From 55aec2695031ea3b172bb4cfb1e60c2cdbd70c27 Mon Sep 17 00:00:00 2001 From: mikedn Date: Sat, 21 Jan 2017 18:30:10 +0200 Subject: Don't fall through endfinally and others (#2560) endfinally, endfilter and rethrow were treated as fallthrough capable when they are not. endfinally was also failing to clear the stack. --- src/Common/src/TypeSystem/IL/ILImporter.cs | 6 +++--- src/ILCompiler.Compiler/src/Compiler/LibraryInitializers.cs | 10 ++++------ src/ILCompiler.Compiler/src/CppCodeGen/ILToCppImporter.cs | 2 ++ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Common/src/TypeSystem/IL/ILImporter.cs b/src/Common/src/TypeSystem/IL/ILImporter.cs index 5240cfb6c..6b9c2efd9 100644 --- a/src/Common/src/TypeSystem/IL/ILImporter.cs +++ b/src/Common/src/TypeSystem/IL/ILImporter.cs @@ -778,7 +778,7 @@ namespace Internal.IL break; case ILOpcode.endfinally: ImportEndFinally(); - break; + return; case ILOpcode.leave: { int delta = (int)ReadILUInt32(); @@ -837,7 +837,7 @@ namespace Internal.IL break; case ILOpcode.endfilter: ImportEndFilter(); - break; + return; case ILOpcode.unaligned: ImportUnalignedPrefix(ReadILByte()); continue; @@ -864,7 +864,7 @@ namespace Internal.IL continue; case ILOpcode.rethrow: ImportRethrow(); - break; + return; case ILOpcode.sizeof_: ImportSizeOf(ReadILToken()); break; diff --git a/src/ILCompiler.Compiler/src/Compiler/LibraryInitializers.cs b/src/ILCompiler.Compiler/src/Compiler/LibraryInitializers.cs index 42b942ce2..ab872cf17 100644 --- a/src/ILCompiler.Compiler/src/Compiler/LibraryInitializers.cs +++ b/src/ILCompiler.Compiler/src/Compiler/LibraryInitializers.cs @@ -25,8 +25,8 @@ namespace ILCompiler private static readonly LibraryInitializerInfo[] s_assembliesWithLibraryInitializers = { new LibraryInitializerInfo(ClassLibraryPlaceHolderString), - new LibraryInitializerInfo("System.Private.TypeLoader", false), - new LibraryInitializerInfo("System.Private.Reflection.Execution", false), + new LibraryInitializerInfo("System.Private.TypeLoader"), + new LibraryInitializerInfo("System.Private.Reflection.Execution"), new LibraryInitializerInfo("System.Private.DeveloperExperience.Console"), }; @@ -39,10 +39,8 @@ namespace ILCompiler { _context = context; // - // We should not care which code-gen is being used, however currently CppCodeGen cannot - // handle code pulled in by all explicit cctors. - // - // See https://github.com/dotnet/corert/issues/2518 + // We should not care which code-gen is being used but for the time being + // this can be useful to workaround CppCodeGen bugs. // _isCppCodeGen = isCppCodeGen; } diff --git a/src/ILCompiler.Compiler/src/CppCodeGen/ILToCppImporter.cs b/src/ILCompiler.Compiler/src/CppCodeGen/ILToCppImporter.cs index b2b5d0bda..03133ce93 100644 --- a/src/ILCompiler.Compiler/src/CppCodeGen/ILToCppImporter.cs +++ b/src/ILCompiler.Compiler/src/CppCodeGen/ILToCppImporter.cs @@ -2164,6 +2164,8 @@ namespace Internal.IL private void ImportEndFinally() { + _stack.Clear(); + int finallyIndex = FindNearestFinally(_currentOffset - 1); AppendLine(); -- cgit v1.2.3 From 8811353cbb66d10f456710c7388ffd3f98f48788 Mon Sep 17 00:00:00 2001 From: mikedn Date: Sun, 22 Jan 2017 18:40:28 +0200 Subject: Add missing cast to intptr_t (#2564) ldobj import casts the stack value to the token type but then wants to store the result as intptr_t again so another cast is needed. --- src/ILCompiler.Compiler/src/CppCodeGen/ILToCppImporter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ILCompiler.Compiler/src/CppCodeGen/ILToCppImporter.cs b/src/ILCompiler.Compiler/src/CppCodeGen/ILToCppImporter.cs index 03133ce93..3c6f7e8ef 100644 --- a/src/ILCompiler.Compiler/src/CppCodeGen/ILToCppImporter.cs +++ b/src/ILCompiler.Compiler/src/CppCodeGen/ILToCppImporter.cs @@ -1944,6 +1944,7 @@ namespace Internal.IL PushTemp(GetStackValueKind(type), type); + AppendCastIfNecessary(GetStackValueKind(type), type); Append("*("); Append(_writer.GetCppSignatureTypeName(type)); Append("*)"); -- cgit v1.2.3