[PATCH] D135858: [clang][Interp] Support pointer arithmethic in binary operators
tbaeder added inline comments. Comment at: clang/lib/AST/Interp/Interp.h:970 + if (!Pointer::hasSameArray(LHS, RHS)) { +// TODO: Diagnose. +return false; This is also not being diagnosed (only rejected) by the current interpreter. But would be nice to have an error message. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D135858/new/ https://reviews.llvm.org/D135858 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D135858: [clang][Interp] Support pointer arithmethic in binary operators
tbaeder updated this revision to Diff 468060. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D135858/new/ https://reviews.llvm.org/D135858 Files: clang/lib/AST/Interp/ByteCodeExprGen.cpp clang/lib/AST/Interp/ByteCodeExprGen.h clang/lib/AST/Interp/Interp.h clang/lib/AST/Interp/Opcodes.td clang/lib/AST/Interp/Pointer.cpp 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 @@ -37,6 +37,56 @@ static_assert(getElement(1) == 4, ""); static_assert(getElement(5) == 36, ""); +constexpr int data[] = {5, 4, 3, 2, 1}; +constexpr int getElement(const int *Arr, int index) { + return *(Arr + index); +} + +static_assert(getElement(data, 1) == 4, ""); +static_assert(getElement(data, 4) == 1, ""); + +constexpr int getElementFromEnd(const int *Arr, int size, int index) { + return *(Arr + size - index - 1); +} +static_assert(getElementFromEnd(data, 5, 0) == 1, ""); +static_assert(getElementFromEnd(data, 5, 4) == 5, ""); + + +constexpr static int arr[2] = {1,2}; +constexpr static int arr2[2] = {3,4}; +constexpr int *p1 = nullptr; +constexpr int *p2 = p1 + 1; // expected-error {{must be initialized by a constant expression}} \ +// expected-note {{cannot perform pointer arithmetic on null pointer}} \ +// ref-error {{must be initialized by a constant expression}} \ +// ref-note {{cannot perform pointer arithmetic on null pointer}} +constexpr int *p3 = p1 + 0; +constexpr int *p4 = p1 - 0; +constexpr int *p5 = 0 + p1; +constexpr int *p6 = 0 - p1; // expected-error {{invalid operands to binary expression}} \ + // ref-error {{invalid operands to binary expression}} + +constexpr int const * ap1 = [0]; +constexpr int const * ap2 = ap1 + 3; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{cannot refer to element 3 of array of 2}} \ + // ref-error {{must be initialized by a constant expression}} \ + // ref-note {{cannot refer to element 3 of array of 2}} + +constexpr auto ap3 = arr - 1; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{cannot refer to element -1}} \ + // ref-error {{must be initialized by a constant expression}} \ + // ref-note {{cannot refer to element -1}} +constexpr int k1 = [1] - [0]; +static_assert(k1 == 1, ""); +static_assert(([0] - [1]) == -1, ""); + +constexpr int k2 = [1] - [0]; // expected-error {{must be initialized by a constant expression}} \ + // ref-error {{must be initialized by a constant expression}} + +static_assert((arr + 0) == arr, ""); +static_assert([0] == arr, ""); +static_assert(*([0]) == 1, ""); +static_assert(*([1]) == 2, ""); + template constexpr T getElementOf(T* array, int i) { @@ -52,7 +102,6 @@ static_assert(getElementOfArray(foo[2], 3) == , ""); -constexpr int data[] = {5, 4, 3, 2, 1}; static_assert(data[0] == 4, ""); // expected-error{{failed}} \ // expected-note{{5 == 4}} \ // ref-error{{failed}} \ Index: clang/lib/AST/Interp/Pointer.cpp === --- clang/lib/AST/Interp/Pointer.cpp +++ clang/lib/AST/Interp/Pointer.cpp @@ -201,5 +201,5 @@ } bool Pointer::hasSameArray(const Pointer , const Pointer ) { - return A.Base == B.Base && A.getFieldDesc()->IsArray; + return hasSameBase(A, B) && A.Base == B.Base && A.getFieldDesc()->IsArray; } Index: clang/lib/AST/Interp/Opcodes.td === --- clang/lib/AST/Interp/Opcodes.td +++ clang/lib/AST/Interp/Opcodes.td @@ -411,6 +411,12 @@ let HasGroup = 1; } +// Pointer, Pointer] - [Integral] +def SubPtr : Opcode { + let Types = [IntegerTypeClass]; + let HasGroup = 1; +} + //===--===// // Binary operators. //===--===// Index: clang/lib/AST/Interp/Interp.h === --- clang/lib/AST/Interp/Interp.h +++ clang/lib/AST/Interp/Interp.h @@ -350,6 +350,16 @@ } else { unsigned VL = LHS.getByteOffset(); unsigned VR = RHS.getByteOffset(); + +// In our Pointer class, a pointer to an array and a pointer to the first +// element in the same array are NOT equal. They have the same Base value, +// but a different Offset. This is a pretty rare case, so we fix this here +// by comparing pointers to the first elements. +if
[PATCH] D135858: [clang][Interp] Support pointer arithmethic in binary operators
tbaeder updated this revision to Diff 468058. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D135858/new/ https://reviews.llvm.org/D135858 Files: clang/lib/AST/Interp/ByteCodeExprGen.cpp clang/lib/AST/Interp/ByteCodeExprGen.h clang/lib/AST/Interp/Interp.h clang/lib/AST/Interp/Opcodes.td clang/lib/AST/Interp/Pointer.cpp 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 @@ -37,6 +37,52 @@ static_assert(getElement(1) == 4, ""); static_assert(getElement(5) == 36, ""); +constexpr int data[] = {5, 4, 3, 2, 1}; +constexpr int getElement(const int *Arr, int index) { + return *(Arr + index); +} + +static_assert(getElement(data, 1) == 4, ""); +static_assert(getElement(data, 4) == 1, ""); + +constexpr int getElementFromEnd(const int *Arr, int size, int index) { + return *(Arr + size - index - 1); +} +static_assert(getElementFromEnd(data, 5, 0) == 1, ""); +static_assert(getElementFromEnd(data, 5, 4) == 5, ""); + + +constexpr static int arr[2] = {1,2}; +constexpr static int arr2[2] = {3,4}; +constexpr int *p1 = nullptr; +constexpr int *p2 = p1 + 1; // expected-error {{must be initialized by a constant expression}} \ +// expected-note {{cannot perform pointer arithmetic on null pointer}} \ +// ref-error {{must be initialized by a constant expression}} \ +// ref-note {{cannot perform pointer arithmetic on null pointer}} +constexpr int *p3 = p1 + 0; +constexpr int *p4 = p1 - 0; +constexpr int *p5 = 0 + p1; +constexpr int *p6 = 0 - p1; // expected-error {{invalid operands to binary expression}} \ + // ref-error {{invalid operands to binary expression}} + +constexpr int const * ap1 = [0]; +constexpr int const * ap2 = ap1 + 3; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{cannot refer to element 3 of array of 2}} \ + // ref-error {{must be initialized by a constant expression}} \ + // ref-note {{cannot refer to element 3 of array of 2}} + +constexpr auto ap3 = arr - 1; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{cannot refer to element -1}} \ + // ref-error {{must be initialized by a constant expression}} \ + // ref-note {{cannot refer to element -1}} +constexpr int k1 = [1] - [0]; +static_assert(k1 == 1, ""); +static_assert(([0] - [1]) == -1, ""); + +// FIXME: These two should work. +static_assert((arr + 0) == arr, ""); // expected-error {{static assertion failed}} +static_assert([0] == arr, ""); // expected-error {{static assertion failed}} + template constexpr T getElementOf(T* array, int i) { @@ -52,7 +98,6 @@ static_assert(getElementOfArray(foo[2], 3) == , ""); -constexpr int data[] = {5, 4, 3, 2, 1}; static_assert(data[0] == 4, ""); // expected-error{{failed}} \ // expected-note{{5 == 4}} \ // ref-error{{failed}} \ Index: clang/lib/AST/Interp/Pointer.cpp === --- clang/lib/AST/Interp/Pointer.cpp +++ clang/lib/AST/Interp/Pointer.cpp @@ -201,5 +201,5 @@ } bool Pointer::hasSameArray(const Pointer , const Pointer ) { - return A.Base == B.Base && A.getFieldDesc()->IsArray; + return hasSameBase(A, B) && A.Base == B.Base && A.getFieldDesc()->IsArray; } Index: clang/lib/AST/Interp/Opcodes.td === --- clang/lib/AST/Interp/Opcodes.td +++ clang/lib/AST/Interp/Opcodes.td @@ -411,6 +411,12 @@ let HasGroup = 1; } +// Pointer, Pointer] - [Integral] +def SubPtr : Opcode { + let Types = [IntegerTypeClass]; + let HasGroup = 1; +} + //===--===// // Binary operators. //===--===// Index: clang/lib/AST/Interp/Interp.h === --- clang/lib/AST/Interp/Interp.h +++ clang/lib/AST/Interp/Interp.h @@ -876,6 +876,14 @@ // Fetch the pointer and the offset. const T = S.Stk.pop(); const Pointer = S.Stk.pop(); + + // Pointer arithmethic with nullptr and 0 is valid and just + // results in nullptr again. + if (Offset.isZero() && Ptr.isZero()) { +S.Stk.push(Ptr); +return true; + } + if (!CheckNull(S, OpPC, Ptr, CSK_ArrayIndex)) return false; if (!CheckRange(S, OpPC, Ptr, CSK_ArrayToPointer)) @@ -946,6 +954,18 @@ return OffsetHelper(S, OpPC); } +/// 1) Pops a Pointer from the stack. +/// 2) Pops another Pointer from the
[PATCH] D135858: [clang][Interp] Support pointer arithmethic in binary operators
tbaeder added inline comments. Comment at: clang/lib/AST/Interp/ByteCodeExprGen.cpp:241 + // Pointer arithmethic special case. This is supported for one of + // LHS and RHS being a pointer type and the other being an integer type. + if (BO->getType()->isPointerType()) { shafik wrote: > I am not sure if this is the right place to handle this but there are a bunch > of other cases. > > - `nullptr` can have `0` added or subtracted > - You can only do addition/subtraction from a pointer if the result in within > bounds or one after the end > - You can subtract two pointers if they point to the same object. > > godbolt: https://godbolt.org/z/5YTY93z8M I will probably move this special case out of the function and into its own. Thanks for the tests, I think this should all already work except for adding two pointers. I'll update the diff once I checked that out. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D135858/new/ https://reviews.llvm.org/D135858 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D135858: [clang][Interp] Support pointer arithmethic in binary operators
shafik added inline comments. Comment at: clang/lib/AST/Interp/ByteCodeExprGen.cpp:241 + // Pointer arithmethic special case. This is supported for one of + // LHS and RHS being a pointer type and the other being an integer type. + if (BO->getType()->isPointerType()) { I am not sure if this is the right place to handle this but there are a bunch of other cases. - `nullptr` can have `0` added or subtracted - You can only do addition/subtraction from a pointer if the result in within bounds or one after the end - You can subtract two pointers if they point to the same object. godbolt: https://godbolt.org/z/5YTY93z8M Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D135858/new/ https://reviews.llvm.org/D135858 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D135858: [clang][Interp] Support pointer arithmethic in binary operators
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. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D135858 Files: clang/lib/AST/Interp/ByteCodeExprGen.cpp 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 @@ -37,6 +37,20 @@ static_assert(getElement(1) == 4, ""); static_assert(getElement(5) == 36, ""); +constexpr int data[] = {5, 4, 3, 2, 1}; +constexpr int getElement(const int *Arr, int index) { + return *(Arr + index); +} + +static_assert(getElement(data, 1) == 4, ""); +static_assert(getElement(data, 4) == 1, ""); + +constexpr int getElementFromEnd(const int *Arr, int size, int index) { + return *(Arr + size - index - 1); +} +static_assert(getElementFromEnd(data, 5, 0) == 1, ""); +static_assert(getElementFromEnd(data, 5, 4) == 5, ""); + template constexpr T getElementOf(T* array, int i) { @@ -52,7 +66,6 @@ static_assert(getElementOfArray(foo[2], 3) == , ""); -constexpr int data[] = {5, 4, 3, 2, 1}; static_assert(data[0] == 4, ""); // expected-error{{failed}} \ // expected-note{{5 == 4}} \ // ref-error{{failed}} \ Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp === --- clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -226,61 +226,79 @@ // Typecheck the args. Optional LT = classify(LHS->getType()); Optional RT = classify(RHS->getType()); - if (!LT || !RT) { + Optional T = classify(BO->getType()); + if (!LT || !RT || !T) { return this->bail(BO); } - if (Optional T = classify(BO->getType())) { -if (!visit(LHS)) + auto Discard = [this, T, BO](bool Result) { +if (!Result) return false; -if (!visit(RHS)) - return false; - -auto Discard = [this, T, BO](bool Result) { - if (!Result) +return DiscardResult ? this->emitPop(*T, BO) : true; + }; + + // Pointer arithmethic special case. This is supported for one of + // LHS and RHS being a pointer type and the other being an integer type. + if (BO->getType()->isPointerType()) { +PrimType OffsetType; +if (LHS->getType()->isIntegerType() && RHS->getType()->isPointerType()) { + if (!visit(RHS) || !visit(LHS)) return false; - return DiscardResult ? this->emitPop(*T, BO) : true; -}; - -switch (BO->getOpcode()) { -case BO_EQ: - return Discard(this->emitEQ(*LT, BO)); -case BO_NE: - return Discard(this->emitNE(*LT, BO)); -case BO_LT: - return Discard(this->emitLT(*LT, BO)); -case BO_LE: - return Discard(this->emitLE(*LT, BO)); -case BO_GT: - return Discard(this->emitGT(*LT, BO)); -case BO_GE: - return Discard(this->emitGE(*LT, BO)); -case BO_Sub: - return Discard(this->emitSub(*T, BO)); -case BO_Add: - return Discard(this->emitAdd(*T, BO)); -case BO_Mul: - return Discard(this->emitMul(*T, BO)); -case BO_Rem: - return Discard(this->emitRem(*T, BO)); -case BO_Div: - return Discard(this->emitDiv(*T, BO)); -case BO_Assign: - if (!this->emitStore(*T, BO)) + OffsetType = *LT; +} else { + if (!visit(LHS) || !visit(RHS)) return false; - return DiscardResult ? this->emitPopPtr(BO) : true; -case BO_And: - return Discard(this->emitBitAnd(*T, BO)); -case BO_Or: - return Discard(this->emitBitOr(*T, BO)); -case BO_LAnd: -case BO_LOr: -default: - return this->bail(BO); + OffsetType = *RT; } + +if (BO->getOpcode() == BO_Add) + return Discard(this->emitAddOffset(OffsetType, BO)); +else if (BO->getOpcode() == BO_Sub) + return Discard(this->emitSubOffset(OffsetType, BO)); +return this->bail(BO); + } + + if (!visit(LHS) || !visit(RHS)) +return false; + + switch (BO->getOpcode()) { + case BO_EQ: +return Discard(this->emitEQ(*LT, BO)); + case BO_NE: +return Discard(this->emitNE(*LT, BO)); + case BO_LT: +return Discard(this->emitLT(*LT, BO)); + case BO_LE: +return Discard(this->emitLE(*LT, BO)); + case BO_GT: +return Discard(this->emitGT(*LT, BO)); + case BO_GE: +return Discard(this->emitGE(*LT, BO)); + case BO_Sub: +return Discard(this->emitSub(*T, BO)); + case BO_Add: +return Discard(this->emitAdd(*T, BO)); + case BO_Mul: +return Discard(this->emitMul(*T, BO)); + case BO_Rem: +return Discard(this->emitRem(*T, BO)); + case BO_Div: +return Discard(this->emitDiv(*T, BO)); + case BO_Assign: +if (!this->emitStore(*T, BO)) + return false; +return DiscardResult ?