Bug 1130811 - Always use list nodes (albeit in some circumstances with only two elements), and never binary nodes, to represent various binary operations. r=luke
authorJeff Walden <jwalden@mit.edu>
Tue, 10 Feb 2015 00:58:11 -0800
changeset 255654 3b00f60dbd69e3a82c699765967341f6ebb68349
parent 255653 b560b5afe0c48dfc380e4b5d55067bc9ed106df4
child 255655 97e200fe37d479cf8b713c93cc86b313e11ee7a4
push id4610
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:32:55 +0000
treeherdermozilla-beta@4df54044d9ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1130811
milestone38.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 1130811 - Always use list nodes (albeit in some circumstances with only two elements), and never binary nodes, to represent various binary operations. r=luke
js/src/asmjs/AsmJSValidate.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/FoldConstants.cpp
js/src/frontend/FullParseHandler.h
js/src/frontend/ParseNode.cpp
js/src/frontend/ParseNode.h
js/src/frontend/Parser.cpp
js/src/frontend/SyntaxParseHandler.h
js/src/jsreflect.cpp
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -178,16 +178,124 @@ CaseExpr(ParseNode *pn)
 
 static inline ParseNode *
 CaseBody(ParseNode *pn)
 {
     MOZ_ASSERT(pn->isKind(PNK_CASE) || pn->isKind(PNK_DEFAULT));
     return BinaryRight(pn);
 }
 
+static inline ParseNode *
+BinaryOpLeft(ParseNode *pn)
+{
+    MOZ_ASSERT(pn->isBinaryOperation());
+    MOZ_ASSERT(pn->isArity(PN_LIST));
+    MOZ_ASSERT(pn->pn_count == 2);
+    return ListHead(pn);
+}
+
+static inline ParseNode *
+BinaryOpRight(ParseNode *pn)
+{
+    MOZ_ASSERT(pn->isBinaryOperation());
+    MOZ_ASSERT(pn->isArity(PN_LIST));
+    MOZ_ASSERT(pn->pn_count == 2);
+    return NextNode(ListHead(pn));
+}
+
+static inline ParseNode *
+BitwiseLeft(ParseNode *pn)
+{
+    return BinaryOpLeft(pn);
+}
+
+static inline ParseNode *
+BitwiseRight(ParseNode *pn)
+{
+    return BinaryOpRight(pn);
+}
+
+static inline ParseNode *
+MultiplyLeft(ParseNode *pn)
+{
+    MOZ_ASSERT(pn->isKind(PNK_STAR));
+    return BinaryOpLeft(pn);
+}
+
+static inline ParseNode *
+MultiplyRight(ParseNode *pn)
+{
+    MOZ_ASSERT(pn->isKind(PNK_STAR));
+    return BinaryOpRight(pn);
+}
+
+static inline ParseNode *
+AddSubLeft(ParseNode *pn)
+{
+    MOZ_ASSERT(pn->isKind(PNK_ADD) || pn->isKind(PNK_SUB));
+    return BinaryOpLeft(pn);
+}
+
+static inline ParseNode *
+AddSubRight(ParseNode *pn)
+{
+    MOZ_ASSERT(pn->isKind(PNK_ADD) || pn->isKind(PNK_SUB));
+    return BinaryOpRight(pn);
+}
+
+static inline ParseNode *
+DivOrModLeft(ParseNode *pn)
+{
+    MOZ_ASSERT(pn->isKind(PNK_DIV) || pn->isKind(PNK_MOD));
+    return BinaryOpLeft(pn);
+}
+
+static inline ParseNode *
+DivOrModRight(ParseNode *pn)
+{
+    MOZ_ASSERT(pn->isKind(PNK_DIV) || pn->isKind(PNK_MOD));
+    return BinaryOpRight(pn);
+}
+
+static inline ParseNode *
+ComparisonLeft(ParseNode *pn)
+{
+    return BinaryOpLeft(pn);
+}
+
+static inline ParseNode *
+ComparisonRight(ParseNode *pn)
+{
+    return BinaryOpRight(pn);
+}
+
+static inline ParseNode *
+AndOrLeft(ParseNode *pn)
+{
+    return BinaryOpLeft(pn);
+}
+
+static inline ParseNode *
+AndOrRight(ParseNode *pn)
+{
+    return BinaryOpRight(pn);
+}
+
+static inline ParseNode *
+RelationalLeft(ParseNode *pn)
+{
+    return BinaryOpLeft(pn);
+}
+
+static inline ParseNode *
+RelationalRight(ParseNode *pn)
+{
+    return BinaryOpRight(pn);
+}
+
 static inline bool
 IsExpressionStatement(ParseNode *pn)
 {
     return pn->isKind(PNK_SEMI);
 }
 
 static inline ParseNode *
 ExpressionStatementExpr(ParseNode *pn)
@@ -3707,23 +3815,23 @@ CheckGlobalVariableInitConstant(ModuleCo
 }
 
 static bool
 CheckTypeAnnotation(ModuleCompiler &m, ParseNode *coercionNode, AsmJSCoercion *coercion,
                     ParseNode **coercedExpr = nullptr)
 {
     switch (coercionNode->getKind()) {
       case PNK_BITOR: {
-        ParseNode *rhs = BinaryRight(coercionNode);
+        ParseNode *rhs = BitwiseRight(coercionNode);
         uint32_t i;
         if (!IsLiteralInt(m, rhs, &i) || i != 0)
             return m.fail(rhs, "must use |0 for argument/return coercion");
         *coercion = AsmJS_ToInt32;
         if (coercedExpr)
-            *coercedExpr = BinaryLeft(coercionNode);
+            *coercedExpr = BitwiseLeft(coercionNode);
         return true;
       }
       case PNK_POS: {
         *coercion = AsmJS_ToNumber;
         if (coercedExpr)
             *coercedExpr = UnaryKid(coercionNode);
         return true;
       }
@@ -4324,18 +4432,20 @@ IsLiteralOrConstInt(FunctionCompiler &f,
 
     return IsLiteralInt(lit, u32);
 }
 
 static bool
 FoldMaskedArrayIndex(FunctionCompiler &f, ParseNode **indexExpr, int32_t *mask,
                      NeedsBoundsCheck *needsBoundsCheck)
 {
-    ParseNode *indexNode = BinaryLeft(*indexExpr);
-    ParseNode *maskNode = BinaryRight(*indexExpr);
+    MOZ_ASSERT((*indexExpr)->isKind(PNK_BITAND));
+
+    ParseNode *indexNode = BitwiseLeft(*indexExpr);
+    ParseNode *maskNode = BitwiseRight(*indexExpr);
 
     uint32_t mask2;
     if (IsLiteralOrConstInt(f, maskNode, &mask2)) {
         // Flag the access to skip the bounds check if the mask ensures that an 'out of
         // bounds' access can not occur based on the current heap length constraint.
         if (mask2 == 0 ||
             CountLeadingZeroes32(f.m().minHeapLength() - 1) <= CountLeadingZeroes32(mask2)) {
             *needsBoundsCheck = NO_BOUNDS_CHECK;
@@ -4383,26 +4493,27 @@ CheckArrayAccess(FunctionCompiler &f, Pa
 
     // Mask off the low bits to account for the clearing effect of a right shift
     // followed by the left shift implicit in the array access. E.g., H32[i>>2]
     // loses the low two bits.
     int32_t mask = ~(TypedArrayElemSize(*viewType) - 1);
 
     MDefinition *pointerDef;
     if (indexExpr->isKind(PNK_RSH)) {
-        ParseNode *shiftNode = BinaryRight(indexExpr);
-        ParseNode *pointerNode = BinaryLeft(indexExpr);
+        ParseNode *shiftAmountNode = BitwiseRight(indexExpr);
 
         uint32_t shift;
-        if (!IsLiteralInt(f.m(), shiftNode, &shift))
-            return f.failf(shiftNode, "shift amount must be constant");
+        if (!IsLiteralInt(f.m(), shiftAmountNode, &shift))
+            return f.failf(shiftAmountNode, "shift amount must be constant");
 
         unsigned requiredShift = TypedArrayShift(*viewType);
         if (shift != requiredShift)
-            return f.failf(shiftNode, "shift amount must be %u", requiredShift);
+            return f.failf(shiftAmountNode, "shift amount must be %u", requiredShift);
+
+        ParseNode *pointerNode = BitwiseLeft(indexExpr);
 
         if (pointerNode->isKind(PNK_BITAND))
             FoldMaskedArrayIndex(f, &pointerNode, &mask, needsBoundsCheck);
 
         f.enterHeapExpression();
 
         Type pointerType;
         if (!CheckExpr(f, pointerNode, &pointerDef, &pointerType))
@@ -4591,16 +4702,17 @@ CheckAssignName(FunctionCompiler &f, Par
     *type = rhsType;
     return true;
 }
 
 static bool
 CheckAssign(FunctionCompiler &f, ParseNode *assign, MDefinition **def, Type *type)
 {
     MOZ_ASSERT(assign->isKind(PNK_ASSIGN));
+
     ParseNode *lhs = BinaryLeft(assign);
     ParseNode *rhs = BinaryRight(assign);
 
     if (lhs->getKind() == PNK_ELEM)
         return CheckStoreArray(f, lhs, rhs, def, type);
 
     if (lhs->getKind() == PNK_NAME)
         return CheckAssignName(f, lhs, rhs, def, type);
@@ -5089,18 +5201,18 @@ CheckFuncPtrCall(FunctionCompiler &f, Pa
     if (const ModuleCompiler::Global *existing = f.lookupGlobal(name)) {
         if (existing->which() != ModuleCompiler::Global::FuncPtrTable)
             return f.failName(tableNode, "'%s' is not the name of a function-pointer array", name);
     }
 
     if (!indexExpr->isKind(PNK_BITAND))
         return f.fail(indexExpr, "function-pointer table index expression needs & mask");
 
-    ParseNode *indexNode = BinaryLeft(indexExpr);
-    ParseNode *maskNode = BinaryRight(indexExpr);
+    ParseNode *indexNode = BitwiseLeft(indexExpr);
+    ParseNode *maskNode = BitwiseRight(indexExpr);
 
     uint32_t mask;
     if (!IsLiteralInt(f.m(), maskNode, &mask) || mask == UINT32_MAX || !IsPowerOfTwo(mask + 1))
         return f.fail(maskNode, "function-pointer table index mask value must be a power of two minus 1");
 
     MDefinition *indexDef;
     Type indexType;
     if (!CheckExpr(f, indexNode, &indexDef, &indexType))
@@ -6282,18 +6394,18 @@ IsValidIntMultiplyConstant(ModuleCompile
 
     MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad literal");
 }
 
 static bool
 CheckMultiply(FunctionCompiler &f, ParseNode *star, MDefinition **def, Type *type)
 {
     MOZ_ASSERT(star->isKind(PNK_STAR));
-    ParseNode *lhs = BinaryLeft(star);
-    ParseNode *rhs = BinaryRight(star);
+    ParseNode *lhs = MultiplyLeft(star);
+    ParseNode *rhs = MultiplyRight(star);
 
     MDefinition *lhsDef;
     Type lhsType;
     if (!CheckExpr(f, lhs, &lhsDef, &lhsType))
         return false;
 
     MDefinition *rhsDef;
     Type rhsType;
@@ -6325,18 +6437,18 @@ CheckMultiply(FunctionCompiler &f, Parse
 
 static bool
 CheckAddOrSub(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type,
               unsigned *numAddOrSubOut = nullptr)
 {
     JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
 
     MOZ_ASSERT(expr->isKind(PNK_ADD) || expr->isKind(PNK_SUB));
-    ParseNode *lhs = BinaryLeft(expr);
-    ParseNode *rhs = BinaryRight(expr);
+    ParseNode *lhs = AddSubLeft(expr);
+    ParseNode *rhs = AddSubRight(expr);
 
     MDefinition *lhsDef, *rhsDef;
     Type lhsType, rhsType;
     unsigned lhsNumAddOrSub, rhsNumAddOrSub;
 
     if (lhs->isKind(PNK_ADD) || lhs->isKind(PNK_SUB)) {
         if (!CheckAddOrSub(f, lhs, &lhsDef, &lhsType, &lhsNumAddOrSub))
             return false;
@@ -6387,18 +6499,19 @@ CheckAddOrSub(FunctionCompiler &f, Parse
         *numAddOrSubOut = numAddOrSub;
     return true;
 }
 
 static bool
 CheckDivOrMod(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type)
 {
     MOZ_ASSERT(expr->isKind(PNK_DIV) || expr->isKind(PNK_MOD));
-    ParseNode *lhs = BinaryLeft(expr);
-    ParseNode *rhs = BinaryRight(expr);
+
+    ParseNode *lhs = DivOrModLeft(expr);
+    ParseNode *rhs = DivOrModRight(expr);
 
     MDefinition *lhsDef, *rhsDef;
     Type lhsType, rhsType;
     if (!CheckExpr(f, lhs, &lhsDef, &lhsType))
         return false;
     if (!CheckExpr(f, rhs, &rhsDef, &rhsType))
         return false;
 
@@ -6441,18 +6554,19 @@ CheckDivOrMod(FunctionCompiler &f, Parse
                    "%s and %s are given", lhsType.toChars(), rhsType.toChars());
 }
 
 static bool
 CheckComparison(FunctionCompiler &f, ParseNode *comp, MDefinition **def, Type *type)
 {
     MOZ_ASSERT(comp->isKind(PNK_LT) || comp->isKind(PNK_LE) || comp->isKind(PNK_GT) ||
                comp->isKind(PNK_GE) || comp->isKind(PNK_EQ) || comp->isKind(PNK_NE));
-    ParseNode *lhs = BinaryLeft(comp);
-    ParseNode *rhs = BinaryRight(comp);
+
+    ParseNode *lhs = ComparisonLeft(comp);
+    ParseNode *rhs = ComparisonRight(comp);
 
     MDefinition *lhsDef, *rhsDef;
     Type lhsType, rhsType;
     if (!CheckExpr(f, lhs, &lhsDef, &lhsType))
         return false;
     if (!CheckExpr(f, rhs, &rhsDef, &rhsType))
         return false;
 
@@ -6479,18 +6593,18 @@ CheckComparison(FunctionCompiler &f, Par
 
     return f.failf(comp, "arguments to a comparison must both be signed, unsigned, floats or doubles; "
                    "%s and %s are given", lhsType.toChars(), rhsType.toChars());
 }
 
 static bool
 CheckBitwise(FunctionCompiler &f, ParseNode *bitwise, MDefinition **def, Type *type)
 {
-    ParseNode *lhs = BinaryLeft(bitwise);
-    ParseNode *rhs = BinaryRight(bitwise);
+    ParseNode *lhs = BitwiseLeft(bitwise);
+    ParseNode *rhs = BitwiseRight(bitwise);
 
     int32_t identityElement;
     bool onlyOnRight;
     switch (bitwise->getKind()) {
       case PNK_BITOR:  identityElement = 0;  onlyOnRight = false; *type = Type::Signed;   break;
       case PNK_BITAND: identityElement = -1; onlyOnRight = false; *type = Type::Signed;   break;
       case PNK_BITXOR: identityElement = 0;  onlyOnRight = false; *type = Type::Signed;   break;
       case PNK_LSH:    identityElement = 0;  onlyOnRight = true;  *type = Type::Signed;   break;
@@ -7203,58 +7317,58 @@ CheckByteLengthCall(ModuleCompiler &m, P
 
     return true;
 }
 
 static bool
 CheckHeapLengthCondition(ModuleCompiler &m, ParseNode *cond, PropertyName *newBufferName,
                          uint32_t *mask, uint32_t *minLength, uint32_t *maxLength)
 {
-    if (!cond->isKind(PNK_OR) || !BinaryLeft(cond)->isKind(PNK_OR))
+    if (!cond->isKind(PNK_OR) || !AndOrLeft(cond)->isKind(PNK_OR))
         return m.fail(cond, "expecting byteLength & K || byteLength <= L || byteLength > M");
 
-    ParseNode *cond1 = BinaryLeft(BinaryLeft(cond));
-    ParseNode *cond2 = BinaryRight(BinaryLeft(cond));
-    ParseNode *cond3 = BinaryRight(cond);
+    ParseNode *cond1 = AndOrLeft(AndOrLeft(cond));
+    ParseNode *cond2 = AndOrRight(AndOrLeft(cond));
+    ParseNode *cond3 = AndOrRight(cond);
 
     if (!cond1->isKind(PNK_BITAND))
         return m.fail(cond1, "expecting byteLength & K");
 
-    if (!CheckByteLengthCall(m, BinaryLeft(cond1), newBufferName))
-        return false;
-
-    ParseNode *maskNode = BinaryRight(cond1);
+    if (!CheckByteLengthCall(m, BitwiseLeft(cond1), newBufferName))
+        return false;
+
+    ParseNode *maskNode = BitwiseRight(cond1);
     if (!IsLiteralInt(m, maskNode, mask))
         return m.fail(maskNode, "expecting integer literal mask");
     if ((*mask & 0xffffff) != 0xffffff)
         return m.fail(maskNode, "mask value must have the bits 0xffffff set");
 
     if (!cond2->isKind(PNK_LE))
         return m.fail(cond2, "expecting byteLength <= L");
 
-    if (!CheckByteLengthCall(m, BinaryLeft(cond2), newBufferName))
-        return false;
-
-    ParseNode *minLengthNode = BinaryRight(cond2);
+    if (!CheckByteLengthCall(m, RelationalLeft(cond2), newBufferName))
+        return false;
+
+    ParseNode *minLengthNode = RelationalRight(cond2);
     uint32_t minLengthExclusive;
     if (!IsLiteralInt(m, minLengthNode, &minLengthExclusive))
         return m.fail(minLengthNode, "expecting integer literal");
     if (minLengthExclusive < 0xffffff)
         return m.fail(minLengthNode, "literal must be >= 0xffffff");
 
     // Add one to convert from exclusive (the branch rejects if ==) to inclusive.
     *minLength = minLengthExclusive + 1;
 
     if (!cond3->isKind(PNK_GT))
         return m.fail(cond3, "expecting byteLength > M");
 
-    if (!CheckByteLengthCall(m, BinaryLeft(cond3), newBufferName))
-        return false;
-
-    ParseNode *maxLengthNode = BinaryRight(cond3);
+    if (!CheckByteLengthCall(m, RelationalLeft(cond3), newBufferName))
+        return false;
+
+    ParseNode *maxLengthNode = RelationalRight(cond3);
     if (!IsLiteralInt(m, maxLengthNode, maxLength))
         return m.fail(maxLengthNode, "expecting integer literal");
     if (*maxLength > 0x80000000)
         return m.fail(maxLengthNode, "literal must be <= 0x80000000");
 
     if (*maxLength < *minLength)
         return m.fail(maxLengthNode, "maximum length must be greater or equal to minimum length");
 
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2034,25 +2034,20 @@ CheckSideEffects(ExclusiveContext *cx, B
                 if (!CheckSideEffects(cx, bce, pn->pn_right, answer))
                     return false;
                 if (!*answer && (!pn->isOp(JSOP_NOP) || !pn2->isConst()))
                     *answer = true;
             }
             return true;
         }
 
-        if (pn->isOp(JSOP_OR) || pn->isOp(JSOP_AND) || pn->isOp(JSOP_STRICTEQ) ||
-            pn->isOp(JSOP_STRICTNE)) {
-            /*
-             * ||, &&, ===, and !== do not convert their operands via
-             * toString or valueOf method calls.
-             */
-            return CheckSideEffects(cx, bce, pn->pn_left, answer) &&
-                   CheckSideEffects(cx, bce, pn->pn_right, answer);
-        }
+        MOZ_ASSERT(!pn->isOp(JSOP_OR), "|| produces a list now");
+        MOZ_ASSERT(!pn->isOp(JSOP_AND), "&& produces a list now");
+        MOZ_ASSERT(!pn->isOp(JSOP_STRICTEQ), "=== produces a list now");
+        MOZ_ASSERT(!pn->isOp(JSOP_STRICTNE), "!== produces a list now");
 
         /*
          * We can't easily prove that neither operand ever denotes an
          * object with a toString or valueOf method.
          */
         *answer = true;
         return true;
 
@@ -6326,46 +6321,28 @@ EmitCallOrNew(ExclusiveContext *cx, Byte
             return false;
     }
     return true;
 }
 
 static bool
 EmitLogical(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
+    MOZ_ASSERT(pn->isArity(PN_LIST));
+
     /*
      * JSOP_OR converts the operand on the stack to boolean, leaves the original
      * value on the stack and jumps if true; otherwise it falls into the next
      * bytecode, which pops the left operand and then evaluates the right operand.
      * The jump goes around the right operand evaluation.
      *
      * JSOP_AND converts the operand on the stack to boolean and jumps if false;
      * otherwise it falls into the right operand's bytecode.
      */
 
-    if (pn->isArity(PN_BINARY)) {
-        if (!EmitTree(cx, bce, pn->pn_left))
-            return false;
-        ptrdiff_t top = EmitJump(cx, bce, JSOP_BACKPATCH, 0);
-        if (top < 0)
-            return false;
-        if (Emit1(cx, bce, JSOP_POP) < 0)
-            return false;
-        if (!EmitTree(cx, bce, pn->pn_right))
-            return false;
-        ptrdiff_t off = bce->offset();
-        jsbytecode *pc = bce->code(top);
-        SET_JUMP_OFFSET(pc, off - top);
-        *pc = pn->getOp();
-        return true;
-    }
-
-    MOZ_ASSERT(pn->isArity(PN_LIST));
-    MOZ_ASSERT(pn->pn_head->pn_next->pn_next);
-
     /* Left-associative operator chain: avoid too much recursion. */
     ParseNode *pn2 = pn->pn_head;
     if (!EmitTree(cx, bce, pn2))
         return false;
     ptrdiff_t top = EmitJump(cx, bce, JSOP_BACKPATCH, 0);
     if (top < 0)
         return false;
     if (Emit1(cx, bce, JSOP_POP) < 0)
@@ -7133,39 +7110,31 @@ frontend::EmitTree(ExclusiveContext *cx,
       case PNK_GE:
       case PNK_IN:
       case PNK_INSTANCEOF:
       case PNK_LSH:
       case PNK_RSH:
       case PNK_URSH:
       case PNK_STAR:
       case PNK_DIV:
-      case PNK_MOD:
-        if (pn->isArity(PN_LIST)) {
-            /* Left-associative operator chain: avoid too much recursion. */
-            ParseNode *pn2 = pn->pn_head;
-            if (!EmitTree(cx, bce, pn2))
-                return false;
-            JSOp op = pn->getOp();
-            while ((pn2 = pn2->pn_next) != nullptr) {
-                if (!EmitTree(cx, bce, pn2))
-                    return false;
-                if (Emit1(cx, bce, op) < 0)
-                    return false;
-            }
-        } else {
-            /* Binary operators that evaluate both operands unconditionally. */
-            if (!EmitTree(cx, bce, pn->pn_left))
-                return false;
-            if (!EmitTree(cx, bce, pn->pn_right))
-                return false;
-            if (Emit1(cx, bce, pn->getOp()) < 0)
+      case PNK_MOD: {
+        MOZ_ASSERT(pn->isArity(PN_LIST));
+        /* Left-associative operator chain: avoid too much recursion. */
+        ParseNode *subexpr = pn->pn_head;
+        if (!EmitTree(cx, bce, subexpr))
+            return false;
+        JSOp op = pn->getOp();
+        while ((subexpr = subexpr->pn_next) != nullptr) {
+            if (!EmitTree(cx, bce, subexpr))
+                return false;
+            if (Emit1(cx, bce, op) < 0)
                 return false;
         }
         break;
+      }
 
       case PNK_THROW:
       case PNK_TYPEOF:
       case PNK_VOID:
       case PNK_NOT:
       case PNK_BITNOT:
       case PNK_POS:
       case PNK_NEG:
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -820,102 +820,57 @@ Fold(ExclusiveContext *cx, ParseNode **p
         }
         if (pn3 && pn3 != pn2)
             handler.freeTree(pn3);
         break;
 
       case PNK_OR:
       case PNK_AND:
         if (sc == SyntacticContext::Condition) {
-            if (pn->isArity(PN_LIST)) {
-                ParseNode **listp = &pn->pn_head;
-                MOZ_ASSERT(*listp == pn1);
-                uint32_t orig = pn->pn_count;
-                do {
-                    Truthiness t = Boolish(pn1);
-                    if (t == Unknown) {
-                        listp = &pn1->pn_next;
-                        continue;
+            ParseNode **listp = &pn->pn_head;
+            MOZ_ASSERT(*listp == pn1);
+            uint32_t orig = pn->pn_count;
+            do {
+                Truthiness t = Boolish(pn1);
+                if (t == Unknown) {
+                    listp = &pn1->pn_next;
+                    continue;
+                }
+                if ((t == Truthy) == pn->isKind(PNK_OR)) {
+                    for (pn2 = pn1->pn_next; pn2; pn2 = pn3) {
+                        pn3 = pn2->pn_next;
+                        handler.freeTree(pn2);
+                        --pn->pn_count;
                     }
-                    if ((t == Truthy) == pn->isKind(PNK_OR)) {
-                        for (pn2 = pn1->pn_next; pn2; pn2 = pn3) {
-                            pn3 = pn2->pn_next;
-                            handler.freeTree(pn2);
-                            --pn->pn_count;
-                        }
-                        pn1->pn_next = nullptr;
-                        break;
-                    }
-                    MOZ_ASSERT((t == Truthy) == pn->isKind(PNK_AND));
-                    if (pn->pn_count == 1)
-                        break;
-                    *listp = pn1->pn_next;
-                    handler.freeTree(pn1);
-                    --pn->pn_count;
-                } while ((pn1 = *listp) != nullptr);
+                    pn1->pn_next = nullptr;
+                    break;
+                }
+                MOZ_ASSERT((t == Truthy) == pn->isKind(PNK_AND));
+                if (pn->pn_count == 1)
+                    break;
+                *listp = pn1->pn_next;
+                handler.freeTree(pn1);
+                --pn->pn_count;
+            } while ((pn1 = *listp) != nullptr);
 
-                // We may have to change arity from LIST to BINARY.
-                pn1 = pn->pn_head;
-                if (pn->pn_count == 2) {
-                    pn2 = pn1->pn_next;
-                    pn1->pn_next = nullptr;
-                    MOZ_ASSERT(!pn2->pn_next);
-                    pn->setArity(PN_BINARY);
-                    pn->pn_left = pn1;
-                    pn->pn_right = pn2;
-                } else if (pn->pn_count == 1) {
-                    ReplaceNode(pnp, pn1);
-                    pn = pn1;
-                } else if (orig != pn->pn_count) {
-                    // Adjust list tail.
-                    pn2 = pn1->pn_next;
-                    for (; pn1; pn2 = pn1, pn1 = pn1->pn_next)
-                        ;
-                    pn->pn_tail = &pn2->pn_next;
-                }
-            } else {
-                Truthiness t = Boolish(pn1);
-                if (t != Unknown) {
-                    if ((t == Truthy) == pn->isKind(PNK_OR)) {
-                        handler.freeTree(pn2);
-                        ReplaceNode(pnp, pn1);
-                        pn = pn1;
-                    } else {
-                        MOZ_ASSERT((t == Truthy) == pn->isKind(PNK_AND));
-                        handler.freeTree(pn1);
-                        ReplaceNode(pnp, pn2);
-                        pn = pn2;
-                    }
-                }
+            // We may have to replace a one-element list with its element.
+            pn1 = pn->pn_head;
+            if (pn->pn_count == 1) {
+                ReplaceNode(pnp, pn1);
+                pn = pn1;
+            } else if (orig != pn->pn_count) {
+                // Adjust list tail.
+                pn2 = pn1->pn_next;
+                for (; pn1; pn2 = pn1, pn1 = pn1->pn_next)
+                    continue;
+                pn->pn_tail = &pn2->pn_next;
             }
         }
         break;
 
-      case PNK_SUBASSIGN:
-      case PNK_BITORASSIGN:
-      case PNK_BITXORASSIGN:
-      case PNK_BITANDASSIGN:
-      case PNK_LSHASSIGN:
-      case PNK_RSHASSIGN:
-      case PNK_URSHASSIGN:
-      case PNK_MULASSIGN:
-      case PNK_DIVASSIGN:
-      case PNK_MODASSIGN:
-        /*
-         * Compound operators such as *= should be subject to folding, in case
-         * the left-hand side is constant, and so that the decompiler produces
-         * the same string that you get from decompiling a script or function
-         * compiled from that same string.  += is special and so must be
-         * handled below.
-         */
-        goto do_binary_op;
-
-      case PNK_ADDASSIGN:
-        MOZ_ASSERT(pn->isOp(JSOP_ADD));
-        /* FALL THROUGH */
       case PNK_ADD:
         if (pn->isArity(PN_LIST)) {
             bool folded = false;
 
             pn2 = pn1->pn_next;
             if (pn1->isKind(PNK_NUMBER)) {
                 // Fold addition of numeric literals: (1 + 2 + x === 3 + x).
                 // Note that we can only do this the front of the list:
@@ -1024,58 +979,52 @@ Fold(ExclusiveContext *cx, ParseNode **p
             pn->setOp(JSOP_STRING);
             pn->setArity(PN_NULLARY);
             handler.freeTree(pn1);
             handler.freeTree(pn2);
             break;
         }
 
         /* Can't concatenate string literals, let's try numbers. */
-        goto do_binary_op;
+        if (!FoldType(cx, pn1, PNK_NUMBER) || !FoldType(cx, pn2, PNK_NUMBER))
+            return false;
+        if (pn1->isKind(PNK_NUMBER) && pn2->isKind(PNK_NUMBER)) {
+            if (!FoldBinaryNumeric(cx, pn->getOp(), pn1, pn2, pn))
+                return false;
+        }
+        break;
 
       case PNK_SUB:
       case PNK_STAR:
       case PNK_LSH:
       case PNK_RSH:
       case PNK_URSH:
       case PNK_DIV:
       case PNK_MOD:
-      do_binary_op:
-        if (pn->isArity(PN_LIST)) {
-            MOZ_ASSERT(pn->pn_count > 2);
-            for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
-                if (!FoldType(cx, pn2, PNK_NUMBER))
-                    return false;
-            }
-            for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
-                /* XXX fold only if all operands convert to number */
-                if (!pn2->isKind(PNK_NUMBER))
-                    break;
-            }
-            if (!pn2) {
-                JSOp op = pn->getOp();
+        MOZ_ASSERT(pn->getArity() == PN_LIST);
+        MOZ_ASSERT(pn->pn_count >= 2);
+        for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
+            if (!FoldType(cx, pn2, PNK_NUMBER))
+                return false;
+        }
+        for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
+            /* XXX fold only if all operands convert to number */
+            if (!pn2->isKind(PNK_NUMBER))
+                break;
+        }
+        if (!pn2) {
+            JSOp op = pn->getOp();
 
-                pn2 = pn1->pn_next;
+            pn2 = pn1->pn_next;
+            pn3 = pn2->pn_next;
+            if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn))
+                return false;
+            while ((pn2 = pn3) != nullptr) {
                 pn3 = pn2->pn_next;
-                if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn))
-                    return false;
-                while ((pn2 = pn3) != nullptr) {
-                    pn3 = pn2->pn_next;
-                    if (!FoldBinaryNumeric(cx, op, pn, pn2, pn))
-                        return false;
-                }
-            }
-        } else {
-            MOZ_ASSERT(pn->isArity(PN_BINARY));
-            if (!FoldType(cx, pn1, PNK_NUMBER) ||
-                !FoldType(cx, pn2, PNK_NUMBER)) {
-                return false;
-            }
-            if (pn1->isKind(PNK_NUMBER) && pn2->isKind(PNK_NUMBER)) {
-                if (!FoldBinaryNumeric(cx, pn->getOp(), pn1, pn2, pn))
+                if (!FoldBinaryNumeric(cx, op, pn, pn2, pn))
                     return false;
             }
         }
         break;
 
       case PNK_TYPEOF:
       case PNK_VOID:
       case PNK_NOT:
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -212,20 +212,20 @@ class FullParseHandler
                          JSOp op = JSOP_NOP) {
         return new_<BinaryNode>(kind, op, left->pn_pos, left, (ParseNode *) nullptr);
     }
     ParseNode *newBinary(ParseNodeKind kind, ParseNode *left, ParseNode *right,
                          JSOp op = JSOP_NOP) {
         TokenPos pos(left->pn_pos.begin, right->pn_pos.end);
         return new_<BinaryNode>(kind, op, pos, left, right);
     }
-    ParseNode *newBinaryOrAppend(ParseNodeKind kind, ParseNode *left, ParseNode *right,
-                                 ParseContext<FullParseHandler> *pc, JSOp op = JSOP_NOP)
+    ParseNode *appendOrCreateList(ParseNodeKind kind, ParseNode *left, ParseNode *right,
+                                  ParseContext<FullParseHandler> *pc, JSOp op = JSOP_NOP)
     {
-        return ParseNode::newBinaryOrAppend(kind, op, left, right, this, pc, foldConstants);
+        return ParseNode::appendOrCreateList(kind, op, left, right, this, pc, foldConstants);
     }
 
     ParseNode *newTernary(ParseNodeKind kind,
                           ParseNode *first, ParseNode *second, ParseNode *third,
                           JSOp op = JSOP_NOP)
     {
         return new_<TernaryNode>(kind, op, first, second, third);
     }
@@ -554,17 +554,17 @@ class FullParseHandler
             return nullptr;
         letBlock->pn_pos = pos;
         return letBlock;
     }
 
     ParseNode *newAssignment(ParseNodeKind kind, ParseNode *lhs, ParseNode *rhs,
                              ParseContext<FullParseHandler> *pc, JSOp op)
     {
-        return newBinaryOrAppend(kind, lhs, rhs, pc, op);
+        return newBinary(kind, lhs, rhs, op);
     }
 
     bool isUnparenthesizedYieldExpression(ParseNode *node) {
         return node->isKind(PNK_YIELD) && !node->isInParens();
     }
 
     bool isUnparenthesizedCommaExpression(ParseNode *node) {
         return node->isKind(PNK_COMMA) && !node->isInParens();
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -240,66 +240,49 @@ ParseNodeAllocator::allocNode()
 
     void *p = alloc.alloc(sizeof (ParseNode));
     if (!p)
         js_ReportOutOfMemory(cx);
     return p;
 }
 
 ParseNode *
-ParseNode::append(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right,
-                  FullParseHandler *handler)
+ParseNode::appendOrCreateList(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right,
+                              FullParseHandler *handler, ParseContext<FullParseHandler> *pc,
+                              bool foldConstants)
 {
-    if (!left || !right)
-        return nullptr;
-
-    MOZ_ASSERT(left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC));
+    // The asm.js specification is written in ECMAScript grammar terms that
+    // specify *only* a binary tree.  It's a royal pain to implement the asm.js
+    // spec to act upon n-ary lists as created below.  So for asm.js, form a
+    // binary tree of lists exactly as ECMAScript would by skipping the
+    // following optimization.
+    if (!pc->useAsmOrInsideUseAsm()) {
+        // Left-associative trees of a given operator (e.g. |a + b + c|) are
+        // binary trees in the spec: (+ (+ a b) c) in Lisp terms.  Recursively
+        // processing such a tree, exactly implemented that way, would blow the
+        // the stack.  We use a list node that uses O(1) stack to represent
+        // such operations: (+ a b c).
+        if (left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
+            ListNode *list = &left->as<ListNode>();
 
-    ListNode *list;
-    if (left->pn_arity == PN_LIST) {
-        list = &left->as<ListNode>();
-    } else {
-        ParseNode *pn1 = left->pn_left, *pn2 = left->pn_right;
-        list = handler->new_<ListNode>(kind, op, pn1);
-        if (!list)
-            return nullptr;
-        list->append(pn2);
+            list->append(right);
+            list->pn_pos.end = right->pn_pos.end;
+
+            return list;
+        }
     }
 
+    ParseNode *list = handler->new_<ListNode>(kind, op, left);
+    if (!list)
+        return nullptr;
+
     list->append(right);
-    list->pn_pos.end = right->pn_pos.end;
-
     return list;
 }
 
-ParseNode *
-ParseNode::newBinaryOrAppend(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right,
-                             FullParseHandler *handler, ParseContext<FullParseHandler> *pc,
-                             bool foldConstants)
-{
-    if (!left || !right)
-        return nullptr;
-
-    /*
-     * Ensure that the parse tree is faithful to the source when "use asm" (for
-     * the purpose of type checking).
-     */
-    if (pc->useAsmOrInsideUseAsm())
-        return handler->new_<BinaryNode>(kind, op, left, right);
-
-    /*
-     * Flatten a left-associative (left-heavy) tree of a given operator into
-     * a list to reduce js::FoldConstants and js::frontend::EmitTree recursion.
-     */
-    if (left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC))
-        return append(kind, op, left, right, handler);
-
-    return handler->new_<BinaryNode>(kind, op, left, right);
-}
-
 const char *
 Definition::kindString(Kind kind)
 {
     static const char * const table[] = {
         "", js_var_str, js_const_str, js_const_str, js_let_str, "argument", js_function_str, "unknown"
     };
 
     MOZ_ASSERT(unsigned(kind) <= unsigned(ARG));
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -505,16 +505,21 @@ class ParseNode
     bool isArity(ParseNodeArity a) const   { return getArity() == a; }
     void setArity(ParseNodeArity a)        { pn_arity = a; }
 
     bool isAssignment() const {
         ParseNodeKind kind = getKind();
         return PNK_ASSIGNMENT_START <= kind && kind <= PNK_ASSIGNMENT_LAST;
     }
 
+    bool isBinaryOperation() const {
+        ParseNodeKind kind = getKind();
+        return PNK_BINOP_FIRST <= kind && kind <= PNK_BINOP_LAST;
+    }
+
     /* Boolean attributes. */
     bool isInParens() const                { return pn_parens; }
     bool isLikelyIIFE() const              { return isInParens(); }
     void setInParens(bool enabled)         { pn_parens = enabled; }
     bool isUsed() const                    { return pn_used; }
     void setUsed(bool enabled)             { pn_used = enabled; }
     bool isDefn() const                    { return pn_defn; }
     void setDefn(bool enabled)             { pn_defn = enabled; }
@@ -636,31 +641,23 @@ class ParseNode
         pn_parens = false;
         MOZ_ASSERT(!pn_used);
         MOZ_ASSERT(!pn_defn);
         pn_next = pn_link = nullptr;
     }
 
   public:
     /*
-     * Append right to left, forming a list node.  |left| must have the given
-     * kind and op, and op must be left-associative.
+     * If |left| is a list of the given kind/left-associative op, append
+     * |right| to it and return |left|.  Otherwise return a [left, right] list.
      */
     static ParseNode *
-    append(ParseNodeKind tt, JSOp op, ParseNode *left, ParseNode *right, FullParseHandler *handler);
-
-    /*
-     * Either append right to left, if left meets the conditions necessary to
-     * append (see append), or form a binary node whose children are right and
-     * left.
-     */
-    static ParseNode *
-    newBinaryOrAppend(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right,
-                      FullParseHandler *handler, ParseContext<FullParseHandler> *pc,
-                      bool foldConstants);
+    appendOrCreateList(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right,
+                       FullParseHandler *handler, ParseContext<FullParseHandler> *pc,
+                       bool foldConstants);
 
     inline PropertyName *name() const;
     inline JSAtom *atom() const;
 
     /*
      * The pn_expr and lexdef members are arms of an unsafe union. Unless you
      * know exactly what you're doing, use only the following methods to access
      * them. For less overhead and assertions for protection, use pn->expr()
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -3876,17 +3876,17 @@ Parser<ParseHandler>::variables(ParseNod
 
                 Node init = assignExpr();
                 if (!init)
                     return null();
 
                 if (!bindBeforeInitializer && !checkDestructuring(&data, pn2))
                     return null();
 
-                pn2 = handler.newBinaryOrAppend(PNK_ASSIGN, pn2, init, pc);
+                pn2 = handler.newBinary(PNK_ASSIGN, pn2, init);
                 if (!pn2)
                     return null();
                 handler.addList(pn, pn2);
                 break;
             }
 
             if (tt != TOK_NAME) {
                 if (tt == TOK_YIELD) {
@@ -6078,17 +6078,17 @@ Parser<ParseHandler>::orExpr1(InvokedPre
         // The >= in this condition works because all the operators in question
         // are left-associative; if any were not, the case where two operators
         // have equal precedence would need to be handled specially, and the
         // stack would need to be a Vector.
         while (depth > 0 && Precedence(kindStack[depth - 1]) >= Precedence(pnk)) {
             depth--;
             ParseNodeKind combiningPnk = kindStack[depth];
             JSOp combiningOp = BinaryOpParseNodeKindToJSOp(combiningPnk);
-            pn = handler.newBinaryOrAppend(combiningPnk, nodeStack[depth], pn, pc, combiningOp);
+            pn = handler.appendOrCreateList(combiningPnk, nodeStack[depth], pn, pc, combiningOp);
             if (!pn)
                 return pn;
         }
 
         if (pnk == PNK_LIMIT)
             break;
 
         nodeStack[depth] = pn;
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -158,18 +158,18 @@ class SyntaxParseHandler
         return NodeGeneric;
     }
 
     Node newBinary(ParseNodeKind kind, JSOp op = JSOP_NOP) { return NodeGeneric; }
     Node newBinary(ParseNodeKind kind, Node left, JSOp op = JSOP_NOP) { return NodeGeneric; }
     Node newBinary(ParseNodeKind kind, Node left, Node right, JSOp op = JSOP_NOP) {
         return NodeGeneric;
     }
-    Node newBinaryOrAppend(ParseNodeKind kind, Node left, Node right,
-                           ParseContext<SyntaxParseHandler> *pc, JSOp op = JSOP_NOP) {
+    Node appendOrCreateList(ParseNodeKind kind, Node left, Node right,
+                            ParseContext<SyntaxParseHandler> *pc, JSOp op = JSOP_NOP) {
         return NodeGeneric;
     }
 
     Node newTernary(ParseNodeKind kind, Node first, Node second, Node third, JSOp op = JSOP_NOP) {
         return NodeGeneric;
     }
 
     // Expressions
@@ -287,17 +287,17 @@ class SyntaxParseHandler
         MOZ_ASSERT(list == NodeGeneric || list == NodeUnparenthesizedCommaExpr);
     }
 
     Node newAssignment(ParseNodeKind kind, Node lhs, Node rhs,
                        ParseContext<SyntaxParseHandler> *pc, JSOp op)
     {
         if (kind == PNK_ASSIGN)
             return NodeUnparenthesizedAssignment;
-        return newBinaryOrAppend(kind, lhs, rhs, pc, op);
+        return newBinary(kind, lhs, rhs, op);
     }
 
     bool isUnparenthesizedYieldExpression(Node node) {
         return node == NodeUnparenthesizedYieldExpr;
     }
 
     bool isUnparenthesizedCommaExpression(Node node) {
         return node == NodeUnparenthesizedCommaExpr;
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -2751,28 +2751,17 @@ ASTSerializer::expression(ParseNode *pn,
         return expression(pn->pn_kid1, &test) &&
                expression(pn->pn_kid2, &cons) &&
                expression(pn->pn_kid3, &alt) &&
                builder.conditionalExpression(test, cons, alt, &pn->pn_pos, dst);
       }
 
       case PNK_OR:
       case PNK_AND:
-      {
-        if (pn->isArity(PN_BINARY)) {
-            MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
-            MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
-
-            RootedValue left(cx), right(cx);
-            return expression(pn->pn_left, &left) &&
-                   expression(pn->pn_right, &right) &&
-                   builder.logicalExpression(pn->isKind(PNK_OR), left, right, &pn->pn_pos, dst);
-        }
         return leftAssociate(pn, dst);
-      }
 
       case PNK_PREINCREMENT:
       case PNK_PREDECREMENT:
       {
         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos));
 
         bool inc = pn->isKind(PNK_PREINCREMENT);
         RootedValue expr(cx);
@@ -2832,28 +2821,16 @@ ASTSerializer::expression(ParseNode *pn,
       case PNK_STAR:
       case PNK_DIV:
       case PNK_MOD:
       case PNK_BITOR:
       case PNK_BITXOR:
       case PNK_BITAND:
       case PNK_IN:
       case PNK_INSTANCEOF:
-        if (pn->isArity(PN_BINARY)) {
-            MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
-            MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
-
-            BinaryOperator op = binop(pn->getKind(), pn->getOp());
-            LOCAL_ASSERT(op > BINOP_ERR && op < BINOP_LIMIT);
-
-            RootedValue left(cx), right(cx);
-            return expression(pn->pn_left, &left) &&
-                   expression(pn->pn_right, &right) &&
-                   builder.binaryExpression(op, left, right, &pn->pn_pos, dst);
-        }
         return leftAssociate(pn, dst);
 
       case PNK_DELETE:
       case PNK_TYPEOF:
       case PNK_VOID:
       case PNK_NOT:
       case PNK_BITNOT:
       case PNK_POS: