Bug 725966 - Part 8: Fold away typeof string comparisons. r=jandem
authorAndré Bargull <andre.bargull@gmail.com>
Thu, 14 Oct 2021 17:59:55 +0000 (2021-10-14)
changeset 595927 e419b06ed3d4aa53276a42ebc162813c0b266dc6
parent 595926 0b43bb2f2d8be40b5a89034ce78a8e3ba4a9513f
child 595928 a490916980ca489866c40bb8f1771d9bb29a5d59
push id38880
push usernerli@mozilla.com
push dateFri, 15 Oct 2021 09:50:04 +0000 (2021-10-15)
treeherdermozilla-central@bb0faec6af52 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs725966
milestone95.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 725966 - Part 8: Fold away typeof string comparisons. r=jandem This optimisation folds away string comparisons when `typeof` appears in equality comparison contexts: MTypeOfName(MTypeOf(input)) == MConstant(string) is now optimised to: MTypeOf(input) == MConstant(int32). Differential Revision: https://phabricator.services.mozilla.com/D128076
js/src/jit-test/tests/warp/typeof-switch.js
js/src/jit/MIR.cpp
js/src/jit/MIR.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/warp/typeof-switch.js
@@ -0,0 +1,78 @@
+// Test case |typeof| folding in switch-statement contexts.
+
+function TypeOf(thing) {
+  switch (typeof thing) {
+  case "undefined":
+    return "undefined";
+  case "object":
+    return "object";
+  case "function":
+    return "function";
+  case "string":
+    return "string";
+  case "number":
+    return "number";
+  case "boolean":
+    return "boolean";
+  case "symbol":
+    return "symbol";
+  case "bigint":
+    return "bigint";
+  case "bad":
+    return "bad";
+  }
+  return "bad2";
+}
+
+function test() {
+  const ccwGlobal = newGlobal({newCompartment: true});
+  const xs = [
+    // "undefined"
+    // Plain undefined and objects emulating undefined, including various
+    // proxy wrapper cases.
+    undefined,
+    createIsHTMLDDA(),
+    wrapWithProto(createIsHTMLDDA(), null),
+    ccwGlobal.eval("createIsHTMLDDA()"),
+
+    // "object"
+    // Plain objects and various proxy wrapper cases.
+    {},
+    this,
+    new Proxy({}, {}),
+    wrapWithProto({}, null),
+    transplantableObject({proxy: true}).object,
+    ccwGlobal.Object(),
+
+    // "function"
+    // Plain functions and various proxy wrapper cases.
+    function(){},
+    new Proxy(function(){}, {}),
+    new Proxy(createIsHTMLDDA(), {}),
+    wrapWithProto(function(){}, null),
+    ccwGlobal.Function(),
+
+    // "string"
+    "",
+
+    // "number"
+    // Int32 and Double numbers.
+    0,
+    1.23,
+
+    // "boolean"
+    true,
+
+    // "symbol"
+    Symbol(),
+
+    // "bigint"
+    0n,
+  ];
+
+  for (let i = 0; i < 500; ++i) {
+    let x = xs[i % xs.length];
+    assertEq(TypeOf(x), typeof x);
+  }
+}
+for (let i = 0; i < 2; ++i) test();
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -4087,16 +4087,32 @@ bool MCompare::evaluateConstantOperands(
     *result =
         FoldComparison(jsop_, lhs->numberToDouble(), rhs->numberToDouble());
     return true;
   }
 
   return false;
 }
 
+MDefinition* MCompare::tryFoldTypeOf(TempAllocator& alloc) {
+  auto typeOfPair = IsTypeOfCompare(this);
+  if (!typeOfPair) {
+    return this;
+  }
+  auto [typeOfName, type] = *typeOfPair;
+  auto* typeOf = typeOfName->input()->toTypeOf();
+
+  MOZ_ASSERT(type != JSTYPE_LIMIT, "unknown typeof strings folded earlier");
+
+  MConstant* cst = MConstant::New(alloc, Int32Value(type));
+  block()->insertBefore(this, cst);
+
+  return MCompare::New(alloc, typeOf, cst, jsop(), MCompare::Compare_Int32);
+}
+
 MDefinition* MCompare::tryFoldCharCompare(TempAllocator& alloc) {
   if (compareType() != Compare_String) {
     return this;
   }
 
   MDefinition* left = lhs();
   MOZ_ASSERT(left->type() == MIRType::String);
 
@@ -4202,16 +4218,20 @@ MDefinition* MCompare::foldsTo(TempAlloc
     if (type() == MIRType::Int32) {
       return MConstant::New(alloc, Int32Value(result));
     }
 
     MOZ_ASSERT(type() == MIRType::Boolean);
     return MConstant::New(alloc, BooleanValue(result));
   }
 
+  if (MDefinition* folded = tryFoldTypeOf(alloc); folded != this) {
+    return folded;
+  }
+
   if (MDefinition* folded = tryFoldCharCompare(alloc); folded != this) {
     return folded;
   }
 
   if (MDefinition* folded = tryFoldStringCompare(alloc); folded != this) {
     return folded;
   }
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -2636,16 +2636,17 @@ class MCompare : public MBinaryInstructi
   }
 #endif
 
   ALLOW_CLONE(MCompare)
 
  private:
   [[nodiscard]] bool tryFoldEqualOperands(bool* result);
   [[nodiscard]] bool tryFoldTypeOf(bool* result);
+  [[nodiscard]] MDefinition* tryFoldTypeOf(TempAllocator& alloc);
   [[nodiscard]] MDefinition* tryFoldCharCompare(TempAllocator& alloc);
   [[nodiscard]] MDefinition* tryFoldStringCompare(TempAllocator& alloc);
 
  public:
   bool congruentTo(const MDefinition* ins) const override {
     if (!binaryCongruentTo(ins)) {
       return false;
     }