https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/119544
>From 3e5ca2bbefddab626003fe10d0b80709f7c96ea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbae...@redhat.com> Date: Wed, 11 Dec 2024 12:35:29 +0100 Subject: [PATCH] [clang][bytecode] Handle __builtin_memcmp --- clang/lib/AST/ByteCode/BitcastBuffer.h | 9 +++ clang/lib/AST/ByteCode/InterpBuiltin.cpp | 68 +++++++++++++++++++ .../lib/AST/ByteCode/InterpBuiltinBitCast.cpp | 6 +- clang/lib/AST/ByteCode/InterpBuiltinBitCast.h | 8 ++- clang/test/AST/ByteCode/builtin-functions.cpp | 33 +++++++++ 5 files changed, 119 insertions(+), 5 deletions(-) diff --git a/clang/lib/AST/ByteCode/BitcastBuffer.h b/clang/lib/AST/ByteCode/BitcastBuffer.h index b1b6b9e5173a7c..d1d6ee39ad17bc 100644 --- a/clang/lib/AST/ByteCode/BitcastBuffer.h +++ b/clang/lib/AST/ByteCode/BitcastBuffer.h @@ -18,6 +18,8 @@ namespace interp { enum class Endian { Little, Big }; +struct Bytes; + /// A quantity in bits. struct Bits { size_t N = 0; @@ -30,6 +32,7 @@ struct Bits { bool isFullByte() const { return N % 8 == 0; } bool nonZero() const { return N != 0; } bool isZero() const { return N == 0; } + Bytes toBytes() const; Bits operator-(Bits Other) const { return Bits(N - Other.N); } Bits operator+(Bits Other) const { return Bits(N + Other.N); } @@ -56,6 +59,11 @@ struct Bytes { Bits toBits() const { return Bits(N * 8); } }; +inline Bytes Bits::toBytes() const { + assert(isFullByte()); + return Bytes(N / 8); +} + /// A bit range. Both Start and End are inclusive. struct BitRange { Bits Start; @@ -83,6 +91,7 @@ struct BitcastBuffer { /// Returns the buffer size in bits. Bits size() const { return FinalBitSize; } + Bytes byteSize() const { return FinalBitSize.toBytes(); } /// Returns \c true if all bits in the buffer have been initialized. bool allInitialized() const; diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index 57175da32b31cd..873e7a81814ccf 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -1830,6 +1830,7 @@ static bool interp__builtin_elementwise_popcount(InterpState &S, CodePtr OpPC, return true; } + static bool interp__builtin_memcpy(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *Func, const CallExpr *Call) { @@ -1900,6 +1901,67 @@ static bool interp__builtin_memcpy(InterpState &S, CodePtr OpPC, return true; } +/// Determine if T is a character type for which we guarantee that +/// sizeof(T) == 1. +static bool isOneByteCharacterType(QualType T) { + return T->isCharType() || T->isChar8Type(); +} + +static bool interp__builtin_memcmp(InterpState &S, CodePtr OpPC, + const InterpFrame *Frame, + const Function *Func, const CallExpr *Call) { + assert(Call->getNumArgs() == 3); + unsigned ID = Func->getBuiltinID(); + Pointer PtrA = getParam<Pointer>(Frame, 0); + const Pointer &PtrB = getParam<Pointer>(Frame, 1); + const APSInt &Size = + peekToAPSInt(S.Stk, *S.getContext().classify(Call->getArg(2))); + + if (ID == Builtin::BImemcmp) + diagnoseNonConstexprBuiltin(S, OpPC, ID); + + if (Size.isZero()) { + pushInteger(S, 0, Call->getType()); + return true; + } + + // FIXME: This is an arbitrary limitation the current constant interpreter + // had. We could remove this. + if (!isOneByteCharacterType(PtrA.getType()) || + !isOneByteCharacterType(PtrB.getType())) { + S.FFDiag(S.Current->getSource(OpPC), + diag::note_constexpr_memcmp_unsupported) + << ("'" + S.getASTContext().BuiltinInfo.getName(ID) + "'").str() + << PtrA.getType() << PtrB.getType(); + return false; + } + + if (PtrA.isDummy() || PtrB.isDummy()) + return false; + + // Now, read both pointers to a buffer and compare those. + BitcastBuffer BufferA( + Bits(S.getASTContext().getTypeSize(PtrA.getFieldDesc()->getType()))); + readPointerToBuffer(S.getContext(), PtrA, BufferA, false); + + BitcastBuffer BufferB( + Bits(S.getASTContext().getTypeSize(PtrB.getFieldDesc()->getType()))); + readPointerToBuffer(S.getContext(), PtrB, BufferB, false); + + size_t MinBufferSize = std::min(BufferA.byteSize().getQuantity(), + BufferB.byteSize().getQuantity()); + size_t CmpSize = std::min(MinBufferSize, Size.getZExtValue()); + int Result = std::memcmp(BufferA.Data.get(), BufferB.Data.get(), CmpSize); + if (Result == 0) + pushInteger(S, 0, Call->getType()); + else if (Result < 0) + pushInteger(S, -1, Call->getType()); + else + pushInteger(S, 1, Call->getType()); + + return true; +} + bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F, const CallExpr *Call, uint32_t BuiltinID) { const InterpFrame *Frame = S.Current; @@ -2373,6 +2435,12 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F, return false; break; + case Builtin::BI__builtin_memcmp: + case Builtin::BImemcmp: + if (!interp__builtin_memcmp(S, OpPC, Frame, F, Call)) + return false; + break; + default: S.FFDiag(S.Current->getLocation(OpPC), diag::note_invalid_subexpr_in_const_expr) diff --git a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp index c9141c0fad2f57..c87993b8739a77 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp @@ -259,8 +259,10 @@ static bool CheckBitcastType(InterpState &S, CodePtr OpPC, QualType T, return true; } -static bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr, - BitcastBuffer &Buffer, bool ReturnOnUninit) { +bool clang::interp::readPointerToBuffer(const Context &Ctx, + const Pointer &FromPtr, + BitcastBuffer &Buffer, + bool ReturnOnUninit) { const ASTContext &ASTCtx = Ctx.getASTContext(); Endian TargetEndianness = ASTCtx.getTargetInfo().isLittleEndian() ? Endian::Little : Endian::Big; diff --git a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.h b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.h index 92e6ffc79fc4f0..08c207c7415dfa 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.h +++ b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.h @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_AST_INTERP_BUILITN_BIT_CAST_H -#define LLVM_CLANG_AST_INTERP_BUILITN_BIT_CAST_H +#ifndef LLVM_CLANG_AST_INTERP_BUILTIN_BIT_CAST_H +#define LLVM_CLANG_AST_INTERP_BUILTIN_BIT_CAST_H #include "BitcastBuffer.h" #include <cstddef> @@ -17,6 +17,7 @@ namespace interp { class Pointer; class InterpState; class CodePtr; +class Context; bool DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &Ptr, std::byte *Buff, Bits BitWidth, Bits FullBitWidth, @@ -25,7 +26,8 @@ bool DoBitCastPtr(InterpState &S, CodePtr OpPC, const Pointer &FromPtr, Pointer &ToPtr); bool DoBitCastPtr(InterpState &S, CodePtr OpPC, const Pointer &FromPtr, Pointer &ToPtr, size_t Size); - +bool readPointerToBuffer(const Context &Ctx, const Pointer &FromPtr, + BitcastBuffer &Buffer, bool ReturnOnUninit); } // namespace interp } // namespace clang diff --git a/clang/test/AST/ByteCode/builtin-functions.cpp b/clang/test/AST/ByteCode/builtin-functions.cpp index ef6faae030a8f2..83caa1d03df3a3 100644 --- a/clang/test/AST/ByteCode/builtin-functions.cpp +++ b/clang/test/AST/ByteCode/builtin-functions.cpp @@ -1223,3 +1223,36 @@ namespace BuiltinMemcpy { static_assert(test_memcpy(0, 1, sizeof(int) * 2) == 2334); // both-error {{not an integral constant expression}} \ // both-note {{in call}} } + +namespace Memcmp { + constexpr unsigned char ku00fe00[] = {0x00, 0xfe, 0x00}; + constexpr unsigned char ku00feff[] = {0x00, 0xfe, 0xff}; + constexpr signed char ks00fe00[] = {0, -2, 0}; + constexpr signed char ks00feff[] = {0, -2, -1}; + static_assert(__builtin_memcmp(ku00feff, ks00fe00, 2) == 0); + static_assert(__builtin_memcmp(ku00feff, ks00fe00, 99) == 1); + static_assert(__builtin_memcmp(ku00fe00, ks00feff, 99) == -1); + static_assert(__builtin_memcmp(ks00feff, ku00fe00, 2) == 0); + static_assert(__builtin_memcmp(ks00feff, ku00fe00, 99) == 1); + static_assert(__builtin_memcmp(ks00fe00, ku00feff, 99) == -1); + static_assert(__builtin_memcmp(ks00fe00, ks00feff, 2) == 0); + static_assert(__builtin_memcmp(ks00feff, ks00fe00, 99) == 1); + static_assert(__builtin_memcmp(ks00fe00, ks00feff, 99) == -1); + + struct Bool3Tuple { bool bb[3]; }; + constexpr Bool3Tuple kb000100 = {{false, true, false}}; + static_assert(sizeof(bool) != 1u || __builtin_memcmp(ks00fe00, kb000100.bb, 1) == 0); // both-error {{constant}} \ + // both-note {{not supported}} + + constexpr char a = 'a'; + constexpr char b = 'a'; + static_assert(__builtin_memcmp(&a, &b, 1) == 0); + + extern struct Incomplete incomplete; + static_assert(__builtin_memcmp(&incomplete, "", 0u) == 0); + static_assert(__builtin_memcmp("", &incomplete, 0u) == 0); + static_assert(__builtin_memcmp(&incomplete, "", 1u) == 42); // both-error {{not an integral constant}} \ + // both-note {{not supported}} + static_assert(__builtin_memcmp("", &incomplete, 1u) == 42); // both-error {{not an integral constant}} \ + // both-note {{not supported}} +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits