Bug 1476203 - Separate IfEmitter and CondEmitter. r=jwalden
authorTooru Fujisawa <arai_a@mac.com>
Mon, 13 Aug 2018 05:19:47 -0700
changeset 486345 125176893a0a113be1865f1d3efc1e38425ee0e6
parent 486344 ec59471499f174cd05086b572f759f67cdfa4476
child 486346 e789bd1ead097ae48c08e6c7b887188008439167
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
bugs1476203
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 1476203 - Separate IfEmitter and CondEmitter. r=jwalden
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/IfEmitter.cpp
js/src/frontend/IfEmitter.h
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -4348,22 +4348,20 @@ BytecodeEmitter::emitTry(ParseNode* pn)
     return true;
 }
 
 bool
 BytecodeEmitter::emitIf(ParseNode* pn)
 {
     IfEmitter ifThenElse(this);
 
+    if (!ifThenElse.emitIf(Some(pn->pn_pos.begin)))
+        return false;
+
   if_again:
-    // Make sure this code is attributed to the "if" so that it gets a useful
-    // column number, instead of the default 0 value.
-    if (!updateSourceCoordNotes(pn->pn_pos.begin))
-        return false;
-
     /* Emit code for the condition before pushing stmtInfo. */
     if (!emitTree(pn->pn_kid1))
         return false;
 
     ParseNode* elseNode = pn->pn_kid3;
     if (elseNode) {
         if (!ifThenElse.emitThenElse())
             return false;
@@ -4375,17 +4373,17 @@ BytecodeEmitter::emitIf(ParseNode* pn)
     /* Emit code for the then part. */
     if (!emitTree(pn->pn_kid2))
         return false;
 
     if (elseNode) {
         if (elseNode->isKind(ParseNodeKind::If)) {
             pn = elseNode;
 
-            if (!ifThenElse.emitElseIf())
+            if (!ifThenElse.emitElseIf(Some(pn->pn_pos.begin)))
                 return false;
 
             goto if_again;
         }
 
         if (!ifThenElse.emitElse())
             return false;
 
@@ -6841,36 +6839,38 @@ BytecodeEmitter::emitLabeledStatement(co
 
     return true;
 }
 
 bool
 BytecodeEmitter::emitConditionalExpression(ConditionalExpression& conditional,
                                            ValueUsage valueUsage /* = ValueUsage::WantValue */)
 {
-    /* Emit the condition, then branch if false to the else part. */
+    CondEmitter cond(this);
+    if (!cond.emitCond())
+        return false;
+
     if (!emitTree(&conditional.condition()))
         return false;
 
-    IfEmitter ifThenElse(this);
-    if (!ifThenElse.emitCond())
+    if (!cond.emitThenElse())
         return false;
 
     if (!emitTree(&conditional.thenExpression(), valueUsage))
         return false;
 
-    if (!ifThenElse.emitElse())
+    if (!cond.emitElse())
         return false;
 
     if (!emitTree(&conditional.elseExpression(), valueUsage))
         return false;
 
-    if (!ifThenElse.emitEnd())
-        return false;
-    MOZ_ASSERT(ifThenElse.pushed() == 1);
+    if (!cond.emitEnd())
+        return false;
+    MOZ_ASSERT(cond.pushed() == 1);
 
     return true;
 }
 
 bool
 BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, PropListType type)
 {
     for (ParseNode* propdef = pn->pn_head; propdef; propdef = propdef->pn_next) {
--- a/js/src/frontend/IfEmitter.cpp
+++ b/js/src/frontend/IfEmitter.cpp
@@ -8,37 +8,34 @@
 
 #include "frontend/BytecodeEmitter.h"
 #include "frontend/SourceNotes.h"
 #include "vm/Opcodes.h"
 
 using namespace js;
 using namespace js::frontend;
 
-IfEmitter::IfEmitter(BytecodeEmitter* bce, Kind kind)
+using mozilla::Maybe;
+
+BranchEmitterBase::BranchEmitterBase(BytecodeEmitter* bce, Kind kind)
   : bce_(bce),
-    thenDepth_(0),
     kind_(kind)
-#ifdef DEBUG
-  , pushed_(0),
-    calculatedPushed_(false),
-    state_(State::Start)
-#endif
+{}
+
+IfEmitter::IfEmitter(BytecodeEmitter* bce, Kind kind)
+  : BranchEmitterBase(bce, kind)
 {}
 
 IfEmitter::IfEmitter(BytecodeEmitter* bce)
   : IfEmitter(bce, Kind::MayContainLexicalAccessInBranch)
 {}
 
 bool
-IfEmitter::emitIfInternal(SrcNoteType type)
+BranchEmitterBase::emitThenInternal(SrcNoteType type)
 {
-    MOZ_ASSERT_IF(state_ == State::ElseIf, tdzCache_.isSome());
-    MOZ_ASSERT_IF(state_ != State::ElseIf, tdzCache_.isNothing());
-
     // The end of TDZCheckCache for cond for else-if.
     if (kind_ == Kind::MayContainLexicalAccessInBranch)
         tdzCache_.reset();
 
     // Emit an annotated branch-if-false around the then part.
     if (!bce_->newSrcNote(type))
         return false;
     if (!bce_->emitJump(JSOP_IFEQ, &jumpAroundThen_))
@@ -56,69 +53,30 @@ IfEmitter::emitIfInternal(SrcNoteType ty
     // Enclose then-branch with TDZCheckCache.
     if (kind_ == Kind::MayContainLexicalAccessInBranch)
         tdzCache_.emplace(bce_);
 
     return true;
 }
 
 void
-IfEmitter::calculateOrCheckPushed()
+BranchEmitterBase::calculateOrCheckPushed()
 {
 #ifdef DEBUG
     if (!calculatedPushed_) {
         pushed_ = bce_->stackDepth - thenDepth_;
         calculatedPushed_ = true;
     } else {
         MOZ_ASSERT(pushed_ == bce_->stackDepth - thenDepth_);
     }
 #endif
 }
 
 bool
-IfEmitter::emitThen()
-{
-    MOZ_ASSERT(state_ == State::Start || state_ == State::ElseIf);
-    if (!emitIfInternal(SRC_IF))
-        return false;
-
-#ifdef DEBUG
-    state_ = State::Then;
-#endif
-    return true;
-}
-
-bool
-IfEmitter::emitCond()
-{
-    MOZ_ASSERT(state_ == State::Start);
-    if (!emitIfInternal(SRC_COND))
-        return false;
-
-#ifdef DEBUG
-    state_ = State::Cond;
-#endif
-    return true;
-}
-
-bool
-IfEmitter::emitThenElse()
-{
-    MOZ_ASSERT(state_ == State::Start || state_ == State::ElseIf);
-    if (!emitIfInternal(SRC_IF_ELSE))
-        return false;
-
-#ifdef DEBUG
-    state_ = State::ThenElse;
-#endif
-    return true;
-}
-
-bool
-IfEmitter::emitElseInternal()
+BranchEmitterBase::emitElseInternal()
 {
     calculateOrCheckPushed();
 
     // The end of TDZCheckCache for then-clause.
     if (kind_ == Kind::MayContainLexicalAccessInBranch) {
         MOZ_ASSERT(tdzCache_.isSome());
         tdzCache_.reset();
     }
@@ -133,67 +91,27 @@ IfEmitter::emitElseInternal()
     if (!bce_->emitJumpTargetAndPatch(jumpAroundThen_))
         return false;
 
     // Clear jumpAroundThen_ offset, to tell emitEnd there was an else part.
     jumpAroundThen_ = JumpList();
 
     // Restore stack depth of the then part.
     bce_->stackDepth = thenDepth_;
-#ifdef DEBUG
-    state_ = State::Else;
-#endif
-    return true;
-}
-
-bool
-IfEmitter::emitElse()
-{
-    MOZ_ASSERT(state_ == State::ThenElse || state_ == State::Cond);
-
-    if (!emitElseInternal())
-        return false;
 
     // Enclose else-branch with TDZCheckCache.
     if (kind_ == Kind::MayContainLexicalAccessInBranch)
         tdzCache_.emplace(bce_);
 
-#ifdef DEBUG
-    state_ = State::Else;
-#endif
     return true;
 }
 
 bool
-IfEmitter::emitElseIf()
+BranchEmitterBase::emitEndInternal()
 {
-    MOZ_ASSERT(state_ == State::ThenElse);
-
-    if (!emitElseInternal())
-        return false;
-
-    // Enclose cond for else-if with TDZCheckCache.
-    if (kind_ == Kind::MayContainLexicalAccessInBranch)
-        tdzCache_.emplace(bce_);
-
-#ifdef DEBUG
-    state_ = State::ElseIf;
-#endif
-    return true;
-}
-
-bool
-IfEmitter::emitEnd()
-{
-    MOZ_ASSERT(state_ == State::Then || state_ == State::Else);
-    // If there was an else part for the last branch, jumpAroundThen_ is
-    // already fixed up when emitting the else part.
-    MOZ_ASSERT_IF(state_ == State::Then, jumpAroundThen_.offset != -1);
-    MOZ_ASSERT_IF(state_ == State::Else, jumpAroundThen_.offset == -1);
-
     // The end of TDZCheckCache for then or else-clause.
     if (kind_ == Kind::MayContainLexicalAccessInBranch) {
         MOZ_ASSERT(tdzCache_.isSome());
         tdzCache_.reset();
     }
 
     calculateOrCheckPushed();
 
@@ -203,17 +121,178 @@ IfEmitter::emitEnd()
         if (!bce_->emitJumpTargetAndPatch(jumpAroundThen_))
             return false;
     }
 
     // Patch all the jumps around else parts.
     if (!bce_->emitJumpTargetAndPatch(jumpsAroundElse_))
         return false;
 
+    return true;
+}
+
+bool
+IfEmitter::emitIf(const Maybe<uint32_t>& ifPos)
+{
+    MOZ_ASSERT(state_ == State::Start);
+
+    if (ifPos) {
+        // Make sure this code is attributed to the "if" so that it gets a
+        // useful column number, instead of the default 0 value.
+        if (!bce_->updateSourceCoordNotes(*ifPos))
+            return false;
+    }
+
+#ifdef DEBUG
+    state_ = State::If;
+#endif
+    return true;
+}
+
+bool
+IfEmitter::emitThen()
+{
+    MOZ_ASSERT(state_ == State::If || state_ == State::ElseIf);
+    MOZ_ASSERT_IF(state_ == State::ElseIf, tdzCache_.isSome());
+    MOZ_ASSERT_IF(state_ != State::ElseIf, tdzCache_.isNothing());
+
+    if (!emitThenInternal(SRC_IF))
+        return false;
+
+#ifdef DEBUG
+    state_ = State::Then;
+#endif
+    return true;
+}
+
+bool
+IfEmitter::emitThenElse()
+{
+    MOZ_ASSERT(state_ == State::If || state_ == State::ElseIf);
+    MOZ_ASSERT_IF(state_ == State::ElseIf, tdzCache_.isSome());
+    MOZ_ASSERT_IF(state_ != State::ElseIf, tdzCache_.isNothing());
+
+    if (!emitThenInternal(SRC_IF_ELSE))
+        return false;
+
+#ifdef DEBUG
+    state_ = State::ThenElse;
+#endif
+    return true;
+}
+
+bool
+IfEmitter::emitElseIf(const Maybe<uint32_t>& ifPos)
+{
+    MOZ_ASSERT(state_ == State::ThenElse);
+
+    if (!emitElseInternal())
+        return false;
+
+    if (ifPos) {
+        // Make sure this code is attributed to the "if" so that it gets a
+        // useful column number, instead of the default 0 value.
+        if (!bce_->updateSourceCoordNotes(*ifPos))
+            return false;
+    }
+
+#ifdef DEBUG
+    state_ = State::ElseIf;
+#endif
+    return true;
+}
+
+bool
+IfEmitter::emitElse()
+{
+    MOZ_ASSERT(state_ == State::ThenElse);
+
+    if (!emitElseInternal())
+        return false;
+
+#ifdef DEBUG
+    state_ = State::Else;
+#endif
+    return true;
+}
+
+bool
+IfEmitter::emitEnd()
+{
+    MOZ_ASSERT(state_ == State::Then || state_ == State::Else);
+    // If there was an else part for the last branch, jumpAroundThen_ is
+    // already fixed up when emitting the else part.
+    MOZ_ASSERT_IF(state_ == State::Then, jumpAroundThen_.offset != -1);
+    MOZ_ASSERT_IF(state_ == State::Else, jumpAroundThen_.offset == -1);
+
+    if (!emitEndInternal())
+        return false;
+
 #ifdef DEBUG
     state_ = State::End;
 #endif
     return true;
 }
 
 InternalIfEmitter::InternalIfEmitter(BytecodeEmitter* bce)
   : IfEmitter(bce, Kind::NoLexicalAccessInBranch)
+{
+#ifdef DEBUG
+    // Skip emitIf (see the comment above InternalIfEmitter declaration).
+    state_ = State::If;
+#endif
+}
+
+CondEmitter::CondEmitter(BytecodeEmitter* bce)
+  : BranchEmitterBase(bce, Kind::MayContainLexicalAccessInBranch)
 {}
+
+bool
+CondEmitter::emitCond()
+{
+    MOZ_ASSERT(state_ == State::Start);
+#ifdef DEBUG
+    state_ = State::Cond;
+#endif
+    return true;
+}
+
+bool
+CondEmitter::emitThenElse()
+{
+    MOZ_ASSERT(state_ == State::Cond);
+    if (!emitThenInternal(SRC_COND))
+        return false;
+
+#ifdef DEBUG
+    state_ = State::ThenElse;
+#endif
+    return true;
+}
+
+bool
+CondEmitter::emitElse()
+{
+    MOZ_ASSERT(state_ == State::ThenElse);
+
+    if (!emitElseInternal())
+        return false;
+
+#ifdef DEBUG
+    state_ = State::Else;
+#endif
+    return true;
+}
+
+bool
+CondEmitter::emitEnd()
+{
+    MOZ_ASSERT(state_ == State::Else);
+    MOZ_ASSERT(jumpAroundThen_.offset == -1);
+
+    if (!emitEndInternal())
+        return false;
+
+#ifdef DEBUG
+    state_ = State::End;
+#endif
+    return true;
+}
--- a/js/src/frontend/IfEmitter.h
+++ b/js/src/frontend/IfEmitter.h
@@ -16,68 +16,33 @@
 #include "frontend/SourceNotes.h"
 #include "frontend/TDZCheckCache.h"
 
 namespace js {
 namespace frontend {
 
 struct BytecodeEmitter;
 
-// Class for emitting bytecode for blocks like if-then-else.
-//
-// This class can be used to emit single if-then-else block, or cascading
-// else-if blocks.
-//
-// Usage: (check for the return value is omitted for simplicity)
-//
-//   `if (cond) then_block`
-//     IfEmitter ifThen(this);
-//     emit(cond);
-//     ifThen.emitThen();
-//     emit(then_block);
-//     ifThen.emitEnd();
-//
-//   `if (cond) then_block else else_block`
-//     IfEmitter ifThenElse(this);
-//     emit(cond);
-//     ifThenElse.emitThenElse();
-//     emit(then_block);
-//     ifThenElse.emitElse();
-//     emit(else_block);
-//     ifThenElse.emitEnd();
-//
-//   `if (c1) b1 else if (c2) b2 else if (c3) b3 else b4`
-//     IfEmitter ifThenElse(this);
-//     emit(c1);
-//     ifThenElse.emitThenElse();
-//     emit(b1);
-//     ifThenElse.emitElseIf();
-//     emit(c2);
-//     ifThenElse.emitThenElse();
-//     emit(b2);
-//     ifThenElse.emitElseIf();
-//     emit(c3);
-//     ifThenElse.emitThenElse();
-//     emit(b3);
-//     ifThenElse.emitElse();
-//     emit(b4);
-//     ifThenElse.emitEnd();
-//
-//   `cond ? then_expr : else_expr`
-//     IfEmitter condElse(this);
-//     emit(cond);
-//     condElse.emitCond();
-//     emit(then_block);
-//     condElse.emitElse();
-//     emit(else_block);
-//     condElse.emitEnd();
-//
-class MOZ_STACK_CLASS IfEmitter
+class MOZ_STACK_CLASS BranchEmitterBase
 {
-  public:
+  protected:
+    BytecodeEmitter* bce_;
+
+    // Jump around the then clause, to the beginning of the else clause.
+    JumpList jumpAroundThen_;
+
+    // Jump around the else clause, to the end of the entire branch.
+    JumpList jumpsAroundElse_;
+
+    // The stack depth before emitting the then block.
+    // Used for restoring stack depth before emitting the else block.
+    // Also used for assertion to make sure then and else blocks pushed the
+    // same number of values.
+    int32_t thenDepth_ = 0;
+
     // Whether the then-clause, the else-clause, or else-if condition may
     // contain declaration or access to lexical variables, which means they
     // should have their own TDZCheckCache.  Basically TDZCheckCache should be
     // created for each basic block, which then-clause, else-clause, and
     // else-if condition are, but for internally used branches which are
     // known not to touch lexical variables we can skip creating TDZCheckCache
     // for them.
     //
@@ -87,131 +52,253 @@ class MOZ_STACK_CLASS IfEmitter
         // which basically may contain declaration or accesses to lexical
         // variables inside then-clause, else-clause, and else-if condition.
         MayContainLexicalAccessInBranch,
 
         // For internally used branches which don't touch lexical variables
         // inside then-clause, else-clause, nor else-if condition.
         NoLexicalAccessInBranch
     };
-
-  private:
-    BytecodeEmitter* bce_;
-
-    // Jump around the then clause, to the beginning of the else clause.
-    JumpList jumpAroundThen_;
+    Kind kind_;
 
-    // Jump around the else clause, to the end of the entire branch.
-    JumpList jumpsAroundElse_;
-
-    // The stack depth before emitting the then block.
-    // Used for restoring stack depth before emitting the else block.
-    // Also used for assertion to make sure then and else blocks pushed the
-    // same number of values.
-    int32_t thenDepth_;
-
-    Kind kind_;
     mozilla::Maybe<TDZCheckCache> tdzCache_;
 
 #ifdef DEBUG
     // The number of values pushed in the then and else blocks.
-    int32_t pushed_;
-    bool calculatedPushed_;
-
-    // The state of this emitter.
-    //
-    // +-------+   emitCond +------+ emitElse +------+        emitEnd +-----+
-    // | Start |-+--------->| Cond |--------->| Else |------>+------->| End |
-    // +-------+ |          +------+          +------+       ^        +-----+
-    //           |                                           |
-    //           v emitThen +------+                         |
-    //        +->+--------->| Then |------------------------>+
-    //        ^  |          +------+                         ^
-    //        |  |                                           |
-    //        |  |                                           +---+
-    //        |  |                                               |
-    //        |  | emitThenElse +----------+   emitElse +------+ |
-    //        |  +------------->| ThenElse |-+--------->| Else |-+
-    //        |                 +----------+ |          +------+
-    //        |                              |
-    //        |                              | emitElseIf +--------+
-    //        |                              +----------->| ElseIf |-+
-    //        |                                           +--------+ |
-    //        |                                                      |
-    //        +------------------------------------------------------+
-    enum class State {
-        // The initial state.
-        Start,
-
-        // After calling emitThen.
-        Then,
-
-        // After calling emitCond.
-        Cond,
-
-        // After calling emitThenElse.
-        ThenElse,
-
-        // After calling emitElse.
-        Else,
-
-        // After calling emitElseIf.
-        ElseIf,
-
-        // After calling emitEnd.
-        End
-    };
-    State state_;
+    int32_t pushed_ = 0;
+    bool calculatedPushed_ = false;
 #endif
 
   protected:
-    // For InternalIfEmitter.
-    IfEmitter(BytecodeEmitter* bce, Kind kind);
+    BranchEmitterBase(BytecodeEmitter* bce, Kind kind);
+
+    MOZ_MUST_USE bool emitThenInternal(SrcNoteType type);
+    void calculateOrCheckPushed();
+    MOZ_MUST_USE bool emitElseInternal();
+    MOZ_MUST_USE bool emitEndInternal();
 
   public:
-    explicit IfEmitter(BytecodeEmitter* bce);
-
-    MOZ_MUST_USE bool emitThen();
-    MOZ_MUST_USE bool emitCond();
-    MOZ_MUST_USE bool emitThenElse();
-
-    MOZ_MUST_USE bool emitElse();
-    MOZ_MUST_USE bool emitElseIf();
-
-    MOZ_MUST_USE bool emitEnd();
-
 #ifdef DEBUG
     // Returns the number of values pushed onto the value stack inside
     // `then_block` and `else_block`.
     // Can be used in assertion after emitting if-then-else.
     int32_t pushed() const {
         return pushed_;
     }
 
     // Returns the number of values popped onto the value stack inside
     // `then_block` and `else_block`.
     // Can be used in assertion after emitting if-then-else.
     int32_t popped() const {
         return -pushed_;
     }
 #endif
+};
 
-  private:
-    MOZ_MUST_USE bool emitIfInternal(SrcNoteType type);
-    void calculateOrCheckPushed();
-    MOZ_MUST_USE bool emitElseInternal();
+// Class for emitting bytecode for blocks like if-then-else.
+//
+// This class can be used to emit single if-then-else block, or cascading
+// else-if blocks.
+//
+// Usage: (check for the return value is omitted for simplicity)
+//
+//   `if (cond) then_block`
+//     IfEmitter ifThen(this);
+//     ifThen.emitIf(Some(offset_of_if));
+//     emit(cond);
+//     ifThen.emitThen();
+//     emit(then_block);
+//     ifThen.emitEnd();
+//
+//   `if (cond) then_block else else_block`
+//     IfEmitter ifThenElse(this);
+//     ifThen.emitIf(Some(offset_of_if));
+//     emit(cond);
+//     ifThenElse.emitThenElse();
+//     emit(then_block);
+//     ifThenElse.emitElse();
+//     emit(else_block);
+//     ifThenElse.emitEnd();
+//
+//   `if (c1) b1 else if (c2) b2 else if (c3) b3 else b4`
+//     IfEmitter ifThenElse(this);
+//     ifThen.emitIf(Some(offset_of_if));
+//     emit(c1);
+//     ifThenElse.emitThenElse();
+//     emit(b1);
+//     ifThenElse.emitElseIf(Some(offset_of_if));
+//     emit(c2);
+//     ifThenElse.emitThenElse();
+//     emit(b2);
+//     ifThenElse.emitElseIf(Some(offset_of_if));
+//     emit(c3);
+//     ifThenElse.emitThenElse();
+//     emit(b3);
+//     ifThenElse.emitElse();
+//     emit(b4);
+//     ifThenElse.emitEnd();
+//
+class MOZ_STACK_CLASS IfEmitter : public BranchEmitterBase
+{
+  protected:
+#ifdef DEBUG
+    // The state of this emitter.
+    //
+    // +-------+ emitIf +----+
+    // | Start |------->| If |-+
+    // +-------+        +----+ |
+    //                         |
+    //    +--------------------+
+    //    |
+    //    v emitThen +------+                               emitEnd +-----+
+    // +->+--------->| Then |---------------------------->+-------->| End |
+    // ^  |          +------+                             ^         +-----+
+    // |  |                                               |
+    // |  |                                               |
+    // |  |                                               |
+    // |  | emitThenElse +----------+   emitElse +------+ |
+    // |  +------------->| ThenElse |-+--------->| Else |-+
+    // |                 +----------+ |          +------+
+    // |                              |
+    // |                              | emitElseIf +--------+
+    // |                              +----------->| ElseIf |-+
+    // |                                           +--------+ |
+    // |                                                      |
+    // +------------------------------------------------------+
+    enum class State {
+        // The initial state.
+        Start,
+
+        // After calling emitIf.
+        If,
+
+        // After calling emitThen.
+        Then,
+
+        // After calling emitThenElse.
+        ThenElse,
+
+        // After calling emitElse.
+        Else,
+
+        // After calling emitElseIf.
+        ElseIf,
+
+        // After calling emitEnd.
+        End
+    };
+    State state_ = State::Start;
+#endif
+
+  protected:
+    // For InternalIfEmitter.
+    IfEmitter(BytecodeEmitter* bce, Kind kind);
+
+  public:
+    explicit IfEmitter(BytecodeEmitter* bce);
+
+    // `ifPos` is the offset in the source code for the character below:
+    //
+    //   if ( cond ) { ... } else if ( cond2 ) { ... }
+    //   ^                        ^
+    //   |                        |
+    //   |                        ifPos for emitElseIf
+    //   |
+    //   ifPos for emitIf
+    //
+    // Can be Nothing() if not available.
+    MOZ_MUST_USE bool emitIf(const mozilla::Maybe<uint32_t>& ifPos);
+
+    MOZ_MUST_USE bool emitThen();
+    MOZ_MUST_USE bool emitThenElse();
+
+    MOZ_MUST_USE bool emitElseIf(const mozilla::Maybe<uint32_t>& ifPos);
+    MOZ_MUST_USE bool emitElse();
+
+    MOZ_MUST_USE bool emitEnd();
 };
 
 // Class for emitting bytecode for blocks like if-then-else which doesn't touch
 // lexical variables.
 //
 // See the comments above NoLexicalAccessInBranch for more details when to use
 // this instead of IfEmitter.
+// Compared to IfEmitter, this class doesn't have emitIf method, given that
+// it doesn't have syntactic `if`, and also the `cond` value can be already
+// on the stack.
+//
+// Usage: (check for the return value is omitted for simplicity)
+//
+//   `if (cond) then_block else else_block` (effectively)
+//     emit(cond);
+//     InternalIfEmitter ifThenElse(this);
+//     ifThenElse.emitThenElse();
+//     emit(then_block);
+//     ifThenElse.emitElse();
+//     emit(else_block);
+//     ifThenElse.emitEnd();
+//
 class MOZ_STACK_CLASS InternalIfEmitter : public IfEmitter
 {
   public:
     explicit InternalIfEmitter(BytecodeEmitter* bce);
 };
 
+// Class for emitting bytecode for conditional expression.
+//
+// Usage: (check for the return value is omitted for simplicity)
+//
+//   `cond ? then_expr : else_expr`
+//     CondEmitter condElse(this);
+//     condElse.emitCond();
+//     emit(cond);
+//     condElse.emitThenElse();
+//     emit(then_expr);
+//     condElse.emitElse();
+//     emit(else_expr);
+//     condElse.emitEnd();
+//
+class MOZ_STACK_CLASS CondEmitter : public BranchEmitterBase
+{
+#ifdef DEBUG
+    // The state of this emitter.
+    //
+    // +-------+ emitCond +------+ emitThenElse +----------+
+    // | Start |--------->| Cond |------------->| ThenElse |-+
+    // +-------+          +------+              +----------+ |
+    //                                                       |
+    //                                     +-----------------+
+    //                                     |
+    //                                     | emitElse +------+ emitEnd +-----+
+    //                                     +--------->| Else |-------->| End |
+    //                                                +------+         +-----+
+    enum class State {
+        // The initial state.
+        Start,
+
+        // After calling emitCond.
+        Cond,
+
+        // After calling emitThenElse.
+        ThenElse,
+
+        // After calling emitElse.
+        Else,
+
+        // After calling emitEnd.
+        End
+    };
+    State state_ = State::Start;
+#endif
+
+  public:
+    explicit CondEmitter(BytecodeEmitter* bce);
+
+    MOZ_MUST_USE bool emitCond();
+    MOZ_MUST_USE bool emitThenElse();
+    MOZ_MUST_USE bool emitElse();
+    MOZ_MUST_USE bool emitEnd();
+};
+
 } /* namespace frontend */
 } /* namespace js */
 
 #endif /* frontend_IfEmitter_h */