[clang] 98470c0 - [clang][bytecode] Handle __builtin_bcmp (#119678)

via cfe-commits cfe-commits at lists.llvm.org
Thu Dec 12 01:57:43 PST 2024


Author: Timm Baeder
Date: 2024-12-12T10:57:39+01:00
New Revision: 98470c0b2e0eef52e6900bf2d524a390edac9d58

URL: https://github.com/llvm/llvm-project/commit/98470c0b2e0eef52e6900bf2d524a390edac9d58
DIFF: https://github.com/llvm/llvm-project/commit/98470c0b2e0eef52e6900bf2d524a390edac9d58.diff

LOG: [clang][bytecode] Handle __builtin_bcmp (#119678)

... the same as `__builtin_memcmp`. Also fix a bug we still had when we
couldn't find a difference in the two inputs after `Size` bytes.

Added: 
    

Modified: 
    clang/lib/AST/ByteCode/InterpBuiltin.cpp
    clang/test/AST/ByteCode/builtin-functions.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index a343280b5ce50f..21baedf832eeac 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -1917,7 +1917,7 @@ static bool interp__builtin_memcmp(InterpState &S, CodePtr OpPC,
   const APSInt &Size =
       peekToAPSInt(S.Stk, *S.getContext().classify(Call->getArg(2)));
 
-  if (ID == Builtin::BImemcmp)
+  if (ID == Builtin::BImemcmp || ID == Builtin::BIbcmp)
     diagnoseNonConstexprBuiltin(S, OpPC, ID);
 
   if (Size.isZero()) {
@@ -1952,15 +1952,34 @@ static bool interp__builtin_memcmp(InterpState &S, CodePtr OpPC,
                                   BufferB.byteSize().getQuantity());
   size_t CmpSize =
       std::min(MinBufferSize, static_cast<size_t>(Size.getZExtValue()));
-  int Result = std::memcmp(BufferA.Data.get(), BufferB.Data.get(), CmpSize);
-  if (Result == 0)
+
+  for (size_t I = 0; I != CmpSize; ++I) {
+    std::byte A = BufferA.Data[I];
+    std::byte B = BufferB.Data[I];
+
+    if (A < B) {
+      pushInteger(S, -1, Call->getType());
+      return true;
+    } else if (A > B) {
+      pushInteger(S, 1, Call->getType());
+      return true;
+    }
+  }
+
+  // We compared CmpSize bytes above. If the limiting factor was the Size
+  // passed, we're done and the result is equality (0).
+  if (Size.getZExtValue() <= CmpSize) {
     pushInteger(S, 0, Call->getType());
-  else if (Result < 0)
-    pushInteger(S, -1, Call->getType());
-  else
-    pushInteger(S, 1, Call->getType());
+    return true;
+  }
 
-  return true;
+  // However, if we read all the available bytes but were instructed to read
+  // even more, diagnose this as a "read of dereferenced one-past-the-end
+  // pointer". This is what would happen if we called CheckRead() on every array
+  // element.
+  S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_access_past_end)
+      << AK_Read << S.Current->getRange(OpPC);
+  return false;
 }
 
 bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
@@ -2438,6 +2457,8 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
 
   case Builtin::BI__builtin_memcmp:
   case Builtin::BImemcmp:
+  case Builtin::BI__builtin_bcmp:
+  case Builtin::BIbcmp:
     if (!interp__builtin_memcmp(S, OpPC, Frame, F, Call))
       return false;
     break;

diff  --git a/clang/test/AST/ByteCode/builtin-functions.cpp b/clang/test/AST/ByteCode/builtin-functions.cpp
index 83caa1d03df3a3..4ee24646286fa8 100644
--- a/clang/test/AST/ByteCode/builtin-functions.cpp
+++ b/clang/test/AST/ByteCode/builtin-functions.cpp
@@ -1255,4 +1255,19 @@ namespace Memcmp {
                                                               // both-note {{not supported}}
   static_assert(__builtin_memcmp("", &incomplete, 1u) == 42); // both-error {{not an integral constant}} \
                                                               // both-note {{not supported}}
+
+  static_assert(__builtin_memcmp(u8"abab\0banana", u8"abab\0banana", 100) == 0); // both-error {{not an integral constant}} \
+                                                                                 // both-note {{dereferenced one-past-the-end}}
+
+  static_assert(__builtin_bcmp("abaa", "abba", 3) != 0);
+  static_assert(__builtin_bcmp("abaa", "abba", 2) == 0);
+  static_assert(__builtin_bcmp("a\203", "a", 2) != 0);
+  static_assert(__builtin_bcmp("a\203", "a\003", 2) != 0);
+  static_assert(__builtin_bcmp(0, 0, 0) == 0);
+  static_assert(__builtin_bcmp("abab\0banana", "abab\0banana", 100) == 0); // both-error {{not an integral constant}}\
+                                                                           // both-note {{dereferenced one-past-the-end}}
+  static_assert(__builtin_bcmp("abab\0banana", "abab\0canada", 100) != 0); // FIXME: Should we reject this?
+  static_assert(__builtin_bcmp("abab\0banana", "abab\0canada", 7) != 0);
+  static_assert(__builtin_bcmp("abab\0banana", "abab\0canada", 6) != 0);
+  static_assert(__builtin_bcmp("abab\0banana", "abab\0canada", 5) == 0);
 }


        


More information about the cfe-commits mailing list