From 2c44b7fb90427e610e6d49bcf8a447b5e046a64d Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Tue, 19 Jan 2021 17:20:31 -0800 Subject: [PATCH] Fix some bugs in debug info decoration for ReadyToRun --- ILSpy.ReadyToRun/ReadyToRunDisassembler.cs | 70 ++++++++++++++++------ ILSpy.ReadyToRun/ReadyToRunLanguage.cs | 9 ++- 2 files changed, 59 insertions(+), 20 deletions(-) diff --git a/ILSpy.ReadyToRun/ReadyToRunDisassembler.cs b/ILSpy.ReadyToRun/ReadyToRunDisassembler.cs index 2cbffa4e5..fc0e8c4cf 100644 --- a/ILSpy.ReadyToRun/ReadyToRunDisassembler.cs +++ b/ILSpy.ReadyToRun/ReadyToRunDisassembler.cs @@ -162,45 +162,65 @@ namespace ICSharpCode.ILSpy.ReadyToRun { public List records; public int i; - public Dictionary> registerRelativeVariables; - public Dictionary registerVariables; + public Dictionary>> registerRelativeVariables; + public Dictionary> registerVariables; public DebugInfoHelper() { - this.registerRelativeVariables = new Dictionary>(); - this.registerVariables = new Dictionary(); + this.registerRelativeVariables = new Dictionary>>(); + this.registerVariables = new Dictionary>(); } public void Update(ulong codeOffset) { + HashSet variables; while (i < records.Count && records[i].codeOffset == codeOffset) { if (records[i].isRegRelative) { + Dictionary> offsetToVariableMap; if (records[i].isStart) { - Dictionary offsetToVariableMap; if (!this.registerRelativeVariables.TryGetValue(records[i].register, out offsetToVariableMap)) { - offsetToVariableMap = new Dictionary(); + offsetToVariableMap = new Dictionary>(); 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(); + 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(); + 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 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 { if (debugRecords != null) { - + HashSet 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 offsetToVariableMap; + Dictionary> 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}"); + } } } } diff --git a/ILSpy.ReadyToRun/ReadyToRunLanguage.cs b/ILSpy.ReadyToRun/ReadyToRunLanguage.cs index 4d7268344..2cbbec696 100644 --- a/ILSpy.ReadyToRun/ReadyToRunLanguage.cs +++ b/ILSpy.ReadyToRun/ReadyToRunLanguage.cs @@ -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 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 } } } +#if STRESS + output = originalOutput; + output.WriteLine("Passed"); +#endif } }