Merge adjacent JOIN_GENERATORS with the same path conditions (#2623)

Summary:
Release notes: Adjacent JOIN_GENERATORS with same path conditions are now merged for optimization.

This pull request attempts to tackle https://github.com/facebook/prepack/issues/2565. Some assumptions were made (for lack of certainty) but commented on for further improvement.
Pull Request resolved: https://github.com/facebook/prepack/pull/2623

Differential Revision: D13864274

Pulled By: NTillmann

fbshipit-source-id: 50f965f4fc3e906d78af9a610b487b2854b085b2
This commit is contained in:
Tiago Tristao 2019-01-29 13:51:25 -08:00 committed by Facebook Github Bot
parent 011e69c153
commit 93d82b55a6
5 changed files with 103 additions and 1 deletions

View File

@ -368,6 +368,10 @@ export type PathType = {
export class PathConditions {
add(c: AbstractValue): void {}
equals(x: PathConditions): boolean {
return false;
}
// this => val. A false value does not imply that !(this => val).
implies(e: Value, depth: number = 0): boolean {
return false;

View File

@ -1197,7 +1197,44 @@ export class Generator {
invariant(other.effectsToApply === undefined);
if (other.empty()) return;
this._entries.push(...other._entries);
let otherEntries = other._entries;
otherEntries.forEach(otherEntry => this._appendEntry(otherEntry));
}
_appendEntry(entry: GeneratorEntry): void {
let attemptToMergeEntry = (): boolean => {
// It attempts to merge the `entry` argument with the last entry item
// in `this._entries` if both are `JOIN_GENERATORS` and have the same
// path conditions
if (entry instanceof TemporalOperationEntry) {
let operationDescriptor = entry.operationDescriptor;
if (operationDescriptor.type === "JOIN_GENERATORS" && !this.empty()) {
invariant(operationDescriptor.data.generators);
let thisLastEntry = this._entries[this._entries.length - 1];
if (thisLastEntry instanceof TemporalOperationEntry) {
let thisLastEntryOperationDescriptor = thisLastEntry.operationDescriptor;
if (thisLastEntryOperationDescriptor.type === "JOIN_GENERATORS") {
invariant(thisLastEntryOperationDescriptor.data.generators);
let [entryGenerator1, entryGenerator2] = operationDescriptor.data.generators;
let [thisLastEntryGenerator1, thisLastEntryGenerator2] = thisLastEntryOperationDescriptor.data.generators;
if (
entryGenerator1.pathConditions.equals(thisLastEntryGenerator1.pathConditions) &&
entryGenerator2.pathConditions.equals(thisLastEntryGenerator2.pathConditions)
) {
if (!entryGenerator1.empty())
entryGenerator1._entries.forEach(e => thisLastEntryGenerator1._entries.push(e));
if (!entryGenerator2.empty())
entryGenerator2._entries.forEach(e => thisLastEntryGenerator2._entries.push(e));
return true;
}
}
}
}
}
return false;
};
attemptToMergeEntry() || this._entries.push(entry);
}
joinGenerators(joinCondition: AbstractValue, generator1: Generator, generator2: Generator): void {

View File

@ -41,6 +41,32 @@ export class PathConditionsImplementation extends PathConditions {
this._failedNegativeImplications = undefined;
}
// It makes the strong assumption that, in order for 2 path conditions to be equal,
// the number of values must be the same, as well as their order.
// This might not always be the case, yielding false negatives?!
equals(x: PathConditions): boolean {
invariant(x instanceof PathConditionsImplementation);
let conditionsAreEqual = () => {
if (this._assumedConditions.size !== x._assumedConditions.size) return false;
let thisConditions = Array.from(this._assumedConditions);
let xConditions = Array.from(x._assumedConditions);
let thisLength = thisConditions.length;
for (let i = 0; i < thisLength; i++) {
let thisCondition = thisConditions[i];
let xCondition = xConditions[i];
if (!thisCondition.equals(xCondition)) return false;
}
return true;
};
let baseConditionsAreEqual = () => {
if (this._baseConditions && !x._baseConditions) return false;
if (!this._baseConditions && x._baseConditions) return false;
if (this._baseConditions && x._baseConditions) return this._baseConditions.equals(x._baseConditions);
return true;
};
return this === x || (conditionsAreEqual() && baseConditionsAreEqual());
}
isReadOnly(): boolean {
return this._readonly;
}

View File

@ -0,0 +1,19 @@
// omit invariants
// Copies of if \(:2
var x = global.__abstract ? __abstract("boolean", "x") : "x";
if (x) {
console.log("Hello");
}
if (x) {
console.log(" World");
}
if (x) {
console.log("!");
}
if (!x) {
console.log("Hello World!");
}

View File

@ -0,0 +1,16 @@
// omit invariants
// Copies of if \(:2
var x = global.__abstract ? __abstract("boolean", "x") : "x";
var y = global.__abstract ? __abstract("boolean", "y") : "y";
if (x) {
console.log("Hello World!");
}
if (x && y) {
console.log("Hello");
}
if (x && y) {
console.log(" World!");
}