Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid reporting empty debug info ranges to the vm #77289

Merged
merged 16 commits into from
Nov 2, 2022

Conversation

BrianBohe
Copy link
Member

This PR is intended to solve issue #47202.

The thing is empty variable live ranges are useless. We are reporting them because they can get extended later if the variable becomes alive in the immediately next emitted instruction. If an empty live range is not getting extended, which we can realize after emitting all the code or creating a new live range for the same variable, we can remove it.

Tested running

<path_to_repo>\artifacts\tests\coreclr\windows.x64.Debug\Tests\Core_Root\corerun.exe  <path_to_repo>\artifacts\bin\coreclr\windows.x64.Debug\R2RDump\R2RDump.dll --in  <path_to_repo>\artifacts\bin\coreclr\windows.x64.Debug\System.Private.CoreLib.dll > output.txt

before and after this PR, checking the differences manually. Files are big and I wasn't able to get a nice diff that I could post here. I am open to any suggestions for more testing.

@ghost ghost assigned BrianBohe Oct 20, 2022
@dotnet-issue-labeler dotnet-issue-labeler bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Oct 20, 2022
@ghost
Copy link

ghost commented Oct 20, 2022

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

Issue Details

This PR is intended to solve issue #47202.

The thing is empty variable live ranges are useless. We are reporting them because they can get extended later if the variable becomes alive in the immediately next emitted instruction. If an empty live range is not getting extended, which we can realize after emitting all the code or creating a new live range for the same variable, we can remove it.

Tested running

<path_to_repo>\artifacts\tests\coreclr\windows.x64.Debug\Tests\Core_Root\corerun.exe  <path_to_repo>\artifacts\bin\coreclr\windows.x64.Debug\R2RDump\R2RDump.dll --in  <path_to_repo>\artifacts\bin\coreclr\windows.x64.Debug\System.Private.CoreLib.dll > output.txt

before and after this PR, checking the differences manually. Files are big and I wasn't able to get a nice diff that I could post here. I am open to any suggestions for more testing.

Author: BrianBohe
Assignees: BrianBohe
Labels:

area-CodeGen-coreclr

Milestone: -

The debugger is not using empty variable live ranges.
We are reporting them because they can get extended
later if the variable becomes alive in the immediately
next emitted instruction. If an empty live range is
not getting extended, which we can realize after
emitting all the code or creating a new live range
for the same variable, we can remove it.
@BrianBohe
Copy link
Member Author

I am running now R2R dump with BrianBohe@ebb0a45 and found there are still empty variable ranges. Will debug it a little more and open this PR again.

@BrianBohe
Copy link
Member Author

BrianBohe commented Oct 21, 2022

I found a case where a variable (V06) dies (in [000054]) at the beginning of a basic block (BB05), and becomes alive (in [000016]) before an instruction is emitter. We would want to extend the live ranges in this case:

                                                                        /--*  t52    long   
Generating: N087 ( 10, 16) [000054] DA---------                         *  STORE_LCL_VAR long   V06 loc5         d:3 rcx REG rcx
							V06 in reg rcx is becoming live  [000054]
							Live regs: 00000000 {} => 00000002 {rcx}
							Live vars: {} => {V06}
New debug range: first
IN000f:        jmp      L_M3923_BB06

Variable Live Range History Dump for BB04
V04 loc3: rdx [(G_M3923_IG03,ins#5,ofs#27), (G_M3923_IG04,ins#1,ofs#10)]
V06 loc5: rcx [(G_M3923_IG04,ins#2,ofs#13), ...]

=============== Generating BB05 [060..06B), preds={BB01} succs={BB06} flags=0x00000000.20010020: i label LIR 
BB05 IN (1)={V09    }
     OUT(1)={    V06}

Recording Var Locations at start of BB05
  V09(rcx)
Change life 0000000000000008 {V06} -> 0000000000000002 {V09}
							V06 in reg rcx is becoming dead  [------]
							Live regs: (unchanged) 00000000 {}
							V09 in reg rcx is becoming live  [------]
							Live regs: 00000000 {} => 00000002 {rcx}
							Live regs: (unchanged) 00000002 {rcx}
							GC regs: (unchanged) 00000000 {}
							Byref regs: (unchanged) 00000000 {}

      L_M3923_BB05:

      G_M3923_IG04:        ; offs=000044H, funclet=00, bbWeight=0.50, byref
Mapped BB05 to G_M3923_IG05
Label: IG05, GCvars=0000000000000000 {}, gcrefRegs=00000000 {}, byrefRegs=00000000 {}

Scope info: begin block BB05, IL range [060..06B)
Added IP mapping: 0x0061 STACK_EMPTY (G_M3923_IG05,ins#0,ofs#0) label
Generating: N091 (???,???) [000093] -----------                            IL_OFFSET void   INLRT @ 0x061[E-] REG NA
Generating: N093 (  1,  1) [000086] -----------                   t86 =    LCL_VAR   long   V09 cse0         u:1 rcx (last use) REG rcx <l:$100, c:$140>
                                                                        /--*  t86    long   
Generating: N095 (  5,  4) [000016] DA--G------                         *  STORE_LCL_VAR long   V06 loc5         d:2 rcx REG rcx
							V09 in reg rcx is becoming dead  [000086]
							Live regs: 00000002 {rcx} => 00000000 {}
							Live vars: {V09} => {}
							V06 in reg rcx is becoming live  [000016]
							Live regs: 00000000 {} => 00000002 {rcx}
							Live vars: {} => {V06}
New debug range: new var or location

@BrianBohe BrianBohe reopened this Oct 24, 2022
@BrianBohe
Copy link
Member Author

BrianBohe commented Oct 25, 2022

[For last results see last comment]

SPMI results show that this approach and its alternative

  • has no significant change on asm diff and downgrade throughput on average +0.01~+0.03 %.
  • This PR is consistently improving 2x in the good cases compared to its alternative. Improving values are between -0.00% and -0.02 %.
  • The alternative is consistently doing 2x worse in the bad cases compared to this PR. Downgrading values are mostly between +0.00% and +0.03 %.
  • The worst bad case is consistently a MinOpt collection called "libraries.crossgen2...checked.mch", which throughput is +0.04~+0.07% for this PR and +0.08~+0.16 % the alterantive.

I was not able to get prints of my code on the log using SPMI locally. But running crossgen to build System.private.corelib dll gets:

  • 32322 functions jitted.
  • 189939 variable live ranges created on jit memory. 189939/32322 = 5.8 live ranges per function.
  • Of which 18119 are empty and not reported (after this changes). 18119/32322 = 0.56 empty live ranges per function.
  • A reported range instance (the struct used to communicate with the vm) uses 40 bytes, so 724760 unused bytes are requested to the vm. 724760/32322 = 22.42 unused bytes per jitted function.

Obs: ~10% of variable live ranges are empty after code is generated and could not be omitted before computing emitLocation absolute offsets.

On main branch, we are requesting memory to the vm, using it entirely, but reporting empty ranges. Running the same command I got:

  • 208763 variable live ranges created on jit memory. 6.45 live ranges per function.
  • Of which 32522 are empty and not reported (after this changes). 1.006 empty live ranges per function.
  • 1300880 bytes are requested to the vm and unused. 40.24 unused bytes per jitted function.

Obs: ~15.57% of variable live ranges are empty. This PR introduces a change which allows extending more variable live ranges and replace the ones we know are empty before finishing emitting code, that is why we see a difference in the % of total variable live ranges.

@BrianBohe
Copy link
Member Author

The important aspect of this pr and its alternative approach is to decide whether we want to filter empty live ranges before asking the vm for memory or not. I am moving the updated logic extending the variable range and the removal of duplicated prints to other PRs. After deciding between this PR and its alternative, I will open a new PR to decide whether we want to preemptively remove variable live ranges when we can tell from its instruction group and offset that it is empty, before finishing emitting code.

@jakobbotsch pointed me that the memory won't be lost as the vm is taking it, comprising and releasing it.

@BrianBohe
Copy link
Member Author

Update now that we are just comparing both ideas:

  • No ASM diff on both PRs
  • No significant throughput differences found on this PR. A few small regressions happen on the alternative +%0.00~+%0.09.

Giving the fact that the extra memory is being free after the jit, I see no point on choosing the alternative.

Links to this spmi diff and the alternative

@BrianBohe BrianBohe changed the title Removing empty variable live ranges when possible Avoid reporting empty debug info ranges to the vm Oct 27, 2022
@jakobbotsch
Copy link
Member

Looks like the VM is not happy that we may end up reporting no mappings after asking for memory. I think it should be fine to fix the VM side to handle this correctly.

@jakobbotsch
Copy link
Member

It looks like crossgen2 will need a small fix to properly free the array:

/// <summary>
/// Create a NativeVarInfo array; a table from native code range to variable location
/// on the stack / in a specific register.
/// </summary>
private void setVars(CORINFO_METHOD_STRUCT_* ftn, uint cVars, NativeVarInfo* vars)
{
Debug.Assert(_debugVarInfos == null);
if (cVars == 0)
return;
_debugVarInfos = new NativeVarInfo[cVars];
for (int i = 0; i < cVars; i++)
{
_debugVarInfos[i] = vars[i];
}
// JIT gave the ownership of this to us, so need to free this.
freeArray(vars);
}

NAOT looks ok.

@jakobbotsch
Copy link
Member

It might be better to make sure we don't call back into the VM with "valid pointer" + "0 length" combo so that we don't change the contract here. E.g. something like:

--- a/src/coreclr/jit/ee_il_dll.cpp
+++ b/src/coreclr/jit/ee_il_dll.cpp
@@ -694,7 +694,19 @@ void Compiler::eeSetLVdone()
     }
 #endif // DEBUG
 
-    info.compCompHnd->setVars(info.compMethodHnd, eeVarsCount, (ICorDebugInfo::NativeVarInfo*)eeVars);
+    if (eeVarsCount > 0)
+    {
+        info.compCompHnd->setVars(info.compMethodHnd, eeVarsCount, (ICorDebugInfo::NativeVarInfo*)eeVars);
+    }
+    else
+    {
+        info.compCompHnd->setVars(info.compMethodHnd, 0, nullptr);
+
+        if (eeVars != nullptr)
+        {
+            info.compCompHnd->freeArray(eeVars);
+        }
+    }
 
     eeVars = nullptr; // We give up ownership after setVars()
 }

@BrianBohe
Copy link
Member Author

@jakobbotsch thanks for the review

Co-authored-by: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
@cshung
Copy link
Member

cshung commented Nov 3, 2022

I am running now R2R dump with BrianBohe@ebb0a45 and found there are still empty variable ranges. Will debug it a little more and open this PR again.

Would you mind adding the validation that we do not report empty ranges to here? This will make sure it stays correct even when the JIT changes.

@BrianBohe
Copy link
Member Author

Would you mind adding the validation that we do not report empty ranges to here? This will make sure it stays correct even when the JIT changes.

Sure, Who and how is running that function currently? Is there a job scheduled running this once in a while or for every commit in PRs?

@cshung
Copy link
Member

cshung commented Nov 4, 2022

Would you mind adding the validation that we do not report empty ranges to here? This will make sure it stays correct even when the JIT changes.

Sure, Who and how is running that function currently? Is there a job scheduled running this once in a while or for every commit in PRs?

Any pipeline running crossgen2 will also run r2rdump (and therefore that function)

@JulieLeeMSFT JulieLeeMSFT added this to the 8.0.0 milestone Nov 4, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Dec 5, 2022
@BrianBohe BrianBohe deleted the issue-47202 branch February 10, 2023 23:06
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants