Skip to content

PR: Refactoring PyOP3 Codegen - #5392

Draft
SamSJackson wants to merge 25 commits into
connorjward/pyop3from
SamSJackson/pyop3-mlir
Draft

PR: Refactoring PyOP3 Codegen#5392
SamSJackson wants to merge 25 commits into
connorjward/pyop3from
SamSJackson/pyop3-mlir

Conversation

@SamSJackson

Copy link
Copy Markdown

Refactoring pyop3/lower/

PR presents abstraction in the lower/ design to allow for alternative code generation backends.

Principally, loopy.py has been broken into three separate files:

  • codegen.py - context orchestrator which returns lowered IR back to translation layer

  • context.py - generic class and high-level traversal of axis trees to generate pyop3 expressions.
    (possible that class and traversal has too many responsibilities).

  • loopy.py -implements backend-specific lowering from pyop3 to respective target IRs

This PR serves as a stepping stone to the introduction of MLIR as a code generation backend.
A new mlir.py module will subclass context.py that will construct MLIR from PyOP3 expressions, using xDSL.

- Goal: Create an interface to a code generation context (MLIR or Loopy)
- Status: Interface created, battling PETSc bug before cleaning more.

- Goal: Integrate MLIR for auto-generation
- Status: Was working but transitioning to pyop3->mlir pipeline as
  opposed to pym->mlir. Refactoring process is ongoing.
requirements tracking pyop3 && typing hints
Merged Connor's update with new setup.
Current issue with values loaded into the buffers.
Occurs even while using old lower/loopy.py

Debugging in process.
PyOP3 updates and path traversals reflected in the new lowering
structure
Still need to integrate dtype support for all op3 expressions.
Removed abstractmethod while testing.
@connorjward connorjward added the base:main Run this PR using a main (dev) build label Aug 26, 2026

@connorjward connorjward left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Key point on an initial first pass is I would like to limit the scope of what the context objects know about.

Comment thread pyop3/insn/exec.py Outdated
Comment thread pyop3/lower/codegen.py
for e in pyop3.collections.as_tuple(ex): # TODO: get rid of this loop
# context manager?
context.set_temporary_shapes(_collect_temporary_shapes(e))
_compile(e, loop_indices, context)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This code is really disgusting but not your problem!

Comment thread pyop3/lower/codegen.py Outdated
else:
cs_expr = (insn,)

if compiler_parameters.codegen == "loopy":

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think I prefer 'backend' to 'codegen'

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

That's fine, I will switch. Was only wary that backend is used often but agree that it makes more sense.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Terminology is really confusing around here. We have:

  • compile
  • codegen
  • lower

which are all in some sense interchangeable. I really want to use "compile" as the single term for everything but this conflicts with the other sense of "compiler" (i.e. GCC etc).

I think in my main branch I will do the following renaming:

  • pyop3/lower to pyop3/compile
  • pyop3/compile.py to pyop3/cc.py

File naming isn't important for this PR, but I think I will make that change soon and you may hit git conflicts.

Comment thread pyop3/lower/codegen.py Outdated
Comment thread pyop3/lower/context.py Outdated
return ctx


# NOTE: Not a big fan of how compile sits in this file.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yeah I would expect to put it in codegen.py. This file can just contain the abstract class.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Moved _compile to codegen.py.

Only point of contention - and why it was not already there - is that parse_loop_properly_this_time uses _compile in the context.py file. As such, _compile now has to be imported. This must be a local import, to avoid a circular import.

It feels like a code smell but it does work.
Possible steps for resolving the potential smell would be:

  • Move _compile function to another separate file (I think this would bloat the directory)
  • Rewrite parse_loop_properly_this_time

Else, we can leave it for later, it works fine. May just be a micro-optimisation.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

parse_loop_properly_this_time (which I think I would like you to rename) is another function that I don't think should belong to the context and ideally could be rewritten in a backend agnostic fashion.

Comment thread pyop3/lower/loopy.py

return indices

def compile_standalone_function(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

When I designed the 'context' class I imagined that it would be entirely independent from any of the pyop3 'language'. It would only see the buffers. For example here I think we could amend the add_function_call to take in more arguments. The visitor for when we hit standalone function can then be

args = [(a.buffer_view, spec.intent) for a, spec in zip(call.arguments, call.argspec, strict=True)]
context.add_function_call(call.function.code, args)

The crucial points are:

  • The context never sees the StandaloneCalledFunction type
  • The visitor for StandaloneCalledFunction can remain generic for different backends

Comment thread pyop3/lower/loopy.py
subkernel = call.function.code.with_entrypoints(frozenset())
self.add_subkernel(subkernel)

def compile_petsc_mat(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same, I'd keep it generic. You can just raise NotImplementedError for non-loopy

- rename compiler option `codegen` -> `backend`
- documenting `backend` compiler option
- functional variable renaming
- moving dispatch _compile function from context.py -> codegen.py
- local import in _parse_loop to avoid circular import
- moving dispatch _collect_temporary_shapes from context.py ->
  transform.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

base:main Run this PR using a main (dev) build

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants