Skip to content

[ARM] Fix undefined behavior in isLegalAddImmediate #132219

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

Merged
merged 1 commit into from
Mar 21, 2025

Conversation

statham-arm
Copy link
Collaborator

Building clang under UBsan, it reported an integer overflow in this function when the input value was -2^63, because it's UB to pass the maximum negative value of an integer type to std::abs.

Fixed by adding a new absolute-value function in MathExtras.h whose return type is the unsigned version of the argument type, and using that instead.

(This seems like the kind of thing C++ should have already had, but apparently it doesn't, and I couldn't find an existing one in LLVM Support either.)

Building clang under UBsan, it reported an integer overflow in this
function when the input value was -2^63, because it's UB to pass the
maximum negative value of an integer type to `std::abs`.

Fixed by adding a new absolute-value function in `MathExtras.h` whose
return type is the unsigned version of the argument type, and using
that instead.

(This seems like the kind of thing C++ should have already had,
but apparently it doesn't, and I couldn't find an existing one in LLVM
Support either.)
@llvmbot
Copy link
Member

llvmbot commented Mar 20, 2025

@llvm/pr-subscribers-llvm-support

@llvm/pr-subscribers-backend-arm

Author: Simon Tatham (statham-arm)

Changes

Building clang under UBsan, it reported an integer overflow in this function when the input value was -2^63, because it's UB to pass the maximum negative value of an integer type to std::abs.

Fixed by adding a new absolute-value function in MathExtras.h whose return type is the unsigned version of the argument type, and using that instead.

(This seems like the kind of thing C++ should have already had, but apparently it doesn't, and I couldn't find an existing one in LLVM Support either.)


Full diff: https://github.com/llvm/llvm-project/pull/132219.diff

2 Files Affected:

  • (modified) llvm/include/llvm/Support/MathExtras.h (+9)
  • (modified) llvm/lib/Target/ARM/ARMISelLowering.cpp (+1-1)
diff --git a/llvm/include/llvm/Support/MathExtras.h b/llvm/include/llvm/Support/MathExtras.h
index 07f7bab0f0a3a..519fcc8fde5d5 100644
--- a/llvm/include/llvm/Support/MathExtras.h
+++ b/llvm/include/llvm/Support/MathExtras.h
@@ -595,6 +595,15 @@ inline int64_t SignExtend64(uint64_t X, unsigned B) {
   return int64_t(X << (64 - B)) >> (64 - B);
 }
 
+/// Return the absolute value of a signed integer, converted to the
+/// corresponding unsigned integer type. Avoids undefined behavior in std::abs
+/// when you pass it INT_MIN or similar.
+template <typename T, typename U = std::make_unsigned_t<T>>
+constexpr U AbsoluteValue(T X) {
+  // If X is negative, cast it to the unsigned type _before_ negating it.
+  return X < 0 ? -static_cast<U>(X) : X;
+}
+
 /// Subtract two unsigned integers, X and Y, of type T and return the absolute
 /// value of the result.
 template <typename U, typename V, typename T = common_uint<U, V>>
diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp b/llvm/lib/Target/ARM/ARMISelLowering.cpp
index 234abefe75ee7..c015448ef69d4 100644
--- a/llvm/lib/Target/ARM/ARMISelLowering.cpp
+++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp
@@ -19691,7 +19691,7 @@ bool ARMTargetLowering::isLegalICmpImmediate(int64_t Imm) const {
 /// immediate into a register.
 bool ARMTargetLowering::isLegalAddImmediate(int64_t Imm) const {
   // Same encoding for add/sub, just flip the sign.
-  int64_t AbsImm = std::abs(Imm);
+  uint64_t AbsImm = AbsoluteValue(Imm);
   if (!Subtarget->isThumb())
     return ARM_AM::getSOImmVal(AbsImm) != -1;
   if (Subtarget->isThumb2())

Copy link
Collaborator

@ostannard ostannard left a comment

Choose a reason for hiding this comment

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

LGTM, I'm also surprised that this doesn't exist in either C++ or MathExtras.h.

@statham-arm statham-arm merged commit f4ec179 into llvm:main Mar 21, 2025
14 checks passed
@statham-arm statham-arm deleted the unsigned-absolute-value branch March 21, 2025 09:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants