OFFSET_MASK = 0x3FFFFFFF
};
- UINT32 ILOffset;
UINT32 Count;
CORINFO_CLASS_HANDLE ClassTable[SIZE];
};
return ReadInstrumentationSchemaWithLayout(pByte, cbDataMax, initialOffset, lambda);
}
-
-template<class ByteWriter>
-bool WriteCompressedIntToBytes(int32_t value, ByteWriter& byteWriter)
-{
- uint8_t isSigned = 0;
-
- // This function is modeled on CorSigCompressSignedInt, but differs in that
- // it handles arbitrary int32 values, not just a subset
- if (value < 0)
- isSigned = 1;
-
- if ((value & SIGN_MASK_ONEBYTE) == 0 || (value & SIGN_MASK_ONEBYTE) == SIGN_MASK_ONEBYTE)
- {
- return byteWriter((uint8_t)((value & ~SIGN_MASK_ONEBYTE) << 1 | isSigned));
- }
- else if ((value & SIGN_MASK_TWOBYTE) == 0 || (value & SIGN_MASK_TWOBYTE) == SIGN_MASK_TWOBYTE)
- {
- int32_t iData = (int32_t)((value & ~SIGN_MASK_TWOBYTE) << 1 | isSigned);
- _ASSERTE(iData <= 0x3fff);
- byteWriter(uint8_t((iData >> 8) | 0x80));
- return byteWriter(uint8_t(iData & 0xff));
- }
- else
- {
- // Unlike CorSigCompressSignedInt, this just writes a header byte
- // then a full 4 bytes, ignoring the whole signed bit detail
- byteWriter(0xC0);
- byteWriter(uint8_t((value >> 24) & 0xff));
- byteWriter(uint8_t((value >> 16) & 0xff));
- byteWriter(uint8_t((value >> 8) & 0xff));
- return byteWriter(uint8_t((value >> 0) & 0xff));
- }
-}
-
#define SIGN_MASK_ONEBYTE_64BIT 0xffffffffffffffc0LL
#define SIGN_MASK_TWOBYTE_64BIT 0xffffffffffffe000LL
#define SIGN_MASK_FOURBYTE_64BIT 0xffffffff80000000LL
template<class ByteWriter>
bool WriteIndividualSchemaToBytes(ICorJitInfo::PgoInstrumentationSchema prevSchema, ICorJitInfo::PgoInstrumentationSchema curSchema, ByteWriter& byteWriter)
{
- int32_t ilOffsetDiff = curSchema.ILOffset - prevSchema.ILOffset;
- int32_t OtherDiff = curSchema.Other - prevSchema.Other;
- int32_t CountDiff = curSchema.Count - prevSchema.Count;
- int32_t TypeDiff = (int32_t)curSchema.InstrumentationKind - (int32_t)prevSchema.InstrumentationKind;
+ int64_t ilOffsetDiff = (int64_t)curSchema.ILOffset - (int64_t)prevSchema.ILOffset;
+ int64_t OtherDiff = (int64_t)curSchema.Other - (int64_t)prevSchema.Other;
+ int64_t CountDiff = (int64_t)curSchema.Count - (int64_t)prevSchema.Count;
+ int64_t TypeDiff = (int64_t)curSchema.InstrumentationKind - (int64_t)prevSchema.InstrumentationKind;
InstrumentationDataProcessingState modifyMask = (InstrumentationDataProcessingState)0;
// Keep these in sync with src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs
#define READYTORUN_MAJOR_VERSION 0x0005
-#define READYTORUN_MINOR_VERSION 0x0001
+#define READYTORUN_MINOR_VERSION 0x0002
#define MINIMUM_READYTORUN_MAJOR_VERSION 0x003
InliningInfo2 = 114, // Added in V4.1
ComponentAssemblies = 115, // Added in V4.1
OwnerCompositeExecutable = 116, // Added in V4.1
+ PgoInstrumentationData = 117, // Added in 5.2
// If you add a new section consider whether it is a breaking or non-breaking change.
// Usually it is non-breaking, but if it is preferable to have older runtimes fail
#else
internal
#endif
+ class PgoInstrumentedDataVertex : Vertex
+ {
+ private uint _version;
+
+ private BlobVertex _instrumentationData;
+
+ public PgoInstrumentedDataVertex(uint version, BlobVertex instrumentationData)
+ {
+ _version = version;
+ _instrumentationData = instrumentationData;
+ }
+
+ internal override void Save(NativeWriter writer)
+ {
+ int existingOffset = _instrumentationData._offset;
+ if (existingOffset != -1)
+ {
+ writer.WriteUnsigned((_version << 2) | 3);
+ writer.WriteUnsigned((uint)(writer.GetCurrentOffset() - existingOffset));
+ }
+ else
+ {
+ writer.WriteUnsigned((_version << 2) | 1);
+ _instrumentationData.Save(writer);
+ }
+ }
+ }
+
+#if NATIVEFORMAT_PUBLICWRITER
+ public
+#else
+ internal
+#endif
+ class PgoInstrumentedDataWithSignatureBlobVertex : PgoInstrumentedDataVertex
+ {
+ private BlobVertex _signatureBlob;
+
+ public PgoInstrumentedDataWithSignatureBlobVertex(BlobVertex signaureBlob, uint version, BlobVertex instrumentationData)
+ : base(version, instrumentationData)
+ {
+ _signatureBlob = signaureBlob;
+ }
+
+ internal override void Save(NativeWriter writer)
+ {
+ _signatureBlob.Save(writer);
+ base.Save(writer);
+ }
+ }
+
+#if NATIVEFORMAT_PUBLICWRITER
+ public
+#else
+ internal
+#endif
class DebugInfoVertex : Vertex
{
private BlobVertex _debugInfo;
public const uint Signature = 0x00525452; // 'RTR'
public const ushort CurrentMajorVersion = 5;
- public const ushort CurrentMinorVersion = 1;
+ public const ushort CurrentMinorVersion = 2;
}
#pragma warning disable 0169
InliningInfo2 = 114, // Added in 4.1
ComponentAssemblies = 115, // Added in 4.1
OwnerCompositeExecutable = 116, // Added in 4.1
+ PgoInstrumentationData = 117, // Added in 5.2
//
// CoreRT ReadyToRun sections
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
using Internal.TypeSystem;
namespace Internal.Pgo
{
- public struct TypeSystemEntityOrUnknown
+ public struct TypeSystemEntityOrUnknown : IEquatable<TypeSystemEntityOrUnknown>
{
public TypeSystemEntityOrUnknown(int unknownIndex)
{
public FieldDesc AsField => _data as FieldDesc;
public int AsUnknown => (!(_data is int)) || _data == null ? 0 : (int)_data;
public bool IsNull => _data == null;
+
+ public bool Equals(TypeSystemEntityOrUnknown other)
+ {
+ if ((_data is int) && (other._data is int))
+ {
+ return other.AsUnknown == AsUnknown;
+ }
+ else
+ {
+ return object.ReferenceEquals(_data, other._data);
+ }
+ }
+
+ public override int GetHashCode() => _data.GetHashCode();
+
+ public override bool Equals(object obj) => obj is TypeSystemEntityOrUnknown other && other.Equals(this);
}
}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Reflection.Metadata.Ecma335;
+
+using Internal.JitInterface;
+using Internal.NativeFormat;
+using Internal.Pgo;
+using Internal.Runtime;
+using Internal.Text;
+using Internal.TypeSystem;
+using Internal.TypeSystem.Ecma;
+
+namespace ILCompiler.DependencyAnalysis.ReadyToRun
+{
+ public class InstrumentationDataTableNode : HeaderTableNode
+ {
+ private readonly NodeFactory _factory;
+ private ReadyToRunSymbolNodeFactory _symbolNodeFactory;
+ private readonly MethodDesc[] _instrumentationDataMethods;
+ private readonly ProfileDataManager _profileDataManager;
+
+ public InstrumentationDataTableNode(NodeFactory factory, MethodDesc[] instrumentationDataMethods, ProfileDataManager profileDataManager)
+ : base(factory.Target)
+ {
+ _factory = factory;
+ _instrumentationDataMethods = instrumentationDataMethods;
+ _profileDataManager = profileDataManager;
+ }
+
+ public void Initialize(ReadyToRunSymbolNodeFactory symbolNodeFactory)
+ {
+ _symbolNodeFactory = symbolNodeFactory;
+ }
+
+ class PgoValueEmitter : IPgoEncodedValueEmitter<TypeSystemEntityOrUnknown>
+ {
+ public PgoValueEmitter(CompilationModuleGroup compilationGroup, ReadyToRunSymbolNodeFactory factory, bool actuallyCaptureOutput)
+ {
+ _compilationGroup = compilationGroup;
+ _symbolFactory = factory;
+ _actuallyCaptureOutput = actuallyCaptureOutput;
+ }
+
+ public void Clear()
+ {
+ _longs.Clear();
+ _imports.Clear();
+ _typeConversions.Clear();
+ _unknownTypesFound = 0;
+ }
+
+ public IReadOnlyList<Import> ReferencedImports => _imports;
+ List<long> _longs = new List<long>();
+ List<Import> _imports = new List<Import>();
+ Dictionary<TypeSystemEntityOrUnknown, int> _typeConversions = new Dictionary<TypeSystemEntityOrUnknown, int>();
+ int _unknownTypesFound = 0;
+ CompilationModuleGroup _compilationGroup;
+ ReadyToRunSymbolNodeFactory _symbolFactory;
+ bool _actuallyCaptureOutput;
+
+ public byte[] ToByteArray()
+ {
+ return PgoProcessor.PgoEncodedCompressedLongGenerator(_longs).ToArray();
+ }
+
+ public bool EmitDone()
+ {
+ // This writer wants the done emitted as a schema terminator
+ return false;
+ }
+ public void EmitLong(long value, long previousValue)
+ {
+ if (_actuallyCaptureOutput)
+ _longs.Add(value - previousValue);
+ }
+ public void EmitType(TypeSystemEntityOrUnknown type, TypeSystemEntityOrUnknown previousValue)
+ {
+ EmitLong(TypeToInt(type), TypeToInt(previousValue));
+ }
+
+ private int TypeToInt(TypeSystemEntityOrUnknown type)
+ {
+ if (type.IsNull || (type.AsType == null && type.AsUnknown == 0))
+ return 0;
+
+ if (_typeConversions.TryGetValue(type, out int computedInt))
+ {
+ return computedInt;
+ }
+ if (type.AsType != null && _compilationGroup.VersionsWithType(type.AsType))
+ {
+ Import typeHandleImport = (Import)_symbolFactory.CreateReadyToRunHelper(ReadyToRunHelperId.TypeHandle, type.AsType);
+ _imports.Add(typeHandleImport);
+
+ if (_actuallyCaptureOutput)
+ {
+ if (typeHandleImport.Table.IndexFromBeginningOfArray >= 0xF)
+ {
+ // The current implementation of this table only allows for 15 different
+ // import tables to be used. This is probably enough for long term
+ // but this code will throw if we use more import tables and attempt
+ // to encode pgo data
+ throw new Exception("Unexpected high index for table import");
+ }
+
+ computedInt = (typeHandleImport.IndexFromBeginningOfArray << 4) | typeHandleImport.Table.IndexFromBeginningOfArray;
+ }
+ else
+ {
+ computedInt = _imports.Count << 1;
+ }
+ }
+ else
+ {
+ computedInt = ((++_unknownTypesFound) << 4) | 0xF;
+ }
+ _typeConversions.Add(type, computedInt);
+ return computedInt;
+ }
+ }
+
+ public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
+ {
+ sb.Append(nameMangler.CompilationUnitPrefix);
+ sb.Append("__ReadyToRunInstrumentationDataTable");
+ }
+
+ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory)
+ {
+ PgoValueEmitter pgoEmitter = new PgoValueEmitter(_factory.CompilationModuleGroup, _symbolNodeFactory, false);
+ foreach (MethodDesc method in _instrumentationDataMethods)
+ {
+ PgoProcessor.EncodePgoData(_profileDataManager[method].SchemaData, pgoEmitter, false);
+ }
+ DependencyListEntry[] symbols = new DependencyListEntry[pgoEmitter.ReferencedImports.Count];
+ for (int i = 0; i < symbols.Length; i++)
+ {
+ symbols[i] = new DependencyListEntry(pgoEmitter.ReferencedImports[i], "Pgo Instrumentation Data");
+ }
+
+ return new DependencyList(symbols);
+ }
+
+
+ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
+ {
+ if (relocsOnly)
+ {
+ return new ObjectData(Array.Empty<byte>(), Array.Empty<Relocation>(), 1, Array.Empty<ISymbolDefinitionNode>());
+ }
+
+ PgoValueEmitter pgoEmitter = new PgoValueEmitter(_factory.CompilationModuleGroup, _symbolNodeFactory, true);
+ NativeWriter hashtableWriter = new NativeWriter();
+
+ Section hashtableSection = hashtableWriter.NewSection();
+ VertexHashtable vertexHashtable = new VertexHashtable();
+ hashtableSection.Place(vertexHashtable);
+
+ Dictionary<byte[], BlobVertex> uniqueInstrumentationData = new Dictionary<byte[], BlobVertex>(ByteArrayComparer.Instance);
+
+ foreach (MethodDesc method in _instrumentationDataMethods)
+ {
+ pgoEmitter.Clear();
+ PgoProcessor.EncodePgoData(_profileDataManager[method].SchemaData, pgoEmitter, false);
+
+ // In composite R2R format, always enforce owning type to let us share generic instantiations among modules
+ EcmaMethod typicalMethod = (EcmaMethod)method.GetTypicalMethodDefinition();
+ ModuleToken moduleToken = new ModuleToken(typicalMethod.Module, typicalMethod.Handle);
+
+ ArraySignatureBuilder signatureBuilder = new ArraySignatureBuilder();
+ signatureBuilder.EmitMethodSignature(
+ new MethodWithToken(method, moduleToken, constrainedType: null, unboxing: false, context: null),
+ enforceDefEncoding: true,
+ enforceOwningType: _factory.CompilationModuleGroup.EnforceOwningType(moduleToken.Module),
+ factory.SignatureContext,
+ isInstantiatingStub: false);
+ byte[] signature = signatureBuilder.ToArray();
+ BlobVertex signatureBlob = new BlobVertex(signature);
+
+ byte[] encodedInstrumentationData = pgoEmitter.ToByteArray();
+ BlobVertex instrumentationDataBlob = null;
+ if (!uniqueInstrumentationData.TryGetValue(encodedInstrumentationData, out instrumentationDataBlob))
+ {
+ instrumentationDataBlob = new BlobVertex(encodedInstrumentationData);
+ hashtableSection.Place(instrumentationDataBlob);
+ uniqueInstrumentationData.Add(encodedInstrumentationData, instrumentationDataBlob);
+ }
+
+ PgoInstrumentedDataWithSignatureBlobVertex pgoDataVertex = new PgoInstrumentedDataWithSignatureBlobVertex(signatureBlob, 0, instrumentationDataBlob);
+ hashtableSection.Place(pgoDataVertex);
+ vertexHashtable.Append(unchecked((uint)ReadyToRunHashCode.MethodHashCode(method)), pgoDataVertex);
+ }
+
+ MemoryStream hashtableContent = new MemoryStream();
+ hashtableWriter.Save(hashtableContent);
+ return new ObjectData(
+ data: hashtableContent.ToArray(),
+ relocs: null,
+ alignment: 8,
+ definedSymbols: new ISymbolDefinitionNode[] { this });
+ }
+
+ public override int ClassCode => 1887299452;
+ }
+}
public CompilationModuleGroup CompilationModuleGroup { get; }
+ public ProfileDataManager ProfileDataManager { get; }
+
public NameMangler NameMangler { get; }
public MetadataManager MetadataManager { get; }
public NodeFactory(
CompilerTypeSystemContext context,
CompilationModuleGroup compilationModuleGroup,
+ ProfileDataManager profileDataManager,
NameMangler nameMangler,
CopiedCorHeaderNode corHeaderNode,
DebugDirectoryNode debugDirectoryNode,
{
TypeSystemContext = context;
CompilationModuleGroup = compilationModuleGroup;
+ ProfileDataManager = profileDataManager;
Target = context.Target;
NameMangler = nameMangler;
MetadataManager = new ReadyToRunTableManager(context);
public ImportSectionsTableNode ImportSectionsTable;
+ public InstrumentationDataTableNode InstrumentationDataTable;
+
public Import ModuleImport;
public ISymbolNode PersonalityRoutine;
graph.AddRoot(FilterFuncletPersonalityRoutine, "Filter funclet personality routine is faster to root early rather than referencing it from each unwind info");
}
+ if ((ProfileDataManager != null) && (ProfileDataManager.EmbedPgoDataInR2RImage))
+ {
+ // Profile instrumentation data attaches here
+ HashSet<MethodDesc> methodsToInsertInstrumentationDataFor = new HashSet<MethodDesc>();
+ foreach (EcmaModule inputModule in CompilationModuleGroup.CompilationModuleSet)
+ {
+ foreach (MethodDesc method in ProfileDataManager.GetMethodsForModuleDesc(inputModule))
+ {
+ if (ProfileDataManager[method].SchemaData != null)
+ {
+ methodsToInsertInstrumentationDataFor.Add(method);
+ }
+ }
+ }
+ if (methodsToInsertInstrumentationDataFor.Count != 0)
+ {
+ MethodDesc[] methodsToInsert = methodsToInsertInstrumentationDataFor.ToArray();
+ methodsToInsert.MergeSort(new TypeSystemComparer().Compare);
+ InstrumentationDataTable = new InstrumentationDataTableNode(this, methodsToInsert, ProfileDataManager);
+ Header.Add(Internal.Runtime.ReadyToRunSectionType.PgoInstrumentationData, InstrumentationDataTable, InstrumentationDataTable);
+ }
+ }
+
MethodImports = new ImportSectionNode(
"MethodImports",
CorCompileImportType.CORCOMPILE_IMPORT_TYPE_STUB_DISPATCH,
IReadOnlyList<string> mibcFiles,
CallChainProfile callChainProfile,
CompilerTypeSystemContext context,
- ReadyToRunCompilationModuleGroupBase compilationGroup)
+ ReadyToRunCompilationModuleGroupBase compilationGroup,
+ bool embedPgoDataInR2RImage)
{
+ EmbedPgoDataInR2RImage = embedPgoDataInR2RImage;
_ibcParser = new IBCProfileParser(logger, possibleReferenceModules);
_compilationGroup = compilationGroup;
_callChainProfile = callChainProfile;
}
}
+ public bool EmbedPgoDataInR2RImage { get; }
public CallChainProfile CallChainProfile => _callChainProfile;
}
}
_generateProfileFile = generateProfileFile;
_customPESectionAlignment = customPESectionAlignment;
SymbolNodeFactory = new ReadyToRunSymbolNodeFactory(nodeFactory, verifyTypeAndFieldLayout);
+ if (nodeFactory.InstrumentationDataTable != null)
+ nodeFactory.InstrumentationDataTable.Initialize(SymbolNodeFactory);
_corInfoImpls = new ConditionalWeakTable<Thread, CorInfoImpl>();
_inputFiles = inputFiles;
_compositeRootPath = compositeRootPath;
NodeFactory componentFactory = new NodeFactory(
_nodeFactory.TypeSystemContext,
_nodeFactory.CompilationModuleGroup,
+ null,
_nodeFactory.NameMangler,
copiedCorHeader,
debugDirectory,
NodeFactory factory = new NodeFactory(
_context,
_compilationGroup,
+ _profileData,
_nameMangler,
corHeaderNode,
debugDirectoryNode,
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\ImportSectionsTableNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\InliningInfoNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\InstanceEntryPointTableNode.cs" />
+ <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\InstrumentationDataTableNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\OwnerCompositeExecutableNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\PrecodeMethodImport.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\DelayLoadMethodImport.cs" />
<Compile Include="..\..\Common\Internal\Runtime\ModuleHeaders.cs" Link="Common\ModuleHeaders.cs" />
<Compile Include="..\..\Common\Internal\Runtime\ReadyToRunConstants.cs" Link="Common\ReadyToRunConstants.cs" />
<Compile Include="..\..\Common\Internal\Runtime\ReadyToRunInstructionSet.cs" Link="Common\ReadyToRunInstructionSet.cs" />
+ <Compile Include="..\..\Common\Pgo\PgoFormat.cs" Link="Common\PgoFormat.cs" />
<Compile Include="..\..\Common\TypeSystem\Common\TargetArchitecture.cs" Link="Common\TargetArchitecture.cs" />
</ItemGroup>
</Project>
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Linq;
+using System.Text;
+using Internal.Pgo;
+
+namespace ILCompiler.Reflection.ReadyToRun
+{
+ public class PgoInfo
+ {
+ public PgoInfoKey Key { get; }
+ public int Offset { get; }
+ public int PgoFormatVersion { get; }
+ public byte[] Image { get; }
+ private ReadyToRunReader _r2rReader;
+
+ public PgoInfo(PgoInfoKey key, ReadyToRunReader r2rReader, int pgoFormatVersion, byte[] image, int offset)
+ {
+ PgoFormatVersion = pgoFormatVersion;
+ Key = key;
+ Offset = offset;
+ Image = image;
+ _r2rReader = r2rReader;
+ }
+
+ // The empty singleton cannot be used for anything except reference equality comparison
+ public static PgoInfo EmptySingleton => new PgoInfo(null, null, 0, null, 0);
+
+ PgoSchemaElem[] _pgoData;
+ int _size;
+
+ class PgoDataLoader : IPgoSchemaDataLoader<string>
+ {
+ ReadyToRunReader _r2rReader;
+ SignatureFormattingOptions _formatOptions;
+
+ public PgoDataLoader(ReadyToRunReader r2rReader, SignatureFormattingOptions formatOptions)
+ {
+ _formatOptions = formatOptions;
+ _r2rReader = r2rReader;
+ }
+
+ string IPgoSchemaDataLoader<string>.TypeFromLong(long input)
+ {
+ int tableIndex = checked((int)(input & 0xF));
+ int fixupIndex = checked((int)(input >> 4));
+ if (tableIndex == 0xF)
+ {
+ return $"Unknown type {fixupIndex}";
+ }
+ else
+ {
+ return _r2rReader.ImportSections[tableIndex].Entries[fixupIndex].Signature.ToString(_formatOptions);
+ }
+ }
+ }
+
+ void EnsurePgoData()
+ {
+ if (_pgoData == null)
+ {
+ var compressedIntParser = new PgoProcessor.PgoEncodedCompressedIntParser(Image, Offset);
+
+ SignatureFormattingOptions formattingOptions = new SignatureFormattingOptions();
+
+ _pgoData = PgoProcessor.ParsePgoData<string>(new PgoDataLoader(_r2rReader, formattingOptions), compressedIntParser, true).ToArray();
+ _size = compressedIntParser.Offset - Offset;
+ }
+ }
+
+ public PgoSchemaElem[] PgoData
+ {
+ get
+ {
+ EnsurePgoData();
+ return _pgoData;
+ }
+ }
+
+ public int Size
+ {
+ get
+ {
+ EnsurePgoData();
+ return _size;
+ }
+ }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ foreach (PgoSchemaElem elem in PgoData)
+ {
+ sb.AppendLine($"ILOffset: {elem.ILOffset} InstrumentationKind: {elem.InstrumentationKind} Other: {elem.Other} Count: {elem.Count}");
+ if (elem.DataHeldInDataLong)
+ {
+ sb.AppendLine(elem.DataLong.ToString());
+ }
+ else
+ {
+ foreach (object o in elem.DataObject)
+ {
+ sb.AppendLine(o.ToString());
+ }
+ }
+ }
+
+ return sb.ToString();
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Reflection.Metadata;
+using System.Text;
+using Internal.Pgo;
+
+namespace ILCompiler.Reflection.ReadyToRun
+{
+ public class PgoInfoKey : IEquatable<PgoInfoKey>
+ {
+ /// <summary>
+ /// The type that the method belongs to
+ /// </summary>
+ public string DeclaringType { get; }
+
+ public string Name { get; }
+
+ /// <summary>
+ /// The method metadata handle
+ /// </summary>
+ public EntityHandle MethodHandle { get; }
+
+ public IAssemblyMetadata ComponentReader { get; }
+
+ public MethodSignature<string> Signature { get; }
+
+ /// <summary>
+ /// The signature with format: namespace.class.methodName<S, T, ...>(S, T, ...)
+ /// </summary>
+ public string SignatureString { get; }
+
+ public PgoInfoKey(IAssemblyMetadata componentReader, string owningType, EntityHandle methodHandle, string[] instanceArgs)
+ {
+ ComponentReader = componentReader;
+ EntityHandle owningTypeHandle;
+ DisassemblingGenericContext genericContext = new DisassemblingGenericContext(typeParameters: Array.Empty<string>(), methodParameters: instanceArgs);
+ DisassemblingTypeProvider typeProvider = new DisassemblingTypeProvider();
+ MethodHandle = methodHandle;
+
+ // get the method signature from the method handle
+ switch (MethodHandle.Kind)
+ {
+ case HandleKind.MethodDefinition:
+ {
+ MethodDefinition methodDef = componentReader.MetadataReader.GetMethodDefinition((MethodDefinitionHandle)MethodHandle);
+ Name = componentReader.MetadataReader.GetString(methodDef.Name);
+ Signature = methodDef.DecodeSignature<string, DisassemblingGenericContext>(typeProvider, genericContext);
+ owningTypeHandle = methodDef.GetDeclaringType();
+ }
+ break;
+
+ case HandleKind.MemberReference:
+ {
+ MemberReference memberRef = componentReader.MetadataReader.GetMemberReference((MemberReferenceHandle)MethodHandle);
+ Name = componentReader.MetadataReader.GetString(memberRef.Name);
+ Signature = memberRef.DecodeMethodSignature<string, DisassemblingGenericContext>(typeProvider, genericContext);
+ owningTypeHandle = memberRef.Parent;
+ }
+ break;
+
+ default:
+ throw new NotImplementedException();
+ }
+
+ if (owningType != null)
+ {
+ DeclaringType = owningType;
+ }
+ else
+ {
+ DeclaringType = MetadataNameFormatter.FormatHandle(componentReader.MetadataReader, owningTypeHandle);
+ }
+
+ StringBuilder sb = new StringBuilder();
+ sb.Append(Signature.ReturnType);
+ sb.Append(" ");
+ sb.Append(DeclaringType);
+ sb.Append(".");
+ sb.Append(Name);
+
+ if (Signature.GenericParameterCount != 0)
+ {
+ sb.Append("<");
+ for (int i = 0; i < Signature.GenericParameterCount; i++)
+ {
+ if (i > 0)
+ {
+ sb.Append(", ");
+ }
+ if (instanceArgs != null && instanceArgs.Length > i)
+ {
+ sb.Append(instanceArgs[i]);
+ }
+ else
+ {
+ sb.Append("!");
+ sb.Append(i);
+ }
+ }
+ sb.Append(">");
+ }
+
+ sb.Append("(");
+ for (int i = 0; i < Signature.ParameterTypes.Length; i++)
+ {
+ if (i > 0)
+ {
+ sb.Append(", ");
+ }
+ sb.AppendFormat($"{Signature.ParameterTypes[i]}");
+ }
+ sb.Append(")");
+
+ SignatureString = sb.ToString();
+ }
+
+ public override string ToString() => SignatureString;
+ public override int GetHashCode() => SignatureString.GetHashCode();
+ public override bool Equals(object obj) => obj is PgoInfoKey other && other.Equals(this);
+
+ // Equality check. This isn't precisely accurate, but it should be good enough
+ public bool Equals(PgoInfoKey other)
+ {
+ return SignatureString.Equals(other.SignatureString);
+ }
+
+ public static PgoInfoKey FromReadyToRunMethod(ReadyToRunMethod method)
+ {
+ var key = new PgoInfoKey(method.ComponentReader, method.DeclaringType, method.MethodHandle, method.InstanceArgs);
+ Debug.Assert(key.SignatureString == method.SignatureString);
+ return key;
+ }
+ }
+}
\ No newline at end of file
private BaseGcInfo _gcInfo;
+ public PgoInfo PgoInfo
+ {
+ get
+ {
+ EnsureInitialized();
+ if (_pgoInfo == PgoInfo.EmptySingleton)
+ return null;
+ return _pgoInfo;
+ }
+ }
+
+ private PgoInfo _pgoInfo;
private ReadyToRunReader _readyToRunReader;
private List<FixupCell> _fixupCells;
}
}
+ public string[] InstanceArgs { get; set; }
+
public int RuntimeFunctionCount { get; set; }
/// <summary>
string[] instanceArgs,
int? fixupOffset)
{
+ InstanceArgs = (string[])instanceArgs?.Clone();
_readyToRunReader = readyToRunReader;
_fixupOffset = fixupOffset;
MethodHandle = methodHandle;
_gcInfo = new Amd64.GcInfo(_readyToRunReader.Image, gcInfoOffset, _readyToRunReader.Machine, _readyToRunReader.ReadyToRunHeader.MajorVersion);
}
}
+ if (_pgoInfo == null)
+ {
+ _pgoInfo = _readyToRunReader.GetPgoInfoByKey(PgoInfoKey.FromReadyToRunMethod(this));
+ if (_pgoInfo == null)
+ {
+ _pgoInfo = PgoInfo.EmptySingleton;
+ }
+ }
}
private void EnsureFixupCells()
// Methods
private List<InstanceMethod> _instanceMethods;
+ // PgoData
+ private Dictionary<PgoInfoKey, PgoInfo> _pgoInfos;
+
// ImportSections
private List<ReadyToRunImportSection> _importSections;
private Dictionary<int, ReadyToRunSignature> _importSignatures;
return;
}
+ if (ReadyToRunHeader.Sections.TryGetValue(ReadyToRunSectionType.PgoInstrumentationData, out _))
+ {
+ ParsePgoMethods();
+ }
+
_instanceMethods = new List<InstanceMethod>();
foreach (ReadyToRunAssembly assembly in _readyToRunAssemblies)
{
}
}
+ public IEnumerable<PgoInfo> AllPgoInfos
+ {
+ get
+ {
+ EnsureMethods();
+ return _pgoInfos.Values;
+ }
+ }
+
+ public PgoInfo GetPgoInfoByKey(PgoInfoKey key)
+ {
+ EnsureMethods();
+ if (_pgoInfos != null)
+ {
+ _pgoInfos.TryGetValue(key, out var returnValue);
+ return returnValue;
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Initialize generic method instances with argument types and runtime function indices from InstanceMethodEntrypoints
+ /// </summary>
+ private void ParsePgoMethods()
+ {
+ _pgoInfos = new Dictionary<PgoInfoKey, PgoInfo>();
+ if (!ReadyToRunHeader.Sections.TryGetValue(ReadyToRunSectionType.PgoInstrumentationData, out ReadyToRunSection pgoInstrumentationDataSection))
+ {
+ return;
+ }
+ int pgoInstrumentationDataOffset = GetOffset(pgoInstrumentationDataSection.RelativeVirtualAddress);
+ NativeParser parser = new NativeParser(Image, (uint)pgoInstrumentationDataOffset);
+ NativeHashtable pgoInstrumentationData = new NativeHashtable(Image, parser, (uint)(pgoInstrumentationDataOffset + pgoInstrumentationDataSection.Size));
+ NativeHashtable.AllEntriesEnumerator allEntriesEnum = pgoInstrumentationData.EnumerateAllEntries();
+ NativeParser curParser = allEntriesEnum.GetNext();
+ while (!curParser.IsNull())
+ {
+ IAssemblyMetadata mdReader = GetGlobalMetadata();
+ SignatureFormattingOptions dummyOptions = new SignatureFormattingOptions();
+ SignatureDecoder decoder = new SignatureDecoder(_assemblyResolver, dummyOptions, mdReader?.MetadataReader, this, (int)curParser.Offset);
+
+ string owningType = null;
+
+ uint methodFlags = decoder.ReadUInt();
+ if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_OwnerType) != 0)
+ {
+ mdReader = decoder.GetMetadataReaderFromModuleOverride() ?? mdReader;
+ if ((_composite) && mdReader == null)
+ {
+ // The only types that don't have module overrides on them in composite images are primitive types within the system module
+ mdReader = GetSystemModuleMetadataReader();
+ }
+ owningType = decoder.ReadTypeSignatureNoEmit();
+ }
+ if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_SlotInsteadOfToken) != 0)
+ {
+ throw new NotImplementedException();
+ }
+ EntityHandle methodHandle;
+ int rid = (int)decoder.ReadUInt();
+ if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_MemberRefToken) != 0)
+ {
+ methodHandle = MetadataTokens.MemberReferenceHandle(rid);
+ }
+ else
+ {
+ methodHandle = MetadataTokens.MethodDefinitionHandle(rid);
+ }
+ string[] methodTypeArgs = null;
+ if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_MethodInstantiation) != 0)
+ {
+ uint typeArgCount = decoder.ReadUInt();
+ methodTypeArgs = new string[typeArgCount];
+ for (int typeArgIndex = 0; typeArgIndex < typeArgCount; typeArgIndex++)
+ {
+ methodTypeArgs[typeArgIndex] = decoder.ReadTypeSignatureNoEmit();
+ }
+ }
+
+ string constrainedType = null;
+ if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_Constrained) != 0)
+ {
+ constrainedType = decoder.ReadTypeSignatureNoEmit();
+ }
+
+ GetPgoOffsetAndVersion(decoder.Offset, out int pgoFormatVersion, out int pgoOffset);
+
+ PgoInfoKey key = new PgoInfoKey(GetGlobalMetadata(), owningType, methodHandle, methodTypeArgs);
+ PgoInfo info = new PgoInfo(key, this, pgoFormatVersion, Image, pgoOffset);
+ _pgoInfos.Add(key, info);
+ curParser = allEntriesEnum.GetNext();
+ }
+
+ return;
+
+ // Broken out into helper function in case we ever implement pgo data that isn't tied to a signature
+ void GetPgoOffsetAndVersion(int offset, out int version, out int pgoDataOffset)
+ {
+ version = 0;
+ // get the id of the entry point runtime function from the MethodEntryPoints NativeArray
+ uint versionAndFlags = 0; // the RUNTIME_FUNCTIONS index
+ offset = (int)NativeReader.DecodeUnsigned(Image, (uint)offset, ref versionAndFlags);
+
+ switch (versionAndFlags & 3)
+ {
+ case 3:
+ uint val = 0;
+ NativeReader.DecodeUnsigned(Image, (uint)offset, ref val);
+ offset -= (int)val;
+ break;
+ case 1:
+ // Offset already correct
+ break;
+ default:
+ throw new Exception("Invalid Pgo format");
+ }
+
+ version = (int)(versionAndFlags >> 2);
+ pgoDataOffset = offset;
+ }
+ }
+
private void CountRuntimeFunctions(bool[] isEntryPoint)
{
foreach (ReadyToRunMethod method in Methods)
public bool Verbose;
public bool Composite;
public bool CompileNoMethods;
+ public bool EmbedPgoData;
public string DgmlLogFileName;
public bool GenerateFullDgmlLog;
syntax.DefineOption("tuning", ref Tuning, SR.TuningImageOption);
syntax.DefineOption("partial", ref Partial, SR.PartialImageOption);
syntax.DefineOption("compilebubblegenerics", ref CompileBubbleGenerics, SR.BubbleGenericsOption);
+ syntax.DefineOption("embed-pgo-data", ref EmbedPgoData, SR.EmbedPgoDataOption);
syntax.DefineOption("dgmllog|dgml-log-file-name", ref DgmlLogFileName, SR.SaveDependencyLogOption);
syntax.DefineOption("fulllog|generate-full-dmgl-log", ref GenerateFullDgmlLog, SR.SaveDetailedLogOption);
syntax.DefineOption("verbose", ref Verbose, SR.VerboseLoggingOption);
mibcFiles,
jsonProfile,
_typeSystemContext,
- compilationGroup);
+ compilationGroup,
+ _commandLineOptions.EmbedPgoData);
if (_commandLineOptions.Partial)
compilationGroup.ApplyProfilerGuidedCompilationRestriction(profileDataManager);
<data name="BubbleGenericsOption" xml:space="preserve">
<value>Compile instantiations from reference modules used in the current module</value>
</data>
+ <data name="EmbedPgoDataOption" xml:space="preserve">
+ <value>Embed instrumentation data in generated file</value>
+ </data>
<data name="CodeGenOptions" xml:space="preserve">
<value>Define a codegen option</value>
</data>
</PropertyGroup>
<ItemGroup>
- <Compile Include="..\Common\Pgo\PgoFormat.cs" Link="PgoFormat.cs" />
<Compile Include="..\Common\Pgo\TypeSystemEntityOrUnknown.cs" Link="TypeSystemEntityOrUnknown.cs" />
</ItemGroup>
command.AddOption(new Option<string[]>(new[] { "--section", "-s" }, "Get section by keyword"));
command.AddOption(new Option<bool>(new[] { "--unwind" }, "Dump unwindInfo"));
command.AddOption(new Option<bool>(new[] { "--gc" }, "Dump gcInfo and slot table"));
+ command.AddOption(new Option<bool>(new[] { "--pgo" }, "Dump embedded pgo instrumentation data"));
command.AddOption(new Option<bool>(new[] { "--sectionContents", "--sc" }, "Dump section contents"));
command.AddOption(new Option<bool>(new[] { "--entrypoints", "-e" }, "Dump list of method / instance entrypoints in the R2R file"));
command.AddOption(new Option<bool>(new[] { "--normalize", "-n" }, "Normalize dump by sorting the various tables and methods (default = unsorted i.e. file order)"));
public bool Unwind { get; set; }
public bool GC { get; set; }
+ public bool Pgo { get; set; }
public bool SectionContents { get; set; }
public bool EntryPoints { get; set; }
public bool Normalize { get; set; }
_options.Disasm = true;
_options.Unwind = true;
_options.GC = true;
+ _options.Pgo = true;
_options.SectionContents = true;
}
WriteDivider("R2R Methods");
_writer.WriteLine($"{_r2r.Methods.Count()} methods");
SkipLine();
+
+ HashSet<PgoInfoKey> pgoEntriesNotDumped = new HashSet<PgoInfoKey>();
+
+ if (_options.Pgo)
+ {
+ foreach (PgoInfo info in _r2r.AllPgoInfos)
+ {
+ pgoEntriesNotDumped.Add(info.Key);
+ }
+ }
foreach (ReadyToRunMethod method in NormalizedMethods())
{
TextWriter temp = _writer;
_writer = new StringWriter();
DumpMethod(method);
+ if (_options.Pgo && method.PgoInfo != null)
+ {
+ pgoEntriesNotDumped.Remove(method.PgoInfo.Key);
+ }
temp.Write(_writer.ToString());
_writer = temp;
}
+
+ if (pgoEntriesNotDumped.Count > 0)
+ {
+ WriteDivider("Pgo entries without code");
+ _writer.WriteLine($"{pgoEntriesNotDumped.Count()} Pgo blobs");
+ IEnumerable<PgoInfoKey> pgoEntriesToDump = pgoEntriesNotDumped;
+ if (_options.Normalize)
+ {
+ pgoEntriesToDump = pgoEntriesToDump.OrderBy((p) => p.SignatureString);
+ }
+ foreach (var entry in pgoEntriesToDump)
+ {
+ WriteSubDivider();
+ _writer.WriteLine(entry.SignatureString);
+ _writer.WriteLine($"Handle: 0x{MetadataTokens.GetToken(entry.ComponentReader.MetadataReader, entry.MethodHandle):X8}");
+ _writer.WriteLine(_r2r.GetPgoInfoByKey(entry));
+ }
+ }
}
/// <summary>
}
SkipLine();
+ if (_options.Pgo && method.PgoInfo != null)
+ {
+ _writer.WriteLine("PGO info:");
+ _writer.Write(method.PgoInfo);
+
+ if (_options.Raw)
+ {
+ DumpBytes(method.PgoInfo.Offset, (uint)method.PgoInfo.Size, convertToOffset: false);
+ }
+ SkipLine();
+ }
+
foreach (RuntimeFunction runtimeFunction in method.RuntimeFunctions)
{
DumpRuntimeFunction(runtimeFunction);
ICorJitInfo::ClassProfile* const classProfile = (ICorJitInfo::ClassProfile*) tableAddress;
volatile unsigned* pCount = (volatile unsigned*) &classProfile->Count;
- const unsigned count = *pCount++;
+ const unsigned count = (*pCount)++;
const unsigned S = ICorJitInfo::ClassProfile::SIZE;
const unsigned N = ICorJitInfo::ClassProfile::SAMPLE_INTERVAL;
_ASSERTE(N >= S);
{
if (pDataCur->m_pMD == pMD)
{
- *pSchema = pDataCur->m_schema.GetElements();
- *pCountSchemaItems = pDataCur->m_schema.GetCount();
- *pInstrumentationData = pDataCur->m_pInstrumentationData;
- hr = pDataCur->m_hr;
break;
}
}
m_foundPgoData = newPgoData;
newPgoData.SuppressRelease();
- newPgoData->m_hr = PgoManager::getPgoInstrumentationResults(pMD, &newPgoData->m_schema, &newPgoData->m_pInstrumentationData);
+ newPgoData->m_hr = PgoManager::getPgoInstrumentationResults(pMD, &newPgoData->m_allocatedData, &newPgoData->m_schema, &newPgoData->m_cSchemaElems, &newPgoData->m_pInstrumentationData);
pDataCur = m_foundPgoData;
}
- *pSchema = pDataCur->m_schema.GetElements();
- *pCountSchemaItems = pDataCur->m_schema.GetCount();
+ *pSchema = pDataCur->m_schema;
+ *pCountSchemaItems = pDataCur->m_cSchemaElems;
*pInstrumentationData = pDataCur->m_pInstrumentationData;
hr = pDataCur->m_hr;
#else
optType = methodInfo->ILCodeSize % OPT_RANDOM;
if (g_pConfig->JitMinOpts())
+ {
flags.Set(CORJIT_FLAGS::CORJIT_FLAG_MIN_OPT);
+ }
+ else
+ {
+ if (!flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_TIER0))
+ flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBOPT);
+ }
if (optType == OPT_SIZE)
{
ComputedPgoData* m_next = nullptr;
MethodDesc *m_pMD;
- SArray<PgoInstrumentationSchema> m_schema;
+ NewArrayHolder<BYTE> m_allocatedData;
+ PgoInstrumentationSchema* m_schema;
+ UINT32 m_cSchemaElems;
BYTE *m_pInstrumentationData = nullptr;
HRESULT m_hr = E_NOTIMPL;
};
{
LoaderHeap *pHeap = pLoaderAllocator->GetHighFrequencyHeap();
- m_pReadyToRunInfo = new ReadyToRunInfo(/*pModule*/ NULL, m_pImageLayout, pHeader, /*compositeImage*/ NULL, pamTracker);
+ m_pReadyToRunInfo = new ReadyToRunInfo(/*pModule*/ NULL, pLoaderAllocator, m_pImageLayout, pHeader, /*compositeImage*/ NULL, pamTracker);
m_pComponentAssemblies = m_pReadyToRunInfo->FindSection(ReadyToRunSectionType::ComponentAssemblies);
m_componentAssemblyCount = m_pComponentAssemblies->Size / sizeof(READYTORUN_COMPONENT_ASSEMBLIES_ENTRY);
struct HistogramEntry
{
// Class that was observed at runtime
- CORINFO_CLASS_HANDLE m_mt;
+ INT_PTR m_mt; // This may be an "unknown type handle"
// Number of observations in the table
unsigned m_count;
};
for (unsigned k = 0; k < entryCount; k++)
{
-
if (histogramEntries[k] == 0)
{
continue;
m_totalCount++;
- if (IsUnknownTypeHandle(histogramEntries[k]))
- {
- if (AddTypeHandleToUnknownTypeHandleMask(histogramEntries[k], &unknownTypeHandleMask))
- {
- m_unknownTypes++;
- }
- // An unknown type handle will adjust total count but not set of entries
- continue;
- }
-
- CORINFO_CLASS_HANDLE currentEntry = (CORINFO_CLASS_HANDLE)histogramEntries[k];
+ INT_PTR currentEntry = histogramEntries[k];
bool found = false;
unsigned h = 0;
void PgoManager::Shutdown()
{
- WritePgoData();
+ static bool written = false;
+
+ if (!written)
+ {
+ written = true;
+ WritePgoData();
+ }
}
void PgoManager::VerifyAddress(void* address)
*pInstrumentationData = NULL;
int codehash;
unsigned ilSize;
+
if (!GetVersionResilientILCodeHashCode(pMD, &codehash, &ilSize))
{
return E_NOTIMPL;
prevSchema = pSchema[iSchema];
}
- S_SIZE_T allocationSize = S_SIZE_T(sizeof(HeaderList)) + S_SIZE_T(pSchema[countSchemaItems - 1].Offset + InstrumentationKindToSize(pSchema[countSchemaItems - 1].InstrumentationKind));
+ S_SIZE_T allocationSize = S_SIZE_T(sizeof(HeaderList)) + S_SIZE_T(pSchema[countSchemaItems - 1].Offset) + S_SIZE_T(pSchema[countSchemaItems - 1].Count) * S_SIZE_T(InstrumentationKindToSize(pSchema[countSchemaItems - 1].InstrumentationKind));
if (allocationSize.IsOverflow())
{
return E_NOTIMPL;
}
size_t unsafeAllocationSize = allocationSize.Value();
+ if (unsafeAllocationSize % sizeof(size_t) == 4)
+ {
+ allocationSize = allocationSize + S_SIZE_T(4);
+
+ if (allocationSize.IsOverflow())
+ {
+ return E_NOTIMPL;
+ }
+ unsafeAllocationSize = allocationSize.Value();
+ }
+
HeaderList* pHeaderList = NULL;
if (pMD->IsDynamicMethod())
}
#ifndef DACCESS_COMPILE
-HRESULT PgoManager::getPgoInstrumentationResults(MethodDesc* pMD, SArray<ICorJitInfo::PgoInstrumentationSchema>* pSchema, BYTE**pInstrumentationData)
+HRESULT PgoManager::getPgoInstrumentationResults(MethodDesc* pMD, BYTE** pAllocatedData, ICorJitInfo::PgoInstrumentationSchema** ppSchema, UINT32 *pCountSchemaItems, BYTE**pInstrumentationData)
{
// Initialize our out params
- pSchema->Clear();
+ *pAllocatedData = NULL;
*pInstrumentationData = NULL;
+ *pCountSchemaItems = 0;
PgoManager *mgr;
if (!pMD->IsDynamicMethod())
HRESULT hr = E_NOTIMPL;
if (mgr != NULL)
{
- hr = mgr->getPgoInstrumentationResultsInstance(pMD, pSchema, pInstrumentationData);
+ hr = mgr->getPgoInstrumentationResultsInstance(pMD, pAllocatedData, ppSchema, pCountSchemaItems, pInstrumentationData);
}
// If not found in the data from the current run, look in the data from the text file
Header *found = s_textFormatPgoData.Lookup(CodeAndMethodHash(codehash, methodhash));
if (found != NULL)
{
- if (ReadInstrumentationSchemaWithLayoutIntoSArray(found->GetData(), found->countsOffset, found->countsOffset, pSchema))
+ StackSArray<ICorJitInfo::PgoInstrumentationSchema> schemaArray;
+
+ if (ReadInstrumentationSchemaWithLayoutIntoSArray(found->GetData(), found->countsOffset, found->countsOffset, &schemaArray))
{
+
EX_TRY
{
// TypeHandles can't reliably be loaded at ReadPGO time
// terminated strings in the TypeHandle slots, and this will
// translate any of those into loaded TypeHandles as appropriate
- for (unsigned iSchema = 0; iSchema < pSchema->GetCount(); iSchema++)
+ for (unsigned iSchema = 0; iSchema < schemaArray.GetCount(); iSchema++)
{
- ICorJitInfo::PgoInstrumentationSchema *schema = &(*pSchema)[iSchema];
+ ICorJitInfo::PgoInstrumentationSchema *schema = &(schemaArray)[iSchema];
if ((schema->InstrumentationKind & ICorJitInfo::PgoInstrumentationKind::MarshalMask) == ICorJitInfo::PgoInstrumentationKind::TypeHandle)
{
for (int iEntry = 0; iEntry < schema->Count; iEntry++)
}
}
+ *pAllocatedData = new BYTE[schemaArray.GetCount() * sizeof(ICorJitInfo::PgoInstrumentationSchema)];
+ memcpy(*pAllocatedData, schemaArray.OpenRawBuffer(), schemaArray.GetCount() * sizeof(ICorJitInfo::PgoInstrumentationSchema));
+ schemaArray.CloseRawBuffer();
+ *ppSchema = (ICorJitInfo::PgoInstrumentationSchema*)pAllocatedData;
+
+ *pCountSchemaItems = schemaArray.GetCount();
*pInstrumentationData = found->GetData();
hr = S_OK;
}
hr = E_FAIL;
}
EX_END_CATCH(RethrowTerminalExceptions)
-
}
else
{
return hr;
}
+
+class R2RInstrumentationDataReader
+{
+ ReadyToRunInfo *m_pReadyToRunInfo;
+ Module* m_pModule;
+ PEDecoder* m_pNativeImage;
+
+public:
+ StackSArray<ICorJitInfo::PgoInstrumentationSchema> schemaArray;
+ StackSArray<BYTE> instrumentationData;
+
+ R2RInstrumentationDataReader(ReadyToRunInfo *pReadyToRunInfo, Module* pModule, PEDecoder* pNativeImage) :
+ m_pReadyToRunInfo(pReadyToRunInfo),
+ m_pModule(pModule),
+ m_pNativeImage(pNativeImage)
+ {}
+
+ bool operator()(const ICorJitInfo::PgoInstrumentationSchema &schema, int64_t dataItem, int32_t iDataItem)
+ {
+ if (iDataItem == 0)
+ {
+ schemaArray.Append(schema);
+ schemaArray[schemaArray.GetCount() - 1].Offset = instrumentationData.GetCount();
+ }
+
+ if ((schema.InstrumentationKind & ICorJitInfo::PgoInstrumentationKind::MarshalMask) == ICorJitInfo::PgoInstrumentationKind::TypeHandle)
+ {
+ intptr_t typeHandleData = 0;
+ if (dataItem != 0)
+ {
+ uint32_t importSection = dataItem & 0xF;
+ int64_t typeIndex = dataItem >> 4;
+ if (importSection != 0xF)
+ {
+ COUNT_T countImportSections;
+ PTR_CORCOMPILE_IMPORT_SECTION pImportSections = m_pReadyToRunInfo->GetImportSections(&countImportSections);
+
+ if (importSection >= countImportSections)
+ {
+ _ASSERTE(!"Malformed pgo type handle data");
+ return false;
+ }
+
+ PTR_CORCOMPILE_IMPORT_SECTION pImportSection = &pImportSections[importSection];
+ COUNT_T cbData;
+ TADDR pData = m_pNativeImage->GetDirectoryData(&pImportSection->Section, &cbData);
+ uint32_t fixupIndex = (uint32_t)typeIndex;
+ PTR_SIZE_T fixupAddress = dac_cast<PTR_SIZE_T>(pData + fixupIndex * sizeof(TADDR));
+ if (!m_pModule->FixupNativeEntry(pImportSections + importSection, fixupIndex, fixupAddress))
+ {
+ return false;
+ }
+
+ typeHandleData = *(intptr_t*)fixupAddress;
+ }
+ else
+ {
+ typeHandleData = HashToPgoUnknownTypeHandle((uint32_t)typeIndex);
+ }
+ }
+
+ BYTE* pTypeHandleData = (BYTE*)&typeHandleData;
+ for (size_t i = 0; i < sizeof(intptr_t); i++)
+ {
+ instrumentationData.Append(pTypeHandleData[i]);
+ }
+ }
+ else if ((schema.InstrumentationKind & ICorJitInfo::PgoInstrumentationKind::MarshalMask) == ICorJitInfo::PgoInstrumentationKind::FourByte)
+ {
+ BYTE* pFourByteData = (BYTE*)&dataItem;
+ for (int i = 0; i < 4; i++)
+ {
+ instrumentationData.Append(pFourByteData[i]);
+ }
+ }
+ else if ((schema.InstrumentationKind & ICorJitInfo::PgoInstrumentationKind::MarshalMask) == ICorJitInfo::PgoInstrumentationKind::EightByte)
+ {
+ BYTE* pEightByteData = (BYTE*)&dataItem;
+ for (int i = 0; i < 8; i++)
+ {
+ instrumentationData.Append(pEightByteData[i]);
+ }
+ }
+
+ return true;
+ }
+};
+
+HRESULT PgoManager::getPgoInstrumentationResultsFromR2RFormat(ReadyToRunInfo *pReadyToRunInfo,
+ Module* pModule,
+ PEDecoder* pNativeImage,
+ BYTE* pR2RFormatData,
+ size_t pR2RFormatDataMaxSize,
+ BYTE** pAllocatedData,
+ ICorJitInfo::PgoInstrumentationSchema** ppSchema,
+ UINT32 *pCountSchemaItems,
+ BYTE**pInstrumentationData)
+{
+ *pAllocatedData = NULL;
+ *ppSchema = NULL;
+ *pInstrumentationData = NULL;
+ *pCountSchemaItems = 0;
+
+ R2RInstrumentationDataReader r2rReader(pReadyToRunInfo, pModule, pNativeImage);
+
+ if (!ReadInstrumentationData(pR2RFormatData, pR2RFormatDataMaxSize, r2rReader))
+ {
+ return E_NOTIMPL;
+ }
+ else
+ {
+ while (r2rReader.instrumentationData.GetCount() & (sizeof(int64_t) - 1))
+ {
+ r2rReader.instrumentationData.Append(0);
+ }
+ size_t schemaDataSize = r2rReader.schemaArray.GetCount() * sizeof(ICorJitInfo::PgoInstrumentationSchema);
+ NewArrayHolder<BYTE> allocatedData = new BYTE[r2rReader.instrumentationData.GetCount() + schemaDataSize];
+
+ *pInstrumentationData = (BYTE*)allocatedData;
+ *pCountSchemaItems = r2rReader.schemaArray.GetCount();
+ memcpy(allocatedData, r2rReader.instrumentationData.OpenRawBuffer(), r2rReader.instrumentationData.GetCount());
+ r2rReader.instrumentationData.CloseRawBuffer();
+ *ppSchema = (ICorJitInfo::PgoInstrumentationSchema*)(((BYTE*)allocatedData) + r2rReader.instrumentationData.GetCount());
+ memcpy(*ppSchema, r2rReader.schemaArray.OpenRawBuffer(), schemaDataSize);
+ r2rReader.schemaArray.CloseRawBuffer();
+
+ allocatedData.SuppressRelease();
+ *pAllocatedData = allocatedData;
+ return S_OK;
+ }
+}
#endif // DACCESS_COMPILE
-HRESULT PgoManager::getPgoInstrumentationResultsInstance(MethodDesc* pMD, SArray<ICorJitInfo::PgoInstrumentationSchema>* pSchema, BYTE**pInstrumentationData)
+HRESULT PgoManager::getPgoInstrumentationResultsInstance(MethodDesc* pMD, BYTE** pAllocatedData, ICorJitInfo::PgoInstrumentationSchema** ppSchema, UINT32 *pCountSchemaItems, BYTE**pInstrumentationData)
{
// Initialize our out params
- pSchema->Clear();
+ *pAllocatedData = NULL;
*pInstrumentationData = NULL;
+ *pCountSchemaItems = 0;
HeaderList *found;
if (found == NULL)
{
+ // Prefer live collected data over data from pgo input, but if live data isn't present, use the data from the R2R file
+ // Consider merging this data with the live data instead in the future
+ if (pMD->GetModule()->IsReadyToRun() && pMD->GetModule()->GetReadyToRunInfo()->GetPgoInstrumentationData(pMD, pAllocatedData, ppSchema, pCountSchemaItems, pInstrumentationData))
+ {
+ return S_OK;
+ }
+
return E_NOTIMPL;
}
- if (ReadInstrumentationSchemaWithLayoutIntoSArray(found->header.GetData(), found->header.countsOffset, found->header.countsOffset, pSchema))
+ StackSArray<ICorJitInfo::PgoInstrumentationSchema> schemaArray;
+ if (ReadInstrumentationSchemaWithLayoutIntoSArray(found->header.GetData(), found->header.countsOffset, found->header.countsOffset, &schemaArray))
{
- *pInstrumentationData = found->header.GetData();
+ size_t schemaDataSize = schemaArray.GetCount() * sizeof(ICorJitInfo::PgoInstrumentationSchema);
+ size_t instrumentationDataSize = 0;
+ if (schemaArray.GetCount() > 0)
+ {
+ auto lastSchema = schemaArray[schemaArray.GetCount() - 1];
+ instrumentationDataSize = lastSchema.Offset + lastSchema.Count * InstrumentationKindToSize(lastSchema.InstrumentationKind) - found->header.countsOffset;
+ }
+ *pAllocatedData = new BYTE[schemaDataSize + instrumentationDataSize];
+ *ppSchema = (ICorJitInfo::PgoInstrumentationSchema*)pAllocatedData;
+ *pCountSchemaItems = schemaArray.GetCount();
+ memcpy(*pAllocatedData, schemaArray.OpenRawBuffer(), schemaDataSize);
+ schemaArray.CloseRawBuffer();
+
+ size_t* pInstrumentationDataDst = (size_t*)((*pAllocatedData) + schemaDataSize);
+ size_t* pInstrumentationDataDstEnd = (size_t*)((*pAllocatedData) + schemaDataSize + instrumentationDataSize);
+ *pInstrumentationData = (BYTE*)pInstrumentationDataDst;
+ volatile size_t*pSrc = (volatile size_t*)found->header.GetData();
+ // Use a volatile memcpy to copy the instrumentation data into a temporary buffer
+ // This allows the instrumentation data to be made stable for reading during the execution of the jit
+ // and since the copy moves through a volatile pointer, there will be no tearing of individual data elements
+ for (;pInstrumentationDataDst < pInstrumentationDataDstEnd; pInstrumentationDataDst++, pSrc++)
+ {
+ *pInstrumentationDataDst = *pSrc;
+ }
return S_OK;
}
else
*pLikelihood = 0;
*pNumberOfClasses = 0;
- StackSArray<ICorJitInfo::PgoInstrumentationSchema> schema;
+ NewArrayHolder<BYTE> allocatedData;
+ ICorJitInfo::PgoInstrumentationSchema* schema;
BYTE* pInstrumentationData;
- HRESULT hr = getPgoInstrumentationResults(pMD, &schema, &pInstrumentationData);
+ UINT32 cSchema;
+ HRESULT hr = getPgoInstrumentationResults(pMD, &allocatedData, &schema, &cSchema, &pInstrumentationData);
// Failed to find any sort of profile data for this method
//
}
// TODO This logic should be moved to the JIT
- for (COUNT_T i = 0; i < schema.GetCount(); i++)
+ for (COUNT_T i = 0; i < cSchema; i++)
{
if (schema[i].ILOffset != (int32_t)ilOffset)
continue;
if ((schema[i].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::TypeHandleHistogramCount) &&
(schema[i].Count == 1) &&
- ((i + 1) < schema.GetCount()) &&
+ ((i + 1) < cSchema) &&
(schema[i + 1].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::TypeHandleHistogramTypeHandle))
{
// Form a histogram
case 1:
{
+ if (IsUnknownTypeHandle(h.m_histogram[0].m_mt))
+ {
+ return NULL;
+ }
*pLikelihood = 100;
- return h.m_histogram[0].m_mt;
+ return (CORINFO_CLASS_HANDLE)h.m_histogram[0].m_mt;
}
break;
case 2:
{
- if (h.m_histogram[0].m_count >= h.m_histogram[1].m_count)
+ if ((h.m_histogram[0].m_count >= h.m_histogram[1].m_count) && !IsUnknownTypeHandle(h.m_histogram[0].m_mt))
{
*pLikelihood = (100 * h.m_histogram[0].m_count) / h.m_totalCount;
- return h.m_histogram[0].m_mt;
+ return (CORINFO_CLASS_HANDLE)h.m_histogram[0].m_mt;
}
- else
+ else if (!IsUnknownTypeHandle(h.m_histogram[1].m_mt))
{
*pLikelihood = (100 * h.m_histogram[1].m_count) / h.m_totalCount;
- return h.m_histogram[1].m_mt;
+ return (CORINFO_CLASS_HANDLE)h.m_histogram[1].m_mt;
+ }
+ else
+ {
+ return NULL;
}
}
break;
{
// Find maximum entry and return it
//
- unsigned maxIndex = 0;
- unsigned maxCount = 0;
+ unsigned maxKnownIndex = 0;
+ unsigned maxKnownCount = 0;
for (unsigned m = 0; m < h.m_histogram.GetCount(); m++)
{
- if (h.m_histogram[m].m_count > maxCount)
+ if ((h.m_histogram[m].m_count > maxKnownCount) && !IsUnknownTypeHandle(h.m_histogram[m].m_mt))
{
- maxIndex = m;
- maxCount = h.m_histogram[m].m_count;
+ maxKnownIndex = m;
+ maxKnownCount = h.m_histogram[m].m_count;
}
}
- if (maxCount > 0)
+ if (maxKnownCount > 0)
{
- *pLikelihood = (100 * maxCount) / h.m_totalCount;
- return h.m_histogram[maxIndex].m_mt;
+ *pLikelihood = (100 * maxKnownCount) / h.m_totalCount;;
+ return (CORINFO_CLASS_HANDLE)h.m_histogram[maxKnownIndex].m_mt;
}
return NULL;
// Stub version for !FEATURE_PGO builds
//
-HRESULT PgoManager::getPgoInstrumentationResults(MethodDesc* pMD, SArray<ICorJitInfo::PgoInstrumentationSchema>* pSchema, BYTE**pInstrumentationData)
+HRESULT PgoManager::getPgoInstrumentationResults(MethodDesc* pMD, NewArrayHolder<BYTE> *pAllocatedData, ICorJitInfo::PgoInstrumentationSchema** ppSchema, UINT32 *pCountSchemaItems, BYTE**pInstrumentationData)
{
+ *pAllocatedData = NULL;
+ *pCountSchemaItems = 0;
*pInstrumentationData = NULL;
return E_NOTIMPL;
}
// Stub version for !FEATURE_PGO builds
//
-CORINFO_CLASS_HANDLE PgoManager::getLikelyClass(MethodDesc* pMD, unsigned ilSize, unsigned ilOffset)
+CORINFO_CLASS_HANDLE PgoManager::getLikelyClass(MethodDesc* pMD, unsigned ilSize, unsigned ilOffset, UINT32* pLikelihood, UINT32* pNumberOfClasses)
{
+ *pLikelihood = 0;
+ *pNumberOfClasses = 0;
return NULL;
}
#include "typehashingalgorithms.h"
#include "shash.h"
+class ReadyToRunInfo;
// PgoManager handles in-process and out of band profile data for jitted code.
class PgoManager
public:
- static HRESULT getPgoInstrumentationResults(MethodDesc* pMD, SArray<ICorJitInfo::PgoInstrumentationSchema>* pSchema, BYTE**pInstrumentationData);
+ static HRESULT getPgoInstrumentationResults(MethodDesc* pMD, BYTE **pAllocatedDatapAllocatedData, ICorJitInfo::PgoInstrumentationSchema** ppSchema, UINT32 *pCountSchemaItems, BYTE**pInstrumentationData);
static HRESULT allocPgoInstrumentationBySchema(MethodDesc* pMD, ICorJitInfo::PgoInstrumentationSchema* pSchema, UINT32 countSchemaItems, BYTE** pInstrumentationData);
+ static HRESULT getPgoInstrumentationResultsFromR2RFormat(ReadyToRunInfo *pReadyToRunInfo,
+ Module* pModule,
+ PEDecoder* pNativeImage,
+ BYTE* pR2RFormatData,
+ size_t pR2RFormatDataMaxSize,
+ BYTE** pAllocatedData,
+ ICorJitInfo::PgoInstrumentationSchema** ppSchema,
+ UINT32 *pCountSchemaItems,
+ BYTE**pInstrumentationData);
static void CreatePgoManager(PgoManager* volatile* ppPgoManager, bool loaderAllocator);
protected:
HRESULT getPgoInstrumentationResultsInstance(MethodDesc* pMD,
- SArray<ICorJitInfo::PgoInstrumentationSchema>* pSchema,
+ BYTE** pAllocatedData,
+ ICorJitInfo::PgoInstrumentationSchema** ppSchema,
+ UINT32 *pCountSchemaItems,
BYTE**pInstrumentationData);
HRESULT allocPgoInstrumentationBySchemaInstance(MethodDesc* pMD,
DoLog("Ready to Run initialized successfully");
- return new (pMemory) ReadyToRunInfo(pModule, pLayout, pHeader, nativeImage, pamTracker);
+ return new (pMemory) ReadyToRunInfo(pModule, pModule->GetLoaderAllocator(), pLayout, pHeader, nativeImage, pamTracker);
}
bool ReadyToRunInfo::IsNativeImageSharedBy(PTR_Module pModule1, PTR_Module pModule2)
return pModule1->GetReadyToRunInfo()->m_pComposite == pModule2->GetReadyToRunInfo()->m_pComposite;
}
-ReadyToRunInfo::ReadyToRunInfo(Module * pModule, PEImageLayout * pLayout, READYTORUN_HEADER * pHeader, NativeImage *pNativeImage, AllocMemTracker *pamTracker)
+ReadyToRunInfo::ReadyToRunInfo(Module * pModule, LoaderAllocator* pLoaderAllocator, PEImageLayout * pLayout, READYTORUN_HEADER * pHeader, NativeImage *pNativeImage, AllocMemTracker *pamTracker)
: m_pModule(pModule),
m_pHeader(pHeader),
m_pNativeImage(pNativeImage),
m_availableTypesHashtable = NativeHashtable(parser);
}
+ // For format version 5.2 and later, there is an optional table of instrumentation data
+#ifdef FEATURE_PGO
+ if (IsImageVersionAtLeast(5, 2))
+ {
+ IMAGE_DATA_DIRECTORY * pPgoInstrumentationDataDir = m_pComposite->FindSection(ReadyToRunSectionType::PgoInstrumentationData);
+ if (pPgoInstrumentationDataDir)
+ {
+ NativeParser parser = NativeParser(&m_nativeReader, pPgoInstrumentationDataDir->VirtualAddress);
+ m_pgoInstrumentationDataHashtable = NativeHashtable(parser);
+ }
+
+ // Force the Pgo manager infrastructure to be initialized
+ pLoaderAllocator->GetOrCreatePgoManager();
+ }
+#endif
+
if (!m_isComponentAssembly)
{
// For component assemblies we don't initialize the reverse lookup map mapping entry points to MethodDescs;
return true;
}
+#ifndef CROSSGEN_COMPILE
+bool ReadyToRunInfo::GetPgoInstrumentationData(MethodDesc * pMD, BYTE** pAllocatedMemory, ICorJitInfo::PgoInstrumentationSchema**ppSchema, UINT *pcSchema, BYTE** pInstrumentationData)
+{
+ STANDARD_VM_CONTRACT;
+
+ PCODE pEntryPoint = NULL;
+#ifndef CROSSGEN_COMPILE
+#ifdef PROFILING_SUPPORTED
+ BOOL fShouldSearchCache = TRUE;
+#endif // PROFILING_SUPPORTED
+#endif // CROSSGEN_COMPILE
+ mdToken token = pMD->GetMemberDef();
+ int rid = RidFromToken(token);
+ if (rid == 0)
+ return false;
+
+ // If R2R code is disabled for this module, simply behave as if it is never found
+ if (m_readyToRunCodeDisabled)
+ return false;
+
+ if (m_pgoInstrumentationDataHashtable.IsNull())
+ return false;
+
+ NativeHashtable::Enumerator lookup = m_pgoInstrumentationDataHashtable.Lookup(GetVersionResilientMethodHashCode(pMD));
+ NativeParser entryParser;
+ while (lookup.GetNext(entryParser))
+ {
+ PCCOR_SIGNATURE pBlob = (PCCOR_SIGNATURE)entryParser.GetBlob();
+ SigPointer sig(pBlob);
+ if (SigMatchesMethodDesc(pMD, sig, m_pModule))
+ {
+ // Get the updated SigPointer location, so we can calculate the size of the blob,
+ // in order to skip the blob and find the entry point data.
+ entryParser = NativeParser(entryParser.GetNativeReader(), entryParser.GetOffset() + (uint)(sig.GetPtr() - pBlob));
+ uint32_t versionAndFlags = entryParser.GetUnsigned();
+ const uint32_t flagsMask = 0x3;
+ const uint32_t versionShift = 2;
+ uint32_t flags = versionAndFlags & flagsMask;
+ uint32_t version = versionAndFlags >> versionShift;
+
+ // Only version 0 is supported
+ if (version != 0)
+ return false;
+
+ uint offset = entryParser.GetOffset();
+
+ if (flags == 1)
+ {
+ // Offset is correct already
+ }
+ else if (flags == 3)
+ {
+ // Adjust offset as relative pointer
+ uint32_t val;
+ m_nativeReader.DecodeUnsigned(offset, &val);
+ offset -= val;
+ }
+
+ BYTE* instrumentationDataPtr = ((BYTE*)GetImage()->GetBase()) + offset;
+ IMAGE_DATA_DIRECTORY * pPgoInstrumentationDataDir = m_pComposite->FindSection(ReadyToRunSectionType::PgoInstrumentationData);
+ size_t maxSize = offset - pPgoInstrumentationDataDir->VirtualAddress + pPgoInstrumentationDataDir->Size;
+
+ return SUCCEEDED(PgoManager::getPgoInstrumentationResultsFromR2RFormat(this, m_pModule, m_pModule->GetNativeOrReadyToRunImage(), instrumentationDataPtr, maxSize, pAllocatedMemory, ppSchema, pcSchema, pInstrumentationData));
+ }
+ }
+
+ return false;
+}
+#endif // !CROSSGEN_COMPILE
+
+
PCODE ReadyToRunInfo::GetEntryPoint(MethodDesc * pMD, PrepareCodeConfig* pConfig, BOOL fFixups)
{
STANDARD_VM_CONTRACT;
NativeFormat::NativeArray m_methodDefEntryPoints;
NativeFormat::NativeHashtable m_instMethodEntryPoints;
NativeFormat::NativeHashtable m_availableTypesHashtable;
+ NativeFormat::NativeHashtable m_pgoInstrumentationDataHashtable;
NativeFormat::NativeHashtable m_pMetaDataHashtable;
NativeFormat::NativeCuckooFilter m_attributesPresence;
PTR_PersistentInlineTrackingMapR2R m_pPersistentInlineTrackingMap;
public:
- ReadyToRunInfo(Module * pModule, PEImageLayout * pLayout, READYTORUN_HEADER * pHeader, NativeImage * pNativeImage, AllocMemTracker *pamTracker);
+ ReadyToRunInfo(Module * pModule, LoaderAllocator* pLoaderAllocator, PEImageLayout * pLayout, READYTORUN_HEADER * pHeader, NativeImage * pNativeImage, AllocMemTracker *pamTracker);
static BOOL IsReadyToRunEnabled();
PCODE GetEntryPoint(MethodDesc * pMD, PrepareCodeConfig* pConfig, BOOL fFixups);
PTR_MethodDesc GetMethodDescForEntryPoint(PCODE entryPoint);
+ bool GetPgoInstrumentationData(MethodDesc * pMD, BYTE** pAllocatedMemory, ICorJitInfo::PgoInstrumentationSchema**ppSchema, UINT *pcSchema, BYTE** pInstrumentationData);
BOOL HasHashtableOfTypes();
BOOL TryLookupTypeTokenFromName(const NameHandle *pName, mdToken * pFoundTypeToken);