[PATCH] D135858: [clang][Interp] Support pointer arithmethic in binary operators

2022-10-16 Thread Timm Bäder via Phabricator via cfe-commits
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

2022-10-16 Thread Timm Bäder via Phabricator via cfe-commits
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

2022-10-16 Thread Timm Bäder via Phabricator via cfe-commits
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

2022-10-14 Thread Timm Bäder via Phabricator via cfe-commits
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

2022-10-13 Thread Shafik Yaghmour via Phabricator via cfe-commits
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

2022-10-13 Thread Timm Bäder via Phabricator via cfe-commits
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 ?