Skip to content

Commit

Permalink
[clang][Interp] Add explicit dummy descriptors (llvm#68888)
Browse files Browse the repository at this point in the history
Instead of (ab)using incomplete array types for this, add a 'Dummy' bit
to Descriptor. We need to be able to differentiate between the two when
adding an offset.
  • Loading branch information
tbaederr authored Oct 26, 2023
1 parent 1b6b4d6 commit 7f677fe
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 15 deletions.
7 changes: 7 additions & 0 deletions clang/lib/AST/Interp/Descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,13 @@ Descriptor::Descriptor(const DeclTy &D, Record *R, MetadataSize MD,
assert(Source && "Missing source");
}

Descriptor::Descriptor(const DeclTy &D, MetadataSize MD)
: Source(D), ElemSize(1), Size(ElemSize), MDSize(MD.value_or(0)),
AllocSize(Size + MDSize), ElemRecord(nullptr), IsConst(true),
IsMutable(false), IsTemporary(false), IsDummy(true) {
assert(Source && "Missing source");
}

QualType Descriptor::getType() const {
if (auto *E = asExpr())
return E->getType();
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/Interp/Descriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ struct Descriptor final {
const bool IsTemporary = false;
/// Flag indicating if the block is an array.
const bool IsArray = false;
/// Flag indicating if this is a dummy descriptor.
const bool IsDummy = false;

/// Storage management methods.
const BlockCtorFn CtorFn = nullptr;
Expand Down Expand Up @@ -139,6 +141,8 @@ struct Descriptor final {
Descriptor(const DeclTy &D, Record *R, MetadataSize MD, bool IsConst,
bool IsTemporary, bool IsMutable);

Descriptor(const DeclTy &D, MetadataSize MD);

QualType getType() const;
QualType getElemQualType() const;
SourceLocation getLocation() const;
Expand Down Expand Up @@ -192,6 +196,8 @@ struct Descriptor final {
bool isArray() const { return IsArray; }
/// Checks if the descriptor is of a record.
bool isRecord() const { return !IsArray && ElemRecord; }
/// Checks if this is a dummy descriptor.
bool isDummy() const { return IsDummy; }
};

/// Bitfield tracking the initialisation status of elements of primitive arrays.
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/Interp/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,10 @@ bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
return true;
}

bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
return !Ptr.isDummy();
}

bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
CheckSubobjectKind CSK) {
if (!Ptr.isZero())
Expand Down Expand Up @@ -297,6 +301,8 @@ bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
}

bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
if (!CheckDummy(S, OpPC, Ptr))
return false;
if (!CheckLive(S, OpPC, Ptr, AK_Read))
return false;
if (!CheckExtern(S, OpPC, Ptr))
Expand Down
20 changes: 14 additions & 6 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ bool CheckArray(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
/// Checks if a pointer is live and accessible.
bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
AccessKinds AK);

/// Checks if a pointer is a dummy pointer.
bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr);

/// Checks if a pointer is null.
bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
CheckSubobjectKind CSK);
Expand Down Expand Up @@ -1415,8 +1419,9 @@ bool OffsetHelper(InterpState &S, CodePtr OpPC, const T &Offset,
// Compute the largest index into the array.
T MaxIndex = T::from(Ptr.getNumElems(), Offset.bitWidth());

bool Invalid = false;
// Helper to report an invalid offset, computed as APSInt.
auto InvalidOffset = [&]() {
auto DiagInvalidOffset = [&]() -> void {
const unsigned Bits = Offset.bitWidth();
APSInt APOffset(Offset.toAPSInt().extend(Bits + 2), false);
APSInt APIndex(Index.toAPSInt().extend(Bits + 2), false);
Expand All @@ -1426,28 +1431,31 @@ bool OffsetHelper(InterpState &S, CodePtr OpPC, const T &Offset,
<< NewIndex
<< /*array*/ static_cast<int>(!Ptr.inArray())
<< static_cast<unsigned>(MaxIndex);
return false;
Invalid = true;
};

T MaxOffset = T::from(MaxIndex - Index, Offset.bitWidth());
if constexpr (Op == ArithOp::Add) {
// If the new offset would be negative, bail out.
if (Offset.isNegative() && (Offset.isMin() || -Offset > Index))
return InvalidOffset();
DiagInvalidOffset();

// If the new offset would be out of bounds, bail out.
if (Offset.isPositive() && Offset > MaxOffset)
return InvalidOffset();
DiagInvalidOffset();
} else {
// If the new offset would be negative, bail out.
if (Offset.isPositive() && Index < Offset)
return InvalidOffset();
DiagInvalidOffset();

// If the new offset would be out of bounds, bail out.
if (Offset.isNegative() && (Offset.isMin() || -Offset > MaxOffset))
return InvalidOffset();
DiagInvalidOffset();
}

if (Invalid && !Ptr.isDummy())
return false;

// Offset is valid - compute it on unsigned.
int64_t WideIndex = static_cast<int64_t>(Index);
int64_t WideOffset = static_cast<int64_t>(Offset);
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/Interp/InterpBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ static bool interp__builtin_strlen(InterpState &S, CodePtr OpPC,
if (!CheckLive(S, OpPC, StrPtr, AK_Read))
return false;

if (!CheckDummy(S, OpPC, StrPtr))
return false;

assert(StrPtr.getFieldDesc()->isPrimitiveArray());

size_t Len = 0;
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/Interp/Pointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,8 @@ class Pointer {
bool isActive() const { return Base == 0 || getInlineDesc()->IsActive; }
/// Checks if a structure is a base class.
bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; }
/// Checks if the pointer pointers to a dummy value.
bool isDummy() const { return getDeclDesc()->isDummy(); }

/// Checks if an object or a subfield is mutable.
bool isConst() const {
Expand Down
20 changes: 11 additions & 9 deletions clang/lib/AST/Interp/Program.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,16 +144,18 @@ std::optional<unsigned> Program::getOrCreateDummy(const ValueDecl *PD) {
It != DummyParams.end())
return It->second;

// Create a pointer to an incomplete array of the specified elements.
QualType ElemTy = PD->getType();
QualType Ty =
Ctx.getASTContext().getIncompleteArrayType(ElemTy, ArrayType::Normal, 0);
// Create dummy descriptor.
Descriptor *Desc = allocateDescriptor(PD, std::nullopt);
// Allocate a block for storage.
unsigned I = Globals.size();

if (auto Idx = createGlobal(PD, Ty, /*isStatic=*/true, /*isExtern=*/true)) {
DummyParams[PD] = *Idx;
return Idx;
}
return std::nullopt;
auto *G = new (Allocator, Desc->getAllocSize())
Global(getCurrentDecl(), Desc, /*IsStatic=*/true, /*IsExtern=*/false);
G->block()->invokeCtor();

Globals.push_back(G);
DummyParams[PD] = I;
return I;
}

std::optional<unsigned> Program::createGlobal(const ValueDecl *VD,
Expand Down
20 changes: 20 additions & 0 deletions clang/test/AST/Interp/c.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,23 @@ _Static_assert(&a != 0, ""); // ref-warning {{always true}} \
// expected-warning {{always true}} \
// pedantic-expected-warning {{always true}} \
// pedantic-expected-warning {{is a GNU extension}}
_Static_assert((&c + 1) != 0, ""); // pedantic-ref-warning {{is a GNU extension}} \
// pedantic-expected-warning {{is a GNU extension}}
_Static_assert((&a + 100) != 0, ""); // pedantic-ref-warning {{is a GNU extension}} \
// pedantic-ref-note {{100 of non-array}} \
// pedantic-expected-note {{100 of non-array}} \
// pedantic-expected-warning {{is a GNU extension}}
_Static_assert((&a - 100) != 0, ""); // pedantic-ref-warning {{is a GNU extension}} \
// pedantic-expected-warning {{is a GNU extension}} \
// pedantic-ref-note {{-100 of non-array}} \
// pedantic-expected-note {{-100 of non-array}}
/// extern variable of a composite type.
/// FIXME: The 'cast from void*' note is missing in the new interpreter.
extern struct Test50S Test50;
_Static_assert(&Test50 != (void*)0, ""); // ref-warning {{always true}} \
// pedantic-ref-warning {{always true}} \
// pedantic-ref-warning {{is a GNU extension}} \
// pedantic-ref-note {{cast from 'void *' is not allowed}} \
// expected-warning {{always true}} \
// pedantic-expected-warning {{always true}} \
// pedantic-expected-warning {{is a GNU extension}}

0 comments on commit 7f677fe

Please sign in to comment.