Skip to content

Conversation

@jakobbotsch
Copy link
Member

@jakobbotsch jakobbotsch commented Dec 4, 2025

The JIT has two ways to represent a 32->64 bit zero extension. For array indexing, for instance, we produce the following:

n---G+-----                            │  └──▌  IND       int    <l:$203, c:$143>
-----+-----                            │     └──▌  ARR_ADDR  byref int[] $81
-----+-N---                            │        └──▌  ADD       byref  $300
-----+-----                            │           ├──▌  LCL_VAR   ref    V01 arg1         u:1 $100
-----+-N---                            │           └──▌  ADD       long   $282
-----+-N---                            │              ├──▌  LSH       long   $281
-----+---U-                            │              │  ├──▌  CAST      long <- uint $280 // zero extension (U flag)
-----+-----                            │              │  │  └──▌  LCL_VAR   int    V03 loc1         u:3 $241
-----+-N---                            │              │  └──▌  CNS_INT   long   2 $2c0
-----+-----                            │              └──▌  CNS_INT   long   16 $2c2

For an explicit zero extension, however, such as when indexing a raw pointer with ptr[(uint)i], we produce the following:

---XG+-----                            ├──▌  IND       int    <l:$142, c:$141>
-----+-N---                            │  └──▌  ADD       long   $1c2
-----+-----                            │     ├──▌  LCL_VAR   long   V01 arg1         u:1 $80
-----+-N---                            │     └──▌  LSH       long   $1c1
-----+---U-                            │        ├──▌  CAST      long <- ulong <- uint $1c0 // zero extension (TYP_ULONG and U flag)
-----+-----                            │        │  └──▌  LCL_VAR   int    V04 loc1         u:3 $181
-----+-----                            │        └──▌  CNS_INT   long   2 $200
-----+-----                            └──▌  LCL_VAR   int    V03 loc0         u:3 (last use) $180

SCEV analysis did not recognize the latter form as a zero extension.

Example:

int TestInt(int* data, int count)
{
    int sum = 0;
    for (int i = 0; i < count; i++)
    {
        sum += data[(uint)i];
    }

    return sum;
}

Before:

G_M29388_IG02:  ;; offset=0x0000
       xor      eax, eax
       xor      ecx, ecx
       test     r8d, r8d
       jle      SHORT G_M29388_IG04
       align    [0 bytes for IG03]
						;; size=9 bbWeight=1 PerfScore 1.75

G_M29388_IG03:  ;; offset=0x0009
       mov      r10d, ecx
       add      eax, dword ptr [rdx+4*r10]
       inc      ecx
       cmp      ecx, r8d
       jl       SHORT G_M29388_IG03
						;; size=14 bbWeight=4 PerfScore 19.00

After:

G_M29388_IG02:  ;; offset=0x0000
       xor      eax, eax
       test     r8d, r8d
       jle      SHORT G_M29388_IG05
						;; size=7 bbWeight=1 PerfScore 1.50

G_M29388_IG03:  ;; offset=0x0007
       align    [0 bytes for IG04]
						;; size=0 bbWeight=0.50 PerfScore 0.00

G_M29388_IG04:  ;; offset=0x0007
       add      eax, dword ptr [rdx]
       add      rdx, 4
       dec      r8d
       jne      SHORT G_M29388_IG04
						;; size=11 bbWeight=4 PerfScore 18.00

The JIT has two ways to represent a 32->64 bit zero extension. For array
indexing, for instance, we produce the following:
```
└──▌  IND       int    <l:$203, c:$143>
   └──▌  ARR_ADDR  byref int[] $81
      └──▌  ADD       byref  $300
         ├──▌  LCL_VAR   ref    V01 arg1         u:1 $100
         └──▌  ADD       long   $282
            ├──▌  LSH       long   $281
            │  ├──▌  CAST      long <- uint $280  <- zero extension
            │  │  └──▌  LCL_VAR   int    V03 loc1         u:3 $241
            │  └──▌  CNS_INT   long   2 $2c0
            └──▌  CNS_INT   long   16 $2c2

```

For an explicit zero extension, however, such as when indexing a raw
pointer, we produce the following:
```

├──▌  IND       int    <l:$142, c:$141>
│  └──▌  ADD       long   $1c2
│     ├──▌  LCL_VAR   long   V01 arg1         u:1 $80
│     └──▌  LSH       long   $1c1
│        ├──▌  CAST      long <- ulong <- uint $1c0  <- also a zero extension
│        │  └──▌  LCL_VAR   int    V04 loc1         u:3 $181
│        └──▌  CNS_INT   long   2 $200
```

SCEV analysis did not recognize the latter form as a zero extension.
Copilot AI review requested due to automatic review settings December 4, 2025 15:52
@github-actions github-actions bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Dec 4, 2025
@dotnet-policy-service
Copy link
Contributor

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

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes SCEV (Scalar Evolution) analysis to recognize both forms of 32-to-64 bit zero extension patterns used by the JIT. Previously, SCEV only recognized CAST long <- uint but not CAST long <- ulong <- uint. The fix adds TYP_ULONG to the cast type check, allowing the analysis to handle both patterns consistently.

Key Change:

  • Modified the cast type check in scev.cpp to accept both TYP_LONG and TYP_ULONG cast types

@jakobbotsch
Copy link
Member Author

cc @dotnet/jit-contrib

Minor diffs

@jakobbotsch jakobbotsch merged commit 304e5a8 into dotnet:main Dec 5, 2025
134 of 137 checks passed
@jakobbotsch jakobbotsch deleted the recognize-zero-extension branch December 5, 2025 07:58
@github-actions github-actions bot locked and limited conversation to collaborators Jan 4, 2026
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.

3 participants