aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimm Baeder <tbaeder@redhat.com>2024-08-06 13:37:11 +0200
committerGitHub <noreply@github.com>2024-08-06 13:37:11 +0200
commit59e13666dd2e81e58253488a29635fb2992ed741 (patch)
treec32b9f648ab650b63f8d713c01a332c0cdb00a0e
parentc3c2370c9a2465f57849861b13c59566b6f26daf (diff)
downloadllvm-59e13666dd2e81e58253488a29635fb2992ed741.zip
llvm-59e13666dd2e81e58253488a29635fb2992ed741.tar.gz
llvm-59e13666dd2e81e58253488a29635fb2992ed741.tar.bz2
[clang][Interp] Fix getField() for integral pointers (#102120)
Instead of just adding the Record::Field offset, instead get the FieldDecl offset in the RecordLayout. Unfortunately, the offset we pass to the ops here is not made to easily go back to a FieldDecl, so we have to iterate over the parent Record.
-rw-r--r--clang/lib/AST/Interp/Compiler.cpp4
-rw-r--r--clang/lib/AST/Interp/Interp.h11
-rw-r--r--clang/lib/AST/Interp/Pointer.cpp27
-rw-r--r--clang/lib/AST/Interp/Pointer.h8
-rw-r--r--clang/test/AST/Interp/c.c7
5 files changed, 49 insertions, 8 deletions
diff --git a/clang/lib/AST/Interp/Compiler.cpp b/clang/lib/AST/Interp/Compiler.cpp
index e528049..02cbe38 100644
--- a/clang/lib/AST/Interp/Compiler.cpp
+++ b/clang/lib/AST/Interp/Compiler.cpp
@@ -335,6 +335,10 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
if (!PointeeType.isNull()) {
if (std::optional<PrimType> T = classify(PointeeType))
Desc = P.createDescriptor(SubExpr, *T);
+ else
+ Desc = P.createDescriptor(SubExpr, PointeeType.getTypePtr(),
+ std::nullopt, true, false,
+ /*IsMutable=*/false, nullptr);
}
return this->emitNull(classifyPrim(CE->getType()), Desc, CE);
}
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 04f88ef..2eed0d3 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -1504,6 +1504,12 @@ inline bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off) {
if (Ptr.isBlockPointer() && Off > Ptr.block()->getSize())
return false;
+
+ if (Ptr.isIntegralPointer()) {
+ S.Stk.push<Pointer>(Ptr.asIntPointer().atOffset(S.getCtx(), Off));
+ return true;
+ }
+
S.Stk.push<Pointer>(Ptr.atField(Off));
return true;
}
@@ -1527,6 +1533,11 @@ inline bool GetPtrFieldPop(InterpState &S, CodePtr OpPC, uint32_t Off) {
if (Ptr.isBlockPointer() && Off > Ptr.block()->getSize())
return false;
+ if (Ptr.isIntegralPointer()) {
+ S.Stk.push<Pointer>(Ptr.asIntPointer().atOffset(S.getCtx(), Off));
+ return true;
+ }
+
S.Stk.push<Pointer>(Ptr.atField(Off));
return true;
}
diff --git a/clang/lib/AST/Interp/Pointer.cpp b/clang/lib/AST/Interp/Pointer.cpp
index 2b1f8b4..ba9683a 100644
--- a/clang/lib/AST/Interp/Pointer.cpp
+++ b/clang/lib/AST/Interp/Pointer.cpp
@@ -597,3 +597,30 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx,
return std::nullopt;
return Result;
}
+
+IntPointer IntPointer::atOffset(const ASTContext &ASTCtx,
+ unsigned Offset) const {
+ if (!this->Desc)
+ return *this;
+ const Record *R = this->Desc->ElemRecord;
+ if (!R)
+ return *this;
+
+ const Record::Field *F = nullptr;
+ for (auto &It : R->fields()) {
+ if (It.Offset == Offset) {
+ F = &It;
+ break;
+ }
+ }
+ if (!F)
+ return *this;
+
+ const FieldDecl *FD = F->Decl;
+ const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(FD->getParent());
+ unsigned FieldIndex = FD->getFieldIndex();
+ uint64_t FieldOffset =
+ ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(FieldIndex))
+ .getQuantity();
+ return IntPointer{this->Desc, FieldOffset};
+}
diff --git a/clang/lib/AST/Interp/Pointer.h b/clang/lib/AST/Interp/Pointer.h
index 6f69834..b7b4f82 100644
--- a/clang/lib/AST/Interp/Pointer.h
+++ b/clang/lib/AST/Interp/Pointer.h
@@ -44,6 +44,8 @@ struct BlockPointer {
struct IntPointer {
const Descriptor *Desc;
uint64_t Value;
+
+ IntPointer atOffset(const ASTContext &ASTCtx, unsigned Offset) const;
};
enum class Storage { Block, Int, Fn };
@@ -88,6 +90,9 @@ public:
PointeeStorage.Int.Value = 0;
PointeeStorage.Int.Desc = nullptr;
}
+ Pointer(IntPointer &&IntPtr) : StorageKind(Storage::Int) {
+ PointeeStorage.Int = std::move(IntPtr);
+ }
Pointer(Block *B);
Pointer(Block *B, uint64_t BaseAndOffset);
Pointer(const Pointer &P);
@@ -161,9 +166,8 @@ public:
/// Creates a pointer to a field.
[[nodiscard]] Pointer atField(unsigned Off) const {
+ assert(isBlockPointer());
unsigned Field = Offset + Off;
- if (isIntegralPointer())
- return Pointer(asIntPointer().Value + Field, asIntPointer().Desc);
return Pointer(asBlockPointer().Pointee, Field, Field);
}
diff --git a/clang/test/AST/Interp/c.c b/clang/test/AST/Interp/c.c
index 9ec305d..13a5e08 100644
--- a/clang/test/AST/Interp/c.c
+++ b/clang/test/AST/Interp/c.c
@@ -101,8 +101,6 @@ int somefunc(int i) {
// all-warning {{overflow in expression; result is 131'073 with type 'int'}}
}
-/// FIXME: The following test is incorrect in the new interpreter.
-/// The null pointer returns 16 from its getIntegerRepresentation().
#pragma clang diagnostic ignored "-Wpointer-to-int-cast"
struct ArrayStruct {
char n[1];
@@ -111,10 +109,7 @@ char name2[(int)&((struct ArrayStruct*)0)->n]; // expected-warning {{folded to c
// pedantic-expected-warning {{folded to constant array}} \
// ref-warning {{folded to constant array}} \
// pedantic-ref-warning {{folded to constant array}}
-_Static_assert(sizeof(name2) == 0, ""); // expected-error {{failed}} \
- // expected-note {{evaluates to}} \
- // pedantic-expected-error {{failed}} \
- // pedantic-expected-note {{evaluates to}}
+_Static_assert(sizeof(name2) == 0, "");
#ifdef __SIZEOF_INT128__
void *PR28739d = &(&PR28739d)[(__int128)(unsigned long)-1]; // all-warning {{refers past the last possible element}}