[PATCH] D137232: [clang][Interp] Support inc/dec operators on pointers
This revision was landed with ongoing or failed builds. This revision was automatically updated to reflect the committed changes. Closed by commit rG7348bb36c557: [clang][Interp] Support inc/dec operators for pointers (authored by tbaeder). Changed prior to commit: https://reviews.llvm.org/D137232?vs=474724=492048#toc Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D137232/new/ https://reviews.llvm.org/D137232 Files: clang/lib/AST/Interp/ByteCodeExprGen.cpp clang/lib/AST/Interp/Interp.h clang/lib/AST/Interp/Opcodes.td clang/test/AST/Interp/arrays.cpp Index: clang/test/AST/Interp/arrays.cpp === --- clang/test/AST/Interp/arrays.cpp +++ clang/test/AST/Interp/arrays.cpp @@ -235,9 +235,6 @@ // 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 @@ } 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}} }; Index: clang/lib/AST/Interp/Opcodes.td === --- clang/lib/AST/Interp/Opcodes.td +++ clang/lib/AST/Interp/Opcodes.td @@ -393,12 +393,21 @@ // [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. //===--===// Index: clang/lib/AST/Interp/Interp.h
[PATCH] D137232: [clang][Interp] Support inc/dec operators on pointers
shafik accepted this revision. shafik added a comment. LGTM CHANGES SINCE LAST ACTION https://reviews.llvm.org/D137232/new/ https://reviews.llvm.org/D137232 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D137232: [clang][Interp] Support inc/dec operators on pointers
aaron.ballman accepted this revision. aaron.ballman added a comment. This revision is now accepted and ready to land. LGTM! CHANGES SINCE LAST ACTION https://reviews.llvm.org/D137232/new/ https://reviews.llvm.org/D137232 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D137232: [clang][Interp] Support inc/dec operators on pointers
tbaeder added a comment. Ping CHANGES SINCE LAST ACTION https://reviews.llvm.org/D137232/new/ https://reviews.llvm.org/D137232 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D137232: [clang][Interp] Support inc/dec operators on pointers
tbaeder added inline comments. Comment at: clang/test/AST/Interp/arrays.cpp:216 - static_assert(getNextElem(E, 1) == 3); -#endif + static_assert(getNextElem(E, 1) == 3, ""); + aaron.ballman wrote: > aaron.ballman wrote: > > I'd like test cases where the pointer arithmetic has run off the > > beginning/end of the object (forming the invalid pointer is UB per > > http://eel.is/c++draft/expr.add#4.3 even if you never dereference the > > pointer). > ``` > constexpr int bad1() { > const int *e = E + 3; > e++; // This is fine because it's a one-past-the-end pointer > return *e; // This is UB > } > > constexpr int bad2() { > const int *e = E + 4; > e++; // This is UB > return *e; // This is UB as well > } > > constexpr int bad3() { > const int *e = E; > --e; // This is UB > return *e; // This is UB as well > } > ``` Interesting side-effect of removing the `Run` call in `isPotentialConstantExpression()`: The new constant interpreter doesn't diagnose the "never produces a constant expression` case. The function must be called for any diagnostics to be printed. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D137232/new/ https://reviews.llvm.org/D137232 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D137232: [clang][Interp] Support inc/dec operators on pointers
tbaeder updated this revision to Diff 474724. tbaeder marked 2 inline comments as done. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D137232/new/ https://reviews.llvm.org/D137232 Files: clang/lib/AST/Interp/ByteCodeExprGen.cpp clang/lib/AST/Interp/Interp.h clang/lib/AST/Interp/Opcodes.td clang/test/AST/Interp/arrays.cpp Index: clang/test/AST/Interp/arrays.cpp === --- clang/test/AST/Interp/arrays.cpp +++ clang/test/AST/Interp/arrays.cpp @@ -215,9 +215,6 @@ static_assert(b.a[1].a == 12, ""); 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; @@ -225,6 +222,91 @@ } 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}} }; Index: clang/lib/AST/Interp/Opcodes.td === --- clang/lib/AST/Interp/Opcodes.td +++ clang/lib/AST/Interp/Opcodes.td @@ -395,12 +395,21 @@ // [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. //===--===// Index: clang/lib/AST/Interp/Interp.h === --- clang/lib/AST/Interp/Interp.h +++ clang/lib/AST/Interp/Interp.h @@ -105,6 +105,8 @@ /// Interpreter entry point. bool Interpret(InterpState , APValue ); +enum class ArithOp { Add, Sub }; +
[PATCH] D137232: [clang][Interp] Support inc/dec operators on pointers
aaron.ballman added inline comments. Comment at: clang/test/AST/Interp/arrays.cpp:216 - static_assert(getNextElem(E, 1) == 3); -#endif + static_assert(getNextElem(E, 1) == 3, ""); + aaron.ballman wrote: > I'd like test cases where the pointer arithmetic has run off the > beginning/end of the object (forming the invalid pointer is UB per > http://eel.is/c++draft/expr.add#4.3 even if you never dereference the > pointer). ``` constexpr int bad1() { const int *e = E + 3; e++; // This is fine because it's a one-past-the-end pointer return *e; // This is UB } constexpr int bad2() { const int *e = E + 4; e++; // This is UB return *e; // This is UB as well } constexpr int bad3() { const int *e = E; --e; // This is UB return *e; // This is UB as well } ``` CHANGES SINCE LAST ACTION https://reviews.llvm.org/D137232/new/ https://reviews.llvm.org/D137232 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D137232: [clang][Interp] Support inc/dec operators on pointers
tbaeder added inline comments. Comment at: clang/lib/AST/Interp/Opcodes.td:421-425 +// [Pointer] -> [] +def IncPtr : Opcode { + let HasGroup = 0; +} +// [Pointer] -> [] aaron.ballman wrote: > Don't both of these result in a `Pointer`? And should they have a `Types` > field? Ah yes, they do. They don't need a type field, we know we're operating on a pointer. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D137232/new/ https://reviews.llvm.org/D137232 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D137232: [clang][Interp] Support inc/dec operators on pointers
tbaeder updated this revision to Diff 474182. tbaeder marked an inline comment as done. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D137232/new/ https://reviews.llvm.org/D137232 Files: clang/lib/AST/Interp/ByteCodeExprGen.cpp clang/lib/AST/Interp/Interp.h clang/lib/AST/Interp/Opcodes.td clang/test/AST/Interp/arrays.cpp Index: clang/test/AST/Interp/arrays.cpp === --- clang/test/AST/Interp/arrays.cpp +++ clang/test/AST/Interp/arrays.cpp @@ -215,9 +215,6 @@ static_assert(b.a[1].a == 12, ""); 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; @@ -225,6 +222,59 @@ } 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, ""); + + }; Index: clang/lib/AST/Interp/Opcodes.td === --- clang/lib/AST/Interp/Opcodes.td +++ clang/lib/AST/Interp/Opcodes.td @@ -395,12 +395,21 @@ // [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. //===--===// Index: clang/lib/AST/Interp/Interp.h === --- clang/lib/AST/Interp/Interp.h +++ clang/lib/AST/Interp/Interp.h @@ -105,6 +105,8 @@ /// Interpreter entry point. bool Interpret(InterpState , APValue ); +enum class ArithOp { Add, Sub }; + //===--===// // Add, Sub, Mul //===--===// @@ -1084,6 +1086,34 @@ return OffsetHelper(S, OpPC); } +template +static inline bool IncDecPtrHelper(InterpState , CodePtr OpPC) { + using OneT = Integral<8, false>; + const Pointer = S.Stk.pop(); + + // Get the current value on the stack. + S.Stk.push(Ptr.deref()); + + // Now the current Ptr again and a constant 1. + // FIXME: We shouldn't have to push these two on the stack. + S.Stk.push(Ptr.deref()); + S.Stk.push(OneT::from(1)); + if (!OffsetHelper(S, OpPC)) +return false; + + // Store the new value. + Ptr.deref() = S.Stk.pop(); + return true; +} + +static inline bool IncPtr(InterpState , CodePtr OpPC) { + return IncDecPtrHelper(S, OpPC); +} + +static inline bool DecPtr(InterpState , CodePtr OpPC) { + return IncDecPtrHelper(S, OpPC); +} + /// 1) Pops a Pointer from the stack. /// 2) Pops another Pointer from the stack. /// 3) Pushes the different of the indices of the two pointers on the stack. Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp === --- clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -1352,24 +1352,44 @@ const Expr *SubExpr = E->getSubExpr(); Optional 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) { +
[PATCH] D137232: [clang][Interp] Support inc/dec operators on pointers
aaron.ballman added inline comments. Comment at: clang/lib/AST/Interp/Opcodes.td:421-425 +// [Pointer] -> [] +def IncPtr : Opcode { + let HasGroup = 0; +} +// [Pointer] -> [] Don't both of these result in a `Pointer`? And should they have a `Types` field? Comment at: clang/test/AST/Interp/arrays.cpp:216 - static_assert(getNextElem(E, 1) == 3); -#endif + static_assert(getNextElem(E, 1) == 3, ""); + I'd like test cases where the pointer arithmetic has run off the beginning/end of the object (forming the invalid pointer is UB per http://eel.is/c++draft/expr.add#4.3 even if you never dereference the pointer). CHANGES SINCE LAST ACTION https://reviews.llvm.org/D137232/new/ https://reviews.llvm.org/D137232 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D137232: [clang][Interp] Support inc/dec operators on pointers
tbaeder updated this revision to Diff 472526. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D137232/new/ https://reviews.llvm.org/D137232 Files: clang/lib/AST/Interp/ByteCodeExprGen.cpp clang/lib/AST/Interp/Interp.h clang/lib/AST/Interp/Opcodes.td clang/test/AST/Interp/arrays.cpp Index: clang/test/AST/Interp/arrays.cpp === --- clang/test/AST/Interp/arrays.cpp +++ clang/test/AST/Interp/arrays.cpp @@ -206,9 +206,6 @@ static_assert(b.a[1].a == 12, ""); 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; @@ -216,6 +213,59 @@ } 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, ""); + + }; Index: clang/lib/AST/Interp/Opcodes.td === --- clang/lib/AST/Interp/Opcodes.td +++ clang/lib/AST/Interp/Opcodes.td @@ -412,12 +412,21 @@ // [Pointer, Integral] -> [Pointer] def SubOffset : AluOpcode; -// Pointer, Pointer] - [Integral] +// [Pointer, Pointer] -> [Integral] def SubPtr : Opcode { let Types = [IntegerTypeClass]; let HasGroup = 1; } +// [Pointer] -> [] +def IncPtr : Opcode { + let HasGroup = 0; +} +// [Pointer] -> [] +def DecPtr : Opcode { + let HasGroup = 0; +} + //===--===// // Binary operators. //===--===// Index: clang/lib/AST/Interp/Interp.h === --- clang/lib/AST/Interp/Interp.h +++ clang/lib/AST/Interp/Interp.h @@ -110,6 +110,8 @@ /// Interpreter entry point. bool Interpret(InterpState , APValue ); +enum class ArithOp { Add, Sub }; + //===--===// // Add, Sub, Mul //===--===// @@ -1141,6 +1143,34 @@ return OffsetHelper(S, OpPC); } +template +static inline bool IncDecPtrHelper(InterpState , CodePtr OpPC) { + using OneT = Integral<8, false>; + const Pointer = S.Stk.pop(); + + // Get the current value on the stack. + S.Stk.push(Ptr.deref()); + + // Now the current Ptr again and a constant 1. + // FIXME: We shouldn't have to push these two on the stack. + S.Stk.push(Ptr.deref()); + S.Stk.push(OneT::from(1)); + if (!OffsetHelper(S, OpPC)) +return false; + + // Store the new value. + Ptr.deref() = S.Stk.pop(); + return true; +} + +static inline bool IncPtr(InterpState , CodePtr OpPC) { + return IncDecPtrHelper(S, OpPC); +} + +static inline bool DecPtr(InterpState , CodePtr OpPC) { + return IncDecPtrHelper(S, OpPC); +} + /// 1) Pops a Pointer from the stack. /// 2) Pops another Pointer from the stack. /// 3) Pushes the different of the indices of the two pointers on the stack. Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp === --- clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -1406,24 +1406,44 @@ const Expr *SubExpr = E->getSubExpr(); Optional 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; + +
[PATCH] D137232: [clang][Interp] Support inc/dec operators on pointers
tbaeder created this revision. tbaeder added reviewers: aaron.ballman, erichkeane, tahonermann, shafik. Herald added a project: All. tbaeder requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits. I think this is pretty uncommon so I was trying to implement it without adding new opcodes, but looks like that's not possible. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D137232 Files: clang/lib/AST/Interp/ByteCodeExprGen.cpp clang/lib/AST/Interp/Interp.h clang/lib/AST/Interp/Opcodes.td clang/test/AST/Interp/arrays.cpp Index: clang/test/AST/Interp/arrays.cpp === --- clang/test/AST/Interp/arrays.cpp +++ clang/test/AST/Interp/arrays.cpp @@ -206,9 +206,6 @@ static_assert(b.a[1].a == 12, ""); 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; @@ -216,6 +213,59 @@ } 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, ""); + + }; Index: clang/lib/AST/Interp/Opcodes.td === --- clang/lib/AST/Interp/Opcodes.td +++ clang/lib/AST/Interp/Opcodes.td @@ -412,12 +412,21 @@ // [Pointer, Integral] -> [Pointer] def SubOffset : AluOpcode; -// Pointer, Pointer] - [Integral] +// [Pointer, Pointer] -> [Integral] def SubPtr : Opcode { let Types = [IntegerTypeClass]; let HasGroup = 1; } +// [Pointer] -> [] +def IncPtr : Opcode { + let HasGroup = 0; +} +// [Pointer] -> [] +def DecPtr : Opcode { + let HasGroup = 0; +} + //===--===// // Binary operators. //===--===// Index: clang/lib/AST/Interp/Interp.h === --- clang/lib/AST/Interp/Interp.h +++ clang/lib/AST/Interp/Interp.h @@ -110,6 +110,8 @@ /// Interpreter entry point. bool Interpret(InterpState , APValue ); +enum class ArithOp { Add, Sub }; + //===--===// // Add, Sub, Mul //===--===// @@ -1141,6 +1143,34 @@ return OffsetHelper(S, OpPC); } +template +static inline bool IncDecPtrHelper(InterpState , CodePtr OpPC) { + using OneT = Integral<8, false>; + const Pointer = S.Stk.pop(); + + // Get the current value on the stack. + S.Stk.push(Ptr.deref()); + + // Now the current Ptr again and a constant 1. + // FIXME: We shouldn't have to push these two on the stack. + S.Stk.push(Ptr.deref()); + S.Stk.push(OneT::from(1)); + if (!OffsetHelper(S, OpPC)) +return false; + + // Store the new value. + Ptr.deref() = S.Stk.pop(); + return true; +} + +static inline bool IncPtr(InterpState , CodePtr OpPC) { + return IncDecPtrHelper(S, OpPC); +} + +static inline bool DecPtr(InterpState , CodePtr OpPC) { + return IncDecPtrHelper(S, OpPC); +} + /// 1) Pops a Pointer from the stack. /// 2) Pops another Pointer from the stack. /// 3) Pushes the different of the indices of the two pointers on the stack. Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp === --- clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -1406,24 +1406,44 @@ const Expr *SubExpr = E->getSubExpr(); Optional 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