-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
JIT: Preserve range check for HW intrinsics with non-const/out-of-range immediates #106765
JIT: Preserve range check for HW intrinsics with non-const/out-of-range immediates #106765
Conversation
cc @dotnet/jit-contrib, @tannergooding PTAL. Thanks! |
Looks like the |
The title What this is doing isn't converting to user calls, but rather just ensuring that a |
The GT_BOUNDS_CHECK node is inserted when the immediate is unknown during importation, but if the immediate is known to be out-of-range (i.e. I can update the title to include the |
Ah, yes. I missed that bit of code, that will cause a user call to be emitted. We don't just import the throw directly (which would be more ideal) because the inliner wasn't happy with that. The important part of the fix is that we're preserving the bounds check, which for most typical code will be due to values that aren't known to be constant during importation. The path where we have an out of bounds constant during import will likely never be hit in production (as users would have to knowingly pass in a bad value and ignore the analyzer warning). |
{ | ||
assert(immOp != nullptr); | ||
// Full-range imm-intrinsics do not need the range-check | ||
// because the imm-parameter of the intrinsic method is a byte. | ||
// AVX2 Gather intrinsics no not need the range-check | ||
// because their imm-parameter have discrete valid values that are handle by managed code | ||
if (mustExpand && HWIntrinsicInfo::isImmOp(intrinsic, immOp) | ||
if (!immOp->IsCnsIntOrI() && HWIntrinsicInfo::isImmOp(intrinsic, immOp) | ||
#ifdef TARGET_XARCH | ||
&& !HWIntrinsicInfo::isAVX2GatherIntrinsic(intrinsic) && !HWIntrinsicInfo::HasFullRangeImm(intrinsic) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may want to explicitly check that AVX2.Gather*
works correctly given its special.
The valid values are 1, 2, 4, and 8; other values should result in an exception.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure thing. I'll add a few more test cases.
The logic for calculating the max index size in @tannergooding I'm nervous that similar intrinsics have similar logic issues, and our test coverage just isn't exposing them. From a quick parse of nearby intrinsics, |
Looks like it is a bug that was introduced in .NET 9 as part of #103689, #93223, etc as part of the general work to expose the tuple overloads of the They were correctly encoded as |
…rimitive as base type arg" This reverts commit 13536da.
@tannergooding thanks for pointing that out. It's not the most elegant solution, but special-casing |
I don't think that's sufficient as there's overloads that take a tuple of
Right. The "proper" way is likely to have a flag on these intrinsics that lets us know we may need to check for You can see that some handling similar to this concept is being done for |
@tannergooding thanks for pointing me in the right direction. I've added a new flag, |
@@ -487,7 +487,7 @@ HARDWARE_INTRINSIC(AdvSimd, SignExtendWideningLower, | |||
HARDWARE_INTRINSIC(AdvSimd, SignExtendWideningUpper, 16, 1, {INS_sxtl2, INS_invalid, INS_sxtl2, INS_invalid, INS_sxtl2, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) | |||
HARDWARE_INTRINSIC(AdvSimd, SqrtScalar, 8, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_fsqrt, INS_fsqrt}, HW_Category_SIMD, HW_Flag_SIMDScalar) | |||
HARDWARE_INTRINSIC(AdvSimd, Store, 8, 2, {INS_st1_2regs, INS_st1_2regs, INS_st1_2regs, INS_st1_2regs, INS_st1_2regs, INS_st1_2regs, INS_invalid, INS_invalid, INS_st1_2regs, INS_invalid}, HW_Category_MemoryStore, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_NeedsConsecutiveRegisters) | |||
HARDWARE_INTRINSIC(AdvSimd, StoreSelectedScalar, 8, 3, {INS_st1, INS_st1, INS_st1, INS_st1, INS_st1, INS_st1, INS_st1, INS_st1, INS_st1, INS_st1}, HW_Category_MemoryStore, HW_Flag_BaseTypeFromFirstArg|HW_Flag_HasImmediateOperand|HW_Flag_SIMDScalar|HW_Flag_SpecialCodeGen|HW_Flag_SpecialImport|HW_Flag_NeedsConsecutiveRegisters) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's several other methods that take ValueTuple
, like Store
, that need the same fix.
They aren't APIs taking an immediate, so I think it's fine to handle in a separate PR, but they do need to be handled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll open a follow-up PR for those -- thanks
/ba-g Linux NativeAOT/Mono legs timed out |
/backport to release/9.0 |
Started backporting to release/9.0: https://github.com/dotnet/runtime/actions/runs/10568056920 |
@amanasifkhalid backporting to release/9.0 failed, the patch most likely resulted in conflicts: $ git am --3way --ignore-whitespace --keep-non-patch changes.patch
Applying: Always convert out-of-range constants to user call
Applying: Add tests
Applying: Tweak StoreSelectedScalar definition
Applying: Remove unnecessary arg
Applying: Add AVX tests
Applying: Use first arg of StoreSelectedScalar for type info
Applying: Update HWIntrinsicInfo::lookupSimdSize to handle pointer to primitive as base type arg
Applying: Revert "Update HWIntrinsicInfo::lookupSimdSize to handle pointer to primitive as base type arg"
Applying: Special-case StoreSelectedScalar during importation
Applying: Revert "Special-case StoreSelectedScalar during importation"
Applying: Add handling for base type from ValueTuple
Using index info to reconstruct a base tree...
M src/coreclr/jit/hwintrinsic.h
Falling back to patching base and 3-way merge...
Auto-merging src/coreclr/jit/hwintrinsic.h
CONFLICT (content): Merge conflict in src/coreclr/jit/hwintrinsic.h
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
hint: When you have resolved this problem, run "git am --continue".
hint: If you prefer to skip this patch, run "git am --skip" instead.
hint: To restore the original branch and stop patching, run "git am --abort".
hint: Disable this message with "git config advice.mergeConflict false"
Patch failed at 0011 Add handling for base type from ValueTuple
Error: The process '/usr/bin/git' failed with exit code 128 Please backport manually! |
@amanasifkhalid an error occurred while backporting to release/9.0, please check the run log for details! Error: git am failed, most likely due to a merge conflict. |
Fix #106608. Fix #106546. Based on discussion here, intrinsics with out-of-range immediates should always be converted to user calls, and intrinsics with unknown immediates should be given range checks -- if we're optimizing and the immediate is later known, range check removal should kick in. For .NET 10, we plan to add more fallback intrinsic implementations so this pessimization doesn't kick in as often.