Skip to content

Commit 2bad7af

Browse files
committed
[clang][bytecode] Fix comparing the addresses of union members
Union members get the same address, so we can't just use `Pointer::getByteOffset()`.
1 parent b3c7d59 commit 2bad7af

File tree

4 files changed

+84
-2
lines changed

4 files changed

+84
-2
lines changed

clang/lib/AST/ByteCode/Interp.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1070,9 +1070,18 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
10701070
}
10711071

10721072
if (Pointer::hasSameBase(LHS, RHS)) {
1073+
if (LHS.inUnion() && RHS.inUnion()) {
1074+
// If the pointers point into a union, things are a little more
1075+
// complicated since the offset we save in interp::Pointer can't be used
1076+
// to compare the pointers directly.
1077+
size_t A = LHS.computeOffsetForComparison();
1078+
size_t B = RHS.computeOffsetForComparison();
1079+
S.Stk.push<BoolT>(BoolT::from(Fn(Compare(A, B))));
1080+
return true;
1081+
}
1082+
10731083
unsigned VL = LHS.getByteOffset();
10741084
unsigned VR = RHS.getByteOffset();
1075-
10761085
// In our Pointer class, a pointer to an array and a pointer to the first
10771086
// element in the same array are NOT equal. They have the same Base value,
10781087
// but a different Offset. This is a pretty rare case, so we fix this here

clang/lib/AST/ByteCode/Pointer.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,39 @@ void Pointer::print(llvm::raw_ostream &OS) const {
339339
}
340340
}
341341

342+
/// Compute an integer that can be used to compare this pointer to
343+
/// another one.
344+
size_t Pointer::computeOffsetForComparison() const {
345+
if (!isBlockPointer())
346+
return Offset;
347+
348+
size_t Result = 0;
349+
Pointer P = *this;
350+
while (!P.isRoot()) {
351+
if (P.isArrayRoot()) {
352+
P = P.getBase();
353+
continue;
354+
}
355+
if (P.isArrayElement()) {
356+
P = P.expand();
357+
Result += (P.getIndex() * P.elemSize());
358+
P = P.getArray();
359+
continue;
360+
}
361+
362+
if (const Record *R = P.getBase().getRecord(); R && R->isUnion()) {
363+
// Direct child of a union - all have offset 0.
364+
P = P.getBase();
365+
continue;
366+
}
367+
368+
Result += P.getInlineDesc()->Offset;
369+
P = P.getBase();
370+
}
371+
372+
return Result;
373+
}
374+
342375
std::string Pointer::toDiagnosticString(const ASTContext &Ctx) const {
343376
if (isZero())
344377
return "nullptr";

clang/lib/AST/ByteCode/Pointer.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ class Pointer {
417417
return false;
418418
}
419419
bool inUnion() const {
420-
if (isBlockPointer())
420+
if (isBlockPointer() && asBlockPointer().Base >= sizeof(InlineDescriptor))
421421
return getInlineDesc()->InUnion;
422422
return false;
423423
};
@@ -727,6 +727,8 @@ class Pointer {
727727
/// Prints the pointer.
728728
void print(llvm::raw_ostream &OS) const;
729729

730+
size_t computeOffsetForComparison() const;
731+
730732
private:
731733
friend class Block;
732734
friend class DeadBlock;

clang/test/AST/ByteCode/unions.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,3 +600,41 @@ namespace MoveOrAssignOp {
600600
static_assert(foo());
601601
}
602602
#endif
603+
604+
namespace AddressComparison {
605+
union {
606+
int a;
607+
int c;
608+
} U;
609+
static_assert(__builtin_addressof(U.a) == (void*)__builtin_addressof(U.c));
610+
static_assert(&U.a == &U.c);
611+
612+
613+
struct {
614+
union {
615+
struct {
616+
int a;
617+
int b;
618+
} a;
619+
struct {
620+
int b;
621+
int a;
622+
}b;
623+
} u;
624+
int b;
625+
} S;
626+
627+
static_assert(&S.u.a.a == &S.u.b.b);
628+
static_assert(&S.u.a.b != &S.u.b.b);
629+
static_assert(&S.u.a.b == &S.u.b.b); // both-error {{failed}}
630+
631+
632+
union {
633+
int a[2];
634+
int b[2];
635+
} U2;
636+
637+
static_assert(&U2.a[0] == &U2.b[0]);
638+
static_assert(&U2.a[0] != &U2.b[1]);
639+
static_assert(&U2.a[0] == &U2.b[1]); // both-error {{failed}}
640+
}

0 commit comments

Comments
 (0)