From 41110a20548c27559e28641fc7bc4fe834107aae Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sun, 8 Jun 2025 16:30:27 +0200 Subject: [PATCH 1/3] Do not count tailcall calli to prevent unnecessary EBP frame This is common pattern in IL stubs. The SPMI diffs are likely to be small but there's something on linux-x64. --- src/coreclr/jit/morph.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index a0425a44b6ea3c..95d803bb4d7d4f 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -6373,7 +6373,13 @@ GenTree* Compiler::fgMorphCall(GenTreeCall* call) if (call->gtCallType == CT_INDIRECT) { optCallCount++; - optIndirectCallCount++; + // Do not count the tailcall morphed from fgMorphCall -> + // fgMorphPotentialTailCall -> fgMorphCall as indirect call + // to prevent forcing EBP frame later. + if ((call->gtCallMoreFlags & GTF_CALL_M_TAILCALL) == 0) + { + optIndirectCallCount++; + } } else if (call->gtCallType == CT_USER_FUNC) { From 1c1883c6d4269120872a19f8bb21e36bf51b16d3 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 10 Jun 2025 20:41:43 +0200 Subject: [PATCH 2/3] Introduce optFastTailCallCount --- src/coreclr/jit/compiler.cpp | 7 ++++--- src/coreclr/jit/compiler.h | 1 + src/coreclr/jit/morph.cpp | 15 +++++++-------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 88e5260a45a9cb..4f3ef9718d0a4d 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -6443,10 +6443,10 @@ void Compiler::compCompileFinish() { // clang-format off headerPrinted = true; - printf(" | Profiled | Method | Method has | calls | Num |LclV |AProp| CSE | Perf |bytes | %3s codesize| \n", Target::g_tgtCPUName); - printf(" mdToken | CNT | RGN | Hash | EH | FRM | LOOP | NRM | IND | BBs | Cnt | Cnt | Cnt | Score | IL | HOT | CLD | method name \n"); + printf(" | Profiled | Method | Method has | calls | Num |LclV |AProp| CSE | Perf |bytes | %3s codesize| \n", Target::g_tgtCPUName); + printf(" mdToken | CNT | RGN | Hash | EH | FRM | LOOP | NRM | IND | FTL | BBs | Cnt | Cnt | Cnt | Score | IL | HOT | CLD | method name \n"); printf("---------+------+------+----------+----+-----+------+-----+-----+-----+-----+-----+-----+---------+------+-------+-----+\n"); - // 06001234 | 1234 | HOT | 0f1e2d3c | EH | ebp | LOOP | 15 | 6 | 12 | 17 | 12 | 8 | 1234.56 | 145 | 1234 | 123 | System.Example(int) + // 06001234 | 1234 | HOT | 0f1e2d3c | EH | ebp | LOOP | 15 | 6 | 0 | 12 | 17 | 12 | 8 | 1234.56 | 145 | 1234 | 123 | System.Example(int) // clang-format on } @@ -6540,6 +6540,7 @@ void Compiler::compCompileFinish() printf(" %3d |", optCallCount); printf(" %3d |", optIndirectCallCount); + printf(" %3d |", optFastTailCallCount); printf(" %3d |", Metrics.BasicBlocksAtCodegen); printf(" %3d |", lvaCount); diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 56a9a1eb543c70..a38237661582cb 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -7094,6 +7094,7 @@ class Compiler unsigned optCallCount = 0; // number of calls made in the method unsigned optIndirectCallCount = 0; // number of virtual, interface and indirect calls made in the method unsigned optNativeCallCount = 0; // number of Pinvoke/Native calls made in the method + unsigned optFastTailCallCount = 0; // number of fast tail calls made in the method #ifdef DEBUG void optCheckPreds(); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 95d803bb4d7d4f..50ff7d82804a69 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -6370,16 +6370,15 @@ GenTree* Compiler::fgMorphCall(GenTreeCall* call) // if (fgGlobalMorph) { - if (call->gtCallType == CT_INDIRECT) + if (call->IsFastTailCall()) { optCallCount++; - // Do not count the tailcall morphed from fgMorphCall -> - // fgMorphPotentialTailCall -> fgMorphCall as indirect call - // to prevent forcing EBP frame later. - if ((call->gtCallMoreFlags & GTF_CALL_M_TAILCALL) == 0) - { - optIndirectCallCount++; - } + optFastTailCallCount++; + } + else if (call->gtCallType == CT_INDIRECT) + { + optCallCount++; + optIndirectCallCount++; } else if (call->gtCallType == CT_USER_FUNC) { From 477d4fc5e65d3f385e5cfe4ac5d0794cdf74d067 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 10 Jun 2025 22:42:55 +0200 Subject: [PATCH 3/3] Take 3 --- src/coreclr/jit/compiler.cpp | 7 +++---- src/coreclr/jit/compiler.h | 9 +++++---- src/coreclr/jit/morph.cpp | 20 ++++++++++++++------ src/coreclr/jit/regalloc.cpp | 4 ++-- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 4f3ef9718d0a4d..88e5260a45a9cb 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -6443,10 +6443,10 @@ void Compiler::compCompileFinish() { // clang-format off headerPrinted = true; - printf(" | Profiled | Method | Method has | calls | Num |LclV |AProp| CSE | Perf |bytes | %3s codesize| \n", Target::g_tgtCPUName); - printf(" mdToken | CNT | RGN | Hash | EH | FRM | LOOP | NRM | IND | FTL | BBs | Cnt | Cnt | Cnt | Score | IL | HOT | CLD | method name \n"); + printf(" | Profiled | Method | Method has | calls | Num |LclV |AProp| CSE | Perf |bytes | %3s codesize| \n", Target::g_tgtCPUName); + printf(" mdToken | CNT | RGN | Hash | EH | FRM | LOOP | NRM | IND | BBs | Cnt | Cnt | Cnt | Score | IL | HOT | CLD | method name \n"); printf("---------+------+------+----------+----+-----+------+-----+-----+-----+-----+-----+-----+---------+------+-------+-----+\n"); - // 06001234 | 1234 | HOT | 0f1e2d3c | EH | ebp | LOOP | 15 | 6 | 0 | 12 | 17 | 12 | 8 | 1234.56 | 145 | 1234 | 123 | System.Example(int) + // 06001234 | 1234 | HOT | 0f1e2d3c | EH | ebp | LOOP | 15 | 6 | 12 | 17 | 12 | 8 | 1234.56 | 145 | 1234 | 123 | System.Example(int) // clang-format on } @@ -6540,7 +6540,6 @@ void Compiler::compCompileFinish() printf(" %3d |", optCallCount); printf(" %3d |", optIndirectCallCount); - printf(" %3d |", optFastTailCallCount); printf(" %3d |", Metrics.BasicBlocksAtCodegen); printf(" %3d |", lvaCount); diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index a38237661582cb..214fcd438ae2e5 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -7091,10 +7091,11 @@ class Compiler bool fgHasLoops = false; protected: - unsigned optCallCount = 0; // number of calls made in the method - unsigned optIndirectCallCount = 0; // number of virtual, interface and indirect calls made in the method - unsigned optNativeCallCount = 0; // number of Pinvoke/Native calls made in the method - unsigned optFastTailCallCount = 0; // number of fast tail calls made in the method + unsigned optCallCount = 0; // number of calls made in the method + unsigned optIndirectCallCount = 0; // number of virtual, interface and indirect calls made in the method + unsigned optNativeCallCount = 0; // number of Pinvoke/Native calls made in the method + unsigned optFastTailCallCount = 0; // number of fast tail calls made in the method + unsigned optIndirectFastTailCallCount = 0; // number of indirect (see above) fast tail calls made in the method #ifdef DEBUG void optCheckPreds(); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 50ff7d82804a69..65f339748c0c22 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -6370,15 +6370,15 @@ GenTree* Compiler::fgMorphCall(GenTreeCall* call) // if (fgGlobalMorph) { - if (call->IsFastTailCall()) - { - optCallCount++; - optFastTailCallCount++; - } - else if (call->gtCallType == CT_INDIRECT) + if (call->gtCallType == CT_INDIRECT) { optCallCount++; optIndirectCallCount++; + if (call->IsFastTailCall()) + { + optFastTailCallCount++; + optIndirectFastTailCallCount++; + } } else if (call->gtCallType == CT_USER_FUNC) { @@ -6387,6 +6387,14 @@ GenTree* Compiler::fgMorphCall(GenTreeCall* call) { optIndirectCallCount++; } + if (call->IsFastTailCall()) + { + optFastTailCallCount++; + if (call->IsVirtual()) + { + optIndirectFastTailCallCount++; + } + } } } diff --git a/src/coreclr/jit/regalloc.cpp b/src/coreclr/jit/regalloc.cpp index a2e3c28005aedb..de010e01c60875 100644 --- a/src/coreclr/jit/regalloc.cpp +++ b/src/coreclr/jit/regalloc.cpp @@ -136,12 +136,12 @@ bool Compiler::rpMustCreateEBPFrame(INDEBUG(const char** wbReason)) INDEBUG(reason = "Method has Loops"); result = true; } - if (!result && (optCallCount >= 2)) + if (!result && (optCallCount >= optFastTailCallCount + 2)) { INDEBUG(reason = "Call Count"); result = true; } - if (!result && (optIndirectCallCount >= 1)) + if (!result && (optIndirectCallCount >= optIndirectFastTailCallCount + 1)) { INDEBUG(reason = "Indirect Call"); result = true;