[PATCH] D137232: [clang][Interp] Support inc/dec operators on pointers

2023-01-25 Thread Timm Bäder via Phabricator via cfe-commits
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

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

2022-11-22 Thread Aaron Ballman via Phabricator via cfe-commits
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

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

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

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

2022-11-09 Thread Aaron Ballman via Phabricator via cfe-commits
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

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

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

2022-11-08 Thread Aaron Ballman via Phabricator via cfe-commits
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

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

2022-11-02 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.

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