Skip to content

Commit

Permalink
[clang][bytecode] Support pointers in __builtin_mem{move,cpy} (llvm#1…
Browse files Browse the repository at this point in the history
…20560)

Unfortunately, that means we can't use the __builtin_bit_cast
implementation for this.
  • Loading branch information
tbaederr authored Dec 19, 2024
1 parent 6f8afaf commit 1f2d934
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 7 deletions.
22 changes: 17 additions & 5 deletions clang/lib/AST/ByteCode/InterpBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1862,10 +1862,10 @@ static bool interp__builtin_memcpy(InterpState &S, CodePtr OpPC,
}

QualType ElemType;
if (SrcPtr.getFieldDesc()->isArray())
ElemType = SrcPtr.getFieldDesc()->getElemQualType();
if (DestPtr.getFieldDesc()->isArray())
ElemType = DestPtr.getFieldDesc()->getElemQualType();
else
ElemType = SrcPtr.getType();
ElemType = DestPtr.getType();

unsigned ElemSize =
S.getASTContext().getTypeSizeInChars(ElemType).getQuantity();
Expand All @@ -1876,6 +1876,18 @@ static bool interp__builtin_memcpy(InterpState &S, CodePtr OpPC,
return false;
}

QualType SrcElemType;
if (SrcPtr.getFieldDesc()->isArray())
SrcElemType = SrcPtr.getFieldDesc()->getElemQualType();
else
SrcElemType = SrcPtr.getType();

if (!S.getASTContext().hasSameUnqualifiedType(ElemType, SrcElemType)) {
S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_memcpy_type_pun)
<< Move << SrcElemType << ElemType;
return false;
}

// Check for overlapping memory regions.
if (!Move && Pointer::pointToSameBlock(SrcPtr, DestPtr)) {
unsigned SrcIndex = SrcPtr.getIndex() * SrcPtr.elemSize();
Expand All @@ -1893,8 +1905,8 @@ static bool interp__builtin_memcpy(InterpState &S, CodePtr OpPC,
// As a last resort, reject dummy pointers.
if (DestPtr.isDummy() || SrcPtr.isDummy())
return false;

if (!DoBitCastPtr(S, OpPC, SrcPtr, DestPtr, Size.getZExtValue()))
assert(Size.getZExtValue() % ElemSize == 0);
if (!DoMemcpy(S, OpPC, SrcPtr, DestPtr, Bytes(Size.getZExtValue()).toBits()))
return false;

S.Stk.push<Pointer>(DestPtr);
Expand Down
31 changes: 31 additions & 0 deletions clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -448,3 +448,34 @@ bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC,

return Success;
}

bool clang::interp::DoMemcpy(InterpState &S, CodePtr OpPC,
const Pointer &SrcPtr, const Pointer &DestPtr,
Bits Size) {
assert(SrcPtr.isBlockPointer());
assert(DestPtr.isBlockPointer());

unsigned SrcStartOffset = SrcPtr.getByteOffset();
unsigned DestStartOffset = DestPtr.getByteOffset();

enumeratePointerFields(SrcPtr, S.getContext(), Size,
[&](const Pointer &P, PrimType T, Bits BitOffset,
Bits FullBitWidth, bool PackedBools) -> bool {
unsigned SrcOffsetDiff =
P.getByteOffset() - SrcStartOffset;

Pointer DestP =
Pointer(DestPtr.asBlockPointer().Pointee,
DestPtr.asBlockPointer().Base,
DestStartOffset + SrcOffsetDiff);

TYPE_SWITCH(T, {
DestP.deref<T>() = P.deref<T>();
DestP.initialize();
});

return true;
});

return true;
}
4 changes: 4 additions & 0 deletions clang/lib/AST/ByteCode/InterpBuiltinBitCast.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ 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);

bool DoMemcpy(InterpState &S, CodePtr OpPC, const Pointer &SrcPtr,
const Pointer &DestPtr, Bits Size);

} // namespace interp
} // namespace clang

Expand Down
3 changes: 1 addition & 2 deletions clang/lib/AST/ByteCode/Pointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ class Pointer {
: Offset(Offset), StorageKind(Storage::Fn) {
PointeeStorage.Fn = FunctionPointer(F);
}
Pointer(Block *Pointee, unsigned Base, uint64_t Offset);
~Pointer();

void operator=(const Pointer &P);
Expand Down Expand Up @@ -706,8 +707,6 @@ class Pointer {
friend struct InitMap;
friend class DynamicAllocator;

Pointer(Block *Pointee, unsigned Base, uint64_t Offset);

/// Returns the embedded descriptor preceding a field.
InlineDescriptor *getInlineDesc() const {
assert(isBlockPointer());
Expand Down
22 changes: 22 additions & 0 deletions clang/test/AST/ByteCode/builtin-functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1222,6 +1222,28 @@ namespace BuiltinMemcpy {
static_assert(test_memcpy(1, 2, sizeof(int)) == 1334);
static_assert(test_memcpy(0, 1, sizeof(int) * 2) == 2334); // both-error {{not an integral constant expression}} \
// both-note {{in call}}

/// Both memcpy and memmove must support pointers.
constexpr bool moveptr() {
int a = 0;
void *x = &a;
void *z = nullptr;

__builtin_memmove(&z, &x, sizeof(void*));
return z == x;
}
static_assert(moveptr());

constexpr bool cpyptr() {
int a = 0;
void *x = &a;
void *z = nullptr;

__builtin_memcpy(&z, &x, sizeof(void*));
return z == x;
}
static_assert(cpyptr());

}

namespace Memcmp {
Expand Down

0 comments on commit 1f2d934

Please sign in to comment.