Skip to content

Commit 8713914

Browse files
authored
[clang][bytecode] Handle __builtin_memcmp (#119544)
1 parent 9c50182 commit 8713914

File tree

5 files changed

+119
-5
lines changed

5 files changed

+119
-5
lines changed

clang/lib/AST/ByteCode/BitcastBuffer.h

+9
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

+68
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,67 @@ static bool interp__builtin_memcpy(InterpState &S, CodePtr OpPC,
19001901
return true;
19011902
}
19021903

1904+
/// Determine if T is a character type for which we guarantee that
1905+
/// sizeof(T) == 1.
1906+
static bool isOneByteCharacterType(QualType T) {
1907+
return T->isCharType() || T->isChar8Type();
1908+
}
1909+
1910+
static bool interp__builtin_memcmp(InterpState &S, CodePtr OpPC,
1911+
const InterpFrame *Frame,
1912+
const Function *Func, const CallExpr *Call) {
1913+
assert(Call->getNumArgs() == 3);
1914+
unsigned ID = Func->getBuiltinID();
1915+
const Pointer &PtrA = getParam<Pointer>(Frame, 0);
1916+
const Pointer &PtrB = getParam<Pointer>(Frame, 1);
1917+
const APSInt &Size =
1918+
peekToAPSInt(S.Stk, *S.getContext().classify(Call->getArg(2)));
1919+
1920+
if (ID == Builtin::BImemcmp)
1921+
diagnoseNonConstexprBuiltin(S, OpPC, ID);
1922+
1923+
if (Size.isZero()) {
1924+
pushInteger(S, 0, Call->getType());
1925+
return true;
1926+
}
1927+
1928+
// FIXME: This is an arbitrary limitation the current constant interpreter
1929+
// had. We could remove this.
1930+
if (!isOneByteCharacterType(PtrA.getType()) ||
1931+
!isOneByteCharacterType(PtrB.getType())) {
1932+
S.FFDiag(S.Current->getSource(OpPC),
1933+
diag::note_constexpr_memcmp_unsupported)
1934+
<< ("'" + S.getASTContext().BuiltinInfo.getName(ID) + "'").str()
1935+
<< PtrA.getType() << PtrB.getType();
1936+
return false;
1937+
}
1938+
1939+
if (PtrA.isDummy() || PtrB.isDummy())
1940+
return false;
1941+
1942+
// Now, read both pointers to a buffer and compare those.
1943+
BitcastBuffer BufferA(
1944+
Bits(S.getASTContext().getTypeSize(PtrA.getFieldDesc()->getType())));
1945+
readPointerToBuffer(S.getContext(), PtrA, BufferA, false);
1946+
1947+
BitcastBuffer BufferB(
1948+
Bits(S.getASTContext().getTypeSize(PtrB.getFieldDesc()->getType())));
1949+
readPointerToBuffer(S.getContext(), PtrB, BufferB, false);
1950+
1951+
size_t MinBufferSize = std::min(BufferA.byteSize().getQuantity(),
1952+
BufferB.byteSize().getQuantity());
1953+
size_t CmpSize = std::min(MinBufferSize, Size.getZExtValue());
1954+
int Result = std::memcmp(BufferA.Data.get(), BufferB.Data.get(), CmpSize);
1955+
if (Result == 0)
1956+
pushInteger(S, 0, Call->getType());
1957+
else if (Result < 0)
1958+
pushInteger(S, -1, Call->getType());
1959+
else
1960+
pushInteger(S, 1, Call->getType());
1961+
1962+
return true;
1963+
}
1964+
19031965
bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
19041966
const CallExpr *Call, uint32_t BuiltinID) {
19051967
const InterpFrame *Frame = S.Current;
@@ -2373,6 +2435,12 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
23732435
return false;
23742436
break;
23752437

2438+
case Builtin::BI__builtin_memcmp:
2439+
case Builtin::BImemcmp:
2440+
if (!interp__builtin_memcmp(S, OpPC, Frame, F, Call))
2441+
return false;
2442+
break;
2443+
23762444
default:
23772445
S.FFDiag(S.Current->getLocation(OpPC),
23782446
diag::note_invalid_subexpr_in_const_expr)

clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp

+4-2
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

+5-3
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

+33
Original file line numberDiff line numberDiff line change
@@ -1223,3 +1223,36 @@ 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); // both-error {{constant}} \
1245+
// both-note {{not supported}}
1246+
1247+
constexpr char a = 'a';
1248+
constexpr char b = 'a';
1249+
static_assert(__builtin_memcmp(&a, &b, 1) == 0);
1250+
1251+
extern struct Incomplete incomplete;
1252+
static_assert(__builtin_memcmp(&incomplete, "", 0u) == 0);
1253+
static_assert(__builtin_memcmp("", &incomplete, 0u) == 0);
1254+
static_assert(__builtin_memcmp(&incomplete, "", 1u) == 42); // both-error {{not an integral constant}} \
1255+
// both-note {{not supported}}
1256+
static_assert(__builtin_memcmp("", &incomplete, 1u) == 42); // both-error {{not an integral constant}} \
1257+
// both-note {{not supported}}
1258+
}

0 commit comments

Comments
 (0)