Skip to content

Commit bdd0870

Browse files
authored
[clang][bytecode] Fix various issues with multidimensional arrays (#134628)
This issue is very convoluted, but in essence, in the new version: For a Pointer P that points to the root of a multidimensional, primitive array: `P.narrow()` does nothing. `P.atIndex(0)` points `P[0]` `P.atIndex(0).atIndex(0)` is the same as `P.atIndex(0)` (as before) `P.atIndex(0).narrow().atIndex(0)` points to `P[0][0]` `P.atIndex(0).narrow().narrow()` is the same as `P.atIndex(0).narrow()`.
1 parent 49d6e39 commit bdd0870

File tree

4 files changed

+123
-30
lines changed

4 files changed

+123
-30
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6148,7 +6148,8 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
61486148

61496149
if (!this->visit(SubExpr))
61506150
return false;
6151-
if (classifyPrim(SubExpr) == PT_Ptr && !E->getType()->isArrayType())
6151+
6152+
if (classifyPrim(SubExpr) == PT_Ptr)
61526153
return this->emitNarrowPtr(E);
61536154
return true;
61546155

clang/lib/AST/ByteCode/Interp.h

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2059,8 +2059,11 @@ bool OffsetHelper(InterpState &S, CodePtr OpPC, const T &Offset,
20592059
// useful thing we can do. Any other index has been diagnosed before and
20602060
// we don't get here.
20612061
if (Result == 0 && Ptr.isOnePastEnd()) {
2062-
S.Stk.push<Pointer>(Ptr.asBlockPointer().Pointee,
2063-
Ptr.asBlockPointer().Base);
2062+
if (Ptr.getFieldDesc()->isArray())
2063+
S.Stk.push<Pointer>(Ptr.atIndex(0));
2064+
else
2065+
S.Stk.push<Pointer>(Ptr.asBlockPointer().Pointee,
2066+
Ptr.asBlockPointer().Base);
20642067
return true;
20652068
}
20662069

@@ -2677,8 +2680,16 @@ inline bool ArrayElemPtr(InterpState &S, CodePtr OpPC) {
26772680
return false;
26782681
}
26792682

2680-
if (!OffsetHelper<T, ArithOp::Add>(S, OpPC, Offset, Ptr))
2681-
return false;
2683+
if (Offset.isZero()) {
2684+
if (Ptr.getFieldDesc()->isArray() && Ptr.getIndex() == 0) {
2685+
S.Stk.push<Pointer>(Ptr.atIndex(0));
2686+
} else {
2687+
S.Stk.push<Pointer>(Ptr);
2688+
}
2689+
} else {
2690+
if (!OffsetHelper<T, ArithOp::Add>(S, OpPC, Offset, Ptr))
2691+
return false;
2692+
}
26822693

26832694
return NarrowPtr(S, OpPC);
26842695
}
@@ -2693,8 +2704,16 @@ inline bool ArrayElemPtrPop(InterpState &S, CodePtr OpPC) {
26932704
return false;
26942705
}
26952706

2696-
if (!OffsetHelper<T, ArithOp::Add>(S, OpPC, Offset, Ptr))
2697-
return false;
2707+
if (Offset.isZero()) {
2708+
if (Ptr.getFieldDesc()->isArray() && Ptr.getIndex() == 0) {
2709+
S.Stk.push<Pointer>(Ptr.atIndex(0));
2710+
} else {
2711+
S.Stk.push<Pointer>(Ptr);
2712+
}
2713+
} else {
2714+
if (!OffsetHelper<T, ArithOp::Add>(S, OpPC, Offset, Ptr))
2715+
return false;
2716+
}
26982717

26992718
return NarrowPtr(S, OpPC);
27002719
}

clang/lib/AST/ByteCode/Pointer.h

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -200,37 +200,28 @@ class Pointer {
200200
if (isZero() || isUnknownSizeArray())
201201
return *this;
202202

203+
unsigned Base = asBlockPointer().Base;
203204
// Pointer to an array of base types - enter block.
204-
if (asBlockPointer().Base == RootPtrMark)
205+
if (Base == RootPtrMark)
205206
return Pointer(asBlockPointer().Pointee, sizeof(InlineDescriptor),
206207
Offset == 0 ? Offset : PastEndMark);
207208

208209
// Pointer is one past end - magic offset marks that.
209210
if (isOnePastEnd())
210-
return Pointer(asBlockPointer().Pointee, asBlockPointer().Base,
211-
PastEndMark);
212-
213-
// Primitive arrays are a bit special since they do not have inline
214-
// descriptors. If Offset != Base, then the pointer already points to
215-
// an element and there is nothing to do. Otherwise, the pointer is
216-
// adjusted to the first element of the array.
217-
if (inPrimitiveArray()) {
218-
if (Offset != asBlockPointer().Base)
211+
return Pointer(asBlockPointer().Pointee, Base, PastEndMark);
212+
213+
if (Offset != Base) {
214+
// If we're pointing to a primitive array element, there's nothing to do.
215+
if (inPrimitiveArray())
219216
return *this;
220-
return Pointer(asBlockPointer().Pointee, asBlockPointer().Base,
221-
Offset + sizeof(InitMapPtr));
217+
// Pointer is to a composite array element - enter it.
218+
if (Offset != Base)
219+
return Pointer(asBlockPointer().Pointee, Offset, Offset);
222220
}
223221

224-
// Pointer is to a field or array element - enter it.
225-
if (Offset != asBlockPointer().Base)
226-
return Pointer(asBlockPointer().Pointee, Offset, Offset);
227-
228-
// Enter the first element of an array.
229-
if (!getFieldDesc()->isArray())
230-
return *this;
231-
232-
const unsigned NewBase = asBlockPointer().Base + sizeof(InlineDescriptor);
233-
return Pointer(asBlockPointer().Pointee, NewBase, NewBase);
222+
// Otherwise, we're pointing to a non-array element or
223+
// are already narrowed to a composite array element. Nothing to do.
224+
return *this;
234225
}
235226

236227
/// Expands a pointer to the containing array, undoing narrowing.

clang/test/AST/ByteCode/arrays.cpp

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -637,11 +637,93 @@ static_assert(get2() == same_entity_2, "failed to find previous decl");
637637

638638
constexpr int zs[2][2][2][2] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
639639
constexpr int fail(const int &p) {
640-
return (&p)[64]; // both-note {{cannot refer to element 64 of array of 2 elements}}
640+
return (&p)[64]; // both-note 2{{cannot refer to element 64 of array of 2 elements}} \
641+
// both-note {{cannot refer to element 65 of array of 2 elements}} \
642+
// both-note {{cannot refer to element 66 of array of 2 elements}}
641643
}
642644
static_assert(fail(*(&(&(*(*&(&zs[2] - 1)[0] + 2 - 2))[2])[-1][2] - 2)) == 11, ""); // both-error {{not an integral constant expression}} \
643645
// both-note {{in call to}}
644646

647+
648+
static_assert(fail( // both-error {{not an integral constant expression}} \
649+
// both-note {{in call to 'fail(zs[1][1][0][0])'}}
650+
*(*(*((*
651+
(zs + 1)) /// int[2][2][2]
652+
+ 1) /// int[2][2]
653+
+ 2 - 2) /// int[2]
654+
+ 2 - 2) /// int
655+
));
656+
657+
static_assert(fail( // both-error {{not an integral constant expression}} \
658+
// both-note {{in call to 'fail(zs[1][0][0][1])'}}
659+
*(*(*((*
660+
(zs + 1)) /// int[2][2][2]
661+
+ 0) /// int[2][2]
662+
+ 2 - 2) /// int[2]
663+
+ 1) /// int
664+
));
665+
666+
static_assert(fail( // both-error {{not an integral constant expression}} \
667+
// both-note {{in call to 'fail(zs[1][0][0][2])'}}
668+
*(*(*((*
669+
(zs + 1)) /// int[2][2][2]
670+
+ 0) /// int[2][2]
671+
+ 2 - 2) /// int[2]
672+
+ 2) /// int
673+
));
674+
675+
namespace ZeroIndex {
676+
constexpr char foo(const char *a) {
677+
return a[0];
678+
}
679+
constexpr const char *f = "abc";
680+
static_assert(foo(f + 1) == 'b', "");
681+
}
682+
683+
namespace MultiDimArrayOffset {
684+
#define assert(x) (x ? void(0) : __builtin_abort())
685+
struct R {
686+
int a;
687+
};
688+
689+
template<typename T>
690+
class view {
691+
public:
692+
T* V;
693+
T* current;
694+
695+
constexpr view(T*V) : V(V), current(V) {}
696+
697+
constexpr void operator+=(unsigned N) {
698+
current += N;
699+
}
700+
701+
constexpr auto operator*() {
702+
return *current;
703+
}
704+
705+
};
706+
707+
constexpr int foo() {
708+
R buffer[2][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}};
709+
710+
auto A = buffer;
711+
A += 1;
712+
assert((**A).a == 5);
713+
assert(buffer == buffer + 1 - 1);
714+
715+
assert(--A+0 == buffer+0);
716+
717+
view V(buffer);
718+
assert(*V == &buffer[0][0]);
719+
V += 1;
720+
assert(*V == &buffer[1][0]);
721+
assert(*(V.current) == &buffer[1][0]);
722+
return 1;
723+
}
724+
static_assert(foo() == 1, "");
725+
}
726+
645727
namespace ZeroSizeTypes {
646728
constexpr int (*p1)[0] = 0, (*p2)[0] = 0;
647729
constexpr int k = p2 - p1; // both-error {{constexpr variable 'k' must be initialized by a constant expression}} \

0 commit comments

Comments
 (0)