Skip to content

Commit e867965

Browse files
authored
JIT: Propagate LCL_ADDR nodes during local morph (#102808)
This changes local morph to run in RPO when optimizations are enabled. It adds infrastructure to track and propagate LCL_ADDR values assigned to locals during local morph. This allows us to avoid address exposure in cases where the destination local does not actually end up escaping in any way. Example: ```csharp public struct Awaitable { public int Opts; public Awaitable(bool value) { Opts = value ? 1 : 2; } } [MethodImpl(MethodImplOptions.NoInlining)] public static int Test() => new Awaitable(false).Opts; ``` Before: ```asm G_M59043_IG01: ;; offset=0x0000 push rax ;; size=1 bbWeight=1 PerfScore 1.00 G_M59043_IG02: ;; offset=0x0001 xor eax, eax mov dword ptr [rsp], eax mov dword ptr [rsp], 2 mov eax, dword ptr [rsp] ;; size=15 bbWeight=1 PerfScore 3.25 G_M59043_IG03: ;; offset=0x0010 add rsp, 8 ret ;; size=5 bbWeight=1 PerfScore 1.25 ; Total bytes of code: 21 ``` After: ```asm G_M59043_IG02: ;; offset=0x0000 mov eax, 2 ;; size=5 bbWeight=1 PerfScore 0.25 G_M59043_IG03: ;; offset=0x0005 ret ``` Propagating the addresses works much like local assertion prop in morph does. Proving that the locals that were stored to do not escape afterwards is done with a simplistic approach: we check globally that no reads of the locals exists, and if so, we replace the `LCL_ADDR` stored to them by a constant 0. We leave it up to liveness to clean up the stores themselves. If we were able to remove any `LCL_ADDR` in this way then we run an additional pass over the locals of the IR to compute the final set of exposed locals. This could be more sophisticated, but in practice this handles the reported cases just fine. Fix #87072 Fix #102273 Fix #102518 This is still not sufficient to handle #69254. To handle that we would need more support around tracking the values of struct fields, and handling of promoted fields. This PR currently does not handle promoted fields at all; we use `lvHasLdAddrOp` as a conservative approximation of address exposure on the destination locals, and promoted structs almost always have this set. If we were to handle promoted fields we would need some other way to determine that a destination holding a local address couldn't be exposed.
1 parent 6b6a5b5 commit e867965

File tree

7 files changed

+696
-41
lines changed

7 files changed

+696
-41
lines changed

src/coreclr/jit/compiler.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6785,6 +6785,8 @@ class Compiler
67856785

67866786
PhaseStatus fgMarkAddressExposedLocals();
67876787
void fgSequenceLocals(Statement* stmt);
6788+
bool fgExposeUnpropagatedLocals(bool propagatedAny, class LocalEqualsLocalAddrAssertions* assertions);
6789+
void fgExposeLocalsInBitVec(BitVec_ValArg_T bitVec);
67886790

67896791
PhaseStatus PhysicalPromotion();
67906792

src/coreclr/jit/jitconfigvalues.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,7 @@ OPT_CONFIG_STRING(JitOnlyOptimizeRange,
567567
OPT_CONFIG_STRING(JitEnablePhysicalPromotionRange, W("JitEnablePhysicalPromotionRange"))
568568
OPT_CONFIG_STRING(JitEnableCrossBlockLocalAssertionPropRange, W("JitEnableCrossBlockLocalAssertionPropRange"))
569569
OPT_CONFIG_STRING(JitEnableInductionVariableOptsRange, W("JitEnableInductionVariableOptsRange"))
570+
OPT_CONFIG_STRING(JitEnableLocalAddrPropagationRange, W("JitEnableLocalAddrPropagationRange"))
570571

571572
OPT_CONFIG_INTEGER(JitDoSsa, W("JitDoSsa"), 1) // Perform Static Single Assignment (SSA) numbering on the variables
572573
OPT_CONFIG_INTEGER(JitDoValueNumber, W("JitDoValueNumber"), 1) // Perform value numbering on method expressions

0 commit comments

Comments
 (0)