Skip to content

Commit

Permalink
InstSimplify: optimize equality comparisons based on type
Browse files Browse the repository at this point in the history
Summary:
Strict equality of non-overlapping types doesn't need to be performed.
Loose equality is trickier, so we constrain ourselves only to comparing
against `null|undefined`.

Reviewed By: davedets

Differential Revision: D67905084

fbshipit-source-id: ca0b7915973962de66ebddd9ecf61981e51d60c0
  • Loading branch information
Tzvetan Mikov authored and facebook-github-bot committed Jan 8, 2025
1 parent 2641863 commit d6c8fe6
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 4 deletions.
26 changes: 22 additions & 4 deletions lib/Optimizer/Scalar/InstSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,14 @@ class InstSimplifyImpl {
return builder_.createBinaryOperatorInst(
lhs, rhs, ValueKind::BinaryStrictlyEqualInstKind);
}

// ~(null|undefined) == null|undefined is always false.
if ((Type::intersectTy(leftTy, kNullOrUndef).isNoType() &&
rightTy.isSubsetOf(kNullOrUndef)) ||
(Type::intersectTy(rightTy, kNullOrUndef).isNoType() &&
leftTy.isSubsetOf(kNullOrUndef))) {
return builder_.getLiteralBool(false);
}
break;

case ValueKind::BinaryNotEqualInstKind: // !=
Expand All @@ -421,6 +429,14 @@ class InstSimplifyImpl {
return builder_.createBinaryOperatorInst(
lhs, rhs, ValueKind::BinaryStrictlyNotEqualInstKind);
}

// ~(null|undefined) != null|undefined is always true.
if ((Type::intersectTy(leftTy, kNullOrUndef).isNoType() &&
rightTy.isSubsetOf(kNullOrUndef)) ||
(Type::intersectTy(rightTy, kNullOrUndef).isNoType() &&
leftTy.isSubsetOf(kNullOrUndef))) {
return builder_.getLiteralBool(true);
}
break;

case ValueKind::BinaryStrictlyEqualInstKind: // ===
Expand All @@ -430,10 +446,8 @@ class InstSimplifyImpl {
}

// Operands of different types can't be strictly equal.
if (leftTy.isPrimitive() && rightTy.isPrimitive()) {
if (Type::intersectTy(leftTy, rightTy).isNoType()) {
return builder_.getLiteralBool(false);
}
if (Type::intersectTy(leftTy, rightTy).isNoType()) {
return builder_.getLiteralBool(false);
}
break;

Expand All @@ -442,6 +456,10 @@ class InstSimplifyImpl {
if (identicalForEquality) {
return builder_.getLiteralBool(false);
}
// Operands of different types can't be strictly equal.
if (Type::intersectTy(leftTy, rightTy).isNoType()) {
return builder_.getLiteralBool(true);
}
break;

case ValueKind::BinaryLessThanInstKind: // <
Expand Down
56 changes: 56 additions & 0 deletions test/Optimizer/simplify-eq-null-undefined.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

// RUN: %hermesc -O -dump-ir %s | %FileCheckOrRegen %s

// Test equality comparisons against null|undefined when the other type can
// be neither type.

let o = {};
sink = o !== null;
sink = o === null;
sink = o != null;
sink = o == null;
sink = undefined !== o;
sink = undefined === o;
sink = undefined != o;
sink = undefined == o;

let n = +glob;
sink = n !== undefined;
sink = n === undefined;
sink = n != undefined;
sink = n == undefined;
sink = null !== n;
sink = null === n;
sink = null != n;
sink = null == n;

// Auto-generated content below. Please do not modify manually.

// CHECK:function global(): boolean
// CHECK-NEXT:%BB0:
// CHECK-NEXT: StorePropertyLooseInst true: boolean, globalObject: object, "sink": string
// CHECK-NEXT: StorePropertyLooseInst false: boolean, globalObject: object, "sink": string
// CHECK-NEXT: StorePropertyLooseInst true: boolean, globalObject: object, "sink": string
// CHECK-NEXT: StorePropertyLooseInst false: boolean, globalObject: object, "sink": string
// CHECK-NEXT: StorePropertyLooseInst true: boolean, globalObject: object, "sink": string
// CHECK-NEXT: StorePropertyLooseInst false: boolean, globalObject: object, "sink": string
// CHECK-NEXT: StorePropertyLooseInst true: boolean, globalObject: object, "sink": string
// CHECK-NEXT: StorePropertyLooseInst false: boolean, globalObject: object, "sink": string
// CHECK-NEXT: %8 = TryLoadGlobalPropertyInst (:any) globalObject: object, "glob": string
// CHECK-NEXT: %9 = AsNumberInst (:number) %8: any
// CHECK-NEXT: StorePropertyLooseInst true: boolean, globalObject: object, "sink": string
// CHECK-NEXT: StorePropertyLooseInst false: boolean, globalObject: object, "sink": string
// CHECK-NEXT: StorePropertyLooseInst true: boolean, globalObject: object, "sink": string
// CHECK-NEXT: StorePropertyLooseInst false: boolean, globalObject: object, "sink": string
// CHECK-NEXT: StorePropertyLooseInst true: boolean, globalObject: object, "sink": string
// CHECK-NEXT: StorePropertyLooseInst false: boolean, globalObject: object, "sink": string
// CHECK-NEXT: StorePropertyLooseInst true: boolean, globalObject: object, "sink": string
// CHECK-NEXT: StorePropertyLooseInst false: boolean, globalObject: object, "sink": string
// CHECK-NEXT: ReturnInst false: boolean
// CHECK-NEXT:function_end

0 comments on commit d6c8fe6

Please sign in to comment.