8000 gh-105775: Convert LOAD_CLOSURE to a pseudo-op by polynomialherder · Pull Request #106059 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-105775: Convert LOAD_CLOSURE to a pseudo-op #106059

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

Merged
merged 14 commits into from
Jun 29, 2023

Conversation

polynomialherder
Copy link
Contributor
@polynomialherder polynomialherder commented Jun 24, 2023

Per #105775 this change converts LOAD_CLOSURE from an opcode to a pseudo-opcode. This enables removal of checks for uninitialized variables, and frees up an instruction.


📚 Documentation preview 📚: https://cpython-previews--106059.org.readthedocs.build/

This change demotes LOAD_CLOSURE from an instruction to a
pseudo-instruction. This enables superinstruction formation,
removal of checks for uninitialized variables, and frees
up an instruction.
@ghost
Copy link
ghost commented Jun 24, 2023

All commit authors signed the Contributor License Agreement.
CLA signed

@gvanrossum gvanrossum added the 🔨 test-with-refleak-buildbots Test PR w/ refleak buildbots; report in status section label Jun 24, 2023
@bedevere-bot
Copy link

🤖 New build scheduled with the buildbot fleet by @gvanrossum for commit 6921930 🤖

If you want to schedule another build, you need to add the 🔨 test-with-refleak-buildbots label again.

@bedevere-bot bedevere-bot removed the 🔨 test-with-refleak-buildbots Test PR w/ refleak buildbots; report in status section label Jun 24, 2023
storage.

Note that ``LOAD_CLOSURE`` is replaced with ``LOAD_FAST`` in the assembler.
It exists to keep bytecode a little more readable.
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this sentence is accurate anymore (if it ever was). LOAD_CLOSURE still exists in order to keep fix_cell_offsets working. Pseudo-instructions can't make bytecode more readable, since they don't exist in bytecode.

I would just remove this sentence.

Comment on lines 2 to 4

* Superinstruction formation
* Removal of checks for uninitialized variables
Copy link
Member
@carljm carljm Jun 26, 2023

Choose a reason for hiding this comment

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

I don't think this PR does enable either of these things. This is because _PyCfg_OptimizeCodeUnit is called before _PyCfg_ConvertPseudoOps, in optimize_and_assemble_code_unit. When we optimize, pseudo instructions are still present.

I think this PR is still a useful incremental step (there's no reason for LOAD_CLOSURE to exist as a distinct opcode in actual bytecode or in the eval loop), but actually enabling it to participate in these optimizations will require either a) refactoring how cell/local offsets are handled in the compiler so we don't need to track the distinction between LOAD_FAST and LOAD_CLOSURE through to fix_cell_offsets, and eliminating the LOAD_CLOSURE pseudo-op entirely, or b) adding explicit LOAD_CLOSURE support to some optimizations.

We could also experiment with trying to remove pseudo-ops before optimizing, but we'd need to audit for any dependence in the optimizer on pseudo-ops, and I think currently there'd also be a problem with prepare_localsplus (which calls fix_cell_offsets) being called after optimization, and needing LOAD_CLOSURE.

Correction: I guess this PR does enable "Removal of checks for uninitialized variables," simply by virtue of unconditionally converting LOAD_CLOSURE to LOAD_FAST (not LOAD_FAST_CHECK.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, that makes sense @carljm -- I realize now that I naively copied what was in the original issue, but should have taken into account the different approach + what was happening in _PyCompile_Assemble

inst(LOAD_CLOSURE, (-- value)) {
/* We keep LOAD_CLOSURE so that the bytecode stays more readable. */
value = GETLOCAL(oparg);
ERROR_IF(value == NULL, unbound_local_error);
Copy link
Member

Choose a reason for hiding this comment

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

I note that we are implicitly eliminating this NULL check by converting LOAD_CLOSURE to LOAD_FAST. I think that this is actually just fine, because ensuring this local exists (and is a cell variable) is the responsibility of the compiler, not the code author. The compiler will always insert a MAKE_CELL at the top of the function for any cell variable, so I can't see any way this null check would ever have failed. If it ever did, that would indicate a compiler bug, so it's not clear that UnboundLocalError would ever have made sense here anyway.

Copy link
Member

Choose a reason for hiding this comment

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

@markshannon What do you think? We could replace it with LOAD_CLOSURE_CHECK instead to preserve this. There might be a way for a debugger to delete the variable.

Copy link
Member
@carljm carljm Jun 27, 2023

Choose a reason for hiding this comment

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

There might be a way for a debugger to delete the variable.

I don't believe there is, unless the debugger is doing something unsupported and poking directly at the fastlocals array on a _PyInterpreterFrame. The supported way for debuggers to change the value of variables is via frame.f_locals dict, which is synced back to fast locals by PyFrame_LocalsToFast, and will only update cell contents, never overwrite or delete the cell itself.

This change adds a unit test for assembly
of code objects containing the LOAD_CLOSURE
pseud-instruction

We also correct the documentation and NEWS
descriptions to more accurately reflect the
effects of converting LOAD_CLOSURE to a pseudo-op
and the reason for its existence in the first place
Copy link
Member
@carljm carljm left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks @polynomialherder !

@carljm
Copy link
Member
carljm commented Jun 27, 2023

@polynomialherder can you merge in main again to resolve the conflict?

Copy link
Member
@gvanrossum gvanrossum left a comment

Choose a reason for hiding this comment

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

I recommend @iritkatriel review this as well.

inst(LOAD_CLOSURE, (-- value)) {
/* We keep LOAD_CLOSURE so that the bytecode stays more readable. */
value = GETLOCAL(oparg);
ERROR_IF(value == NULL, unbound_local_error);
Copy link
Member

Choose a reason for hiding this comment

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

@markshannon What do you think? We could replace it with LOAD_CLOSURE_CHECK instead to preserve this. There might be a way for a debugger to delete the variable.

Copy link
Member
@iritkatriel iritkatriel left a comment

Choose a reason for hiding this comment

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

Let's not insult pseudo ops by calling this a demotion. Pseudo ops are awesome.

Note that ``LOAD_CLOSURE`` is replaced with ``LOAD_FAST`` in the assembler.

.. versionchanged:: 3.13
This opcode was demoted from full opcode to pseudo-instruction
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
This opcode was demoted from full opcode to pseudo-instruction
This opcode is now a pseudo-instruction.

@iritkatriel iritkatriel changed the title gh-105775: Demote LOAD_CLOSURE to a pseudo-op gh-105775: Convert LOAD_CLOSURE to a pseudo-op Jun 27, 2023
- Describe the LOAD_CLOSURE change using more neutral language
- Add back in deleted whitespace
- Add an assertion to
  IsolatedAssembleTests.test_expression_with_pseudo_instruction_load_closure
- Regenerate generated files
@brettcannon brettcannon removed their request for review June 27, 2023 22:54
@polynomialherder
Copy link
Contributor Author

Thank you @iritkatriel and @gvanrossum for the thorough reviews -- I think I worked in those changes, let me know if I missed anything

Copy link
Member
@gvanrossum gvanrossum 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 to me. Thanks for your contribution -- this is good work, and we're looking forward to your next project!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants
0