Skip to content

[JIT] add CondValue to unify refinements and code emission#26145

Closed
zdevito wants to merge 11 commits intogh/zdevito/107/basefrom
gh/zdevito/107/head
Closed

[JIT] add CondValue to unify refinements and code emission#26145
zdevito wants to merge 11 commits intogh/zdevito/107/basefrom
gh/zdevito/107/head

Conversation

@zdevito
Copy link
Contributor

@zdevito zdevito commented Sep 13, 2019

Stack from ghstack:

This is step towards isinstance type refinement.
It primarily does yak shaving in compiler.cpp to unify the handling
of special case behavior that occurs in conditional expressions:

  • Handling type refinement as part of emission.
  • Handling is None static-if specialization.

It introduces a CondValue object that is a Value that also has
additional type refinements that are true when that Value is true,
and potentialy a static-true/false value that, if set, will cause if
statements to be handled statically, omitting typechecking of the other side.

This ends up expanding some behavior, for instance is None specialization
used to happen only for single expressions, but now works through
boolean logic.

Differential Revision: D17359500

This is step towards isinstance type refinement.
It primarily does yak shaving in compiler.cpp to unify the handling
of special case behavior that occurs in conditional expressions:

* Handling type refinement as part of emission.
* Handling `is None` static-if specialization.

It introduces a CondValue object that is a Value that also has
additional type refinements that are true when that Value is true,
and potentialy a static-true/false value that, if set, will cause if
statements to be handled statically, omitting typechecking of the other side.

This ends up expanding some behavior, for instance `is None` specialization
used to happen only for single expressions, but now works through
boolean logic.
@zdevito zdevito requested a review from apaszke as a code owner September 13, 2019 00:21
@pytorchbot pytorchbot added the oncall: jit Add this issue/PR to JIT oncall triage queue label Sep 13, 2019
@zdevito zdevito requested review from eellison and suo September 13, 2019 00:22
[JIT] add CondValue to unify refinements and code emission

This is step towards isinstance type refinement.
It primarily does yak shaving in compiler.cpp to unify the handling
of special case behavior that occurs in conditional expressions:

* Handling type refinement as part of emission.
* Handling `is None` static-if specialization.

It introduces a CondValue object that is a Value that also has
additional type refinements that are true when that Value is true,
and potentialy a static-true/false value that, if set, will cause if
statements to be handled statically, omitting typechecking of the other side.

This ends up expanding some behavior, for instance `is None` specialization
used to happen only for single expressions, but now works through
boolean logic.

gh-metadata: pytorch pytorch 26145 gh/zdevito/107/head
Copy link
Contributor

@eellison eellison left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool! CondValue will lead to less surprising user behavior around selective compilation of if branches.

I have a couple questions / comments on some decisions made. Also i don't think we should regress the refinement calculation for intersection and union given that we are opening the door for more complicated refinements with isinstance checks moving forward.


using TypeAndRange = std::pair<TypePtr, const SourceRange*>;
enum RefinementKind {
OPTIONAL_PRESENT, // the value, which has type optional[T] is present, and
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason for RefinementKind instead of just containing a typeptr ?

bool operator==(const Refinement& rhs) const {
bool type_same =
(type_arg_ && rhs.type_arg_) ? *type_arg_ == *rhs.type_arg_ : false;
return type_same && identifier_ == rhs.identifier_ && kind_ == rhs.kind_;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure exactly how the == operator is being used but if got rid of RefinementKind and just stored a reference to the refined type, semantically equivalent refinements would work here:

x: Optional[int] = 1
isisstance(x, int) and (x is not None)

// with its true value and its false value. If a boolean that has refinements
// associated with it is used in a conditional of an if statememt, the true
// and false refinements are inserted into the corresponding blocks
using Refinements = std::vector<Refinement>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a little confusing that the struct is named RefinementSet, but is backed by a vector, and in union we add duplicates of a refinement.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed the union as we discussed in person, it now behaves more like a set.

}
case TK_NOT: {
CondValue v = emitCondExpr(Expr(expr.tree()->trees()[0]));
Value* input = emitToBool(v.value());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any case when emitCondExpr should be called but emitToBool should not follow ? could we just move emitToBool within emitCondExpr

// so we take their intersection.
return RefinementSet(
unionList(true_refinements_, rhs.true_refinements_),
intersectList(false_refinements_, rhs.false_refinements_));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

intersectList but the struct is named refinementSet

ret.setRefinement(name_mapping.first, name_mapping.second);
static Refinements intersectList(const Refinements& a, const Refinements& b) {
Refinements result;
for (const Refinement& r : a) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the motivation is for removing the existing intersection and union implementations? The new implementation probably suffices for just optional refinement, but with the possibility of more complicated refinement using isinstance and an Any type i think we should use the more correct existing implementation.

Something like isinstance(x, float) or isinstance(x, int) would just accept the second type instead of unifying them.

Note: the existing intersection and union produces the correct refinements, just the insertRefinements function incorrectly inserts a refinement when the refinement is not None.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

discussed in person: there may be unexpected consequences of unifyTypes refinement. For instance if we derive a type Scalar, it is another instance of injecting a Scalar in addition to the item call. This has led to surprising bugs in the past, so we should have a good example case before we make more liberal use of refinement. The behavior currently implemented is a subset of the valid refinements that we would do in the future, so it is safe to expand.

[JIT] add CondValue to unify refinements and code emission

This is step towards isinstance type refinement.
It primarily does yak shaving in compiler.cpp to unify the handling
of special case behavior that occurs in conditional expressions:

* Handling type refinement as part of emission.
* Handling `is None` static-if specialization.

It introduces a CondValue object that is a Value that also has
additional type refinements that are true when that Value is true,
and potentialy a static-true/false value that, if set, will cause if
statements to be handled statically, omitting typechecking of the other side.

This ends up expanding some behavior, for instance `is None` specialization
used to happen only for single expressions, but now works through
boolean logic.

gh-metadata: pytorch pytorch 26145 gh/zdevito/107/head
[JIT] add CondValue to unify refinements and code emission

This is step towards isinstance type refinement.
It primarily does yak shaving in compiler.cpp to unify the handling
of special case behavior that occurs in conditional expressions:

* Handling type refinement as part of emission.
* Handling `is None` static-if specialization.

It introduces a CondValue object that is a Value that also has
additional type refinements that are true when that Value is true,
and potentialy a static-true/false value that, if set, will cause if
statements to be handled statically, omitting typechecking of the other side.

This ends up expanding some behavior, for instance `is None` specialization
used to happen only for single expressions, but now works through
boolean logic.

gh-metadata: pytorch pytorch 26145 gh/zdevito/107/head
@zdevito zdevito requested a review from eellison September 17, 2019 00:37
[JIT] add CondValue to unify refinements and code emission

This is step towards isinstance type refinement.
It primarily does yak shaving in compiler.cpp to unify the handling
of special case behavior that occurs in conditional expressions:

* Handling type refinement as part of emission.
* Handling `is None` static-if specialization.

It introduces a CondValue object that is a Value that also has
additional type refinements that are true when that Value is true,
and potentialy a static-true/false value that, if set, will cause if
statements to be handled statically, omitting typechecking of the other side.

This ends up expanding some behavior, for instance `is None` specialization
used to happen only for single expressions, but now works through
boolean logic.

gh-metadata: pytorch pytorch 26145 gh/zdevito/107/head
Copy link
Contributor

@eellison eellison left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

// Refinement none(name, RefinementKind::OPTIONAL_NONE);

Refinement present(
name, lhs_value->type()->expect<OptionalType>()->getElementType());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably don't want to expect<OptionalType> here because it will error for something like:

a: int = 1
if a is None:

which can happen with a constant attribute that is initialized non-null

return a.identifier() == b.identifier();
}
static Refinements unionList(const Refinements& a, const Refinements& b) {
Refinements result = a;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could consider calling this unionSet now that we are enforcing uniqueness of each element

ret.setRefinement(name_mapping.first, name_mapping.second);
return result;
}
static Refinements intersectList(const Refinements& a, const Refinements& b) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment

// submodule is absent, an is None check can be used to ensure the
// accesses to the None check, which would error, are not compiled.
if (cond_value.staticIf()) {
if (*cond_value.staticIf()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not compiling one branch of an if statement with multiple staticIf checks is a new feature so maybe add tests for that

[JIT] add CondValue to unify refinements and code emission

This is step towards isinstance type refinement.
It primarily does yak shaving in compiler.cpp to unify the handling
of special case behavior that occurs in conditional expressions:

* Handling type refinement as part of emission.
* Handling `is None` static-if specialization.

It introduces a CondValue object that is a Value that also has
additional type refinements that are true when that Value is true,
and potentialy a static-true/false value that, if set, will cause if
statements to be handled statically, omitting typechecking of the other side.

This ends up expanding some behavior, for instance `is None` specialization
used to happen only for single expressions, but now works through
boolean logic.

gh-metadata: pytorch pytorch 26145 gh/zdevito/107/head
[JIT] add CondValue to unify refinements and code emission

This is step towards isinstance type refinement.
It primarily does yak shaving in compiler.cpp to unify the handling
of special case behavior that occurs in conditional expressions:

* Handling type refinement as part of emission.
* Handling `is None` static-if specialization.

It introduces a CondValue object that is a Value that also has
additional type refinements that are true when that Value is true,
and potentialy a static-true/false value that, if set, will cause if
statements to be handled statically, omitting typechecking of the other side.

This ends up expanding some behavior, for instance `is None` specialization
used to happen only for single expressions, but now works through
boolean logic.

gh-metadata: pytorch pytorch 26145 gh/zdevito/107/head
[JIT] add CondValue to unify refinements and code emission

This is step towards isinstance type refinement.
It primarily does yak shaving in compiler.cpp to unify the handling
of special case behavior that occurs in conditional expressions:

* Handling type refinement as part of emission.
* Handling `is None` static-if specialization.

It introduces a CondValue object that is a Value that also has
additional type refinements that are true when that Value is true,
and potentialy a static-true/false value that, if set, will cause if
statements to be handled statically, omitting typechecking of the other side.

This ends up expanding some behavior, for instance `is None` specialization
used to happen only for single expressions, but now works through
boolean logic.

gh-metadata: pytorch pytorch 26145 gh/zdevito/107/head
[JIT] add CondValue to unify refinements and code emission

This is step towards isinstance type refinement.
It primarily does yak shaving in compiler.cpp to unify the handling
of special case behavior that occurs in conditional expressions:

* Handling type refinement as part of emission.
* Handling `is None` static-if specialization.

It introduces a CondValue object that is a Value that also has
additional type refinements that are true when that Value is true,
and potentialy a static-true/false value that, if set, will cause if
statements to be handled statically, omitting typechecking of the other side.

This ends up expanding some behavior, for instance `is None` specialization
used to happen only for single expressions, but now works through
boolean logic.

gh-metadata: pytorch pytorch 26145 gh/zdevito/107/head
[JIT] add CondValue to unify refinements and code emission

This is step towards isinstance type refinement.
It primarily does yak shaving in compiler.cpp to unify the handling
of special case behavior that occurs in conditional expressions:

* Handling type refinement as part of emission.
* Handling `is None` static-if specialization.

It introduces a CondValue object that is a Value that also has
additional type refinements that are true when that Value is true,
and potentialy a static-true/false value that, if set, will cause if
statements to be handled statically, omitting typechecking of the other side.

This ends up expanding some behavior, for instance `is None` specialization
used to happen only for single expressions, but now works through
boolean logic.

gh-metadata: pytorch pytorch 26145 gh/zdevito/107/head
[JIT] add CondValue to unify refinements and code emission

This is step towards isinstance type refinement.
It primarily does yak shaving in compiler.cpp to unify the handling
of special case behavior that occurs in conditional expressions:

* Handling type refinement as part of emission.
* Handling `is None` static-if specialization.

It introduces a CondValue object that is a Value that also has
additional type refinements that are true when that Value is true,
and potentialy a static-true/false value that, if set, will cause if
statements to be handled statically, omitting typechecking of the other side.

This ends up expanding some behavior, for instance `is None` specialization
used to happen only for single expressions, but now works through
boolean logic.

gh-metadata: pytorch pytorch 26145 gh/zdevito/107/head
@facebook-github-bot
Copy link
Contributor

@zdevito merged this pull request in fcd1354.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Merged oncall: jit Add this issue/PR to JIT oncall triage queue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants