Distribute equality checks if that allows simplification (#2174)

Summary:
Release note: More simplification of equality expressions

Resolves issue: #2172

Expressions like (c ? x : y) === null can sometimes be simplified by first rewriting them as (c ? x === null : y === null). Add such a case to the simplifier.
Closes https://github.com/facebook/prepack/pull/2174

Differential Revision: D8666251

Pulled By: hermanventer

fbshipit-source-id: 5b0d56fb091e25bfcdf879be3403cb5a982375ae
This commit is contained in:
Herman Venter 2018-06-27 11:55:58 -07:00 committed by Facebook Github Bot
parent 30f07ead22
commit fa4b52f573
2 changed files with 52 additions and 12 deletions

View File

@ -16,6 +16,7 @@ import invariant from "../invariant.js";
import { Realm } from "../realm.js";
import { AbstractValue, BooleanValue, ConcreteValue, Value } from "../values/index.js";
import { Path, To } from "../singletons.js";
import EmptyValue from "../values/EmptyValue";
export default function simplifyAndRefineAbstractValue(
realm: Realm,
@ -257,24 +258,42 @@ function simplifyEquality(realm: Realm, equality: AbstractValue): Value {
let loc = equality.expressionLocation;
let op = equality.kind;
let [x, y] = equality.args;
if (y instanceof EmptyValue) return equality;
if (x instanceof ConcreteValue) [x, y] = [y, x];
if (x instanceof AbstractValue && x.kind === "conditional" && (!y.mightNotBeUndefined() || !y.mightNotBeNull())) {
function simplified(v: Value) {
return v instanceof AbstractValue ? v.kind !== op : true;
}
// try to simplify "(cond ? xx : xy) op undefined/null" to just "cond" or "!cond"
let [cond, xx, xy] = x.args;
invariant(cond instanceof AbstractValue); // otherwise the the conditional should not have been created
if (op === "===" || op === "!==") {
// if xx === undefined && xy !== undefined then cond <=> x === undefined
if (!y.mightNotBeUndefined() && !xx.mightNotBeUndefined() && !xy.mightBeUndefined())
return op === "===" ? makeBoolean(realm, cond, loc) : negate(realm, cond, loc);
// if xx !== undefined && xy === undefined then !cond <=> x === undefined
if (!y.mightNotBeUndefined() && !xx.mightBeUndefined() && !xy.mightNotBeUndefined())
return op === "===" ? negate(realm, cond, loc) : makeBoolean(realm, cond, loc);
// if xx === null && xy !== null then cond <=> x === null
if (!y.mightNotBeNull() && !xx.mightNotBeNull() && !xy.mightBeNull())
return op === "===" ? makeBoolean(realm, cond, loc) : negate(realm, cond, loc);
// if xx !== null && xy === null then !cond <=> x === null
if (!y.mightNotBeNull() && !xx.mightBeNull() && !xy.mightNotBeNull())
return op === "===" ? negate(realm, cond, loc) : makeBoolean(realm, cond, loc);
if (!y.mightNotBeUndefined()) {
// if xx === undefined && xy !== undefined then cond <=> x === undefined
if (!xx.mightNotBeUndefined() && !xy.mightBeUndefined())
return op === "===" ? makeBoolean(realm, cond, loc) : negate(realm, cond, loc);
// if xx !== undefined && xy === undefined then !cond <=> x === undefined
if (!xx.mightBeUndefined() && !xy.mightNotBeUndefined())
return op === "===" ? negate(realm, cond, loc) : makeBoolean(realm, cond, loc);
// distribute equality test, creating more simplication opportunities
let sxx = AbstractValue.createFromBinaryOp(realm, op, xx, realm.intrinsics.undefined, xx.expressionLocation);
let sxy = AbstractValue.createFromBinaryOp(realm, op, xy, realm.intrinsics.undefined, xy.expressionLocation);
if (simplified(sxx) || simplified(sxy))
return AbstractValue.createFromConditionalOp(realm, cond, sxx, sxy, equality.expressionLocation, true);
}
if (!y.mightNotBeNull()) {
// if xx === null && xy !== null then cond <=> x === null
if (!xx.mightNotBeNull() && !xy.mightBeNull())
return op === "===" ? makeBoolean(realm, cond, loc) : negate(realm, cond, loc);
// if xx !== null && xy === null then !cond <=> x === null
if (!xx.mightBeNull() && !xy.mightNotBeNull())
return op === "===" ? negate(realm, cond, loc) : makeBoolean(realm, cond, loc);
// distribute equality test, creating more simplication opportunities
let sxx = AbstractValue.createFromBinaryOp(realm, op, xx, realm.intrinsics.null, xx.expressionLocation);
let sxy = AbstractValue.createFromBinaryOp(realm, op, xy, realm.intrinsics.null, xy.expressionLocation);
if (simplified(sxx) || simplified(sxy))
return AbstractValue.createFromConditionalOp(realm, cond, sxx, sxy, equality.expressionLocation, true);
}
} else {
invariant(op === "==" || op === "!=");
// if xx cannot be undefined/null and xy is undefined/null then !cond <=> x == undefined/null
@ -283,6 +302,11 @@ function simplifyEquality(realm: Realm, equality: AbstractValue): Value {
// if xx is undefined/null and xy cannot be undefined/null then cond <=> x == undefined/null
if ((!xx.mightNotBeUndefined() || !xx.mightNotBeNull()) && !xy.mightBeUndefined() && !xy.mightBeNull())
return op === "==" ? makeBoolean(realm, cond, loc) : negate(realm, cond, loc);
// distribute equality test, creating more simplication opportunities
let sxx = AbstractValue.createFromBinaryOp(realm, op, xx, y, xx.expressionLocation);
let sxy = AbstractValue.createFromBinaryOp(realm, op, xy, y, xy.expressionLocation);
if (simplified(sxx) || simplified(sxy))
return AbstractValue.createFromConditionalOp(realm, cond, sxx, sxy, equality.expressionLocation, true);
}
}
return equality;

View File

@ -0,0 +1,16 @@
// does not contain:{}
let x = global.__abstract ? __abstract("boolean", "(1 === 1)") : true;
let y = global.__abstract ? __abstract("boolean", "(2 === 2)") : true;
let a = x ? null : {};
let b = y ? null : a;
var c = b === null;
let a1 = x ? undefined : {};
let b1 = y ? undefined : a1;
var c1 = b1 === undefined;
let a2 = x ? undefined : {};
let b2 = y ? null : a2;
var c2 = b2 == undefined;
var c3 = b2 == null;
inspect = function() { return [c, c1, c2, c3].join(" "); }