Author: Timm Bäder Date: 2023-01-25T11:57:05+01:00 New Revision: 7348bb36c5570e718469f043504e80491bb7f12b
URL: https://github.com/llvm/llvm-project/commit/7348bb36c5570e718469f043504e80491bb7f12b DIFF: https://github.com/llvm/llvm-project/commit/7348bb36c5570e718469f043504e80491bb7f12b.diff LOG: [clang][Interp] Support inc/dec operators for pointers Differential Revision: https://reviews.llvm.org/D137232 Added: Modified: clang/lib/AST/Interp/ByteCodeExprGen.cpp clang/lib/AST/Interp/Interp.h clang/lib/AST/Interp/Opcodes.td clang/test/AST/Interp/arrays.cpp Removed: ################################################################################ diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index fa2c74c4e1a5..dd7d88ae715d 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -1333,24 +1333,44 @@ bool ByteCodeExprGen<Emitter>::VisitUnaryOperator(const UnaryOperator *E) { const Expr *SubExpr = E->getSubExpr(); std::optional<PrimType> T = classify(SubExpr->getType()); - // TODO: Support pointers for inc/dec operators. switch (E->getOpcode()) { case UO_PostInc: { // x++ if (!this->visit(SubExpr)) return false; + if (T == PT_Ptr) { + if (!this->emitIncPtr(E)) + return false; + + return DiscardResult ? this->emitPopPtr(E) : true; + } + return DiscardResult ? this->emitIncPop(*T, E) : this->emitInc(*T, E); } case UO_PostDec: { // x-- if (!this->visit(SubExpr)) return false; + if (T == PT_Ptr) { + if (!this->emitDecPtr(E)) + return false; + + return DiscardResult ? this->emitPopPtr(E) : true; + } + return DiscardResult ? this->emitDecPop(*T, E) : this->emitDec(*T, E); } case UO_PreInc: { // ++x if (!this->visit(SubExpr)) return false; + if (T == PT_Ptr) { + this->emitLoadPtr(E); + this->emitConstUint8(1, E); + this->emitAddOffsetUint8(E); + return DiscardResult ? this->emitStorePopPtr(E) : this->emitStorePtr(E); + } + // Post-inc and pre-inc are the same if the value is to be discarded. if (DiscardResult) return this->emitIncPop(*T, E); @@ -1364,6 +1384,13 @@ bool ByteCodeExprGen<Emitter>::VisitUnaryOperator(const UnaryOperator *E) { if (!this->visit(SubExpr)) return false; + if (T == PT_Ptr) { + this->emitLoadPtr(E); + this->emitConstUint8(1, E); + this->emitSubOffsetUint8(E); + return DiscardResult ? this->emitStorePopPtr(E) : this->emitStorePtr(E); + } + // Post-dec and pre-dec are the same if the value is to be discarded. if (DiscardResult) return this->emitDecPop(*T, E); diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index ed3accd98a90..9433652f1526 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -140,6 +140,8 @@ bool CheckDivRem(InterpState &S, CodePtr OpPC, const T &LHS, const T &RHS) { /// Interpreter entry point. bool Interpret(InterpState &S, APValue &Result); +enum class ArithOp { Add, Sub }; + //===----------------------------------------------------------------------===// // Add, Sub, Mul //===----------------------------------------------------------------------===// @@ -1112,6 +1114,34 @@ bool SubOffset(InterpState &S, CodePtr OpPC) { return OffsetHelper<T, false>(S, OpPC); } +template <ArithOp Op> +static inline bool IncDecPtrHelper(InterpState &S, CodePtr OpPC) { + using OneT = Integral<8, false>; + const Pointer &Ptr = S.Stk.pop<Pointer>(); + + // Get the current value on the stack. + S.Stk.push<Pointer>(Ptr.deref<Pointer>()); + + // Now the current Ptr again and a constant 1. + // FIXME: We shouldn't have to push these two on the stack. + S.Stk.push<Pointer>(Ptr.deref<Pointer>()); + S.Stk.push<OneT>(OneT::from(1)); + if (!OffsetHelper<OneT, Op == ArithOp::Add>(S, OpPC)) + return false; + + // Store the new value. + Ptr.deref<Pointer>() = S.Stk.pop<Pointer>(); + return true; +} + +static inline bool IncPtr(InterpState &S, CodePtr OpPC) { + return IncDecPtrHelper<ArithOp::Add>(S, OpPC); +} + +static inline bool DecPtr(InterpState &S, CodePtr OpPC) { + return IncDecPtrHelper<ArithOp::Sub>(S, OpPC); +} + /// 1) Pops a Pointer from the stack. /// 2) Pops another Pointer from the stack. /// 3) Pushes the diff erent of the indices of the two pointers on the stack. diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td index 058475b2d399..07facb6a6024 100644 --- a/clang/lib/AST/Interp/Opcodes.td +++ b/clang/lib/AST/Interp/Opcodes.td @@ -393,12 +393,21 @@ def AddOffset : AluOpcode; // [Pointer, Integral] -> [Pointer] def SubOffset : AluOpcode; -// Pointer, Pointer] - [Integral] +// [Pointer, Pointer] -> [Integral] def SubPtr : Opcode { let Types = [IntegerTypeClass]; let HasGroup = 1; } +// [Pointer] -> [Pointer] +def IncPtr : Opcode { + let HasGroup = 0; +} +// [Pointer] -> [Pointer] +def DecPtr : Opcode { + let HasGroup = 0; +} + //===----------------------------------------------------------------------===// // Binary operators. //===----------------------------------------------------------------------===// diff --git a/clang/test/AST/Interp/arrays.cpp b/clang/test/AST/Interp/arrays.cpp index 3c0eaefeb796..accef60e38d6 100644 --- a/clang/test/AST/Interp/arrays.cpp +++ b/clang/test/AST/Interp/arrays.cpp @@ -235,9 +235,6 @@ constexpr BU bu; // expected-error {{must be initialized by a constant expressio // ref-note {{in call to 'BU()'}} namespace IncDec { - // FIXME: Pointer arithmethic needs to be supported in inc/dec - // unary operators -#if 0 constexpr int getNextElem(const int *A, int I) { const int *B = (A + I); ++B; @@ -245,6 +242,91 @@ namespace IncDec { } constexpr int E[] = {1,2,3,4}; - static_assert(getNextElem(E, 1) == 3); -#endif + static_assert(getNextElem(E, 1) == 3, ""); + + constexpr int getFirst() { + const int *e = E; + return *(e++); + } + static_assert(getFirst() == 1, ""); + + constexpr int getFirst2() { + const int *e = E; + e++; + return *e; + } + static_assert(getFirst2() == 2, ""); + + constexpr int getSecond() { + const int *e = E; + return *(++e); + } + static_assert(getSecond() == 2, ""); + + constexpr int getSecond2() { + const int *e = E; + ++e; + return *e; + } + static_assert(getSecond2() == 2, ""); + + constexpr int getLast() { + const int *e = E + 3; + return *(e--); + } + static_assert(getLast() == 4, ""); + + constexpr int getLast2() { + const int *e = E + 3; + e--; + return *e; + } + static_assert(getLast2() == 3, ""); + + constexpr int getSecondToLast() { + const int *e = E + 3; + return *(--e); + } + static_assert(getSecondToLast() == 3, ""); + + constexpr int getSecondToLast2() { + const int *e = E + 3; + --e; + return *e; + } + static_assert(getSecondToLast2() == 3, ""); + + constexpr int bad1() { // ref-error {{never produces a constant expression}} + const int *e = E + 3; + e++; // This is fine because it's a one-past-the-end pointer + return *e; // expected-note {{read of dereferenced one-past-the-end pointer}} \ + // ref-note 2{{read of dereferenced one-past-the-end pointer}} + } + static_assert(bad1() == 0, ""); // expected-error {{not an integral constant expression}} \ + // expected-note {{in call to}} \ + // ref-error {{not an integral constant expression}} \ + // ref-note {{in call to}} + + constexpr int bad2() { // ref-error {{never produces a constant expression}} + const int *e = E + 4; + e++; // expected-note {{cannot refer to element 5 of array of 4 elements}} \ + // ref-note 2{{cannot refer to element 5 of array of 4 elements}} + return *e; // This is UB as well + } + static_assert(bad2() == 0, ""); // expected-error {{not an integral constant expression}} \ + // expected-note {{in call to}} \ + // ref-error {{not an integral constant expression}} \ + // ref-note {{in call to}} + + + constexpr int bad3() { // ref-error {{never produces a constant expression}} + const int *e = E; + e--; // expected-note {{cannot refer to element -1 of array of 4 elements}} \ + // ref-note 2{{cannot refer to element -1 of array of 4 elements}} + return *e; // This is UB as well + } + static_assert(bad3() == 0, ""); // expected-error {{not an integral constant expression}} \ + // expected-note {{in call to}} \ + // ref-error {{not an integral constant expression}} \ + // ref-note {{in call to}} }; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits