-
Notifications
You must be signed in to change notification settings - Fork 425
Transitive materialization for Array operators #2456
Transitive materialization for Array operators #2456
Conversation
7561720
to
2a1a6b5
Compare
This looks good. I noticed quite a lot of tests were failing though and after looking into them, it you are materializing intrinsic objects – in this case the Another area I noticed was https://github.com/facebook/prepack/blob/master/src/utils/leak.js#L93 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made a few comments on some things that aren't quite right/missing as to why the current React tests on CI are failing.
7f6169f
to
c99449e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me now. Although I found it somewhat harder/longer to review as you squashed your recent changes so I had to read through everything again. :/
c99449e
to
8885c88
Compare
I squashed because I moved a fair bit of code around while proofreading the code, so not sure if a diff would have been useful. The main changes:
|
Added a few more tests. Verified that they fail without this changeset, and succeed with it. I will add more tests covering other materialization paths in the future. For now, removing the WIP tag. |
35e3311
to
e894c49
Compare
// are used to optimize the nested optimized function. We compute the set of | ||
// objects that are transitively reachable from read bindings and materialize them. | ||
|
||
Materialize.materializeObjectsTransitive(realm, func); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about materializing the abstractArrayValue
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have an invariant that abstractArrayValue
has already leaked?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the index properties of widened arrays are not backed by locations, so no materialization is needed. I'll add an invariant that fails if this every changes.
@@ -385,7 +385,8 @@ export type LeakType = { | |||
}; | |||
|
|||
export type MaterializeType = { | |||
materialize(realm: Realm, object: ObjectValue): void, | |||
materializeObject(realm: Realm, object: ObjectValue): void, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why even expose the single-object materialization? Doesn't seem to be needed, and nobody should call that, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eventually it should go away. Right now, it is dangling out of the leaking implementation.
src/values/ArrayValue.js
Outdated
// Check if effects were pure then add them | ||
if (abstractArrayValue.nestedOptimizedFunctionEffects === undefined) { | ||
abstractArrayValue.nestedOptimizedFunctionEffects = new Map(); | ||
} | ||
abstractArrayValue.nestedOptimizedFunctionEffects.set(funcToModel, effects); | ||
realm.collectedNestedOptimizedFunctionEffects.set(funcToModel, effects); | ||
} | ||
return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This return doesn't do anything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dropped.
src/utils/leak.js
Outdated
// #2446 | ||
if (found && binding.value !== undefined) { | ||
if (binding.hasLeaked) { | ||
notSupportedForTransitiveMaterialization(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be fine (at least the way things are right now), there's simply nothing TODO. Add a comment to this end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. There was a case I wanted to cover, it's now checked in as a test.
src/utils/leak.js
Outdated
traverse.cache.clear(); | ||
|
||
// If the function is not pure, we should not be here | ||
invariant(functionInfo.unboundWrites.size === 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not true. There an assumption based on a dynamic analysis that this should not happen, but here we are doing a static traversal where we might indeed find unbound writes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True. We ought to use the interpreter to compute the set of non-local bindings: #2478
let code = func.$ECMAScriptCode; | ||
invariant(code != null); | ||
|
||
traverse( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This thing is expensive. We should globally cache the result.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using the interpreter should eliminate this #2478.
src/utils/leak.js
Outdated
|
||
// TODO: Add items to nonLocalReadBindings in passing | ||
let nonLocalReadBindings = nonLocalReadBindingsOfFunction(fn); | ||
computeFromBindings(fn, nonLocalReadBindings); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if there are any nonLocalReadBindings
left at the end? Shouldn't happen in strict mode, but otherwise there might be some global variable access. What does that mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have added an invariant to ensure that it empties out after rewinding down the scopes, and added an issue #2484 to deal with global environments.
src/utils/leak.js
Outdated
} | ||
|
||
function computeFromObjectPropertiesWithComputedNames(absVal: AbstractValue): void { | ||
if (absVal.kind === "widened property") return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it okay to just ignore those abstract value kinds here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My assumption was that these lead to intrinsic values, which do not need materialization, but I have just dropped the exceptions. This call now fails, and will be dealt with as part of #2484.
src/utils/leak.js
Outdated
} else if (value instanceof AbstractValue) { | ||
ifNotVisited(value, computeFromAbstractValue); | ||
} else if ( | ||
value.isIntrinsic() && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cannot be
src/utils/leak.js
Outdated
} | ||
function computeFromObjectValue(value: Value): void { | ||
invariant(value instanceof ObjectValue); | ||
if (!objectsToMaterialize.has(value)) objectsToMaterialize.add(value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about arrays and functions which are objects too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We shouldn't have to add values that have already leaked (and have thus been materialized and all environment interactions go through generator entries).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#2 - Lumped into the larger, #2485.
#1 - The function is handled by line 804, which visits the object properties of a function. We now have an invariant that insures that the array value is intrinsic, and so property accesses are at best temporal.
I have added a test for the function-object case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But how does that function-object case work when we don't add function values to the objects to materialize?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you elaborate what you mean by "don't add function values to the objects to materialize?" An example would help. The main outer function (i.e. the array op) is passed to computeFromFunctionValue
which in turn explores its properties.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Lots of little things to consider in going forward...
Thanks. I'll create issues for the points that are not already lumped into future work. Some others, I will resolve before landing. |
// We assume that we do not have to materialize widened arrays because they are intrinsic. | ||
// If somebody changes the underlying design in a major way, then materialization could be | ||
// needed, and this check will fail. | ||
invariant(abstractArrayValue.isIntrinsic()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This invariant now appears twice in the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sb98052 is landing this pull request. If you are a Facebook employee, you can view this diff on Phabricator.
83728d2
to
c69d6b9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sb98052 is landing this pull request. If you are a Facebook employee, you can view this diff on Phabricator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sb98052 is landing this pull request. If you are a Facebook employee, you can view this diff on Phabricator.
c69d6b9
to
b5779c0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sb98052 is landing this pull request. If you are a Facebook employee, you can view this diff on Phabricator.
7886271
to
1074ace
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
trueadm is landing this pull request. If you are a Facebook employee, you can view this diff on Phabricator.
1074ace
to
a0b5981
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good work fixing those issues.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sb98052 is landing this pull request. If you are a Facebook employee, you can view this diff on Phabricator.
This PR implements a step of the way to getting leaked value analysis working for optimized Array operators. It is desirable to leak as little as possible, so that the operators can be specialized to take into account values in the environment in which they run. In the beginning, we are focusing on the narrow range of scenarios in which this is possible. We will start by enforcing the assumptions that we rely on, and make sure that the code that we generate is correct. Once we have correct code, we will start progressively relaxing the assumptions to increase coverage. The overall plan can be found here: #2452.
More specifically, this PR transitively materializes objects reachable via reads to bindings in the optimized function. This is necessary to snapshot the contents of those objects at specialization time.
Fixes #2405.