[JIT] add CondValue to unify refinements and code emission#26145
[JIT] add CondValue to unify refinements and code emission#26145zdevito wants to merge 11 commits intogh/zdevito/107/basefrom
Conversation
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.
[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
eellison
left a comment
There was a problem hiding this comment.
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.
torch/csrc/jit/script/compiler.cpp
Outdated
|
|
||
| using TypeAndRange = std::pair<TypePtr, const SourceRange*>; | ||
| enum RefinementKind { | ||
| OPTIONAL_PRESENT, // the value, which has type optional[T] is present, and |
There was a problem hiding this comment.
What's the reason for RefinementKind instead of just containing a typeptr ?
torch/csrc/jit/script/compiler.cpp
Outdated
| 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_; |
There was a problem hiding this comment.
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>; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
fixed the union as we discussed in person, it now behaves more like a set.
torch/csrc/jit/script/compiler.cpp
Outdated
| } | ||
| case TK_NOT: { | ||
| CondValue v = emitCondExpr(Expr(expr.tree()->trees()[0])); | ||
| Value* input = emitToBool(v.value()); |
There was a problem hiding this comment.
Is there any case when emitCondExpr should be called but emitToBool should not follow ? could we just move emitToBool within emitCondExpr
torch/csrc/jit/script/compiler.cpp
Outdated
| // so we take their intersection. | ||
| return RefinementSet( | ||
| unionList(true_refinements_, rhs.true_refinements_), | ||
| intersectList(false_refinements_, rhs.false_refinements_)); |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
[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
torch/csrc/jit/script/compiler.cpp
Outdated
| // Refinement none(name, RefinementKind::OPTIONAL_NONE); | ||
|
|
||
| Refinement present( | ||
| name, lhs_value->type()->expect<OptionalType>()->getElementType()); |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
Could consider calling this unionSet now that we are enforcing uniqueness of each element
torch/csrc/jit/script/compiler.cpp
Outdated
| ret.setRefinement(name_mapping.first, name_mapping.second); | ||
| return result; | ||
| } | ||
| static Refinements intersectList(const Refinements& a, const Refinements& b) { |
| // 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()) { |
There was a problem hiding this comment.
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
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:
is Nonestatic-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 Nonespecializationused to happen only for single expressions, but now works through
boolean logic.
Differential Revision: D17359500