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.

This only works on integrals for now.

Add four new opcodes, `Inc`, `IncPop`, `Dec` and `DecPop`. The `*Pop` variants 
don't leave anything on the stack and exist because the common case is that the 
result is unused.

There are still a few corner cases left like floating values, pointers, and 
over/underflow handling.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D136423

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
  clang/test/AST/Interp/literals.cpp

Index: clang/test/AST/Interp/literals.cpp
===================================================================
--- clang/test/AST/Interp/literals.cpp
+++ clang/test/AST/Interp/literals.cpp
@@ -361,3 +361,52 @@
   constexpr double db = true;
   static_assert(db == 1.0, "");
 };
+
+#if __cplusplus >= 202002L
+namespace IncDec {
+  constexpr int zero() {
+    int a = 0;
+    a++;
+    ++a;
+    a--;
+    --a;
+    return a;
+  }
+  static_assert(zero() == 0, "");
+
+  constexpr int three() {
+    int a = 0;
+    return ++a + ++a; // expected-warning {{multiple unsequenced modifications to 'a'}} \
+                      // ref-warning {{multiple unsequenced modifications to 'a'}} \
+
+  }
+  static_assert(three() == 3, "");
+
+  constexpr bool incBool() {
+    bool b = false;
+    return ++b; // expected-error {{ISO C++17 does not allow incrementing expression of type bool}} \
+                // ref-error {{ISO C++17 does not allow incrementing expression of type bool}}
+  }
+  static_assert(incBool(), "");
+
+  constexpr int uninit() {
+    int a;
+    ++a; // ref-note {{increment of uninitialized}} \
+         // FIXME: Should also be rejected by new interpreter
+    return 1;
+  }
+  static_assert(uninit(), ""); // ref-error {{not an integral constant expression}} \
+                               // ref-note {{in call to 'uninit()'}}
+
+  constexpr int OverUnderFlow() { // ref-error {{never produces a constant expression}}
+    int a = INT_MAX;
+    ++a; // ref-note {{is outside the range}}
+    // FIXME: Overflow
+
+    int b = INT_MIN;
+    --b; // FIXME: Underflow
+
+    return -1;
+  }
+};
+#endif
Index: clang/test/AST/Interp/arrays.cpp
===================================================================
--- clang/test/AST/Interp/arrays.cpp
+++ clang/test/AST/Interp/arrays.cpp
@@ -180,3 +180,18 @@
      constexpr int x = arr.a[0];
   }
 };
+
+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;
+    return *B;
+  }
+  constexpr int E[] = {1,2,3,4};
+
+  static_assert(getNextElem(E, 1) == 3);
+#endfi
+};
Index: clang/lib/AST/Interp/Opcodes.td
===================================================================
--- clang/lib/AST/Interp/Opcodes.td
+++ clang/lib/AST/Interp/Opcodes.td
@@ -452,6 +452,11 @@
   let HasGroup = 1;
 }
 
+def Inc: IntegerOpcode;
+def IncPop : IntegerOpcode;
+def Dec: IntegerOpcode;
+def DecPop: IntegerOpcode;
+
 // [Real] -> [Real]
 def Neg: Opcode {
   let Types = [NonPtrTypeClass];
Index: clang/lib/AST/Interp/Interp.h
===================================================================
--- clang/lib/AST/Interp/Interp.h
+++ clang/lib/AST/Interp/Interp.h
@@ -328,6 +328,70 @@
   return true;
 }
 
+/// 1) Pops a pointer from the stack
+/// 2) Load the value from the pointer
+/// 3) Writes the value increased by one back to the pointer
+/// 4) Pushes the original (pre-inc) value on the stack.
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Inc(InterpState &S, CodePtr OpPC) {
+  // FIXME: Check initialization of Ptr
+  const Pointer &Ptr = S.Stk.pop<Pointer>();
+  T Value = Ptr.deref<T>();
+  T Result;
+  if (T::increment(Value, &Result))
+    return false;
+  Ptr.deref<T>() = Result;
+  S.Stk.push<T>(Value);
+  return true;
+}
+
+/// 1) Pops a pointer from the stack
+/// 2) Load the value from the pointer
+/// 3) Writes the value increased by one back to the pointer
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool IncPop(InterpState &S, CodePtr OpPC) {
+  // FIXME: Check initialization of Ptr
+  const Pointer &Ptr = S.Stk.pop<Pointer>();
+  T Value = Ptr.deref<T>();
+  T Result;
+  if (T::increment(Value, &Result))
+    return false;
+  Ptr.deref<T>() = Result;
+  return true;
+}
+
+/// 1) Pops a pointer from the stack
+/// 2) Load the value from the pointer
+/// 3) Writes the value decreased by one back to the pointer
+/// 4) Pushes the original (pre-dec) value on the stack.
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Dec(InterpState &S, CodePtr OpPC) {
+  // FIXME: Check initialization of Ptr
+  const Pointer &Ptr = S.Stk.pop<Pointer>();
+  T Value = Ptr.deref<T>();
+  T Result;
+  if (T::decrement(Value, &Result))
+    return false;
+  Ptr.deref<T>() = Result;
+  S.Stk.push<T>(Value);
+  return true;
+}
+
+/// 1) Pops a pointer from the stack
+/// 2) Load the value from the pointer
+/// 3) Writes the value decreased by one back to the pointer
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool DecPop(InterpState &S, CodePtr OpPC) {
+  // FIXME: Check initialization of Ptr
+  const Pointer &Ptr = S.Stk.pop<Pointer>();
+  T Value = Ptr.deref<T>();
+  T Result;
+  if (T::decrement(Value, &Result))
+    return false;
+  Ptr.deref<T>() = Result;
+  return true;
+}
+
 /// 1) Pops the value from the stack.
 /// 2) Pushes the bitwise complemented value on the stack (~V).
 template <PrimType Name, class T = typename PrimConv<Name>::T>
Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp
===================================================================
--- clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -1226,35 +1226,81 @@
 
 template <class Emitter>
 bool ByteCodeExprGen<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
-  if (DiscardResult)
-    return true;
-
   const Expr *SubExpr = E->getSubExpr();
+  Optional<PrimType> T = classify(SubExpr->getType());
 
+  // TODO: Support pointers for inc/dec operators.
   switch (E->getOpcode()) {
-  case UO_PostInc: // x++
-  case UO_PostDec: // x--
-  case UO_PreInc:  // --x
-  case UO_PreDec:  // ++x
-    return false;
+  case UO_PostInc: { // x++
+    if (!this->visit(SubExpr))
+      return false;
+
+    if (DiscardResult)
+      return this->emitIncPop(*T, E);
+
+    return this->emitInc(*T, E);
+  }
+  case UO_PostDec: { // x--
+    if (!this->visit(SubExpr))
+      return false;
+
+    if (DiscardResult)
+      return this->emitDecPop(*T, E);
+
+    return this->emitDec(*T, E);
+  }
+  case UO_PreInc: { // ++x
+    if (!this->visit(SubExpr))
+      return false;
+
+    // Post-inc and pre-inc are the same if the value is to be discarded.
+    if (DiscardResult)
+      return this->emitIncPop(*T, E);
 
+    this->emitLoad(*T, E);
+    this->emitConst(E, 1);
+    this->emitAdd(*T, E);
+    return this->emitStore(*T, E);
+  }
+  case UO_PreDec: { // --x
+    if (!this->visit(SubExpr))
+      return false;
+
+    // Post-dec and pre-dec are the same if the value is to be discarded.
+    if (DiscardResult)
+      return this->emitDecPop(*T, E);
+
+    this->emitLoad(*T, E);
+    this->emitConst(E, 1);
+    this->emitSub(*T, E);
+    return this->emitStore(*T, E);
+  }
   case UO_LNot: // !x
     if (!this->visit(SubExpr))
       return false;
+    // The Inv doesn't change anything, so skip it if we don't need the result.
+    if (DiscardResult)
+      return this->emitPop(*T, E);
     return this->emitInvBool(E);
   case UO_Minus: // -x
     if (!this->visit(SubExpr))
       return false;
-    if (Optional<PrimType> T = classify(E->getType()))
-      return this->emitNeg(*T, E);
-    return false;
+    if (DiscardResult)
+      return this->emitPop(*T, E);
+    return this->emitNeg(*T, E);
   case UO_Plus:  // +x
-    return this->visit(SubExpr); // noop
-
+    if (!this->visit(SubExpr)) // noop
+      return false;
+    if (DiscardResult)
+      return this->emitPop(*T, E);
+    return true;
   case UO_AddrOf: // &x
     // We should already have a pointer when we get here.
-    return this->visit(SubExpr);
-
+    if (!this->visit(SubExpr))
+      return false;
+    if (DiscardResult)
+      return this->emitPop(*T, E);
+    return true;
   case UO_Deref:  // *x
     return dereference(
         SubExpr, DerefKind::Read,
@@ -1268,9 +1314,9 @@
   case UO_Not:    // ~x
     if (!this->visit(SubExpr))
       return false;
-    if (Optional<PrimType> T = classify(E->getType()))
-      return this->emitComp(*T, E);
-    return false;
+    if (DiscardResult)
+      return this->emitPop(*T, E);
+    return this->emitComp(*T, E);
   case UO_Real:   // __real x
   case UO_Imag:   // __imag x
   case UO_Extension:
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to