Bug 1456404 - Part 6: Add DoWhileEmitter. r=jwalden
authorTooru Fujisawa <arai_a@mac.com>
Fri, 10 Aug 2018 07:49:18 +0900
changeset 486021 61ec9ed5da1aa0a2654ea251223b561cab60073b
parent 486020 91e18d8d1e48340f27242342f20f047389af6703
child 486022 65c4fa236298578b59f3d32893ff69f96793c608
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwalden
bugs1456404
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1456404 - Part 6: Add DoWhileEmitter. r=jwalden
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/DoWhileEmitter.cpp
js/src/frontend/DoWhileEmitter.h
js/src/frontend/SourceNotes.h
js/src/jit/IonControlFlow.cpp
js/src/moz.build
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -21,16 +21,17 @@
 #include "jsapi.h"
 #include "jsnum.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "ds/Nestable.h"
 #include "frontend/BytecodeControlStructures.h"
 #include "frontend/CForEmitter.h"
+#include "frontend/DoWhileEmitter.h"
 #include "frontend/EmitterScope.h"
 #include "frontend/ForInEmitter.h"
 #include "frontend/ForOfEmitter.h"
 #include "frontend/ForOfLoopControl.h"
 #include "frontend/IfEmitter.h"
 #include "frontend/Parser.h"
 #include "frontend/SwitchEmitter.h"
 #include "frontend/TDZCheckCache.h"
@@ -5305,74 +5306,31 @@ BytecodeEmitter::emitAsyncWrapper(unsign
             return false;
     }
     return true;
 }
 
 bool
 BytecodeEmitter::emitDo(ParseNode* pn)
 {
-    // Ensure that the column of the 'do' is set properly.
-    if (!updateSourceCoordNotes(pn->pn_pos.begin))
-        return false;
-
-    /* Emit an annotated nop so IonBuilder can recognize the 'do' loop. */
-    unsigned noteIndex;
-    if (!newSrcNote(SRC_WHILE, &noteIndex))
-        return false;
-    if (!emit1(JSOP_NOP))
-        return false;
-
-    unsigned noteIndex2;
-    if (!newSrcNote(SRC_WHILE, &noteIndex2))
-        return false;
-
-    /* Compile the loop body. */
-    LoopControl loopInfo(this, StatementKind::DoLoop);
-
-    if (!loopInfo.emitLoopHead(this, getOffsetForLoop(pn->pn_left)))
-        return false;
-
-    if (!loopInfo.emitLoopEntry(this, Nothing()))
+    DoWhileEmitter doWhile(this);
+
+    if (!doWhile.emitBody(Some(pn->pn_pos.begin), getOffsetForLoop(pn->pn_left)))
         return false;
 
     if (!emitTree(pn->pn_left))
         return false;
 
-    // Set the offset for continues.
-    if (!loopInfo.emitContinueTarget(this))
-        return false;
-
-    /* Compile the loop condition, now that continues know where to go. */
+    if (!doWhile.emitCond())
+        return false;
+
     if (!emitTree(pn->pn_right))
         return false;
 
-    if (!loopInfo.emitLoopEnd(this, JSOP_IFNE))
-        return false;
-
-    if (!tryNoteList.append(JSTRY_LOOP, stackDepth, loopInfo.headOffset(),
-                            loopInfo.breakTargetOffset()))
-    {
-        return false;
-    }
-
-    /*
-     * Update the annotations with the update and back edge positions, for
-     * IonBuilder.
-     *
-     * Be careful: We must set noteIndex2 before noteIndex in case the noteIndex
-     * note gets bigger.
-     */
-    if (!setSrcNoteOffset(noteIndex2, 0, loopInfo.loopEndOffsetFromLoopHead()))
-        return false;
-    // +1 for NOP above.
-    if (!setSrcNoteOffset(noteIndex, 0, loopInfo.continueTargetOffsetFromLoopHead() + 1))
-        return false;
-
-    if (!loopInfo.patchBreaksAndContinues(this))
+    if (!doWhile.emitEnd())
         return false;
 
     return true;
 }
 
 bool
 BytecodeEmitter::emitWhile(ParseNode* pn)
 {
new file mode 100644
--- /dev/null
+++ b/js/src/frontend/DoWhileEmitter.cpp
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "frontend/DoWhileEmitter.h"
+
+#include "frontend/BytecodeEmitter.h"
+#include "frontend/SourceNotes.h"
+#include "vm/Opcodes.h"
+
+using namespace js;
+using namespace js::frontend;
+
+using mozilla::Maybe;
+using mozilla::Nothing;
+
+DoWhileEmitter::DoWhileEmitter(BytecodeEmitter* bce)
+  : bce_(bce)
+{}
+
+bool
+DoWhileEmitter::emitBody(const Maybe<uint32_t>& doPos, const Maybe<uint32_t>& bodyPos)
+{
+    MOZ_ASSERT(state_ == State::Start);
+
+    // Ensure that the column of the 'do' is set properly.
+    if (doPos) {
+        if (!bce_->updateSourceCoordNotes(*doPos))
+            return false;
+    }
+
+    // Emit an annotated nop so IonBuilder can recognize the 'do' loop.
+    if (!bce_->newSrcNote(SRC_WHILE, &noteIndex_))
+        return false;
+    if (!bce_->emit1(JSOP_NOP))
+        return false;
+
+    if (!bce_->newSrcNote(SRC_WHILE, &noteIndex2_))
+        return false;
+
+    loopInfo_.emplace(bce_, StatementKind::DoLoop);
+
+    if (!loopInfo_->emitLoopHead(bce_, bodyPos))
+        return false;
+
+    if (!loopInfo_->emitLoopEntry(bce_, Nothing()))
+        return false;
+
+#ifdef DEBUG
+    state_ = State::Body;
+#endif
+    return true;
+}
+
+bool
+DoWhileEmitter::emitCond()
+{
+    MOZ_ASSERT(state_ == State::Body);
+
+    if (!loopInfo_->emitContinueTarget(bce_))
+        return false;
+
+#ifdef DEBUG
+    state_ = State::Cond;
+#endif
+    return true;
+}
+
+bool
+DoWhileEmitter::emitEnd()
+{
+    MOZ_ASSERT(state_ == State::Cond);
+
+    if (!loopInfo_->emitLoopEnd(bce_, JSOP_IFNE))
+        return false;
+
+    if (!bce_->tryNoteList.append(JSTRY_LOOP, bce_->stackDepth, loopInfo_->headOffset(),
+                                  loopInfo_->breakTargetOffset()))
+    {
+        return false;
+    }
+
+    // Update the annotations with the update and back edge positions, for
+    // IonBuilder.
+    //
+    // Be careful: We must set noteIndex2_ before_ noteIndex in case the
+    // noteIndex_ note gets bigger.  Otherwise noteIndex2_ can point wrong
+    // position.
+    if (!bce_->setSrcNoteOffset(noteIndex2_, SrcNote::DoWhile2::BackJumpOffset,
+                                loopInfo_->loopEndOffsetFromLoopHead()))
+    {
+        return false;
+    }
+    // +1 for NOP in emitBody.
+    if (!bce_->setSrcNoteOffset(noteIndex_, SrcNote::DoWhile1::CondOffset,
+                                loopInfo_->continueTargetOffsetFromLoopHead() + 1))
+    {
+        return false;
+    }
+
+    if (!loopInfo_->patchBreaksAndContinues(bce_))
+        return false;
+
+    loopInfo_.reset();
+
+#ifdef DEBUG
+    state_ = State::End;
+#endif
+    return true;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/frontend/DoWhileEmitter.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef frontend_DoWhileEmitter_h
+#define frontend_DoWhileEmitter_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+
+#include <stdint.h>
+
+#include "frontend/BytecodeControlStructures.h"
+
+namespace js {
+namespace frontend {
+
+struct BytecodeEmitter;
+
+// Class for emitting bytecode for do-while loop.
+//
+// Usage: (check for the return value is omitted for simplicity)
+//
+//   `do body while (cond);`
+//     DoWhileEmitter doWhile(this);
+//     doWhile.emitBody(Some(offset_of_do), Some(offset_of_body));
+//     emit(body);
+//     doWhile.emitCond();
+//     emit(cond);
+//     doWhile.emitEnd();
+//
+class MOZ_STACK_CLASS DoWhileEmitter
+{
+    BytecodeEmitter* bce_;
+
+    // The source note indices for 2 SRC_WHILE's.
+    // FIXME: Add SRC_DO_WHILE (bug 1477896).
+    unsigned noteIndex_ = 0;
+    unsigned noteIndex2_ = 0;
+
+    mozilla::Maybe<LoopControl> loopInfo_;
+
+#ifdef DEBUG
+    // The state of this emitter.
+    //
+    // +-------+ emitBody +------+ emitCond +------+ emitEnd  +-----+
+    // | Start |--------->| Body |--------->| Cond |--------->| End |
+    // +-------+          +------+          +------+          +-----+
+    enum class State {
+        // The initial state.
+        Start,
+
+        // After calling emitBody.
+        Body,
+
+        // After calling emitCond.
+        Cond,
+
+        // After calling emitEnd.
+        End
+    };
+    State state_ = State::Start;
+#endif
+
+  public:
+    explicit DoWhileEmitter(BytecodeEmitter* bce);
+
+    // Parameters are the offset in the source code for each character below:
+    //
+    //   do { ... } while ( x < 20 );
+    //   ^  ^
+    //   |  |
+    //   |  bodyPos
+    //   |
+    //   doPos
+    //
+    // Can be Nothing() if not available.
+    MOZ_MUST_USE bool emitBody(const mozilla::Maybe<uint32_t>& doPos,
+                               const mozilla::Maybe<uint32_t>& bodyPos);
+    MOZ_MUST_USE bool emitCond();
+    MOZ_MUST_USE bool emitEnd();
+};
+
+} /* namespace frontend */
+} /* namespace js */
+
+#endif /* frontend_DoWhileEmitter_h */
--- a/js/src/frontend/SourceNotes.h
+++ b/js/src/frontend/SourceNotes.h
@@ -59,16 +59,39 @@ class SrcNote {
     class While {
       public:
         enum Fields {
             // The offset of JSOP_IFNE at the end of the loop from JSOP_GOTO.
             BackJumpOffset,
             Count,
         };
     };
+    // SRC_WHILE for do-while: Source note for JSOP_NOP at the top of do-while
+    //                         loop
+    // FIXME: Add SRC_DO_WHILE (bug 1477896).
+    class DoWhile1 {
+      public:
+        enum Fields {
+            // The offset of the condition ops from JSOP_NOP.
+            CondOffset,
+            Count,
+        };
+    };
+    // SRC_WHILE for do-while: Source note for JSOP_LOOPHEAD at the top of
+    //                         do-while loop
+    // FIXME: Add SRC_DO_WHILE (bug 1477896).
+    class DoWhile2 {
+      public:
+        enum Fields {
+            // The offset of JSOP_IFNE at the end of the loop from
+            // JSOP_LOOPHEAD.
+            BackJumpOffset,
+            Count,
+        };
+    };
     // SRC_FOR_IN: Source note for JSOP_GOTO at the top of for-in loop,
     //             which jumps to JSOP_LOOPENTRY.
     class ForIn {
       public:
         enum Fields {
             // The offset of JSOP_IFEQ at the end of the loop from JSOP_GOTO.
             BackJumpOffset,
             Count,
@@ -143,16 +166,22 @@ class SrcNote {
         enum Fields {
             // The file-absolute source line number of the current op.
             Line,
             Count
         };
     };
 };
 
+// FIXME: Add SRC_DO_WHILE (bug 1477896).
+static_assert(unsigned(SrcNote::While::Count) == unsigned(SrcNote::DoWhile1::Count),
+              "SRC_WHILE can be shared between while and do-while");
+static_assert(unsigned(SrcNote::While::Count) == unsigned(SrcNote::DoWhile2::Count),
+              "SRC_WHILE can be shared between while and do-while");
+
 #define FOR_EACH_SRC_NOTE_TYPE(M)                                                                  \
     M(SRC_NULL,         "null",        0)  /* Terminates a note vector. */                         \
     M(SRC_IF,           "if",          0)  /* JSOP_IFEQ bytecode is from an if-then. */            \
     M(SRC_IF_ELSE,      "if-else",     0)  /* JSOP_IFEQ bytecode is from an if-then-else. */       \
     M(SRC_COND,         "cond",        0)  /* JSOP_IFEQ is from conditional ?: operator. */        \
     M(SRC_FOR,          "for",         SrcNote::For::Count) \
     M(SRC_WHILE,        "while",       SrcNote::While::Count) \
     M(SRC_FOR_IN,       "for-in",      SrcNote::ForIn::Count) \
--- a/js/src/jit/IonControlFlow.cpp
+++ b/js/src/jit/IonControlFlow.cpp
@@ -1536,21 +1536,21 @@ ControlFlowGenerator::processDoWhileLoop
     //    NOP         ; SRC_WHILE (offset to COND)
     //    LOOPHEAD    ; SRC_WHILE (offset to IFNE)
     //    LOOPENTRY
     //    ...         ; body
     //    ...
     //    COND        ; start of condition
     //    ...
     //    IFNE ->     ; goes to LOOPHEAD
-    int condition_offset = GetSrcNoteOffset(sn, 0);
+    int condition_offset = GetSrcNoteOffset(sn, SrcNote::DoWhile1::CondOffset);
     jsbytecode* conditionpc = pc + condition_offset;
 
     jssrcnote* sn2 = GetSrcNote(gsn, script, pc + 1);
-    int offset = GetSrcNoteOffset(sn2, 0);
+    int offset = GetSrcNoteOffset(sn2, SrcNote::DoWhile2::BackJumpOffset);
     jsbytecode* ifne = pc + offset + 1;
     MOZ_ASSERT(ifne > pc);
 
     // Verify that the IFNE goes back to a loophead op.
     jsbytecode* loopHead = GetNextPc(pc);
     MOZ_ASSERT(JSOp(*loopHead) == JSOP_LOOPHEAD);
     MOZ_ASSERT(loopHead == ifne + GetJumpOffset(ifne));
 
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -207,16 +207,17 @@ UNIFIED_SOURCES += [
     'devtools/sharkctl.cpp',
     'ds/Bitmap.cpp',
     'ds/LifoAlloc.cpp',
     'ds/MemoryProtectionExceptionHandler.cpp',
     'frontend/BytecodeCompiler.cpp',
     'frontend/BytecodeControlStructures.cpp',
     'frontend/BytecodeEmitter.cpp',
     'frontend/CForEmitter.cpp',
+    'frontend/DoWhileEmitter.cpp',
     'frontend/EmitterScope.cpp',
     'frontend/FoldConstants.cpp',
     'frontend/ForInEmitter.cpp',
     'frontend/ForOfEmitter.cpp',
     'frontend/ForOfLoopControl.cpp',
     'frontend/IfEmitter.cpp',
     'frontend/JumpList.cpp',
     'frontend/NameFunctions.cpp',