Merge inbound to mozilla-central. a=merge
authorCiure Andrei <aciure@mozilla.com>
Wed, 24 Apr 2019 01:15:47 +0300
changeset 470540 4b0811d7b8e1591d4e48a26bdb8666a1bc5ccf61
parent 470533 0ce3633f8b80b9bcb69165f6ec4dc97f19fa8f0a (current diff)
parent 470539 5e18acc4a42b127d90ef83658aa132bb139d7483 (diff)
child 470571 ab21aa4e92f6063ccc9b51a725ce49e2040055af
child 470932 0f9df9c293c3368e51296197cf94a945c1689812
push id35907
push useraciure@mozilla.com
push dateTue, 23 Apr 2019 22:16:10 +0000
treeherdermozilla-central@4b0811d7b8e1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone68.0a1
first release with
nightly linux32
4b0811d7b8e1 / 68.0a1 / 20190423221610 / files
nightly linux64
4b0811d7b8e1 / 68.0a1 / 20190423221610 / files
nightly mac
4b0811d7b8e1 / 68.0a1 / 20190423221610 / files
nightly win32
4b0811d7b8e1 / 68.0a1 / 20190423221610 / files
nightly win64
4b0811d7b8e1 / 68.0a1 / 20190423221610 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/select-int32.js
@@ -0,0 +1,123 @@
+
+// Checks that optimised handling, via Ion, of select-of-I32-values as driven
+// by comparison-of-I32-values, works correctly.  See bug 1532286 comment 7.
+
+function moduleText(cmp_kind /* "eq", "ne", "le_u" etc */)
+{
+  return `(module
+          (func (export "cmpI32_selI32")
+              (result i32)
+              (param i32) (param i32) (param i32) (param i32)
+              (select (get_local 2)
+                      (get_local 3)
+                      (i32.${cmp_kind} (get_local 0) (get_local 1))
+              )
+          ))`;
+}
+
+// Make sure both endpoints and the middlepoint are covered, with one value
+// either side, so as to expose both signedness and off-by-one failures.
+// Ditto quarter points for good measure.
+const vals = [0x0, 0x1,
+              0x3fffffff, 0x40000000, 0x40000001,
+              0x7fffffff, 0x80000000, 0x80000001,
+              0xbfffffff, 0xC0000000, 0xC0000001,
+              0xfffffffe, 0xffffffff];
+
+const testNames
+      = ["eq", "ne", "lt_s", "lt_u", "gt_s", "gt_u",
+                     "le_s", "le_u", "ge_s", "ge_u"];
+
+const expected_eq
+      = [0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,
+         1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,
+         0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,
+         1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,
+         0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0];
+
+const expected_ne
+      = [1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,
+         0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,
+         1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,
+         0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,
+         1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1];
+
+const expected_lt_s
+      = [1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,
+         1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,
+         1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,
+         0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,
+         1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1];
+
+const expected_lt_u
+      = [1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
+         0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,
+         1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,1,
+         1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,
+         1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1];
+
+const expected_gt_s
+      = [1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,
+         0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,
+         1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,
+         1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,
+         1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1];
+
+const expected_gt_u
+      = [1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,
+         1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
+         1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,0,
+         0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,
+         1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1];
+
+const expected_le_s
+      = [0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,
+         1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
+         0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+         0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,
+         0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0];
+
+const expected_le_u
+      = [0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,
+         0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,
+         0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,1,
+         1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,
+         0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0];
+
+const expected_ge_s
+      = [0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,
+         0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,
+         0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,
+         1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,
+         0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0];
+
+const expected_ge_u
+      = [0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,
+         1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,
+         0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,0,
+         0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,
+         0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0];
+
+const testExpVecs
+      = [expected_eq, expected_ne,
+         expected_lt_s, expected_lt_u, expected_gt_s, expected_gt_u,
+         expected_le_s, expected_le_u, expected_ge_s, expected_ge_u]
+
+assertEq(testNames.length, testExpVecs.length);
+
+for (i in testNames) {
+    const testExp = testExpVecs[i];
+    assertEq(testExp.length, vals.length * vals.length);
+
+    const inst = wasmEvalText(moduleText(testNames[i]));
+
+    let ctr = 0;
+    for (let a of vals) {
+        for (let b of vals) {
+            const actual = inst.exports.cmpI32_selI32(a, b, 42, 1337);
+            const expected = testExp[ctr] == 0 ? 42 : 1337;
+            assertEq(expected, actual);
+            ctr++;
+        }
+    }
+}
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -6211,17 +6211,17 @@ bool CodeGenerator::generateBody() {
 
     for (LInstructionIterator iter = current->begin(); iter != current->end();
          iter++) {
       if (!alloc().ensureBallast()) {
         return false;
       }
 
 #ifdef JS_JITSPEW
-      JitSpewStart(JitSpew_Codegen, "instruction %s", iter->opName());
+      JitSpewStart(JitSpew_Codegen, "# instruction %s", iter->opName());
       if (const char* extra = iter->getExtraName()) {
         JitSpewCont(JitSpew_Codegen, ":%s", extra);
       }
       JitSpewFin(JitSpew_Codegen);
 #endif
 
       if (counts) {
         blockCounts->visitInstruction(*iter);
@@ -14005,13 +14005,52 @@ void CodeGenerator::visitWasmNullConstan
   masm.xorPtr(ToRegister(lir->output()), ToRegister(lir->output()));
 }
 
 void CodeGenerator::visitIsNullPointer(LIsNullPointer* lir) {
   masm.cmpPtrSet(Assembler::Equal, ToRegister(lir->value()), ImmWord(0),
                  ToRegister(lir->output()));
 }
 
+void CodeGenerator::visitWasmCompareAndSelect(LWasmCompareAndSelect* ins) {
+  bool cmpIs32bit = ins->compareType() == MCompare::Compare_Int32 ||
+                    ins->compareType() == MCompare::Compare_UInt32;
+  bool selIs32bit = ins->mir()->type() == MIRType::Int32;
+
+  if (cmpIs32bit && selIs32bit) {
+    Register out = ToRegister(ins->output());
+    MOZ_ASSERT(ToRegister(ins->ifTrueExpr()) == out,
+               "true expr input is reused for output");
+
+    Assembler::Condition cond = Assembler::InvertCondition(JSOpToCondition(
+                                    ins->compareType(), ins->jsop()));
+    const LAllocation* rhs = ins->rightExpr();
+    const LAllocation* falseExpr = ins->ifFalseExpr();
+    Register lhs = ToRegister(ins->leftExpr());
+
+    if (rhs->isRegister()) {
+      if (falseExpr->isRegister()) {
+        // On arm32, this is the only one of the four cases that can actually
+        // happen, since |rhs| and |falseExpr| are marked useAny() by
+        // LIRGenerator::visitWasmSelect, and useAny() means "register only"
+        // on arm32.
+        masm.cmp32Move32(cond, lhs, ToRegister(rhs), ToRegister(falseExpr), out);
+      } else {
+        masm.cmp32Load32(cond, lhs, ToRegister(rhs), ToAddress(falseExpr), out);
+      }
+    } else {
+      if (falseExpr->isRegister()) {
+        masm.cmp32Move32(cond, lhs, ToAddress(rhs), ToRegister(falseExpr), out);
+      } else {
+        masm.cmp32Load32(cond, lhs, ToAddress(rhs), ToAddress(falseExpr), out);
+      }
+    }
+    return;
+  }
+
+  MOZ_CRASH("in CodeGenerator::visitWasmCompareAndSelect: unexpected types");
+}
+
 static_assert(!std::is_polymorphic<CodeGenerator>::value,
               "CodeGenerator should not have any virtual methods");
 
 }  // namespace jit
 }  // namespace js
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -899,31 +899,38 @@ void LIRGenerator::visitObjectGroupDispa
   add(lir, ins);
 }
 
 static inline bool CanEmitCompareAtUses(MInstruction* ins) {
   if (!ins->canEmitAtUses()) {
     return false;
   }
 
-  bool foundTest = false;
-  for (MUseIterator iter(ins->usesBegin()); iter != ins->usesEnd(); iter++) {
-    MNode* node = iter->consumer();
-    if (!node->isDefinition()) {
-      return false;
-    }
-    if (!node->toDefinition()->isTest()) {
-      return false;
-    }
-    if (foundTest) {
-      return false;
-    }
-    foundTest = true;
-  }
-  return true;
+  // If the result is never used, we can usefully defer emission to the use
+  // point, since that will never happen.
+  MUseIterator iter(ins->usesBegin());
+  if (iter == ins->usesEnd()) {
+    return true;
+  }
+
+  // If the first use isn't of the expected form, the answer is No.
+  MNode* node = iter->consumer();
+  if (!node->isDefinition()) {
+    return false;
+  }
+
+  MDefinition* use = node->toDefinition();
+  if (!use->isTest() && !use->isWasmSelect()) {
+    return false;
+  }
+
+  // Emission can be deferred to the first use point, but only if there are no
+  // other use points.
+  iter++;
+  return iter == ins->usesEnd();
 }
 
 void LIRGenerator::visitCompare(MCompare* comp) {
   MDefinition* left = comp->lhs();
   MDefinition* right = comp->rhs();
 
   // Try to fold the comparison so that we don't have to handle all cases.
   bool result;
@@ -5044,10 +5051,56 @@ void LIRGenerator::visitIonToWasmCall(MI
         MOZ_CRASH("Uninitialized ABIArg kind");
     }
   }
 
   defineReturn(lir, ins);
   assignSafepoint(lir, ins);
 }
 
+void LIRGenerator::visitWasmSelect(MWasmSelect* ins) {
+  MDefinition* condExpr = ins->condExpr();
+
+  // Pick off specific cases that we can do with LWasmCompareAndSelect.
+  // Currently only{U,}Int32 selection driven by a comparison of {U,}Int32
+  // values.
+  if (condExpr->isCompare() && condExpr->isEmittedAtUses() &&
+      ins->type() == MIRType::Int32) {
+    MCompare* comp = condExpr->toCompare();
+    JSOp jsop = comp->jsop();
+    MCompare::CompareType compTy = comp->compareType();
+    // We don't currently generate any other JSOPs for the comparison, and if
+    // that changes, we want to know about it.  Hence this assertion.
+    MOZ_ASSERT(jsop == JSOP_EQ || jsop == JSOP_NE || jsop == JSOP_LT ||
+               jsop == JSOP_GT || jsop == JSOP_LE || jsop == JSOP_GE);
+    if (compTy == MCompare::Compare_Int32 ||
+        compTy == MCompare::Compare_UInt32) {
+      auto* lir = new (alloc())
+        LWasmCompareAndSelect(useRegister(comp->lhs()), useAny(comp->rhs()),
+                              compTy, jsop,
+                              useRegisterAtStart(ins->trueExpr()),
+                              useAny(ins->falseExpr()));
+
+      defineReuseInput(lir, ins, LWasmCompareAndSelect::IfTrueExprIndex);
+      return;
+    }
+    // Otherwise fall through to normal handling, which appears to emit the
+    // condexpr itself anyway.
+  }
+
+  if (ins->type() == MIRType::Int64) {
+    auto* lir = new (alloc()) LWasmSelectI64(
+        useInt64RegisterAtStart(ins->trueExpr()), useInt64(ins->falseExpr()),
+        useRegister(ins->condExpr()));
+
+    defineInt64ReuseInput(lir, ins, LWasmSelectI64::TrueExprIndex);
+    return;
+  }
+
+  auto* lir = new (alloc())
+      LWasmSelect(useRegisterAtStart(ins->trueExpr()), useAny(ins->falseExpr()),
+                  useRegister(ins->condExpr()));
+
+  defineReuseInput(lir, ins, LWasmSelect::TrueExprIndex);
+}
+
 static_assert(!std::is_polymorphic<LIRGenerator>::value,
               "LIRGenerator should not have any virtual methods");
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -1627,16 +1627,24 @@ class MacroAssembler : public MacroAssem
   inline void cmp32Move32(Condition cond, Register lhs, Register rhs,
                           Register src, Register dest)
       DEFINED_ON(arm, arm64, mips_shared, x86_shared);
 
   inline void cmp32Move32(Condition cond, Register lhs, const Address& rhs,
                           Register src, Register dest)
       DEFINED_ON(arm, arm64, mips_shared, x86_shared);
 
+  inline void cmp32Load32(Condition cond, Register lhs, const Address& rhs,
+                          const Address& src, Register dest)
+      DEFINED_ON(arm, arm64, x86_shared);
+
+  inline void cmp32Load32(Condition cond, Register lhs, Register rhs,
+                          const Address& src, Register dest)
+      DEFINED_ON(arm, arm64, x86_shared);
+
   inline void cmp32MovePtr(Condition cond, Register lhs, Imm32 rhs,
                            Register src, Register dest)
       DEFINED_ON(arm, arm64, mips_shared, x86, x64);
 
   inline void test32LoadPtr(Condition cond, const Address& addr, Imm32 mask,
                             const Address& src, Register dest)
       DEFINED_ON(arm, arm64, mips_shared, x86, x64);
 
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -466,33 +466,16 @@ void LIRGeneratorARM::lowerUrshD(MUrsh* 
   MOZ_ASSERT(lhs->type() == MIRType::Int32);
   MOZ_ASSERT(rhs->type() == MIRType::Int32);
 
   LUrshD* lir = new (alloc())
       LUrshD(useRegister(lhs), useRegisterOrConstant(rhs), temp());
   define(lir, mir);
 }
 
-void LIRGenerator::visitWasmSelect(MWasmSelect* ins) {
-  if (ins->type() == MIRType::Int64) {
-    auto* lir = new (alloc()) LWasmSelectI64(
-        useInt64RegisterAtStart(ins->trueExpr()), useInt64(ins->falseExpr()),
-        useRegister(ins->condExpr()));
-
-    defineInt64ReuseInput(lir, ins, LWasmSelectI64::TrueExprIndex);
-    return;
-  }
-
-  auto* lir = new (alloc())
-      LWasmSelect(useRegisterAtStart(ins->trueExpr()),
-                  useRegister(ins->falseExpr()), useRegister(ins->condExpr()));
-
-  defineReuseInput(lir, ins, LWasmSelect::TrueExprIndex);
-}
-
 void LIRGenerator::visitWasmNeg(MWasmNeg* ins) {
   if (ins->type() == MIRType::Int32) {
     define(new (alloc()) LNegI(useRegisterAtStart(ins->input())), ins);
   } else if (ins->type() == MIRType::Float32) {
     define(new (alloc()) LNegF(useRegisterAtStart(ins->input())), ins);
   } else {
     MOZ_ASSERT(ins->type() == MIRType::Double);
     define(new (alloc()) LNegD(useRegisterAtStart(ins->input())), ins);
--- a/js/src/jit/arm/MacroAssembler-arm-inl.h
+++ b/js/src/jit/arm/MacroAssembler-arm-inl.h
@@ -1894,16 +1894,30 @@ void MacroAssembler::cmp32Move32(Conditi
                                  const Address& rhs, Register src,
                                  Register dest) {
   ScratchRegisterScope scratch(*this);
   SecondScratchRegisterScope scratch2(*this);
   ma_ldr(rhs, scratch, scratch2);
   cmp32Move32(cond, lhs, scratch, src, dest);
 }
 
+void MacroAssembler::cmp32Load32(Condition cond, Register lhs,
+                                 const Address& rhs, const Address& src,
+                                 Register dest) {
+  // This is never used, but must be present to facilitate linking on arm.
+  MOZ_CRASH("No known use cases");
+}
+
+void MacroAssembler::cmp32Load32(Condition cond, Register lhs,
+                                 Register rhs, const Address& src,
+                                 Register dest) {
+  // This is never used, but must be present to facilitate linking on arm.
+  MOZ_CRASH("No known use cases");
+}
+
 void MacroAssembler::test32LoadPtr(Condition cond, const Address& addr,
                                    Imm32 mask, const Address& src,
                                    Register dest) {
   MOZ_ASSERT(cond == Assembler::Zero || cond == Assembler::NonZero);
   test32(addr, mask);
   ScratchRegisterScope scratch(*this);
   ma_ldr(src, dest, scratch, Offset, cond);
 }
--- a/js/src/jit/arm64/Lowering-arm64.cpp
+++ b/js/src/jit/arm64/Lowering-arm64.cpp
@@ -303,20 +303,16 @@ void LIRGenerator::visitWasmNeg(MWasmNeg
     case MIRType::Double:
       define(new (alloc()) LNegD(useRegisterAtStart(ins->input())), ins);
       break;
     default:
       MOZ_CRASH("unexpected type");
   }
 }
 
-void LIRGenerator::visitWasmSelect(MWasmSelect* ins) {
-  MOZ_CRASH("visitWasmSelect");
-}
-
 void LIRGeneratorARM64::lowerUDiv(MDiv* div) {
   LAllocation lhs = useRegister(div->lhs());
   // TODO (Bug 1523568): Implement the division-avoidance paths when rhs is
   // constant.
 
   // Generate UDiv
   LAllocation rhs = useRegister(div->rhs());
   LDefinition remainder = LDefinition::BogusTemp();
--- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h
@@ -1584,16 +1584,28 @@ void MacroAssembler::cmp32Move32(Conditi
 void MacroAssembler::cmp32Move32(Condition cond, Register lhs,
                                  const Address& rhs, Register src,
                                  Register dest) {
   cmp32(lhs, rhs);
   Csel(ARMRegister(dest, 32), ARMRegister(src, 32), ARMRegister(dest, 32),
        cond);
 }
 
+void MacroAssembler::cmp32Load32(Condition cond, Register lhs,
+                                 const Address& rhs, const Address& src,
+                                 Register dest) {
+  MOZ_CRASH("NYI");
+}
+
+void MacroAssembler::cmp32Load32(Condition cond, Register lhs,
+                                 Register rhs, const Address& src,
+                                 Register dest) {
+  MOZ_CRASH("NYI");
+}
+
 void MacroAssembler::cmp32MovePtr(Condition cond, Register lhs, Imm32 rhs,
                                   Register src, Register dest) {
   cmp32(lhs, rhs);
   Csel(ARMRegister(dest, 64), ARMRegister(src, 64), ARMRegister(dest, 64),
        cond);
 }
 
 void MacroAssembler::test32LoadPtr(Condition cond, const Address& addr,
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -6193,16 +6193,49 @@ class LWasmSelectI64
     setOperand(CondIndex, cond);
   }
 
   const LInt64Allocation trueExpr() { return getInt64Operand(TrueExprIndex); }
   const LInt64Allocation falseExpr() { return getInt64Operand(FalseExprIndex); }
   const LAllocation* condExpr() { return getOperand(CondIndex); }
 };
 
+class LWasmCompareAndSelect : public LWasmSelectBase<1, 4> {
+  MCompare::CompareType compareType_;
+  JSOp jsop_;
+
+ public:
+  LIR_HEADER(WasmCompareAndSelect);
+
+  static const size_t LeftExprIndex = 0;
+  static const size_t RightExprIndex = 1;
+  static const size_t IfTrueExprIndex = 2;
+  static const size_t IfFalseExprIndex = 3;
+
+  LWasmCompareAndSelect(const LAllocation& leftExpr,
+                        const LAllocation& rightExpr,
+                        MCompare::CompareType compareType, JSOp jsop,
+                        const LAllocation& ifTrueExpr,
+                        const LAllocation& ifFalseExpr)
+      : LWasmSelectBase(classOpcode), compareType_(compareType), jsop_(jsop) {
+    setOperand(LeftExprIndex, leftExpr);
+    setOperand(RightExprIndex, rightExpr);
+    setOperand(IfTrueExprIndex, ifTrueExpr);
+    setOperand(IfFalseExprIndex, ifFalseExpr);
+  }
+
+  const LAllocation* leftExpr() { return getOperand(LeftExprIndex); }
+  const LAllocation* rightExpr() { return getOperand(RightExprIndex); }
+  const LAllocation* ifTrueExpr() { return getOperand(IfTrueExprIndex); }
+  const LAllocation* ifFalseExpr() { return getOperand(IfFalseExprIndex); }
+
+  MCompare::CompareType compareType() { return compareType_; }
+  JSOp jsop() { return jsop_; }
+};
+
 class LWasmAddOffset : public LInstructionHelper<1, 1, 0> {
  public:
   LIR_HEADER(WasmAddOffset);
   explicit LWasmAddOffset(const LAllocation& base)
       : LInstructionHelper(classOpcode) {
     setOperand(0, base);
   }
   MWasmAddOffset* mir() const { return mir_->toWasmAddOffset(); }
--- a/js/src/jit/x86-shared/Lowering-x86-shared.cpp
+++ b/js/src/jit/x86-shared/Lowering-x86-shared.cpp
@@ -241,33 +241,16 @@ void LIRGeneratorX86Shared::lowerModI(MM
   LModI* lir = new (alloc())
       LModI(useRegister(mod->lhs()), useRegister(mod->rhs()), tempFixed(eax));
   if (mod->fallible()) {
     assignSnapshot(lir, Bailout_DoubleOutput);
   }
   defineFixed(lir, mod, LAllocation(AnyRegister(edx)));
 }
 
-void LIRGenerator::visitWasmSelect(MWasmSelect* ins) {
-  if (ins->type() == MIRType::Int64) {
-    auto* lir = new (alloc()) LWasmSelectI64(
-        useInt64RegisterAtStart(ins->trueExpr()), useInt64(ins->falseExpr()),
-        useRegister(ins->condExpr()));
-
-    defineInt64ReuseInput(lir, ins, LWasmSelectI64::TrueExprIndex);
-    return;
-  }
-
-  auto* lir = new (alloc())
-      LWasmSelect(useRegisterAtStart(ins->trueExpr()), use(ins->falseExpr()),
-                  useRegister(ins->condExpr()));
-
-  defineReuseInput(lir, ins, LWasmSelect::TrueExprIndex);
-}
-
 void LIRGenerator::visitWasmNeg(MWasmNeg* ins) {
   switch (ins->type()) {
     case MIRType::Int32:
       defineReuseInput(new (alloc()) LNegI(useRegisterAtStart(ins->input())),
                        ins, 0);
       break;
     case MIRType::Float32:
       defineReuseInput(new (alloc()) LNegF(useRegisterAtStart(ins->input())),
--- a/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h
+++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h
@@ -987,16 +987,30 @@ void MacroAssembler::cmp32Move32(Conditi
 
 void MacroAssembler::cmp32Move32(Condition cond, Register lhs,
                                  const Address& rhs, Register src,
                                  Register dest) {
   cmp32(lhs, Operand(rhs));
   cmovCCl(cond, src, dest);
 }
 
+void MacroAssembler::cmp32Load32(Condition cond, Register lhs,
+                                 const Address& rhs, const Address& src,
+                                 Register dest) {
+  cmp32(lhs, Operand(rhs));
+  cmovCCl(cond, Operand(src), dest);
+}
+
+void MacroAssembler::cmp32Load32(Condition cond, Register lhs,
+                                 Register rhs, const Address& src,
+                                 Register dest) {
+  cmp32(lhs, rhs);
+  cmovCCl(cond, Operand(src), dest);
+}
+
 void MacroAssembler::spectreZeroRegister(Condition cond, Register scratch,
                                          Register dest) {
   // Note: use movl instead of move32/xorl to ensure flags are not clobbered.
   movl(Imm32(0), scratch);
   spectreMovePtr(cond, scratch, dest);
 }
 
 // ========================================================================
--- a/python/mozboot/mozboot/archlinux.py
+++ b/python/mozboot/mozboot/archlinux.py
@@ -27,36 +27,26 @@ class ArchlinuxBootstrapper(NodeInstall,
         'python',  # This is Python 3 on Arch.
         'unzip',
         'zip',
     ]
 
     BROWSER_PACKAGES = [
         'alsa-lib',
         'dbus-glib',
-        'desktop-file-utils',
         'gconf',
         'gtk2',
         'gtk3',
-        'hicolor-icon-theme',
-        'hunspell',
-        'icu',
         'libevent',
         'libvpx',
         'libxt',
         'mime-types',
-        'mozilla-common',
         'nasm',
-        'nss',
-        'sqlite',
         'startup-notification',
-        'diffutils',
         'gst-plugins-base-libs',
-        'imake',
-        'inetutils',
         'libpulse',
         'xorg-server-xvfb',
         'yasm',
         'gst-libav',
         'gst-plugins-good',
     ]
 
     BROWSER_AUR_PACKAGES = [
--- a/toolkit/mozapps/extensions/AddonManagerStartup.cpp
+++ b/toolkit/mozapps/extensions/AddonManagerStartup.cpp
@@ -45,22 +45,16 @@
 
 #include <stdlib.h>
 
 namespace mozilla {
 
 using Compression::LZ4;
 using dom::ipc::StructuredCloneData;
 
-#ifdef XP_WIN
-#  define READ_BINARYMODE "rb"
-#else
-#  define READ_BINARYMODE "r"
-#endif
-
 AddonManagerStartup& AddonManagerStartup::GetSingleton() {
   static RefPtr<AddonManagerStartup> singleton;
   if (!singleton) {
     singleton = new AddonManagerStartup();
     ClearOnShutdown(&singleton);
   }
   return *singleton;
 }
@@ -111,22 +105,28 @@ static Result<nsCString, nsresult> Decod
   if (lz4.Length() < HEADER_SIZE || StringHead(lz4, magic.Length()) != magic) {
     return Err(NS_ERROR_UNEXPECTED);
   }
 
   auto data = lz4.BeginReading() + magic.Length();
   auto size = LittleEndian::readUint32(data);
   data += 4;
 
+  size_t dataLen = lz4.EndReading() - data;
+  size_t outputSize;
+
   nsCString result;
   if (!result.SetLength(size, fallible) ||
-      !LZ4::decompress(data, result.BeginWriting(), size)) {
+      !LZ4::decompress(data, dataLen, result.BeginWriting(), size,
+                       &outputSize)) {
     return Err(NS_ERROR_UNEXPECTED);
   }
 
+  MOZ_DIAGNOSTIC_ASSERT(size == outputSize);
+
   return result;
 }
 
 // Our zlib headers redefine this to MOZ_Z_compress, which breaks LZ4::compress
 #undef compress
 
 template <typename T>
 static Result<nsCString, nsresult> EncodeLZ4(const nsACString& data,