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 242205 3b00f60dbd69e3a82c699765967341f6ebb68349
parent 242204 b560b5afe0c48dfc380e4b5d55067bc9ed106df4
child 242206 97e200fe37d479cf8b713c93cc86b313e11ee7a4
push id649
push userwcosta@mozilla.com
push dateWed, 11 Feb 2015 16:57:44 +0000
reviewersluke
bugs1130811
milestone38.0a1
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: