[clang][Interp] Add ArrayElemPtr{,Pop} opcode
authorTimm Bäder <tbaeder@redhat.com>
Sat, 31 Dec 2022 16:06:52 +0000 (17:06 +0100)
committerTimm Bäder <tbaeder@redhat.com>
Thu, 2 Mar 2023 08:45:18 +0000 (09:45 +0100)
We usually access array elements in the same pattern, which uses
narrow(). Add an extra opcode for this. This saves us quite some
instructions and makes the bytecode easier to read.

Differential Revision: https://reviews.llvm.org/D140805

clang/lib/AST/Interp/ByteCodeExprGen.cpp
clang/lib/AST/Interp/Interp.h
clang/lib/AST/Interp/Opcodes.td

index 7b8253c..1f5c4f6 100644 (file)
@@ -413,7 +413,7 @@ bool ByteCodeExprGen<Emitter>::VisitArraySubscriptExpr(
   const Expr *Index = E->getIdx();
   PrimType IndexT = classifyPrim(Index->getType());
 
-  // Take pointer of LHS, add offset from RHS, narrow result.
+  // Take pointer of LHS, add offset from RHS.
   // What's left on the stack after this is a pointer.
   if (!this->visit(Base))
     return false;
@@ -421,10 +421,7 @@ bool ByteCodeExprGen<Emitter>::VisitArraySubscriptExpr(
   if (!this->visit(Index))
     return false;
 
-  if (!this->emitAddOffset(IndexT, E))
-    return false;
-
-  if (!this->emitNarrowPtr(E))
+  if (!this->emitArrayElemPtrPop(IndexT, E))
     return false;
 
   if (DiscardResult)
@@ -1214,16 +1211,11 @@ bool ByteCodeExprGen<Emitter>::visitArrayInitializer(const Expr *Initializer) {
           return false;
       } else {
         // Advance the pointer currently on the stack to the given
-        // dimension and narrow().
-        if (!this->emitDupPtr(Init))
-          return false;
+        // dimension.
         if (!this->emitConstUint32(ElementIndex, Init))
           return false;
-        if (!this->emitAddOffsetUint32(Init))
+        if (!this->emitArrayElemPtrUint32(Init))
           return false;
-        if (!this->emitNarrowPtr(Init))
-          return false;
-
         if (!visitInitializer(Init))
           return false;
         if (!this->emitPopPtr(Init))
@@ -1249,31 +1241,22 @@ bool ByteCodeExprGen<Emitter>::visitArrayInitializer(const Expr *Initializer) {
     for (size_t I = 0; I != Size; ++I) {
       ArrayIndexScope<Emitter> IndexScope(this, I);
 
-      if (!this->emitDupPtr(SubExpr)) // LHS
-        return false;
-
       if (ElemT) {
         if (!this->visit(SubExpr))
           return false;
         if (!this->emitInitElem(*ElemT, I, Initializer))
           return false;
       } else {
-        // Narrow to our array element and recurse into visitInitializer()
+        // Get to our array element and recurse into visitInitializer()
         if (!this->emitConstUint64(I, SubExpr))
           return false;
-
-        if (!this->emitAddOffsetUint64(SubExpr))
-          return false;
-
-        if (!this->emitNarrowPtr(SubExpr))
+        if (!this->emitArrayElemPtrUint64(SubExpr))
           return false;
-
         if (!visitInitializer(SubExpr))
           return false;
+        if (!this->emitPopPtr(Initializer))
+          return false;
       }
-
-      if (!this->emitPopPtr(Initializer))
-        return false;
     }
     return true;
   } else if (const auto *IVIE = dyn_cast<ImplicitValueInitExpr>(Initializer)) {
@@ -1309,13 +1292,9 @@ bool ByteCodeExprGen<Emitter>::visitArrayInitializer(const Expr *Initializer) {
     // FIXME(perf): We're calling the constructor once per array element here,
     //   in the old intepreter we had a special-case for trivial constructors.
     for (size_t I = 0; I != NumElems; ++I) {
-      if (!this->emitDupPtr(Initializer))
-        return false;
       if (!this->emitConstUint64(I, Initializer))
         return false;
-      if (!this->emitAddOffsetUint64(Initializer))
-        return false;
-      if (!this->emitNarrowPtr(Initializer))
+      if (!this->emitArrayElemPtrUint64(Initializer))
         return false;
 
       // Constructor arguments.
index fbf74b5..98561f0 100644 (file)
@@ -1455,6 +1455,36 @@ inline bool ExpandPtr(InterpState &S, CodePtr OpPC) {
   return true;
 }
 
+// 1) Pops an integral value from the stack
+// 2) Peeks a pointer
+// 3) Pushes a new pointer that's a narrowed array
+//   element of the peeked pointer with the value
+//   from 1) added as offset.
+//
+// This leaves the original pointer on the stack and pushes a new one
+// with the offset applied and narrowed.
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+inline bool ArrayElemPtr(InterpState &S, CodePtr OpPC) {
+  const T &Offset = S.Stk.pop<T>();
+  const Pointer &Ptr = S.Stk.peek<Pointer>();
+
+  if (!OffsetHelper<T, ArithOp::Add>(S, OpPC, Offset, Ptr))
+    return false;
+
+  return NarrowPtr(S, OpPC);
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+inline bool ArrayElemPtrPop(InterpState &S, CodePtr OpPC) {
+  const T &Offset = S.Stk.pop<T>();
+  const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+  if (!OffsetHelper<T, ArithOp::Add>(S, OpPC, Offset, Ptr))
+    return false;
+
+  return NarrowPtr(S, OpPC);
+}
+
 inline bool CheckGlobalCtor(InterpState &S, CodePtr &PC) {
   const Pointer &Obj = S.Stk.peek<Pointer>();
   return CheckCtorCall(S, PC, Obj);
index 1e32505..80d5c65 100644 (file)
@@ -306,6 +306,9 @@ def RVOPtr : Opcode;
 def NarrowPtr : Opcode;
 // [Pointer] -> [Pointer]
 def ExpandPtr : Opcode;
+// [Pointer, Offset] -> [Pointer]
+def ArrayElemPtr : AluOpcode;
+def ArrayElemPtrPop : AluOpcode;
 
 //===----------------------------------------------------------------------===//
 // Direct field accessors