JIT: (bug) LEA address-mode folding silently deletes a nested `checked` multiply (missing `OverflowException`)
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
`CodeGen::genCreateAddrMode` checks `gtOverflow()` on the outermost `GT_MUL` it folds, but the inner scale-folding loops use only `GenTree::GetScaledIndex()`, which ignores `GTF_OVERFLOW`. `Lowering::TryCreateAddrMode` therefore folds `b + checked(i * 2) * 4` into `LEA(b + i*8)` and deletes the `MUL_ovfl` node, so the mandated `OverflowException` is never thrown.
### Minimal Repro
```csharp
using System;
using System.Runtime.CompilerServices;
class Program
{
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
static long Test(long b, long i) => b + checked(i * 2) * 4;
static void Main()
{
try { Console.WriteLine(Test(100, 0x4000000000000000)); }
catch (OverflowException) { Console.WriteLine("OverflowException"); }
}
}
```
No JIT env vars required.
### Expected
```
OverflowException
```
(`0x4000000000000000 * 2` overflows `Int64`, so the `mul.ovf` must throw.)
### Actual
```
100
```
```asm
; Program:Test(long,long):long (FullOpts)
lea rax, [rcx+8*rdx]
ret
; Total bytes of code 5
```
The `imul`/`jo`/`CORINFO_HELP_OVERFLOW` sequence is gone. `DOTNET_JitDump=Test` shows lowering deleting the checked multiply:
```
Removing unused node:
N004 ( 9, 8) [000004] ---X-+----- * MUL_ovfl long $141 <<<< checked multiply deleted
New addressing mode node:
N007 ( 13, 12) [000008] -----+----- * LEA(b+(i*8)+0) long
```
### Notes
- `src/coreclr/jit/codegencommon.cpp` ~L1596 (op1) and ~L1672 (op2): `while ((rv2->OperIs(GT_MUL) || rv2->OperIs(GT_LSH)) && (argScale = rv2->GetScaledIndex()) != 0)` swallows `rv2` even when `rv2->gtOverflow()`. A fix is `&& !rv2->gtOverflow()` in both loops, or making `GetScaledIndex()` return 0 for overflow-checking `GT_MUL`.
- Also repros with reversed operand order, with `int`/`ulong`, and for other scale pairs (e.g. `checked(i * 4) * 2`) — any nested pair whose product is a legal x64 LEA scale.
- The `int` variant repros even with `DOTNET_JITMinOpts=1`, since `Lowering::LowerAdd` calls `TryCreateAddrMode` unconditionally on xarch.
- `main` @ `b44cd904110a27d96ea83621e94332d55150d482`, windows-x64 Checked corerun; also repros on released .NET 10.0.12, so long-standing rather than a .NET 11 regression.
Contributor guide
Assessment
This issue has not been assessed yet.