Skip to content

Commit d4518ea

Browse files
committed
[clang][bytecode] Handle __builtin_memcmp
1 parent d416cae commit d4518ea

File tree

5 files changed

+95
-5
lines changed

5 files changed

+95
-5
lines changed

clang/lib/AST/ByteCode/BitcastBuffer.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ namespace interp {
1818

1919
enum class Endian { Little, Big };
2020

21+
struct Bytes;
22+
2123
/// A quantity in bits.
2224
struct Bits {
2325
size_t N = 0;
@@ -30,6 +32,7 @@ struct Bits {
3032
bool isFullByte() const { return N % 8 == 0; }
3133
bool nonZero() const { return N != 0; }
3234
bool isZero() const { return N == 0; }
35+
Bytes toBytes() const;
3336

3437
Bits operator-(Bits Other) const { return Bits(N - Other.N); }
3538
Bits operator+(Bits Other) const { return Bits(N + Other.N); }
@@ -56,6 +59,11 @@ struct Bytes {
5659
Bits toBits() const { return Bits(N * 8); }
5760
};
5861

62+
inline Bytes Bits::toBytes() const {
63+
assert(isFullByte());
64+
return Bytes(N / 8);
65+
}
66+
5967
/// A bit range. Both Start and End are inclusive.
6068
struct BitRange {
6169
Bits Start;
@@ -83,6 +91,7 @@ struct BitcastBuffer {
8391

8492
/// Returns the buffer size in bits.
8593
Bits size() const { return FinalBitSize; }
94+
Bytes byteSize() const { return FinalBitSize.toBytes(); }
8695

8796
/// Returns \c true if all bits in the buffer have been initialized.
8897
bool allInitialized() const;

clang/lib/AST/ByteCode/InterpBuiltin.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1830,6 +1830,7 @@ static bool interp__builtin_elementwise_popcount(InterpState &S, CodePtr OpPC,
18301830

18311831
return true;
18321832
}
1833+
18331834
static bool interp__builtin_memcpy(InterpState &S, CodePtr OpPC,
18341835
const InterpFrame *Frame,
18351836
const Function *Func, const CallExpr *Call) {
@@ -1900,6 +1901,51 @@ static bool interp__builtin_memcpy(InterpState &S, CodePtr OpPC,
19001901
return true;
19011902
}
19021903

1904+
static bool interp__builtin_memcmp(InterpState &S, CodePtr OpPC,
1905+
const InterpFrame *Frame,
1906+
const Function *Func, const CallExpr *Call) {
1907+
assert(Call->getNumArgs() == 3);
1908+
unsigned ID = Func->getBuiltinID();
1909+
Pointer PtrA = getParam<Pointer>(Frame, 0);
1910+
const Pointer &PtrB = getParam<Pointer>(Frame, 1);
1911+
const APSInt &Size =
1912+
peekToAPSInt(S.Stk, *S.getContext().classify(Call->getArg(2)));
1913+
1914+
if (ID == Builtin::BImemcmp)
1915+
diagnoseNonConstexprBuiltin(S, OpPC, ID);
1916+
1917+
if (Size.isZero()) {
1918+
pushInteger(S, 0, Call->getType());
1919+
return true;
1920+
}
1921+
1922+
if (PtrA.isDummy() || PtrB.isDummy())
1923+
return false;
1924+
1925+
// Now, read both pointers to a buffer and compare those.
1926+
1927+
BitcastBuffer BufferA(
1928+
Bits(S.getASTContext().getTypeSize(PtrA.getFieldDesc()->getType())));
1929+
readPointerToBuffer(S.getContext(), PtrA, BufferA, false);
1930+
1931+
BitcastBuffer BufferB(
1932+
Bits(S.getASTContext().getTypeSize(PtrB.getFieldDesc()->getType())));
1933+
readPointerToBuffer(S.getContext(), PtrB, BufferB, false);
1934+
1935+
size_t MinBufferSize = std::min(BufferA.byteSize().getQuantity(),
1936+
BufferB.byteSize().getQuantity());
1937+
size_t CmpSize = std::min(MinBufferSize, Size.getZExtValue());
1938+
int Result = std::memcmp(BufferA.Data.get(), BufferB.Data.get(), CmpSize);
1939+
if (Result == 0)
1940+
pushInteger(S, 0, Call->getType());
1941+
else if (Result < 0)
1942+
pushInteger(S, -1, Call->getType());
1943+
else
1944+
pushInteger(S, 1, Call->getType());
1945+
1946+
return true;
1947+
}
1948+
19031949
bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
19041950
const CallExpr *Call, uint32_t BuiltinID) {
19051951
const InterpFrame *Frame = S.Current;
@@ -2373,6 +2419,12 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
23732419
return false;
23742420
break;
23752421

2422+
case Builtin::BI__builtin_memcmp:
2423+
case Builtin::BImemcmp:
2424+
if (!interp__builtin_memcmp(S, OpPC, Frame, F, Call))
2425+
return false;
2426+
break;
2427+
23762428
default:
23772429
S.FFDiag(S.Current->getLocation(OpPC),
23782430
diag::note_invalid_subexpr_in_const_expr)

clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,8 +259,10 @@ static bool CheckBitcastType(InterpState &S, CodePtr OpPC, QualType T,
259259
return true;
260260
}
261261

262-
static bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr,
263-
BitcastBuffer &Buffer, bool ReturnOnUninit) {
262+
bool clang::interp::readPointerToBuffer(const Context &Ctx,
263+
const Pointer &FromPtr,
264+
BitcastBuffer &Buffer,
265+
bool ReturnOnUninit) {
264266
const ASTContext &ASTCtx = Ctx.getASTContext();
265267
Endian TargetEndianness =
266268
ASTCtx.getTargetInfo().isLittleEndian() ? Endian::Little : Endian::Big;

clang/lib/AST/ByteCode/InterpBuiltinBitCast.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9-
#ifndef LLVM_CLANG_AST_INTERP_BUILITN_BIT_CAST_H
10-
#define LLVM_CLANG_AST_INTERP_BUILITN_BIT_CAST_H
9+
#ifndef LLVM_CLANG_AST_INTERP_BUILTIN_BIT_CAST_H
10+
#define LLVM_CLANG_AST_INTERP_BUILTIN_BIT_CAST_H
1111

1212
#include "BitcastBuffer.h"
1313
#include <cstddef>
@@ -17,6 +17,7 @@ namespace interp {
1717
class Pointer;
1818
class InterpState;
1919
class CodePtr;
20+
class Context;
2021

2122
bool DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
2223
std::byte *Buff, Bits BitWidth, Bits FullBitWidth,
@@ -25,7 +26,8 @@ bool DoBitCastPtr(InterpState &S, CodePtr OpPC, const Pointer &FromPtr,
2526
Pointer &ToPtr);
2627
bool DoBitCastPtr(InterpState &S, CodePtr OpPC, const Pointer &FromPtr,
2728
Pointer &ToPtr, size_t Size);
28-
29+
bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr,
30+
BitcastBuffer &Buffer, bool ReturnOnUninit);
2931
} // namespace interp
3032
} // namespace clang
3133

clang/test/AST/ByteCode/builtin-functions.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,3 +1223,28 @@ namespace BuiltinMemcpy {
12231223
static_assert(test_memcpy(0, 1, sizeof(int) * 2) == 2334); // both-error {{not an integral constant expression}} \
12241224
// both-note {{in call}}
12251225
}
1226+
1227+
namespace Memcmp {
1228+
constexpr unsigned char ku00fe00[] = {0x00, 0xfe, 0x00};
1229+
constexpr unsigned char ku00feff[] = {0x00, 0xfe, 0xff};
1230+
constexpr signed char ks00fe00[] = {0, -2, 0};
1231+
constexpr signed char ks00feff[] = {0, -2, -1};
1232+
static_assert(__builtin_memcmp(ku00feff, ks00fe00, 2) == 0);
1233+
static_assert(__builtin_memcmp(ku00feff, ks00fe00, 99) == 1);
1234+
static_assert(__builtin_memcmp(ku00fe00, ks00feff, 99) == -1);
1235+
static_assert(__builtin_memcmp(ks00feff, ku00fe00, 2) == 0);
1236+
static_assert(__builtin_memcmp(ks00feff, ku00fe00, 99) == 1);
1237+
static_assert(__builtin_memcmp(ks00fe00, ku00feff, 99) == -1);
1238+
static_assert(__builtin_memcmp(ks00fe00, ks00feff, 2) == 0);
1239+
static_assert(__builtin_memcmp(ks00feff, ks00fe00, 99) == 1);
1240+
static_assert(__builtin_memcmp(ks00fe00, ks00feff, 99) == -1);
1241+
1242+
struct Bool3Tuple { bool bb[3]; };
1243+
constexpr Bool3Tuple kb000100 = {{false, true, false}};
1244+
static_assert(sizeof(bool) != 1u || __builtin_memcmp(ks00fe00, kb000100.bb, 1) == 0); // ref-error {{constant}} \
1245+
// ref-note {{not supported}}
1246+
1247+
constexpr char a = 'a';
1248+
constexpr char b = 'a';
1249+
static_assert(__builtin_memcmp(&a, &b, 1) == 0);
1250+
}

0 commit comments

Comments
 (0)