Skip to content

Conversation

@reidenong
Copy link
Contributor

@reidenongreidenong 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

@markshannonmarkshannon 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=aisbp1=bisNoneexit_if(notp1) exit_if(notp0) # 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.

externJitOptRef_Py_uop_sym_new_compact_int(JitOptContext*ctx);
externvoid_Py_uop_sym_set_compact_int(JitOptContext*ctx, JitOptRefsym);
externJitOptRef_Py_uop_sym_new_predicate(JitOptContext*ctx, JitOptRefsubject_ref, JitOptRefconstant_ref, JitOptPredicateKindkind, boolinvert);
externbool_Py_uop_sym_is_known_singleton(JitOptContext*ctx, JitOptRefsym);
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.


typedefenum{
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);
boolinvert= (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;
caseJIT_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, JitOptRefsubject_ref, JitOptRefconstant_ref, JitOptPredicateKindkind, boolinvert)
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

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
Copy link
ContributorAuthor

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-botbot 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
ContributorAuthor

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.

@reidenong
Copy link
ContributorAuthor

@Fidget-Spinner I have added the unit tests, ptal. thanks!

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

btw it builds now but what was the issue? I'm a bit confused on what happened.

@reidenong
Copy link
ContributorAuthor

BTW is there a need to update the symbol-lattice diagram in optimizer_symbols.c?

Copy link
Member

@markshannonmarkshannon left a comment

Choose a reason for hiding this comment

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

This looks correct, and thanks for updating the ascii art.

I've a couple of suggestions that should help long term maintenance, otherwise LGTM.

externbool_Py_uop_sym_is_compact_int(JitOptRefsym);
externJitOptRef_Py_uop_sym_new_compact_int(JitOptContext*ctx);
externvoid_Py_uop_sym_set_compact_int(JitOptContext*ctx, JitOptRefsym);
externJitOptRef_Py_uop_sym_new_predicate(JitOptContext*ctx, JitOptReflhs_ref, JitOptRefrhs_ref, JitOptPredicateKindkind, boolinvert);
Copy link
Member

Choose a reason for hiding this comment

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

Now that the predicate is symmetrical (meaning that we treat the lhs and rhs the same, not that it is commutative), I think we should drop the invert parameter and field and only use the kind, adding an IS_NOT kind.

boollhs_is_const=_Py_uop_sym_is_safe_const(ctx, lhs_ref);
boolrhs_is_const=_Py_uop_sym_is_safe_const(ctx, rhs_ref);

if (pred.kind==JIT_PRED_IS&& (lhs_is_const||rhs_is_const)){
Copy link
Member

Choose a reason for hiding this comment

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

Can you use a switch, that way the compiler will complain about missing cases as we add more kinds.

@bedevere-app
Copy link

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.

@reidenong
Copy link
ContributorAuthor

I have made the requested changes; please review again

@bedevere-app
Copy link

Thanks for making the requested changes!

@markshannon: please review the changes made to this pull request.

@Fidget-Spinner
Copy link
Member

Please fix the merge conflicts, thank you!

Copy link
Member

@markshannonmarkshannon left a comment

Choose a reason for hiding this comment

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

This looks good now. Thanks for doing this.

@markshannonmarkshannon merged commit 0b08438 into python:mainJan 22, 2026
63 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@reidenong@Fidget-Spinner@markshannon