Browse Source

Fix some bugs in debug info decoration for ReadyToRun

pull/2279/head
Andrew Au 5 years ago
parent
commit
2c44b7fb90
  1. 70
      ILSpy.ReadyToRun/ReadyToRunDisassembler.cs
  2. 9
      ILSpy.ReadyToRun/ReadyToRunLanguage.cs

70
ILSpy.ReadyToRun/ReadyToRunDisassembler.cs

@ -162,45 +162,65 @@ namespace ICSharpCode.ILSpy.ReadyToRun @@ -162,45 +162,65 @@ namespace ICSharpCode.ILSpy.ReadyToRun
{
public List<NativeVarInfoRecord> records;
public int i;
public Dictionary<string, Dictionary<int, Variable>> registerRelativeVariables;
public Dictionary<string, Variable> registerVariables;
public Dictionary<string, Dictionary<int, HashSet<Variable>>> registerRelativeVariables;
public Dictionary<string, HashSet<Variable>> registerVariables;
public DebugInfoHelper()
{
this.registerRelativeVariables = new Dictionary<string, Dictionary<int, Variable>>();
this.registerVariables = new Dictionary<string, Variable>();
this.registerRelativeVariables = new Dictionary<string, Dictionary<int, HashSet<Variable>>>();
this.registerVariables = new Dictionary<string, HashSet<Variable>>();
}
public void Update(ulong codeOffset)
{
HashSet<Variable> variables;
while (i < records.Count && records[i].codeOffset == codeOffset)
{
if (records[i].isRegRelative)
{
Dictionary<int, HashSet<Variable>> offsetToVariableMap;
if (records[i].isStart)
{
Dictionary<int, Variable> offsetToVariableMap;
if (!this.registerRelativeVariables.TryGetValue(records[i].register, out offsetToVariableMap))
{
offsetToVariableMap = new Dictionary<int, Variable>();
offsetToVariableMap = new Dictionary<int, HashSet<Variable>>();
this.registerRelativeVariables.Add(records[i].register, offsetToVariableMap);
}
offsetToVariableMap.Add(records[i].registerOffset, records[i].variable);
if (!offsetToVariableMap.TryGetValue(records[i].registerOffset, out variables))
{
variables = new HashSet<Variable>();
offsetToVariableMap.Add(records[i].registerOffset, variables);
}
variables.Add(records[i].variable);
}
else
{
this.registerRelativeVariables[records[i].register].Remove(records[i].registerOffset);
offsetToVariableMap = this.registerRelativeVariables[records[i].register];
variables = offsetToVariableMap[records[i].registerOffset];
variables.Remove(records[i].variable);
}
}
else
{
if (records[i].isStart)
{
this.registerVariables.Add(records[i].register, records[i].variable);
if (!this.registerVariables.TryGetValue(records[i].register, out variables))
{
variables = new HashSet<Variable>();
this.registerVariables.Add(records[i].register, variables);
}
variables.Add(records[i].variable);
}
else
{
this.registerVariables.Remove(records[i].register);
// If the optimizing compiler decides that two variables will always have the same value within a basic block
// It might assign the same location for two variables.
// The compiler also generates potentially wrong 1 byte long debug info record for arguments in prolog.
// These record might describe the same variable in overlapping ranges.
// See https://cshung.github.io/posts/debug-info-debugging/ for the investigation.
variables = this.registerVariables[records[i].register];
variables.Remove(records[i].variable);
}
}
i++;
@ -219,7 +239,13 @@ namespace ICSharpCode.ILSpy.ReadyToRun @@ -219,7 +239,13 @@ namespace ICSharpCode.ILSpy.ReadyToRun
for (int i = 0; i < debugInfo.VariablesList.Count; ++i)
{
var varLoc = debugInfo.VariablesList[i];
if (varLoc.StartOffset == varLoc.EndOffset)
{
// This could happen if the compiler is generating bogus variable info mapping record that covers 0 instructions
// See https://github.com/dotnet/runtime/issues/47202
Debug.Assert(false);
continue;
}
switch (varLoc.VariableLocation.VarLocType)
{
case VarLocType.VLT_STK:
@ -344,32 +370,40 @@ namespace ICSharpCode.ILSpy.ReadyToRun @@ -344,32 +370,40 @@ namespace ICSharpCode.ILSpy.ReadyToRun
{
if (debugRecords != null)
{
HashSet<Variable> variables;
InstructionInfoFactory factory = new InstructionInfoFactory();
InstructionInfo info = factory.GetInfo(instr);
ulong codeOffset = instr.IP - baseInstrIP;
debugRecords.Update(codeOffset);
Variable variable = null;
foreach (UsedMemory usedMemInfo in info.GetUsedMemory())
{
string baseRegister = usedMemInfo.Base.ToString();
int displacement;
unchecked
{ displacement = (int)usedMemInfo.Displacement; }
Dictionary<int, Variable> offsetToVariableMap;
Dictionary<int, HashSet<Variable>> offsetToVariableMap;
if (debugRecords.registerRelativeVariables.TryGetValue(usedMemInfo.Base.ToString(), out offsetToVariableMap))
{
if (offsetToVariableMap.TryGetValue(displacement, out variable))
if (offsetToVariableMap.TryGetValue(displacement, out variables))
{
output.Write($"; [{usedMemInfo.Base.ToString().ToLower()}{(displacement < 0 ? '-' : '+')}{Math.Abs(displacement):X}h] = {variable.Type} {variable.Index}");
output.Write($";");
foreach (Variable variable in variables)
{
output.Write($" [{usedMemInfo.Base.ToString().ToLower()}{(displacement < 0 ? '-' : '+')}{Math.Abs(displacement):X}h] = {variable.Type} {variable.Index}");
}
}
}
}
foreach (UsedRegister usedMemInfo in info.GetUsedRegisters())
{
if (debugRecords.registerVariables.TryGetValue(usedMemInfo.Register.ToString(), out variable))
// TODO, if the code is accessing EAX but the debug info maps to RAX, then this match is going to fail.
if (debugRecords.registerVariables.TryGetValue(usedMemInfo.Register.ToString(), out variables))
{
output.Write($"; {usedMemInfo.Register.ToString().ToLower()} = {variable.Type} {variable.Index}");
output.Write($";");
foreach (Variable variable in variables)
{
output.Write($" {usedMemInfo.Register.ToString().ToLower()} = {variable.Type} {variable.Index}");
}
}
}
}

9
ILSpy.ReadyToRun/ReadyToRunLanguage.cs

@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
// #define STRESS
#define STRESS
using System;
using System.Collections.Generic;
@ -154,10 +154,11 @@ namespace ICSharpCode.ILSpy.ReadyToRun @@ -154,10 +154,11 @@ namespace ICSharpCode.ILSpy.ReadyToRun
bool showMetadataTokens = ILSpy.Options.DisplaySettingsPanel.CurrentDisplaySettings.ShowMetadataTokens;
bool showMetadataTokensInBase10 = ILSpy.Options.DisplaySettingsPanel.CurrentDisplaySettings.ShowMetadataTokensInBase10;
#if STRESS
ITextOutput originalOutput = output;
output = new DummyOutput();
{
foreach (var readyToRunMethod in reader.Methods)
{
{
#else
if (cacheEntry.methodMap.TryGetValue(method.MetadataToken, out var methods))
{
@ -170,6 +171,10 @@ namespace ICSharpCode.ILSpy.ReadyToRun @@ -170,6 +171,10 @@ namespace ICSharpCode.ILSpy.ReadyToRun
}
}
}
#if STRESS
output = originalOutput;
output.WriteLine("Passed");
#endif
}
}

Loading…
Cancel
Save