Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GH-128939: Refactor JIT optimize structs #128940

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

markshannon
Copy link
Member

@markshannon markshannon commented Jan 17, 2025

Converts the ad-hoc set of flags in _Py_UopsSymbol into a tagged union.
This:

  • Makes the code more robust. Using an enum in a switch allows the C compiler to tell us if we miss any cases
  • More extensible. New kinds can be added by adding a new tag type
  • Easier to maintain. There are no (at least much fewer) hidden assumptions in the data
  • Renames all structs starting to _Py_Uops to start with JitOpt.
  • Adds a new tuple kind to demonstrate that it can be done easily and reliably.

@Fidget-Spinner
Copy link
Member

Can you add a test that the tuple information is actually propagated? Either through testing type guard elimination happens or something. Unless you plan to refactor the tests as well :).

@brandtbucher
Copy link
Member

Random comment: JIT is an acronym, and we use the "JIT" capitalization elsewhere. "Jit" just looks... wrong.

Copy link
Member

@brandtbucher brandtbucher left a comment

Choose a reason for hiding this comment

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

Various thoughts and questions I had while reading through. I think this is going to be a very nice improvement, though!

Other thoughts:

  • It's probably worth adding a helper for immortality checks. Basically, check if we have an immortal constant, or a known bool type. The bool part is nice, but easy to forget.
  • Any plans to update the _Py_uop_ naming to be "JIT"-ier?

Comment on lines +225 to +231
typedef union _jit_opt_symbol {
uint8_t tag;
JitOptKnownClass cls;
JitOptKnownValue value;
JitOptKnownVersion version;
JitOptTuple tuple;
} JitOptSymbol;
Copy link
Member

Choose a reason for hiding this comment

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

Could this be:

Suggested change
typedef union _jit_opt_symbol {
uint8_t tag;
JitOptKnownClass cls;
JitOptKnownValue value;
JitOptKnownVersion version;
JitOptTuple tuple;
} JitOptSymbol;
typedef struct {
uint8_t tag;
union {
JitOptKnownClass cls;
JitOptKnownValue value;
JitOptKnownVersion version;
JitOptTuple tuple;
};
} JitOptSymbol;

Then we wouldn't need to repeat the tag in each struct.

Or does the current scheme potentially pack better?

Comment on lines +203 to +207
typedef struct _jit_opt_known_class {
uint8_t tag;
uint32_t version;
PyTypeObject *type;
} JitOptKnownClass;
Copy link
Member

Choose a reason for hiding this comment

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

We could probably just get the version by doing cls.type->tp_version_tag, right? Having both here just creates opportunities for them to be stale or out-of-sync, I think.

typedef struct _jit_opt_tuple {
uint8_t tag;
uint8_t length;
uint16_t items[6];
Copy link
Member

Choose a reason for hiding this comment

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

I think we have room for 7, right?

Suggested change
uint16_t items[6];
uint16_t items[7];

Comment on lines +176 to +182
case JIT_SYM_KNOWN_VALUE_TAG:
Py_CLEAR(sym->value.value);
sym_set_bottom(ctx, sym);
return false;
case JIT_SYM_TUPLE_TAG:
sym_set_bottom(ctx, sym);
return false;
Copy link
Member

Choose a reason for hiding this comment

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

These could potentially have the same version tag, right? Looking up a method or whatever on a constant value is a normal thing to do.

Comment on lines +227 to +229
case JIT_SYM_TUPLE_TAG:
sym_set_bottom(ctx, sym);
return;
Copy link
Member

@brandtbucher brandtbucher Jan 18, 2025

Choose a reason for hiding this comment

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

This also seems like it could potentially be fine (transitioning from an abstract tuple to a concrete one)... could potentially need to do something recursive for the elements, though.

_Py_UopsSymbol *
_Py_uop_sym_new_unknown(_Py_UOpsContext *ctx)
JitOptSymbol *
_Py_uop_sym_new_unknown(JitOptContext *ctx)
{
return sym_new(ctx);
Copy link
Member

Choose a reason for hiding this comment

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

Should this be checked for NULL and replaced with out_of_space like the others?

_Py_uop_sym_tuple_getitem(JitOptContext *ctx, JitOptSymbol *sym, int item)
{

if (sym->tag != JIT_SYM_TUPLE_TAG || item < 0 || item >= sym->tuple.length) {
Copy link
Member

Choose a reason for hiding this comment

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

We can handle negative indexes pretty easily here, right?

Also, this can probably be updated to work correctly for constant tuples.

int
_Py_uop_sym_tuple_length(JitOptSymbol *sym)
{
if (sym->tag != JIT_SYM_TUPLE_TAG) {
Copy link
Member

Choose a reason for hiding this comment

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

Same here, this can probably be updated to work for constants.

Comment on lines +447 to +450
if (size > 6) {
res->tag = JIT_SYM_KNOWN_CLASS_TAG;
res->cls.type = &PyTuple_Type;
}
Copy link
Member

Choose a reason for hiding this comment

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

There are probably cases where this can be represented as a constant value (when all items are known).

Copy link
Member

Choose a reason for hiding this comment

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

Hm, although it could get tricky because nothing owns that constant, so we have to be careful not to promote its uses to constant loads.

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