未验证 提交 311bd7ea 编写于 作者: D David Wrighton 提交者: GitHub

Profile driven method layout (#37469)

* Profile driven layout
- Update to mpgo file format to hold weighted call graph data, and exclusive hit sample data
- Implementation of call graph data capture from both jitted and R2R code
- Add mechanism for parsing entrypoints from R2R files into arbitrary type systems insetad of just strings
- Update command line parser in dotnet-pgo to use named properties on class instead of arguments to main method
- Update dotnet-pgo to be able to use a etl.zip file
- Implement infrastructure for performing layout of PE file based on profile guided information
- Implement various profile guided method ordering routines
  - There are a variety of possible algorithms, these are simple to
  implement. More capable ones will generally use the call graph data.
  - Sort by exclusive weight
  - Sort by Hot (known to be used) and cold (not known to be used)
  - Sort by Hot (known to be used for more than 128 sample) Warm (known
  to be used) and Cold (not known to be used)
- Since the BulkType logging lock seems to be losing some types, skip the lock for MethodDetails data
上级 128adb4c
......@@ -12,7 +12,7 @@
namespace ILCompiler.DependencyAnalysis
{
public abstract class SortableDependencyNode : DependencyNodeCore<NodeFactory>, ISortableNode
public abstract partial class SortableDependencyNode : DependencyNodeCore<NodeFactory>, ISortableNode
{
#if !SUPPORT_JIT
/// <summary>
......@@ -154,6 +154,8 @@ public int Compare(DependencyNodeCore<NodeFactory> x1, DependencyNodeCore<NodeFa
}
}
static partial void ApplyCustomSort(SortableDependencyNode x, SortableDependencyNode y, ref int result);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int CompareImpl(SortableDependencyNode x, SortableDependencyNode y, CompilerComparer comparer)
{
......@@ -162,6 +164,11 @@ public static int CompareImpl(SortableDependencyNode x, SortableDependencyNode y
if (phaseX == phaseY)
{
int customSort = 0;
ApplyCustomSort(x, y, ref customSort);
if (customSort != 0)
return customSort;
int codeX = x.ClassCode;
int codeY = y.ClassCode;
if (codeX == codeY)
......
......@@ -114,6 +114,22 @@ public int Length
}
}
public bool HasEmbeddedSignatureData
{
get
{
return _embeddedSignatureData != null;
}
}
public EmbeddedSignatureData[] GetEmbeddedSignatureData()
{
if ((_embeddedSignatureData == null) || (_embeddedSignatureData.Length == 0))
return null;
return (EmbeddedSignatureData[])_embeddedSignatureData.Clone();
}
public bool Equals(MethodSignature otherSignature)
{
return Equals(otherSignature, allowCovariantReturn: false);
......
......@@ -45,6 +45,8 @@ public void SetCode(ObjectData data)
public List<ISymbolNode> Fixups => _fixups;
public int Size => _methodCode.Data.Length;
public bool IsEmpty => _methodCode.Data.Length == 0;
public override ObjectData GetData(NodeFactory factory, bool relocsOnly)
......@@ -293,6 +295,7 @@ public void InitializeDebugEHClauseInfos(DebugEHClauseInfo[] debugEHClauseInfos)
Debug.Assert(_debugEHClauseInfos == null);
_debugEHClauseInfos = debugEHClauseInfos;
}
public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
{
MethodWithGCInfo otherNode = (MethodWithGCInfo)other;
......
// 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 System.Runtime.CompilerServices;
using ILCompiler.DependencyAnalysisFramework;
using Internal.TypeSystem;
namespace ILCompiler.DependencyAnalysis
{
partial class SortableDependencyNode
{
// Custom sort order. Used to override the default sorting mechanics.
public int CustomSort = int.MaxValue;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static partial void ApplyCustomSort(SortableDependencyNode x, SortableDependencyNode y, ref int result)
{
result = x.CustomSort.CompareTo(y.CustomSort);
}
}
}
......@@ -36,15 +36,20 @@ public enum MethodProfilingDataFlags
public class MethodProfileData
{
public MethodProfileData(MethodDesc method, MethodProfilingDataFlags flags, uint scenarioMask)
public MethodProfileData(MethodDesc method, MethodProfilingDataFlags flags, double exclusiveWeight, Dictionary<MethodDesc, int> callWeights, uint scenarioMask)
{
Method = method;
Flags = flags;
ScenarioMask = scenarioMask;
ExclusiveWeight = exclusiveWeight;
CallWeights = callWeights;
}
public readonly MethodDesc Method;
public readonly MethodProfilingDataFlags Flags;
public readonly uint ScenarioMask;
public readonly double ExclusiveWeight;
public readonly Dictionary<MethodDesc, int> CallWeights;
}
public abstract class ProfileData
......@@ -179,7 +184,27 @@ private void MergeProfileData(ref bool partialNgen, Dictionary<MethodDesc, Metho
MethodProfileData dataToMerge;
if (mergedProfileData.TryGetValue(data.Method, out dataToMerge))
{
mergedProfileData[data.Method] = new MethodProfileData(data.Method, dataToMerge.Flags | data.Flags, dataToMerge.ScenarioMask | data.ScenarioMask);
var mergedCallWeights = data.CallWeights;
if (mergedCallWeights == null)
{
mergedCallWeights = dataToMerge.CallWeights;
}
else if (dataToMerge.CallWeights != null)
{
mergedCallWeights = new Dictionary<MethodDesc, int>(data.CallWeights);
foreach (var entry in dataToMerge.CallWeights)
{
if (mergedCallWeights.TryGetValue(entry.Key, out var initialWeight))
{
mergedCallWeights[entry.Key] = initialWeight + entry.Value;
}
else
{
mergedCallWeights[entry.Key] = entry.Value;
}
}
}
mergedProfileData[data.Method] = new MethodProfileData(data.Method, dataToMerge.Flags | data.Flags, data.ExclusiveWeight + dataToMerge.ExclusiveWeight, mergedCallWeights, dataToMerge.ScenarioMask | data.ScenarioMask);
}
else
{
......@@ -214,5 +239,14 @@ public bool IsMethodInProfileData(MethodDesc method)
{
return _placedProfileMethodsAll.Contains(method);
}
public MethodProfileData this[MethodDesc method]
{
get
{
_mergedProfileData.TryGetValue(method, out var profileData);
return profileData;
}
}
}
}
......@@ -6,7 +6,6 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection.PortableExecutable;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
......@@ -20,7 +19,6 @@
using ILCompiler.DependencyAnalysis.ReadyToRun;
using ILCompiler.DependencyAnalysisFramework;
using Internal.TypeSystem.Ecma;
using System.Linq;
namespace ILCompiler
{
......@@ -230,6 +228,9 @@ public sealed class ReadyToRunCodegenCompilation : Compilation
private bool _generateMapFile;
private ProfileDataManager _profileData;
private ReadyToRunFileLayoutOptimizer _fileLayoutOptimizer;
public ReadyToRunSymbolNodeFactory SymbolNodeFactory { get; }
public ReadyToRunCompilationModuleGroupBase CompilationModuleGroup { get; }
......@@ -244,7 +245,10 @@ public sealed class ReadyToRunCodegenCompilation : Compilation
InstructionSetSupport instructionSetSupport,
bool resilient,
bool generateMapFile,
int parallelism)
int parallelism,
ProfileDataManager profileData,
ReadyToRunMethodLayoutAlgorithm methodLayoutAlgorithm,
ReadyToRunFileLayoutAlgorithm fileLayoutAlgorithm)
: base(
dependencyGraph,
nodeFactory,
......@@ -268,13 +272,21 @@ public sealed class ReadyToRunCodegenCompilation : Compilation
string instructionSetSupportString = ReadyToRunInstructionSetSupportSignature.ToInstructionSetSupportString(instructionSetSupport);
ReadyToRunInstructionSetSupportSignature instructionSetSupportSig = new ReadyToRunInstructionSetSupportSignature(instructionSetSupportString);
_dependencyGraph.AddRoot(new Import(NodeFactory.EagerImports, instructionSetSupportSig), "Baseline instruction set support");
_profileData = profileData;
_fileLayoutOptimizer = new ReadyToRunFileLayoutOptimizer(methodLayoutAlgorithm, fileLayoutAlgorithm, profileData, _nodeFactory);
}
public override void Compile(string outputFile)
{
_dependencyGraph.ComputeMarkedNodes();
var nodes = _dependencyGraph.MarkedNodeList;
nodes = _fileLayoutOptimizer.ApplyProfilerGuidedMethodSort(nodes);
using (PerfEventSource.StartStopEvents.EmittingEvents())
{
NodeFactory.SetMarkingComplete();
......
......@@ -26,6 +26,9 @@ public sealed class ReadyToRunCodegenCompilationBuilder : CompilationBuilder
private bool _generateMapFile;
private int _parallelism;
private InstructionSetSupport _instructionSetSupport;
private ProfileDataManager _profileData;
private ReadyToRunMethodLayoutAlgorithm _r2rMethodLayoutAlgorithm;
private ReadyToRunFileLayoutAlgorithm _r2rFileLayoutAlgorithm;
private string _jitPath;
private string _outputFile;
......@@ -98,6 +101,19 @@ public ReadyToRunCodegenCompilationBuilder UseResilience(bool resilient)
return this;
}
public ReadyToRunCodegenCompilationBuilder UseProfileData(ProfileDataManager profileData)
{
_profileData = profileData;
return this;
}
public ReadyToRunCodegenCompilationBuilder FileLayoutAlgorithms(ReadyToRunMethodLayoutAlgorithm r2rMethodLayoutAlgorithm, ReadyToRunFileLayoutAlgorithm r2rFileLayoutAlgorithm)
{
_r2rMethodLayoutAlgorithm = r2rMethodLayoutAlgorithm;
_r2rFileLayoutAlgorithm = r2rFileLayoutAlgorithm;
return this;
}
public ReadyToRunCodegenCompilationBuilder UseMapFile(bool generateMapFile)
{
_generateMapFile = generateMapFile;
......@@ -205,7 +221,10 @@ public override ICompilation ToCompilation()
_instructionSetSupport,
_resilient,
_generateMapFile,
_parallelism);
_parallelism,
_profileData,
_r2rMethodLayoutAlgorithm,
_r2rFileLayoutAlgorithm);
}
}
}
// 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.TypeSystem;
using ILCompiler.DependencyAnalysis;
using ILCompiler.DependencyAnalysis.ReadyToRun;
using ILCompiler.DependencyAnalysisFramework;
using System.Linq;
using System.Collections.Immutable;
using System.Text;
using System.Reflection.Metadata.Ecma335;
namespace ILCompiler
{
public enum ReadyToRunMethodLayoutAlgorithm
{
DefaultSort,
ExclusiveWeight,
HotCold,
HotWarmCold
}
public enum ReadyToRunFileLayoutAlgorithm
{
DefaultSort,
MethodOrder
}
class ReadyToRunFileLayoutOptimizer
{
public ReadyToRunFileLayoutOptimizer (ReadyToRunMethodLayoutAlgorithm methodAlgorithm,
ReadyToRunFileLayoutAlgorithm fileAlgorithm,
ProfileDataManager profileData,
NodeFactory nodeFactory)
{
_methodLayoutAlgorithm = methodAlgorithm;
_fileLayoutAlgorithm = fileAlgorithm;
_profileData = profileData;
_nodeFactory = nodeFactory;
}
private ReadyToRunMethodLayoutAlgorithm _methodLayoutAlgorithm = ReadyToRunMethodLayoutAlgorithm.DefaultSort;
private ReadyToRunFileLayoutAlgorithm _fileLayoutAlgorithm = ReadyToRunFileLayoutAlgorithm.DefaultSort;
private ProfileDataManager _profileData;
private NodeFactory _nodeFactory;
public ImmutableArray<DependencyNodeCore<NodeFactory>> ApplyProfilerGuidedMethodSort(ImmutableArray<DependencyNodeCore<NodeFactory>> nodes)
{
if (_methodLayoutAlgorithm == ReadyToRunMethodLayoutAlgorithm.DefaultSort)
return nodes;
List<MethodWithGCInfo> methods = new List<MethodWithGCInfo>();
foreach (var node in nodes)
{
if (node is MethodWithGCInfo method)
{
methods.Add(method);
}
}
if (_methodLayoutAlgorithm == ReadyToRunMethodLayoutAlgorithm.ExclusiveWeight)
{
methods.MergeSortAllowDuplicates(sortMethodWithGCInfoByWeight);
int sortMethodWithGCInfoByWeight(MethodWithGCInfo left, MethodWithGCInfo right)
{
return -MethodWithGCInfoToWeight(left).CompareTo(MethodWithGCInfoToWeight(right));
}
}
else if (_methodLayoutAlgorithm == ReadyToRunMethodLayoutAlgorithm.HotCold)
{
methods.MergeSortAllowDuplicates((MethodWithGCInfo left, MethodWithGCInfo right) => ComputeHotColdRegion(left).CompareTo(ComputeHotColdRegion(right)));
int ComputeHotColdRegion(MethodWithGCInfo method)
{
return MethodWithGCInfoToWeight(method) > 0 ? 0 : 1;
}
}
else if (_methodLayoutAlgorithm == ReadyToRunMethodLayoutAlgorithm.HotWarmCold)
{
methods.MergeSortAllowDuplicates((MethodWithGCInfo left, MethodWithGCInfo right) => ComputeHotWarmColdRegion(left).CompareTo(ComputeHotWarmColdRegion(right)));
int ComputeHotWarmColdRegion(MethodWithGCInfo method)
{
double weight = MethodWithGCInfoToWeight(method);
// If weight is greater than 128 its probably signicantly used at runtime
if (weight > 128)
return 0;
// If weight is less than 128 but greater than 0, then its probably used at startup
// or some at runtime, but is less critical than the hot code
if (weight > 0)
return 1;
// Methods without weight are probably relatively rarely used
return 2;
};
}
int sortOrder = 0;
List<MethodWithGCInfo> sortedMethodsList = methods;
foreach (var methodNode in sortedMethodsList)
{
methodNode.CustomSort = sortOrder;
sortOrder++;
}
if (_fileLayoutAlgorithm == ReadyToRunFileLayoutAlgorithm.MethodOrder)
{
// Sort the dependencies of methods by the method order
foreach (var method in sortedMethodsList)
{
ApplySortToDependencies(method, 0);
}
}
var newNodesArray = nodes.ToArray();
newNodesArray.MergeSortAllowDuplicates(new SortableDependencyNode.ObjectNodeComparer(new CompilerComparer()));
return newNodesArray.ToImmutableArray();
double MethodWithGCInfoToWeight(MethodWithGCInfo method)
{
var profileData = _profileData[method.Method];
double weight = 0;
if (profileData != null)
{
weight = profileData.ExclusiveWeight;
}
return weight;
}
void ApplySortToDependencies(DependencyNodeCore<NodeFactory> node, int depth)
{
if (depth > 5)
return;
if (node is SortableDependencyNode sortableNode)
{
if (sortableNode.CustomSort != Int32.MaxValue)
return; // Node already sorted
sortableNode.CustomSort += sortOrder++;
}
foreach (var dependency in node.GetStaticDependencies(_nodeFactory))
{
ApplySortToDependencies(dependency.Node, depth + 1);
}
}
}
}
}
......@@ -141,7 +141,7 @@ public ProfileData ParseIBCDataFromModule(EcmaModule ecmaModule)
{
if (methodsFoundInData.Add(associatedMethod))
{
methodProfileData.Add(new MethodProfileData(associatedMethod, (MethodProfilingDataFlags)entry.Flags, scenarioMask));
methodProfileData.Add(new MethodProfileData(associatedMethod, (MethodProfilingDataFlags)entry.Flags, 0, null, scenarioMask));
}
else
{
......
......@@ -13,6 +13,7 @@
using System.Linq;
using System.IO;
using System.Diagnostics;
namespace ILCompiler.IBC
{
......@@ -126,16 +127,38 @@ public static ProfileData ParseMIbcFile(CompilerTypeSystemContext tsc, string fi
}
}
enum MibcGroupParseState
{
LookingForNextMethod,
LookingForOptionalData,
ProcessingExclusiveWeight,
ProcessingCallgraphCount,
ProcessingCallgraphToken,
ProcessingCallgraphWeight,
}
/// <summary>
/// Parse MIbcGroup method and return enumerable of MethodProfileData
///
/// Like the AssemblyDictionary method, data is encoded via IL instructions. The format is
///
/// ldtoken methodInProfileData
/// Any series of instructions that does not include pop
/// Any series of instructions that does not include pop. Expansion data is encoded via ldstr "id"
/// followed by a expansion specific sequence of il opcodes.
/// pop
/// {Repeat N times for N methods described}
///
/// Extensions supported with current parser:
///
/// ldstr "ExclusiveWeight"
/// Any ldc.i4 or ldc.r4 or ldc.r8 instruction to indicate the exclusive weight
///
/// ldstr "WeightedCallData"
/// ldc.i4 <Count of methods called>
/// Repeat <Count of methods called times>
/// ldtoken <Method called from this method>
/// ldc.i4 <Weight associated with calling the <Method called from this method>>
///
/// This format is designed to be extensible to hold more data as we add new per method profile data without breaking existing parsers.
/// </summary>
static IEnumerable<MethodProfileData> ReadMIbcGroup(TypeSystemContext tsc, EcmaMethod method)
......@@ -143,19 +166,28 @@ static IEnumerable<MethodProfileData> ReadMIbcGroup(TypeSystemContext tsc, EcmaM
EcmaMethodIL ilBody = EcmaMethodIL.Create(method);
byte[] ilBytes = ilBody.GetILBytes();
int currentOffset = 0;
object methodInProgress = null;
object metadataNotResolvable = new object();
object metadataObject = null;
object methodNotResolvable = new object();
MibcGroupParseState state = MibcGroupParseState.LookingForNextMethod;
int intValue = 0;
int weightedCallGraphSize = 0;
int profileEntryFound = 0;
double exclusiveWeight = 0;
Dictionary<MethodDesc, int> weights = null;
bool processIntValue = false;
while (currentOffset < ilBytes.Length)
{
ILOpcode opcode = (ILOpcode)ilBytes[currentOffset];
if (opcode == ILOpcode.prefix1)
opcode = 0x100 + (ILOpcode)ilBytes[currentOffset + 1];
processIntValue = false;
switch (opcode)
{
case ILOpcode.ldtoken:
if (metadataObject == null)
{
uint token = (uint)(ilBytes[currentOffset + 1] + (ilBytes[currentOffset + 2] << 8) + (ilBytes[currentOffset + 3] << 16) + (ilBytes[currentOffset + 4] << 24));
uint token = BitConverter.ToUInt32(ilBytes.AsSpan(currentOffset + 1, 4));
metadataObject = null;
try
{
metadataObject = ilBody.GetObject((int)token);
......@@ -163,21 +195,188 @@ static IEnumerable<MethodProfileData> ReadMIbcGroup(TypeSystemContext tsc, EcmaM
catch (TypeSystemException)
{
// The method being referred to may be missing. In that situation,
// use the methodNotResolvable sentinel to indicate that this record should be ignored
metadataObject = methodNotResolvable;
// use the metadataNotResolvable sentinel to indicate that this record should be ignored
metadataObject = metadataNotResolvable;
}
switch (state)
{
case MibcGroupParseState.ProcessingCallgraphToken:
state = MibcGroupParseState.ProcessingCallgraphWeight;
break;
case MibcGroupParseState.LookingForNextMethod:
methodInProgress = metadataObject;
state = MibcGroupParseState.LookingForOptionalData;
break;
default:
state = MibcGroupParseState.LookingForOptionalData;
break;
}
}
break;
case ILOpcode.ldc_r4:
{
float fltValue = BitConverter.ToSingle(ilBytes.AsSpan(currentOffset + 1, 4));
switch (state)
{
case MibcGroupParseState.ProcessingExclusiveWeight:
exclusiveWeight = fltValue;
state = MibcGroupParseState.LookingForOptionalData;
break;
default:
state = MibcGroupParseState.LookingForOptionalData;
break;
}
break;
}
case ILOpcode.ldc_r8:
{
double dblValue = BitConverter.ToDouble(ilBytes.AsSpan(currentOffset + 1, 8));
switch (state)
{
case MibcGroupParseState.ProcessingExclusiveWeight:
exclusiveWeight = dblValue;
state = MibcGroupParseState.LookingForOptionalData;
break;
default:
state = MibcGroupParseState.LookingForOptionalData;
break;
}
break;
}
case ILOpcode.ldc_i4_0:
intValue = 0;
processIntValue = true;
break;
case ILOpcode.ldc_i4_1:
intValue = 1;
processIntValue = true;
break;
case ILOpcode.ldc_i4_2:
intValue = 2;
processIntValue = true;
break;
case ILOpcode.ldc_i4_3:
intValue = 3;
processIntValue = true;
break;
case ILOpcode.ldc_i4_4:
intValue = 4;
processIntValue = true;
break;
case ILOpcode.ldc_i4_5:
intValue = 5;
processIntValue = true;
break;
case ILOpcode.ldc_i4_6:
intValue = 6;
processIntValue = true;
break;
case ILOpcode.ldc_i4_7:
intValue = 7;
processIntValue = true;
break;
case ILOpcode.ldc_i4_8:
intValue = 8;
processIntValue = true;
break;
case ILOpcode.ldc_i4_m1:
intValue = -1;
processIntValue = true;
break;
case ILOpcode.ldc_i4_s:
intValue = (sbyte)ilBytes[currentOffset + 1];
processIntValue = true;
break;
case ILOpcode.ldc_i4:
intValue = BitConverter.ToInt32(ilBytes.AsSpan(currentOffset + 1, 4));
processIntValue = true;
break;
case ILOpcode.ldstr:
{
UInt32 userStringToken = BitConverter.ToUInt32(ilBytes.AsSpan(currentOffset + 1, 4));
string optionalDataName = (string)ilBody.GetObject((int)userStringToken);
switch (optionalDataName)
{
case "ExclusiveWeight":
state = MibcGroupParseState.ProcessingExclusiveWeight;
break;
case "WeightedCallData":
state = MibcGroupParseState.ProcessingCallgraphCount;
break;
default:
state = MibcGroupParseState.LookingForOptionalData;
break;
}
}
break;
case ILOpcode.pop:
if (metadataObject != methodNotResolvable)
if (methodInProgress != metadataNotResolvable)
{
MethodProfileData mibcData = new MethodProfileData((MethodDesc)metadataObject, MethodProfilingDataFlags.ReadMethodCode, 0xFFFFFFFF);
profileEntryFound++;
if (exclusiveWeight == 0)
{
// If no exclusive weight is found assign a non zero value that assumes the order in the pgo file is significant.
exclusiveWeight = Math.Min(1000000.0 - profileEntryFound, 0.0) / 1000000.0;
}
MethodProfileData mibcData = new MethodProfileData((MethodDesc)methodInProgress, MethodProfilingDataFlags.ReadMethodCode, exclusiveWeight, weights, 0xFFFFFFFF);
state = MibcGroupParseState.LookingForNextMethod;
exclusiveWeight = 0;
weights = null;
yield return mibcData;
}
metadataObject = null;
methodInProgress = null;
break;
default:
state = MibcGroupParseState.LookingForOptionalData;
break;
}
if (processIntValue)
{
switch (state)
{
case MibcGroupParseState.ProcessingExclusiveWeight:
exclusiveWeight = intValue;
state = MibcGroupParseState.LookingForOptionalData;
break;
case MibcGroupParseState.ProcessingCallgraphCount:
weightedCallGraphSize = intValue;
weights = new Dictionary<MethodDesc, int>();
if (weightedCallGraphSize > 0)
state = MibcGroupParseState.ProcessingCallgraphToken;
else
state = MibcGroupParseState.LookingForOptionalData;
break;
case MibcGroupParseState.ProcessingCallgraphWeight:
if (metadataObject != metadataNotResolvable)
{
weights.Add((MethodDesc)metadataObject, intValue);
}
weightedCallGraphSize--;
if (weightedCallGraphSize > 0)
state = MibcGroupParseState.ProcessingCallgraphToken;
else
state = MibcGroupParseState.LookingForOptionalData;
break;
default:
state = MibcGroupParseState.LookingForOptionalData;
break;
}
}
// This isn't correct if there is a switch opcode, but since we won't do that, its ok
currentOffset += opcode.GetSize();
}
......
......@@ -169,6 +169,7 @@
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\TypesTableNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\Win32ResourcesNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReadyToRunSymbolNodeFactory.cs" />
<Compile Include="Compiler\DependencyAnalysis\SortableDependencyNodeCompilerSpecific.cs" />
<Compile Include="Compiler\DependencyAnalysis\TypeAndMethod.cs" />
<Compile Include="Compiler\IRootingServiceProvider.cs" />
<Compile Include="Compiler\MethodExtensions.cs" />
......@@ -183,6 +184,7 @@
<Compile Include="Compiler\ReadyToRunCompilerContext.cs" />
<Compile Include="Compiler\ReadyToRunCodegenCompilation.cs" />
<Compile Include="Compiler\ReadyToRunCodegenCompilationBuilder.cs" />
<Compile Include="Compiler\ReadyToRunFileLayoutOptimizer.cs" />
<Compile Include="Compiler\ReadyToRunMetadataFieldLayoutAlgorithm.cs" />
<Compile Include="Compiler\ReadyToRunSingleAssemblyCompilationModuleGroup.cs" />
<Compile Include="Compiler\ReadyToRunTableManager.cs" />
......
......@@ -23,13 +23,69 @@ public DisassemblingGenericContext(string[] typeParameters, string[] methodParam
public string[] TypeParameters { get; }
}
// Test implementation of ISignatureTypeProvider<TType, TGenericContext> that uses strings in ilasm syntax as TType.
// A real provider in any sort of perf constraints would not want to allocate strings freely like this, but it keeps test code simple.
public class DisassemblingTypeProvider : ISignatureTypeProvider<string, DisassemblingGenericContext>
public abstract class StringTypeProviderBase<TGenericContext> : ISignatureTypeProvider<string, TGenericContext>
{
public virtual string GetPrimitiveType(PrimitiveTypeCode typeCode)
{
return typeCode.ToString();
switch (typeCode)
{
case PrimitiveTypeCode.Void:
return "void";
case PrimitiveTypeCode.Boolean:
return "bool";
case PrimitiveTypeCode.Char:
return "char";
case PrimitiveTypeCode.SByte:
return "sbyte";
case PrimitiveTypeCode.Byte:
return "byte";
case PrimitiveTypeCode.Int16:
return "short";
case PrimitiveTypeCode.UInt16:
return "ushort";
case PrimitiveTypeCode.Int32:
return "int";
case PrimitiveTypeCode.UInt32:
return "uint";
case PrimitiveTypeCode.Int64:
return "long";
case PrimitiveTypeCode.UInt64:
return "ulong";
case PrimitiveTypeCode.Single:
return "float";
case PrimitiveTypeCode.Double:
return "double";
case PrimitiveTypeCode.String:
return "string";
case PrimitiveTypeCode.TypedReference:
return "typedbyref";
case PrimitiveTypeCode.IntPtr:
return "IntPtr";
case PrimitiveTypeCode.UIntPtr:
return "UIntPtr";
case PrimitiveTypeCode.Object:
return "object";
default:
throw new NotImplementedException();
}
}
public virtual string GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind = 0)
......@@ -42,11 +98,6 @@ public virtual string GetTypeFromReference(MetadataReader reader, TypeReferenceH
return MetadataNameFormatter.FormatHandle(reader, handle);
}
public virtual string GetTypeFromSpecification(MetadataReader reader, DisassemblingGenericContext genericContext, TypeSpecificationHandle handle, byte rawTypeKind = 0)
{
return MetadataNameFormatter.FormatHandle(reader, handle);
}
public virtual string GetSZArrayType(string elementType)
{
return elementType + "[]";
......@@ -62,24 +113,6 @@ public virtual string GetByReferenceType(string elementType)
return "ref " + elementType;
}
public virtual string GetGenericMethodParameter(DisassemblingGenericContext genericContext, int index)
{
if (genericContext.MethodParameters == null || index >= genericContext.MethodParameters.Length)
{
return "!!" + index.ToString();
}
return genericContext.MethodParameters[index];
}
public virtual string GetGenericTypeParameter(DisassemblingGenericContext genericContext, int index)
{
if (genericContext.TypeParameters == null || index >= genericContext.TypeParameters.Length)
{
return "!" + index.ToString();
}
return genericContext.TypeParameters[index];
}
public virtual string GetPinnedType(string elementType)
{
return elementType + " pinned";
......@@ -125,11 +158,6 @@ public virtual string GetArrayType(string elementType, ArrayShape shape)
return builder.ToString();
}
public virtual string GetTypeFromHandle(MetadataReader reader, DisassemblingGenericContext genericContext, EntityHandle handle)
{
return MetadataNameFormatter.FormatHandle(reader, handle);
}
public virtual string GetModifiedType(string modifierType, string unmodifiedType, bool isRequired)
{
return unmodifiedType + (isRequired ? " modreq(" : " modopt(") + modifierType + ")";
......@@ -172,6 +200,43 @@ public virtual string GetFunctionPointerType(MethodSignature<string> signature)
builder.Append(')');
return builder.ToString();
}
public abstract string GetGenericMethodParameter(TGenericContext genericContext, int index);
public abstract string GetGenericTypeParameter(TGenericContext genericContext, int index);
public abstract string GetTypeFromSpecification(MetadataReader reader, TGenericContext genericContext, TypeSpecificationHandle handle, byte rawTypeKind);
}
// Test implementation of ISignatureTypeProvider<TType, TGenericContext> that uses strings in ilasm syntax as TType.
// A real provider in any sort of perf constraints would not want to allocate strings freely like this, but it keeps test code simple.
public class DisassemblingTypeProvider : StringTypeProviderBase<DisassemblingGenericContext>
{
public override string GetGenericMethodParameter(DisassemblingGenericContext genericContext, int index)
{
if (genericContext.MethodParameters == null || index >= genericContext.MethodParameters.Length)
{
return "!!" + index.ToString();
}
return genericContext.MethodParameters[index];
}
public override string GetGenericTypeParameter(DisassemblingGenericContext genericContext, int index)
{
if (genericContext.TypeParameters == null || index >= genericContext.TypeParameters.Length)
{
return "!" + index.ToString();
}
return genericContext.TypeParameters[index];
}
public override string GetTypeFromSpecification(MetadataReader reader, DisassemblingGenericContext genericContext, TypeSpecificationHandle handle, byte rawTypeKind)
{
return MetadataNameFormatter.FormatHandle(reader, handle);
}
public string GetTypeFromHandle(MetadataReader reader, DisassemblingGenericContext genericContext, EntityHandle handle)
{
return MetadataNameFormatter.FormatHandle(reader, handle);
}
}
}
......@@ -449,12 +449,46 @@ private void EnsureMethods()
bool[] isEntryPoint = new bool[nRuntimeFunctions];
// initialize R2RMethods
ParseMethodDefEntrypoints(isEntryPoint);
ParseMethodDefEntrypoints((section, reader) => ParseMethodDefEntrypointsSection(section, reader, isEntryPoint));
ParseInstanceMethodEntrypoints(isEntryPoint);
CountRuntimeFunctions(isEntryPoint);
}
}
private Dictionary<int, ReadyToRunMethod> _runtimeFunctionToMethod = null;
private void EnsureEntrypointRuntimeFunctionToReadyToRunMethodDict()
{
EnsureMethods();
if (_runtimeFunctionToMethod == null)
{
_runtimeFunctionToMethod = new Dictionary<int, ReadyToRunMethod>();
foreach (var section in _methods)
{
foreach (var method in section.Value)
{
if (!_runtimeFunctionToMethod.ContainsKey(method.EntryPointRuntimeFunctionId))
_runtimeFunctionToMethod.Add(method.EntryPointRuntimeFunctionId, method);
}
}
}
}
public IReadOnlyDictionary<TMethod, ReadyToRunMethod> GetCustomMethodToRuntimeFunctionMapping<TType, TMethod, TGenericContext>(IR2RSignatureTypeProvider<TType, TMethod, TGenericContext> provider)
{
EnsureEntrypointRuntimeFunctionToReadyToRunMethodDict();
Dictionary<TMethod, ReadyToRunMethod> customMethods = new Dictionary<TMethod, ReadyToRunMethod>();
if (ReadyToRunHeader.Sections.TryGetValue(ReadyToRunSectionType.RuntimeFunctions, out ReadyToRunSection runtimeFunctionSection))
{
ParseMethodDefEntrypoints((section, reader) => ParseMethodDefEntrypointsSectionCustom<TType, TMethod, TGenericContext>(provider, customMethods, section, reader));
ParseInstanceMethodEntrypointsCustom<TType, TMethod, TGenericContext>(provider, customMethods);
}
return customMethods;
}
private bool TryLocateNativeReadyToRunHeader()
{
PEExportTable exportTable = PEReader.GetExportTable();
......@@ -667,12 +701,12 @@ internal int CalculateRuntimeFunctionSize()
/// <summary>
/// Initialize non-generic R2RMethods with method signatures from MethodDefHandle, and runtime function indices from MethodDefEntryPoints
/// </summary>
private void ParseMethodDefEntrypoints(bool[] isEntryPoint)
private void ParseMethodDefEntrypoints(Action<ReadyToRunSection, MetadataReader> methodDefSectionReader)
{
ReadyToRunSection methodEntryPointSection;
if (ReadyToRunHeader.Sections.TryGetValue(ReadyToRunSectionType.MethodDefEntryPoints, out methodEntryPointSection))
{
ParseMethodDefEntrypointsSection(methodEntryPointSection, GetGlobalMetadataReader(), isEntryPoint);
methodDefSectionReader(methodEntryPointSection, GetGlobalMetadataReader());
}
else if (ReadyToRunAssemblyHeaders != null)
{
......@@ -680,7 +714,7 @@ private void ParseMethodDefEntrypoints(bool[] isEntryPoint)
{
if (ReadyToRunAssemblyHeaders[assemblyIndex].Sections.TryGetValue(ReadyToRunSectionType.MethodDefEntryPoints, out methodEntryPointSection))
{
ParseMethodDefEntrypointsSection(methodEntryPointSection, OpenReferenceAssembly(assemblyIndex + 1), isEntryPoint);
methodDefSectionReader(methodEntryPointSection, OpenReferenceAssembly(assemblyIndex + 1));
}
}
}
......@@ -725,6 +759,69 @@ private void ParseMethodDefEntrypointsSection(ReadyToRunSection section, Metadat
}
}
/// <summary>
/// Parse a single method def entrypoint section. For composite R2R images, this method is called multiple times
/// are method entrypoints are stored separately for each component assembly of the composite R2R executable.
/// </summary>
/// <param name="section">Method entrypoint section to parse</param>
/// <param name="metadataReader">ECMA metadata reader representing this method entrypoint section</param>
/// <param name="isEntryPoint">Set to true for each runtime function index representing a method entrypoint</param>
private void ParseMethodDefEntrypointsSectionCustom<TType, TMethod, TGenericContext>(IR2RSignatureTypeProvider<TType, TMethod, TGenericContext> provider, Dictionary<TMethod, ReadyToRunMethod> foundMethods, ReadyToRunSection section, MetadataReader metadataReader)
{
int methodDefEntryPointsOffset = GetOffset(section.RelativeVirtualAddress);
NativeArray methodEntryPoints = new NativeArray(Image, (uint)methodDefEntryPointsOffset);
uint nMethodEntryPoints = methodEntryPoints.GetCount();
for (uint rid = 1; rid <= nMethodEntryPoints; rid++)
{
int offset = 0;
if (methodEntryPoints.TryGetAt(Image, rid - 1, ref offset))
{
EntityHandle methodHandle = MetadataTokens.MethodDefinitionHandle((int)rid);
int runtimeFunctionId;
int? fixupOffset;
GetRuntimeFunctionIndexFromOffset(offset, out runtimeFunctionId, out fixupOffset);
ReadyToRunMethod r2rMethod = _runtimeFunctionToMethod[runtimeFunctionId];
var customMethod = provider.GetMethodFromMethodDef(metadataReader, MetadataTokens.MethodDefinitionHandle((int)rid), default(TType));
if (!Object.ReferenceEquals(customMethod, null) && !foundMethods.ContainsKey(customMethod))
foundMethods.Add(customMethod, r2rMethod);
}
}
}
/// <summary>
/// Initialize generic method instances with argument types and runtime function indices from InstanceMethodEntrypoints
/// </summary>
private void ParseInstanceMethodEntrypointsCustom<TType, TMethod, TGenericContext>(IR2RSignatureTypeProvider<TType, TMethod, TGenericContext> provider, Dictionary<TMethod, ReadyToRunMethod> foundMethods)
{
if (!ReadyToRunHeader.Sections.TryGetValue(ReadyToRunSectionType.InstanceMethodEntryPoints, out ReadyToRunSection instMethodEntryPointSection))
{
return;
}
int instMethodEntryPointsOffset = GetOffset(instMethodEntryPointSection.RelativeVirtualAddress);
NativeParser parser = new NativeParser(Image, (uint)instMethodEntryPointsOffset);
NativeHashtable instMethodEntryPoints = new NativeHashtable(Image, parser, (uint)(instMethodEntryPointsOffset + instMethodEntryPointSection.Size));
NativeHashtable.AllEntriesEnumerator allEntriesEnum = instMethodEntryPoints.EnumerateAllEntries();
NativeParser curParser = allEntriesEnum.GetNext();
while (!curParser.IsNull())
{
MetadataReader mdReader = _composite ? null : _assemblyCache[0];
var decoder = new R2RSignatureDecoder<TType, TMethod, TGenericContext>(provider, default(TGenericContext), mdReader, this, (int)curParser.Offset);
TMethod customMethod = decoder.ParseMethod();
int runtimeFunctionId;
int? fixupOffset;
GetRuntimeFunctionIndexFromOffset((int)decoder.Offset, out runtimeFunctionId, out fixupOffset);
ReadyToRunMethod r2rMethod = _runtimeFunctionToMethod[runtimeFunctionId];
if (!Object.ReferenceEquals(customMethod, null) && !foundMethods.ContainsKey(customMethod))
foundMethods.Add(customMethod, r2rMethod);
foundMethods.Add(customMethod, r2rMethod);
curParser = allEntriesEnum.GetNext();
}
}
/// <summary>
/// Initialize generic method instances with argument types and runtime function indices from InstanceMethodEntrypoints
/// </summary>
......
......@@ -38,8 +38,8 @@ public class CommandLineOptions
public bool Resilient { get; set; }
public bool Map { get; set; }
public int Parallelism { get; set; }
public ReadyToRunMethodLayoutAlgorithm MethodLayout { get; set; }
public ReadyToRunFileLayoutAlgorithm FileLayout { get; set; }
public string SingleMethodTypeName { get; set; }
public string SingleMethodName { get; set; }
public string[] SingleMethodGenericArgs { get; set; }
......@@ -178,6 +178,14 @@ public static Command RootCommand()
{
Argument = new Argument<bool>()
},
new Option(new[] { "--method-layout" }, SR.MethodLayoutOption)
{
Argument = new Argument<ReadyToRunMethodLayoutAlgorithm>()
},
new Option(new[] { "--file-layout" }, SR.FileLayoutOption)
{
Argument = new Argument<ReadyToRunFileLayoutAlgorithm>()
},
};
}
}
......
......@@ -474,6 +474,8 @@ private int Run()
.UseResilience(_commandLineOptions.Resilient)
.UseMapFile(_commandLineOptions.Map)
.UseParallelism(_commandLineOptions.Parallelism)
.UseProfileData(profileDataManager)
.FileLayoutAlgorithms(_commandLineOptions.MethodLayout, _commandLineOptions.FileLayout)
.UseJitPath(_commandLineOptions.JitPath)
.UseInstructionSetSupport(instructionSetSupport)
.GenerateOutputFile(_commandLineOptions.OutputFilePath.FullName)
......
......@@ -261,4 +261,10 @@
<data name="ErrorMultipleInputFilesCompositeModeOnly" xml:space="preserve">
<value>Error: multiple input files are only supported in composite build mode: {0}</value>
</data>
<data name="MethodLayoutOption" xml:space="preserve">
<value>Layout methods in file using profile driven optimization assuming the layout algorithm specified. The default value is DefaultSort, which indicates that complex layout is disabled</value>
</data>
<data name="FileLayoutOption" xml:space="preserve">
<value>Layout non-method contents of file using profile driven optimization assuming the layout algorithm specified. The default value is DefaultSort, which indicates that complex layout is disabled</value>
</data>
</root>
\ No newline at end of file
// 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.Collections.Immutable;
using System.Linq;
using System.Reflection.Metadata;
using ILCompiler.Reflection.ReadyToRun;
using Internal.ReadyToRunConstants;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;
namespace Microsoft.Diagnostics.Tools.Pgo
{
struct R2RSigProviderContext
{
}
class R2RSignatureTypeProvider : IR2RSignatureTypeProvider<TypeDesc, MethodDesc, R2RSigProviderContext>
{
public R2RSignatureTypeProvider(TraceTypeSystemContext tsc)
{
_tsc = tsc;
}
TraceTypeSystemContext _tsc;
TypeDesc IConstructedTypeProvider<TypeDesc>.GetArrayType(TypeDesc elementType, ArrayShape shape)
{
if (elementType == null)
return null;
return elementType.MakeArrayType(shape.Rank);
}
TypeDesc IConstructedTypeProvider<TypeDesc>.GetByReferenceType(TypeDesc elementType)
{
if (elementType == null)
return null;
return elementType.MakeByRefType();
}
TypeDesc IR2RSignatureTypeProvider<TypeDesc, MethodDesc, R2RSigProviderContext>.GetCanonType()
{
return _tsc.CanonType;
}
MethodDesc IR2RSignatureTypeProvider<TypeDesc, MethodDesc, R2RSigProviderContext>.GetConstrainedMethod(MethodDesc method, TypeDesc constraint)
{
// Cannot exist in entrypoint definition
throw new System.NotImplementedException();
}
TypeDesc ISignatureTypeProvider<TypeDesc, R2RSigProviderContext>.GetFunctionPointerType(MethodSignature<TypeDesc> signature)
{
// Cannot exist in entrypoint definition
throw new System.NotImplementedException();
}
TypeDesc IConstructedTypeProvider<TypeDesc>.GetGenericInstantiation(TypeDesc genericType, ImmutableArray<TypeDesc> typeArguments)
{
if (genericType == null)
return null;
foreach (var type in typeArguments)
{
if (type == null)
return null;
}
return _tsc.GetInstantiatedType((MetadataType)genericType, new Instantiation(typeArguments.ToArray()));
}
TypeDesc ISignatureTypeProvider<TypeDesc, R2RSigProviderContext>.GetGenericMethodParameter(R2RSigProviderContext genericContext, int index)
{
// Cannot exist in entrypoint definition
throw new System.NotImplementedException();
}
TypeDesc ISignatureTypeProvider<TypeDesc, R2RSigProviderContext>.GetGenericTypeParameter(R2RSigProviderContext genericContext, int index)
{
// Cannot exist in entrypoint definition
throw new System.NotImplementedException();
}
MethodDesc IR2RSignatureTypeProvider<TypeDesc, MethodDesc, R2RSigProviderContext>.GetInstantiatedMethod(MethodDesc uninstantiatedMethod, ImmutableArray<TypeDesc> instantiation)
{
if (uninstantiatedMethod == null)
return null;
foreach (var type in instantiation)
{
if (type == null)
return null;
}
return uninstantiatedMethod.MakeInstantiatedMethod(instantiation.ToArray());
}
MethodDesc IR2RSignatureTypeProvider<TypeDesc, MethodDesc, R2RSigProviderContext>.GetMethodFromMemberRef(MetadataReader reader, MemberReferenceHandle handle, TypeDesc owningTypeOverride)
{
var ecmaModule = (EcmaModule)_tsc.GetModuleForSimpleName(reader.GetString(reader.GetAssemblyDefinition().Name));
var method = (MethodDesc)ecmaModule.GetObject(handle);
if (owningTypeOverride != null)
{
return _tsc.GetMethodForInstantiatedType(method.GetTypicalMethodDefinition(), (InstantiatedType)owningTypeOverride);
}
return method;
}
MethodDesc IR2RSignatureTypeProvider<TypeDesc, MethodDesc, R2RSigProviderContext>.GetMethodFromMethodDef(MetadataReader reader, MethodDefinitionHandle handle, TypeDesc owningTypeOverride)
{
var ecmaModule = (EcmaModule)_tsc.GetModuleForSimpleName(reader.GetString(reader.GetAssemblyDefinition().Name));
var method = (MethodDesc)ecmaModule.GetObject(handle);
if (owningTypeOverride != null)
{
return _tsc.GetMethodForInstantiatedType(method.GetTypicalMethodDefinition(), (InstantiatedType)owningTypeOverride);
}
return method;
}
MethodDesc IR2RSignatureTypeProvider<TypeDesc, MethodDesc, R2RSigProviderContext>.GetMethodWithFlags(ReadyToRunMethodSigFlags flags, MethodDesc method)
{
return method;
}
TypeDesc ISignatureTypeProvider<TypeDesc, R2RSigProviderContext>.GetModifiedType(TypeDesc modifier, TypeDesc unmodifiedType, bool isRequired)
{
// Cannot exist in entrypoint definition
throw new System.NotImplementedException();
}
TypeDesc ISignatureTypeProvider<TypeDesc, R2RSigProviderContext>.GetPinnedType(TypeDesc elementType)
{
// Cannot exist in entrypoint definition
throw new System.NotImplementedException();
}
TypeDesc IConstructedTypeProvider<TypeDesc>.GetPointerType(TypeDesc elementType)
{
// Cannot exist in entrypoint definition
throw new System.NotImplementedException();
}
TypeDesc ISimpleTypeProvider<TypeDesc>.GetPrimitiveType(PrimitiveTypeCode typeCode)
{
WellKnownType wkt = 0;
switch (typeCode)
{
case PrimitiveTypeCode.Void:
wkt = WellKnownType.Void;
break;
case PrimitiveTypeCode.Boolean:
wkt = WellKnownType.Boolean;
break;
case PrimitiveTypeCode.Char:
wkt = WellKnownType.Char;
break;
case PrimitiveTypeCode.SByte:
wkt = WellKnownType.SByte;
break;
case PrimitiveTypeCode.Byte:
wkt = WellKnownType.Byte;
break;
case PrimitiveTypeCode.Int16:
wkt = WellKnownType.Int16;
break;
case PrimitiveTypeCode.UInt16:
wkt = WellKnownType.UInt16;
break;
case PrimitiveTypeCode.Int32:
wkt = WellKnownType.Int32;
break;
case PrimitiveTypeCode.UInt32:
wkt = WellKnownType.UInt32;
break;
case PrimitiveTypeCode.Int64:
wkt = WellKnownType.Int64;
break;
case PrimitiveTypeCode.UInt64:
wkt = WellKnownType.UInt64;
break;
case PrimitiveTypeCode.Single:
wkt = WellKnownType.Single;
break;
case PrimitiveTypeCode.Double:
wkt = WellKnownType.Double;
break;
case PrimitiveTypeCode.String:
wkt = WellKnownType.String;
break;
case PrimitiveTypeCode.TypedReference:
wkt = WellKnownType.TypedReference;
break;
case PrimitiveTypeCode.IntPtr:
wkt = WellKnownType.IntPtr;
break;
case PrimitiveTypeCode.UIntPtr:
wkt = WellKnownType.UIntPtr;
break;
case PrimitiveTypeCode.Object:
wkt = WellKnownType.Object;
break;
}
return _tsc.GetWellKnownType(wkt);
}
TypeDesc ISZArrayTypeProvider<TypeDesc>.GetSZArrayType(TypeDesc elementType)
{
if (elementType == null)
return null;
return elementType.MakeArrayType();
}
TypeDesc ISimpleTypeProvider<TypeDesc>.GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind)
{
var ecmaModule = (EcmaModule)_tsc.GetModuleForSimpleName(reader.GetString(reader.GetAssemblyDefinition().Name));
return (TypeDesc)ecmaModule.GetObject(handle);
}
TypeDesc ISimpleTypeProvider<TypeDesc>.GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind)
{
var ecmaModule = (EcmaModule)_tsc.GetModuleForSimpleName(reader.GetString(reader.GetAssemblyDefinition().Name));
return (TypeDesc)ecmaModule.GetObject(handle);
}
TypeDesc ISignatureTypeProvider<TypeDesc, R2RSigProviderContext>.GetTypeFromSpecification(MetadataReader reader, R2RSigProviderContext genericContext, TypeSpecificationHandle handle, byte rawTypeKind)
{
var ecmaModule = (EcmaModule)_tsc.GetModuleForSimpleName(reader.GetString(reader.GetAssemblyDefinition().Name));
return (TypeDesc)ecmaModule.GetObject(handle);
}
}
}
......@@ -14,6 +14,7 @@
using System.Reflection;
using System.IO;
using System.Text;
using System.Net;
namespace Microsoft.Diagnostics.Tools.Pgo
{
......@@ -84,6 +85,11 @@ public TypeHandleInfo(long id, TraceTypeData traceData)
public readonly long ID;
public TypeDesc Type;
public readonly TraceTypeData TypeValue;
public override string ToString()
{
return Type != null ? Type.ToString() : "NULL";
}
}
class ModuleDescInfo
......@@ -103,6 +109,8 @@ public ModuleDescInfo(long id, TraceManagedModule traceManagedModule)
private readonly Dictionary<long, TypeHandleInfo> _types = new Dictionary<long, TypeHandleInfo>();
private readonly Dictionary<long, ModuleDescInfo> _modules = new Dictionary<long, ModuleDescInfo>();
private readonly object _lock = new object();
private readonly int s_bulkTypeEvents = 0;
private readonly int s_bulkTypeTypes = 0;
public TraceRuntimeDescToTypeSystemDesc(TraceProcess traceProcess, TypeSystemContext context, int clrInstanceID)
{
......@@ -157,6 +165,8 @@ public TraceRuntimeDescToTypeSystemDesc(TraceProcess traceProcess, TypeSystemCon
foreach (var bulkTypeTrace in traceProcess.EventsInProcess.ByEventType<GCBulkTypeTraceData>())
{
s_bulkTypeEvents++;
if (bulkTypeTrace.ClrInstanceID != _clrInstanceID)
continue;
......@@ -164,6 +174,7 @@ public TraceRuntimeDescToTypeSystemDesc(TraceProcess traceProcess, TypeSystemCon
{
TypeHandleInfo currentInfo;
var typeTrace = bulkTypeTrace.Values(i);
s_bulkTypeTypes++;
if (_types.TryGetValue((long)typeTrace.TypeID, out currentInfo))
{
......@@ -242,6 +253,13 @@ public TraceRuntimeDescToTypeSystemDesc(TraceProcess traceProcess, TypeSystemCon
}
}
}
// Fill in all the types
foreach (var entry in _types)
{
ResolveTypeHandle(entry.Key, false);
}
}
public ModuleDesc ResolveModuleID(long handle, bool throwIfNotFound = true)
......
......@@ -16,10 +16,11 @@
using Microsoft.Diagnostics.Tracing.Parsers.Clr;
using System.Reflection.Metadata;
using ILCompiler.Reflection.ReadyToRun;
namespace Microsoft.Diagnostics.Tools.Pgo
{
class TraceTypeSystemContext : MetadataTypeSystemContext, IMetadataStringDecoderProvider
class TraceTypeSystemContext : MetadataTypeSystemContext, IMetadataStringDecoderProvider, IAssemblyResolver
{
private readonly PgoTraceProcess _pgoTraceProcess;
private readonly ModuleLoadLogger _moduleLoadLogger;
......@@ -359,5 +360,22 @@ public MetadataStringDecoder GetMetadataStringDecoder()
_metadataStringDecoder = new CachingMetadataStringDecoder(0x10000); // TODO: Tune the size
return _metadataStringDecoder;
}
MetadataReader IAssemblyResolver.FindAssembly(MetadataReader metadataReader, AssemblyReferenceHandle assemblyReferenceHandle, string parentFile)
{
return ((EcmaAssembly)this.GetModuleForSimpleName(metadataReader.GetString(metadataReader.GetAssemblyReference(assemblyReferenceHandle).Name), false)).MetadataReader;
}
MetadataReader IAssemblyResolver.FindAssembly(string simpleName, string parentFile)
{
return ((EcmaAssembly)this.GetModuleForSimpleName(simpleName, false)).MetadataReader;
}
bool IAssemblyResolver.Naked => false;
bool IAssemblyResolver.SignatureBinary => false;
bool IAssemblyResolver.InlineSignatureBinary => false;
}
}
......@@ -158,7 +158,7 @@ public EntityHandle GetTypeRef(MetadataType type)
else
{
var typeSpecSignature = new BlobBuilder();
EncodeType(typeSpecSignature, type);
EncodeType(typeSpecSignature, type, EmbeddedSignatureDataEmitter.EmptySingleton);
var blobSigHandle = _metadataBuilder.GetOrAddBlob(typeSpecSignature);
typeHandle = _metadataBuilder.AddTypeSpecification(blobSigHandle);
}
......@@ -183,7 +183,7 @@ public EntityHandle GetMethodRef(MethodDesc method)
BlobEncoder methodSpecEncoder = new BlobEncoder(methodSpecSig);
methodSpecEncoder.MethodSpecificationSignature(method.Instantiation.Length);
foreach (var type in method.Instantiation)
EncodeType(methodSpecSig, type);
EncodeType(methodSpecSig, type, EmbeddedSignatureDataEmitter.EmptySingleton);
var methodSpecSigHandle = _metadataBuilder.GetOrAddBlob(methodSpecSig);
methodHandle = _metadataBuilder.AddMethodSpecification(uninstantiatedHandle, methodSpecSigHandle);
......@@ -194,8 +194,21 @@ public EntityHandle GetMethodRef(MethodDesc method)
StringHandle methodName = _metadataBuilder.GetOrAddString(method.Name);
var sig = method.GetTypicalMethodDefinition().Signature;
EmbeddedSignatureDataEmitter signatureDataEmitter;
if (sig.HasEmbeddedSignatureData)
{
signatureDataEmitter = new EmbeddedSignatureDataEmitter(sig.GetEmbeddedSignatureData(), this);
}
else
{
signatureDataEmitter = EmbeddedSignatureDataEmitter.EmptySingleton;
}
BlobBuilder memberRefSig = new BlobBuilder();
EncodeMethodSignature(memberRefSig, sig);
EncodeMethodSignature(memberRefSig, sig, signatureDataEmitter);
if (!signatureDataEmitter.Complete)
throw new ArgumentException();
var sigBlob = _metadataBuilder.GetOrAddBlob(memberRefSig);
methodHandle = _metadataBuilder.AddMemberReference(typeHandle, methodName, sigBlob);
......@@ -205,8 +218,14 @@ public EntityHandle GetMethodRef(MethodDesc method)
return methodHandle;
}
private void EncodeType(BlobBuilder blobBuilder, TypeDesc type)
private void EncodeType(BlobBuilder blobBuilder, TypeDesc type, EmbeddedSignatureDataEmitter signatureDataEmitter)
{
signatureDataEmitter.Push();
signatureDataEmitter.Push();
signatureDataEmitter.EmitAtCurrentIndexStack(blobBuilder);
signatureDataEmitter.Pop();
signatureDataEmitter.Push();
if (type.IsPrimitive)
{
SignatureTypeCode primitiveCode;
......@@ -266,13 +285,13 @@ private void EncodeType(BlobBuilder blobBuilder, TypeDesc type)
else if (type.IsSzArray)
{
blobBuilder.WriteByte((byte)SignatureTypeCode.SZArray);
EncodeType(blobBuilder, type.GetParameterType());
EncodeType(blobBuilder, type.GetParameterType(), signatureDataEmitter);
}
else if (type.IsArray)
{
var arrayType = (ArrayType)type;
blobBuilder.WriteByte((byte)SignatureTypeCode.Array);
EncodeType(blobBuilder, type.GetParameterType());
EncodeType(blobBuilder, type.GetParameterType(), signatureDataEmitter);
var shapeEncoder = new ArrayShapeEncoder(blobBuilder);
// TODO Add support for non-standard array shapes
shapeEncoder.Shape(arrayType.Rank, default(ImmutableArray<int>), default(ImmutableArray<int>));
......@@ -280,17 +299,17 @@ private void EncodeType(BlobBuilder blobBuilder, TypeDesc type)
else if (type.IsPointer)
{
blobBuilder.WriteByte((byte)SignatureTypeCode.Pointer);
EncodeType(blobBuilder, type.GetParameterType());
EncodeType(blobBuilder, type.GetParameterType(), signatureDataEmitter);
}
else if (type.IsFunctionPointer)
{
FunctionPointerType fnptrType = (FunctionPointerType)type;
EncodeMethodSignature(blobBuilder, fnptrType.Signature);
EncodeMethodSignature(blobBuilder, fnptrType.Signature, signatureDataEmitter);
}
else if (type.IsByRef)
{
blobBuilder.WriteByte((byte)SignatureTypeCode.ByReference);
EncodeType(blobBuilder, type.GetParameterType());
EncodeType(blobBuilder, type.GetParameterType(), signatureDataEmitter);
}
else if (type.IsObject)
{
......@@ -318,10 +337,10 @@ private void EncodeType(BlobBuilder blobBuilder, TypeDesc type)
else if (type is InstantiatedType)
{
blobBuilder.WriteByte((byte)SignatureTypeCode.GenericTypeInstance);
EncodeType(blobBuilder, type.GetTypeDefinition());
EncodeType(blobBuilder, type.GetTypeDefinition(), signatureDataEmitter);
blobBuilder.WriteCompressedInteger(type.Instantiation.Length);
foreach (var instantiationArg in type.Instantiation)
EncodeType(blobBuilder, instantiationArg);
EncodeType(blobBuilder, instantiationArg, signatureDataEmitter);
}
else if (type is MetadataType)
{
......@@ -335,10 +354,96 @@ private void EncodeType(BlobBuilder blobBuilder, TypeDesc type)
{
throw new Exception("Unexpected type");
}
signatureDataEmitter.Pop();
signatureDataEmitter.Pop();
}
void EncodeMethodSignature(BlobBuilder signatureBuilder, MethodSignature sig)
class EmbeddedSignatureDataEmitter
{
EmbeddedSignatureData[] _embeddedData;
int _embeddedDataIndex;
Stack<int> _indexStack = new Stack<int>();
TypeSystemMetadataEmitter _metadataEmitter;
public static EmbeddedSignatureDataEmitter EmptySingleton = new EmbeddedSignatureDataEmitter(null, null);
public EmbeddedSignatureDataEmitter(EmbeddedSignatureData[] embeddedData, TypeSystemMetadataEmitter metadataEmitter)
{
_embeddedData = embeddedData;
_indexStack.Push(0);
_metadataEmitter = metadataEmitter;
}
public void Push()
{
if (!Complete)
{
int was = _indexStack.Pop();
_indexStack.Push(was + 1);
_indexStack.Push(0);
}
}
public void EmitAtCurrentIndexStack(BlobBuilder signatureBuilder)
{
if (!Complete)
{
if (_embeddedDataIndex < _embeddedData.Length)
{
string indexData = string.Join(".", _indexStack);
while ((_embeddedDataIndex < _embeddedData.Length) && _embeddedData[_embeddedDataIndex].index == indexData)
{
switch (_embeddedData[_embeddedDataIndex].kind)
{
case EmbeddedSignatureDataKind.OptionalCustomModifier:
{
signatureBuilder.WriteByte((byte)SignatureTypeCode.OptionalModifier);
EntityHandle handle = _metadataEmitter.GetTypeRef((MetadataType)_embeddedData[_embeddedDataIndex].type);
signatureBuilder.WriteCompressedInteger(CodedIndex.TypeDefOrRefOrSpec(handle));
}
break;
case EmbeddedSignatureDataKind.RequiredCustomModifier:
{
signatureBuilder.WriteByte((byte)SignatureTypeCode.RequiredModifier);
EntityHandle handle = _metadataEmitter.GetTypeRef((MetadataType)_embeddedData[_embeddedDataIndex].type);
signatureBuilder.WriteCompressedInteger(CodedIndex.TypeDefOrRefOrSpec(handle));
}
break;
default:
throw new NotImplementedException();
}
_embeddedDataIndex++;
}
}
}
}
public bool Complete
{
get
{
if (_embeddedData == null)
return true;
return _embeddedDataIndex >= _embeddedData.Length;
}
}
public void Pop()
{
if (!Complete)
{
_indexStack.Pop();
}
}
}
void EncodeMethodSignature(BlobBuilder signatureBuilder, MethodSignature sig, EmbeddedSignatureDataEmitter signatureDataEmitter)
{
signatureDataEmitter.Push();
BlobEncoder signatureEncoder = new BlobEncoder(signatureBuilder);
int genericParameterCount = sig.GenericParameterCount;
bool isInstanceMethod = !sig.IsStatic;
......@@ -362,9 +467,11 @@ void EncodeMethodSignature(BlobBuilder signatureBuilder, MethodSignature sig)
signatureEncoder.MethodSignature(sigCallingConvention, genericParameterCount, isInstanceMethod);
signatureBuilder.WriteCompressedInteger(sig.Length);
// TODO Process custom modifiers in some way
EncodeType(signatureBuilder, sig.ReturnType);
EncodeType(signatureBuilder, sig.ReturnType, signatureDataEmitter);
for (int i = 0; i < sig.Length; i++)
EncodeType(signatureBuilder, sig[i]);
EncodeType(signatureBuilder, sig[i], signatureDataEmitter);
signatureDataEmitter.Pop();
}
public UserStringHandle GetUserStringHandle(string userString)
......
......@@ -14,6 +14,7 @@
<ItemGroup>
<ProjectReference Include="../crossgen2/ILCompiler.TypeSystem.ReadyToRun/ILCompiler.TypeSystem.ReadyToRun.csproj" />
<ProjectReference Include="../crossgen2/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj" />
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.55" />
<PackageReference Include="System.CommandLine" Version="$(SystemCommandLineVersion)" />
<PackageReference Include="System.Reflection.Metadata" Version="1.8.1" />
......

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-pgo", "dotnet-pgo.csproj", "{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.Reflection.ReadyToRun", "..\crossgen2\ILCompiler.Reflection.ReadyToRun\ILCompiler.Reflection.ReadyToRun.csproj", "{ED3FE303-74EB-43D1-BEA1-14484A14B22E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.TypeSystem.ReadyToRun", "..\crossgen2\ILCompiler.TypeSystem.ReadyToRun\ILCompiler.TypeSystem.ReadyToRun.csproj", "{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Checked|Any CPU = Checked|Any CPU
Checked|x64 = Checked|x64
Checked|x86 = Checked|x86
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Checked|Any CPU.ActiveCfg = Release|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Checked|Any CPU.Build.0 = Release|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Checked|x64.ActiveCfg = Release|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Checked|x64.Build.0 = Release|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Checked|x86.ActiveCfg = Release|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Checked|x86.Build.0 = Release|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Debug|x64.ActiveCfg = Debug|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Debug|x64.Build.0 = Debug|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Debug|x86.ActiveCfg = Debug|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Debug|x86.Build.0 = Debug|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Release|Any CPU.Build.0 = Release|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Release|x64.ActiveCfg = Release|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Release|x64.Build.0 = Release|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Release|x86.ActiveCfg = Release|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Release|x86.Build.0 = Release|Any CPU
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Checked|Any CPU.ActiveCfg = Release|Any CPU
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Checked|Any CPU.Build.0 = Release|Any CPU
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Checked|x64.ActiveCfg = Release|x64
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Checked|x64.Build.0 = Release|x64
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Checked|x86.ActiveCfg = Release|Any CPU
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Checked|x86.Build.0 = Release|Any CPU
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Debug|x64.ActiveCfg = Debug|x64
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Debug|x64.Build.0 = Debug|x64
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Debug|x86.ActiveCfg = Debug|Any CPU
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Debug|x86.Build.0 = Debug|Any CPU
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Release|Any CPU.Build.0 = Release|Any CPU
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Release|x64.ActiveCfg = Release|x64
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Release|x64.Build.0 = Release|x64
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Release|x86.ActiveCfg = Release|Any CPU
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Release|x86.Build.0 = Release|Any CPU
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Checked|Any CPU.ActiveCfg = Checked|x86
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Checked|x64.ActiveCfg = Checked|x64
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Checked|x64.Build.0 = Checked|x64
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Checked|x86.ActiveCfg = Checked|x86
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Checked|x86.Build.0 = Checked|x86
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Debug|Any CPU.ActiveCfg = Debug|x86
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Debug|x64.ActiveCfg = Debug|x64
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Debug|x64.Build.0 = Debug|x64
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Debug|x86.ActiveCfg = Debug|x86
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Debug|x86.Build.0 = Debug|x86
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Release|Any CPU.ActiveCfg = Release|x86
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Release|x64.ActiveCfg = Release|x64
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Release|x64.Build.0 = Release|x64
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Release|x86.ActiveCfg = Release|x86
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {EEBDF807-A078-4F0D-A7F3-A6386B6C0A68}
EndGlobalSection
EndGlobal
......@@ -6286,7 +6286,7 @@ VOID ETW::MethodLog::SendMethodDetailsEvent(MethodDesc *pMethodDesc)
BulkTypeEventLogger typeLogger;
ULONGLONG typeID = (ULONGLONG)pMethodDesc->GetMethodTable_NoLogging();
ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(&typeLogger, typeID, ETW::TypeSystemLog::kTypeLogBehaviorTakeLockAndLogIfFirstTime);
ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(&typeLogger, typeID, ETW::TypeSystemLog::kTypeLogBehaviorAlwaysLog);
ULONGLONG loaderModuleID = (ULONGLONG)pMethodDesc->GetLoaderModule();
StackSArray<ULONGLONG> rgTypeParameters;
......@@ -6312,7 +6312,7 @@ VOID ETW::MethodLog::SendMethodDetailsEvent(MethodDesc *pMethodDesc)
// Log any referenced parameter types
for (COUNT_T i=0; i < cParams; i++)
{
ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(&typeLogger, rgTypeParameters[i], ETW::TypeSystemLog::kTypeLogBehaviorTakeLockAndLogIfFirstTime);
ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(&typeLogger, rgTypeParameters[i], ETW::TypeSystemLog::kTypeLogBehaviorAlwaysLog);
}
typeLogger.FireBulkTypeEvent();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册