dotnet / dotnet/runtime

JIT: (bug) x64: assert `dstOffset < (INT32_MAX - size)` in `genCodeForInitBlkUnroll`/`genCodeForCpBlkUnroll` for a boundary address-mode displacement

Open
#133,785 0 comments 0 reactions 1 assignee Claimed by @EgorBo View on GitHub
area-CodeGen-coreclr
Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Description

`Lowering::ContainBlockStoreAddress` accepts `addrMode->Offset() == INT32_MAX - size` (it rejects only `>`), while `genCodeFor{Init,Cp}BlkUnroll` assert the strict `srcOffset/dstOffset < (INT32_MAX - size)`. A contained `LEA(base + (INT32_MAX - size))` feeding an unrolled `STORE_BLK` therefore trips the assert in a Checked JIT.

### Minimal Repro

```csharp
using System;
using System.Runtime.CompilerServices;

public struct S3 { public byte A, B, C; }

public static class P
{
// element offset = 16 + 715827876*3 = 2147483644 == int.MaxValue - 3
const int Idx = 715827876;

[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void StoreCopy(S3[] arr, S3 v) => arr[Idx] = v;

[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
public static void StoreInit(S3[] arr) => arr[Idx] = default;

public static void Main(string[] args)
{
var a = new S3[1];
if (args.Length == 0)
{
try { StoreInit(a); } catch (IndexOutOfRangeException) { Console.WriteLine("ok2"); }
}
else
{
try { StoreCopy(a, default); } catch (IndexOutOfRangeException) { Console.WriteLine("ok1"); }
}
}
}
```

```
set DOTNET_TieredCompilation=0
corerun out\blkdisp.dll :: STORE_BLK (init) -> genCodeForInitBlkUnroll
corerun out\blkdisp.dll x :: STORE_BLK (copy) -> genCodeForCpBlkUnroll
```

(The two methods are split by `args` because the first one jitted aborts the process.)

### Expected

```
ok2
```
and
```
ok1
```

### Actual

```
Assert failure(PID 30132, Thread: 33480): Assertion failed 'dstOffset < (INT32_MAX - static_cast(size))'
in 'P:StoreInit(S3[])' during 'Generate code' (IL size 18; hash 0xe8a9310d; FullOpts)

File: C:\prj\runtime-main3\src\coreclr\jit\codegenxarch.cpp:3219
```

```
Assert failure(PID 43972, Thread: 31304): Assertion failed 'dstOffset < (INT32_MAX - static_cast(size))'
in 'P:StoreCopy(S3[],S3)' during 'Generate code' (IL size 13; hash 0x2433ddfe; FullOpts)

File: C:\prj\runtime-main3\src\coreclr\jit\codegenxarch.cpp:3550
```

`DOTNET_JitDump=StoreInit` shows the contained boundary address mode right before the assert:

```
N017 ( 1, 5) [000014] -c---+----- t14 = * LEA(b+2147483644) byref REG NA
N021 ( 17, 25) [000004] nA-XG+----- * STORE_BLK struct (init) (Unroll) REG NA $145
```

### Notes

- Off-by-one between `lowerxarch.cpp:405` (`>`) and `codegenxarch.cpp:3219`/`:3549-3550` (`<`); either lowering should reject `>=` or the asserts should use `<=`.
- Over-strict assert, not a codegen bug: the largest displacement actually emitted is `offset + size - 1 <= INT32_MAX - 1`, so release codegen is correct (verified on .NET 10.0.12, which prints `ok2`/`ok1`).
- Checked JIT, optimized codegen, x64/x86 only; with `DOTNET_JITMinOpts=1` no containment happens and both methods run fine.
- `main` @ `b44cd904110a27d96ea83621e94332d55150d482`; not a regression, the mismatch is present in the .NET 10 sources too.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.