Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bounds Checks] Make union/intersect operations more precise #113862

Merged
merged 5 commits into from
Mar 25, 2025

Conversation

EgorBo
Copy link
Member

@EgorBo EgorBo commented Mar 24, 2025

Closes #113809
Partially addresses #104890 (removes bounds check)

public static int UnrolledSum(Span<int> arr)
{
    int sum = 0;
    int i = 0;
    for (; i < arr.Length - 3; i += 4)
    {
        sum += arr[i + 0];
        sum += arr[i + 1];
        sum += arr[i + 2];
        sum += arr[i + 3];
    }

    // Handle the remaining elements
    for (; i < arr.Length; i++)
        sum += arr[i];
    return sum;
}

We are now able to drop bounds checks completely here:

; Method Tests:UnrolledSum(System.Span`1[int]):int (FullOpts)
-      sub      rsp, 40
       mov      rax, bword ptr [rcx]
       mov      ecx, dword ptr [rcx+0x08]
       xor      edx, edx
       xor      r8d, r8d
       lea      r10d, [rcx-0x03]
       test     r10d, r10d
       jle      SHORT G_M39394_IG05
       align    [0 bytes for IG03]
G_M39394_IG03:
       mov      r9d, r8d
       add      edx, dword ptr [rax+4*r9]
       lea      r9d, [r8+0x01]
       add      edx, dword ptr [rax+4*r9]
       lea      r9d, [r8+0x02]
       add      edx, dword ptr [rax+4*r9]
       lea      r9d, [r8+0x03]
       add      edx, dword ptr [rax+4*r9]
       add      r8d, 4
       cmp      r8d, r10d
       jl       SHORT G_M39394_IG03
       jmp      SHORT G_M39394_IG05
-      align    [0 bytes for IG04]
+      align    [2 bytes for IG04]
G_M39394_IG04:
-      cmp      r8d, ecx
-      jae      SHORT G_M39394_IG08
       mov      r10d, r8d
       add      edx, dword ptr [rax+4*r10]
       inc      r8d
G_M39394_IG05:
       cmp      r8d, ecx
       jl       SHORT G_M39394_IG04
       mov      eax, edx
-      add      rsp, 40
       ret      
-G_M39394_IG08:
-      call     CORINFO_HELP_RNGCHKFAIL
-      int3     
-; Total bytes of code: 99
+; Total bytes of code: 82

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Mar 24, 2025
@EgorBo EgorBo force-pushed the improve-range-union-intersection branch from 6f4b5c0 to 9545f62 Compare March 24, 2025 23:37
return l1;
// Otherwise, prefer the BinOpArray(preferredBound) over the constant for the upper bound
// and the constant for the lower bound.
return isLower ? l2 : l1;
Copy link
Member Author

@EgorBo EgorBo Mar 25, 2025

Choose a reason for hiding this comment

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

In this case we just tighten the range (from multiple facts), it's fine to be liberal here. Seems like for lower bound it's always better to report a constant than trying to be smart with adjusted "bnd + offset"

@@ -510,10 +510,15 @@ struct RangeOps
{
result.lLimit = r1lo;
}
// Widen Upper Limit => Max(k, (a.len + n)) yields (a.len + n),
// This is correct if k >= 0 and n >= k, since a.len always >= 0
Copy link
Member Author

Choose a reason for hiding this comment

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

since a.len always >= 0

NOTE: we've been already relying on this fact in RangeOps::Merge before my change

@EgorBo
Copy link
Member Author

EgorBo commented Mar 25, 2025

Diffs

PTAL @jakobbotsch @dotnet/jit-contrib

@EgorBo EgorBo requested a review from jakobbotsch March 25, 2025 12:33
Comment on lines +537 to +544
if (r1lo.IsBinOpArray() && r2lo.IsConstant() && (r1lo.cns <= 0))
{
result.lLimit = Limit(Limit::keConstant, min(r1lo.cns, r2lo.cns));
}
if (r2lo.IsBinOpArray() && r1lo.IsConstant() && (r2lo.cns <= 0))
{
result.lLimit = Limit(Limit::keConstant, min(r2lo.cns, r1lo.cns));
}
Copy link
Member

Choose a reason for hiding this comment

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

This is adding more reliance on the unsound limits created based on IsVNCheckedBound, isn't it?
It's a little bit worrying...

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep, but we either shouldn't do it at all (depends on the bound actually, if it's VNF_ArrLen then it's always fine to rely on) or keep using it. I hope the "is montonic"/overflow analysis make it safe

Copy link
Member

@jakobbotsch jakobbotsch left a comment

Choose a reason for hiding this comment

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

LGTM with a small worry around using facts created based on IsVNCheckedBound

@EgorBo
Copy link
Member Author

EgorBo commented Mar 25, 2025

/ba-g "azurelinux.3 issues"

@EgorBo EgorBo merged commit a92f652 into dotnet:main Mar 25, 2025
104 of 108 checks passed
@EgorBo EgorBo deleted the improve-range-union-intersection branch March 25, 2025 15:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
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.

JIT: Missed i >= 0 assertion
3 participants