Skip to content

Conversation

@reidenong
Copy link
Contributor

@reidenong reidenong commented Jan 15, 2026

This PR does constant narrowing in the JIT optimizer for branches with _IS_OP comparisons with the constant singletons True, False, None.

It introduces a new optimizer symbol, predicate, which describes a relationship between two objects. We create a new optimizer symbol upon encountering a comparison with a constant, and when we have definitive information on a object after a branch is taken (or not taken) we narrow it to a constant. Ideally, this new symbol will be easily extendable for other ops like (==, !=, contains etc.).

Unit tests intend to show constant narrowing by performing narrowing, then constant folding with other ops and showing the proof of the latter.

All feedback is welcome, with emphasis on the overall approach, how this new symbol fits into the lattice, and naming.

Thanks in advance.

@Fidget-Spinner
Copy link
Member

This is pretty cool! Thanks for doing this, I'll review it soon.

Copy link
Member

@markshannon markshannon left a comment

Choose a reason for hiding this comment

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

I like the idea, it provides some more reasoning about values and fits nicely into the existing framework.

Thanks for doing this.

I would recommend changing the construction of predicates to be more relaxed and only check for constants when the predicate is resolved. It would be more powerful, easier to reason about and maybe faster.

As an example:

p0 = a is b
p1 = b is None
exit_if(not p1)
exit_if(not p0)
# a must be None

By restricting p0 to cases where a is a const or b is a const, we cannot infer that a is None after the second exit.

extern JitOptRef _Py_uop_sym_new_compact_int(JitOptContext *ctx);
extern void _Py_uop_sym_set_compact_int(JitOptContext *ctx, JitOptRef sym);
extern JitOptRef _Py_uop_sym_new_predicate(JitOptContext *ctx, JitOptRef subject_ref, JitOptRef constant_ref, JitOptPredicateKind kind, bool invert);
extern bool _Py_uop_sym_is_known_singleton(JitOptContext *ctx, JitOptRef sym);
Copy link
Member

Choose a reason for hiding this comment

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

It doesn't matters whether something is a singleton or not, so you can drop this.


typedef enum {
JIT_PRED_IS,
// JIT_PRED_EQ,
Copy link
Member

Choose a reason for hiding this comment

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

Either remove this, or add a TODO note that we'll be adding EQ, NE, etc.


op(_IS_OP, (left, right -- b, l, r)) {
b = sym_new_type(ctx, &PyBool_Type);
bool invert = (oparg != 0);
Copy link
Member

Choose a reason for hiding this comment

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

It would be both more powerful and result in cleaner code, if we left all analysis until the predicate is resolved.
We will often have more information then.

return;
case JIT_SYM_PREDICATE_TAG:
if (!PyBool_Check(const_val) ||
(_Py_uop_sym_is_const(ctx, ref) &&
Copy link
Member

Choose a reason for hiding this comment

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

Drop these extra checks. I'm not sure they are necessary and may be creating a contradiction where none exists.

If const_val is a boolean, then we can resolve the predicate, sym_apply_predicate_narrowing(ctx, flag, as_bool(const_val))

}

JitOptRef
_Py_uop_sym_new_predicate(JitOptContext *ctx, JitOptRef subject_ref, JitOptRef constant_ref, JitOptPredicateKind kind, bool invert)
Copy link
Member

Choose a reason for hiding this comment

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

We don't need to restrict this to the case where one of the values is a constant. If a is b is true, then a and b refer to the same object regardless of whether one is constant.

@bedevere-app
Copy link

bedevere-app bot commented Jan 16, 2026

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

- Delay analysis until predicate resolves
- Generalize predicate to lhs, rhs
- Expand to accept all safe constants (not just singletons)
@reidenong reidenong requested a review from markshannon January 16, 2026 20:52
@reidenong
Copy link
Contributor Author

Hi @markshannon I think I've addressed the stated problems, a summary of changes is that

  • the predicate symbol now takes general lhs and rhs references
  • analysis is done when predicate resolves
  • all constants are accepted (not just singletons)

Additionally, you mentioned that for sym_set_const, we can resolve the predicate with sym_apply_predicate_narrowing(ctx, flag, as_bool(const_val)). I may be mistaken, but I think that it is not necessary to place this within sym_set_const, because for each _GUARD_IS_*_POP we will apply predicate narrowing first before setting the predicate symbol to const. (Is there a case where we set the predicate symbol to const not from _GUARD_IS_*_POP?)

Another implementation I initially considered would be to move the sym_apply_predicate_narrowing call from _GUARD_IS_*_POP to within sym_set_const, but I thought that might introduce an additional layer of indirection and make the code harder to reason about. Let me know if this would be a preferred approach.

Thanks!

@Fidget-Spinner
Copy link
Member

We need a test in the optimizer_symbols.c file (at the bottom, the last function).

Also CI is failing, can you please investigate? Thanks!

@python-cla-bot
Copy link

python-cla-bot bot commented Jan 16, 2026

All commit authors signed the Contributor License Agreement.

CLA signed

@Fidget-Spinner
Copy link
Member

CI failing due to #142911

@reidenong
Copy link
Contributor Author

CI failing due to #142911

ok, will take a look and get back asap

@Fidget-Spinner
Copy link
Member

@reidenong you should be able to pull in main now and it will build.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants