Skip to content

Conversation

@TIHan
Copy link
Contributor

@TIHan TIHan commented Jul 26, 2024

Resolves #105479

@ghost ghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jul 26, 2024
@TIHan TIHan changed the title JIT: Make certain ARM64 HWIntrinsic categories as throwing exceptions due to immediate operands going out of range JIT: Mark certain ARM64 HWIntrinsic categories as throwing exceptions due to immediate operands going out of range Jul 26, 2024
@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.

@TIHan TIHan changed the title JIT: Mark certain ARM64 HWIntrinsic categories as throwing exceptions due to immediate operands going out of range JIT: Determine which ARM64 HWIntrinsics will throw ArgumentOufOfRangeExceptions Jul 26, 2024
@kunalspathak
Copy link
Contributor

@dotnet/jit-contrib @dotnet/arm64-contrib

IndexOutOfRangeException = 0x10,
StackOverflowException = 0x20,
#ifdef TARGET_ARM64
ArgumentOutOfRangeException = 0x40,
Copy link
Member

Choose a reason for hiding this comment

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

We're going to need this for x64 as well, so we can shouldn't need to #ifdef it

private void Method1()
{
Vector128<ulong> vr0 = Vector128.CreateScalar(1698800584428641629UL);
AdvSimd.ShiftLeftLogicalSaturate(vr0, 229);
Copy link
Contributor

Choose a reason for hiding this comment

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

I thought we have coverage in unit test for invalid immediate too. can you double check why this problem didn't appear in those tests?

Copy link
Member

Choose a reason for hiding this comment

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

We should validate that the GT_HWINTRINSIC node is marked GTF_SIDE_EFFECT when we insert the GT_BOUNDS_CHK node over op2 here (this should be happening already as part of GenTreeMultiOp::InitializeOperands which runs in the constructor).

Beyond that, we likely need to also ensure that hwintrinsic nodes marked as GTF_HW_USER_CALL are also marked as GTF_EXCEPT when the allowed input range is a partial range and for that to be stripped in rationalization (where we already strip GTF_HW_USER_CALL) if it is determined to be "in range".

}

#ifdef TARGET_ARM64
static bool HasImmediateOperandRange(NamedIntrinsic id)
Copy link
Contributor

Choose a reason for hiding this comment

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

DuplicateSelectedScalarToVector128 has HW_Flag_HasImmediateOperand and not any of the HW_Category_ flag in this method, but still it should check for the range.

/// <summary>
        /// uint8x16_t vdupq_lane_u8 (uint8x8_t vec, const int lane)
        ///   A32: VDUP.8 Qd, Dm[index]
        ///   A64: DUP Vd.16B, Vn.B[index]
        /// </summary>
        public static [Vector128](https://source.dot.net/System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128_1.cs.html#a7d647b6968ad0f9)<byte> [DuplicateSelectedScalarToVector128](https://source.dot.net/#System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/AdvSimd.PlatformNotSupported.cs,c98f4003aa811a2f)([Vector64](https://source.dot.net/System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64_1.cs.html#576983e071d8f012)<byte> value, [[ConstantExpected](https://source.dot.net/System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/ConstantExpectedAttribute.cs.html#12103fea5f68fa88)([Max](https://source.dot.net/System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/ConstantExpectedAttribute.cs.html#bcd0dee62624224e) = (byte)(7))] byte index) { throw new [PlatformNotSupportedException](https://source.dot.net/System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/PlatformNotSupportedException.cs.html#ad07c3864de72561)(); }

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It should, though I'm not sure the best way to cover all the intrinsics that have a possible ArgumentOutOfRangeException without going through them one by one (which would take some time). At least this gets most of it blocked and we can fix the others as they come in through Fuzzlyn.

Copy link
Contributor

Choose a reason for hiding this comment

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

can you open an issue for this?

// or have a behavioral semantic that is undesirable to remove
break;
}
#if defined(TARGET_ARM64)
Copy link
Contributor

Choose a reason for hiding this comment

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

can you elaborate on why we are doing this? Why was the repro working in Debug and not in Release?

IndexOutOfRangeException = 0x10,
StackOverflowException = 0x20,
#ifdef TARGET_ARM64
ArgumentOutOfRangeException = 0x40,
Copy link
Member

Choose a reason for hiding this comment

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

Notably was talking about this with @jakobbotsch on Discord earlier and mentioned that we can't model ArgumentOutOfRangeException with OperExceptions because we can't reorder two such exceptions due to containing parameters.

I don't have the context as to why that is, but my guess is there's some other usage/handling of these that makes it problematic

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What should we do instead?

break;
}
#if defined(TARGET_ARM64)
else if (HWIntrinsicInfo::HasImmediateOperandRange(intrinsicId))
Copy link
Member

Choose a reason for hiding this comment

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

I think this bit is overly conservative. We don't need to preserve any intrinsic with an immediate range, rather only ones that have something we can't determine is in bounds.

Consider for example if it was ShiftLeftLogicalSaturate(vr0, 1).

Copy link
Contributor Author

@TIHan TIHan Jul 26, 2024

Choose a reason for hiding this comment

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

It is conservative, but I think that's ok for now. Given this fixes a correctness issue, we can figure out how to optimize it later.

Copy link
Member

Choose a reason for hiding this comment

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

Normally I'd agree, but this is going to mark a very large number of core APIs as side effecting even when they aren't and will likely have impact to important workloads.

Today, the general logic of "do I need a bounds check" goes through lookupImmBounds followed by addRangeCheckIfNeeded, so I'd expect us to be able to use this to mark the GT_HWINTRINSIC as side effecting and then key this liveness check based on that.

I'd expect if we do that, it also solves the issue of ensuring all nodes which insert bounds checks are marked that way, without needing to use this separate HasImmediateOperandRange check

Copy link
Member

Choose a reason for hiding this comment

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

For reference, here's where we check in rationalization for which operands are immediate and whether they are in range or not: https://github.com/dotnet/runtime/blob/main/src/coreclr/jit/rationalize.cpp#L457-L499

That then helps us decide whether or not we need to convert it back to a user call or if we can handle it "as is".

We have similar overall logic in the importer (https://github.com/dotnet/runtime/blob/main/src/coreclr/jit/hwintrinsic.cpp and https://github.com/dotnet/runtime/blob/main/src/coreclr/jit/hwintrinsicarm64.cpp, look for addRangeCheckIfNeeded), and I think we can mostly hook into the importation handling here to ensure this all works nicely and tracks the side effect.

@TIHan
Copy link
Contributor Author

TIHan commented Jul 26, 2024

@dotnet/jit-contrib @tannergooding this is ready

Copy link
Member

@tannergooding tannergooding left a comment

Choose a reason for hiding this comment

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

LGTM, thanks for cleaning this up and simplifying the fix!

#ifdef FEATURE_HW_INTRINSICS
else if (OperIsHWIntrinsic())
{
if ((gtFlags & (GTF_HW_USER_CALL | GTF_EXCEPT)) == (GTF_HW_USER_CALL | GTF_EXCEPT))
Copy link
Member

@jakobbotsch jakobbotsch Jul 26, 2024

Choose a reason for hiding this comment

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

OperMayThrow is expected to recompute whether we need to set GTF_EXCEPT on a node, without taking into account its operand nodes. This does take into account the operands nodes; I think instead the check should be:

Suggested change
if ((gtFlags & (GTF_HW_USER_CALL | GTF_EXCEPT)) == (GTF_HW_USER_CALL | GTF_EXCEPT))
if ((gtFlags & GTF_HW_USER_CALL) != 0)

(I don't think it makes any difference in practice since all GTF_HW_USER_CALL have GTF_EXCEPT, but still good to make the intent here clearer)

@jakobbotsch
Copy link
Member

jakobbotsch commented Jul 26, 2024

I think there is one more thing we need to do here. Right now OperExceptions is invalid to call on these hw intrinsics; it will return an incorrect result, and some transformations (like forward sub and call args morphing) can end up reordering these intrinsics illegally.
Can you add an assert in OperExceptions for the GT_HWINTRINSIC case that GTF_HW_USER_CALL is not set? Then, to make sure that callers do not try to reorder these intrinsics you will have to also set GTF_CALL on the node where we are now setting GTF_HW_USER_CALL | GTF_EXCEPT.

I notice that GenTreeHWIntrinsic::OperRequiresCallFlag is already correct (ends up checking GTF_HW_USER_CALL), so that one doesn't look like it will need to change.

@TIHan
Copy link
Contributor Author

TIHan commented Jul 27, 2024

@jakobbotsch this is ready, made the changes you suggested

@TIHan TIHan merged commit f832e66 into dotnet:main Jul 28, 2024
@github-actions github-actions bot locked and limited conversation to collaborators Aug 30, 2024
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.

JIT: Missing argument validation for ExtractVector64 when optimizing

4 participants