Skip to content
Draft
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
efe4ccb
sample mlir generation and runnable
SamSJackson Jun 19, 2026
39930f6
demo to satisfy
SamSJackson Jun 19, 2026
94e9c15
building plan and stepping through loopy.py to see inputs
SamSJackson Jul 8, 2026
285db41
designing make_kernel function
SamSJackson Jul 14, 2026
a081fa7
incorporating mlir -> loopy flag and building pymbolic to mlir pipeline
SamSJackson Jul 20, 2026
8b4f6d8
MLIR generating stage - but MLIR typing is incorrect, need type infer…
SamSJackson Jul 21, 2026
8a94094
refactoring pyop3/lower && integrating MLIR
SamSJackson Aug 24, 2026
f50a0f7
introducing dtypes for type-inference and testing case
SamSJackson Aug 24, 2026
b573e66
push before connorjward/pyop3 merge update attempt
SamSJackson Aug 24, 2026
4c5bac4
Merge branch 'connorjward/pyop3' into SamSJackson/pyop3-mlir
SamSJackson Aug 25, 2026
3358de0
Updating lowering paths for new context class
SamSJackson Aug 25, 2026
5a1f473
folder for testing with mlir files
SamSJackson Aug 25, 2026
134d0cb
bug fixed, delayed dtype integration
SamSJackson Aug 25, 2026
8f18475
removing debugging python/text files
SamSJackson Aug 25, 2026
c569f30
cleaning up refactor, to add docstrings
SamSJackson Aug 26, 2026
28ea6c9
removing testing files from git
SamSJackson Aug 26, 2026
ea0996f
removing offloading demo files from remote
SamSJackson Aug 26, 2026
950ff8e
removing mlir class for merge
SamSJackson Aug 26, 2026
6090c48
adding docstrings
SamSJackson Aug 26, 2026
bf47636
removing dtype method for future pr
SamSJackson Aug 26, 2026
6a18af4
removing debug flags
SamSJackson Aug 26, 2026
e6b37d5
removing unnecessary requirements-build update
SamSJackson Aug 26, 2026
0fcd377
returning requirements.txt
SamSJackson Aug 26, 2026
f82365b
resolving simple PR comments
SamSJackson Aug 27, 2026
b45bbf7
moving _compile to codegen
SamSJackson Aug 27, 2026
4a2d5ab
abstracting backend from pyop3 language
SamSJackson Aug 27, 2026
af7d47e
moving petsc_mat and exscan to codegen
SamSJackson Aug 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions pyop3/expr/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
from pyop3.axis_tree import UNIT_AXIS_TREE, AxisTree
from pyop3.node import Node, Terminal


class Expression(Node, abc.ABC):

# {{{ abstract methods
Expand Down Expand Up @@ -333,7 +332,7 @@ def local_min(self) -> numbers.Number:
from pyop3.expr.visitors import get_local_min

return get_local_min(self.a) + get_local_min(self.b)

# }}}


Expand Down
5 changes: 3 additions & 2 deletions pyop3/insn/exec.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class CompilerParameters:

# TODO: handle these - need to build CompilerOptions

codegen: str = "loopy"
Comment thread
SamSJackson marked this conversation as resolved.
Outdated
# extra_cflags: tuple[str, ...] = ()
# extra_ldflags: tuple[str, ...] = ()

Expand Down Expand Up @@ -228,7 +229,7 @@ def compile(self) -> Callable[[int, ...], None]:
)
def _compile(self) -> CompiledCodeExecutor:
from pyop3.insn.visitors import collect_compiler_options
from pyop3.lower.loopy import _compile_static
from pyop3.lower.codegen import _compile_static

# Preprocess the instruction. This is an expensive operation so we
# want to avoid doing it if at all possible.
Expand Down Expand Up @@ -683,7 +684,7 @@ def _(self, arr: np.ndarray, /) -> int:
try:
import cupy as cp

@_handle_to_pointer.register
@_handle_to_pointer.register(cp.ndarray)
def _(self, arr: cp.ndarray, /) -> int:
# NOTE: This gives a pointer to a GPU memory address.
# Loopy cannot work with GPU so this will lead to a segfault.
Expand Down
130 changes: 130 additions & 0 deletions pyop3/lower/codegen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
from __future__ import annotations

import abc
import contextlib
import functools
import numbers
import os
from typing import Any

import loopy as lp
import numpy as np
import pymbolic as pym
from immutabledict import immutabledict as idict
from petsc4py import PETSc

import pyop3.axis_tree
import pyop3.buffer
import pyop3.cache
import pyop3.config
import pyop3.constants
import pyop3.dtypes
import pyop3.expr
from pyop3 import mpi, utils
from pyop3.axis_tree.tree import (
UNIT_AXIS_TREE,
IndexedAxisTree,
)
from pyop3.buffer import (
AbstractBuffer,
NullBuffer,
PetscMatBuffer,
)
from pyop3.constants import INC, MAX_RW, MAX_WRITE, MIN_RW, MIN_WRITE, READ, RW, WRITE
from pyop3.dtypes import IntType
from pyop3.insn.base import (
AbstractAssignment,
AssignmentType,
Exscan,
InstructionList,
Loop,
NonEmptyArrayAssignment,
NullInstruction,
StandaloneCalledFunction,
assignment_type_as_intent,
)

from pyop3.lower.loopy import LoopyCodegenContext
from pyop3.lower.context import _collect_temporary_shapes, _compile # NOTE: Maybe these functions could both go in transform?

# TODO: import other way around?
from pyop3.lower.transform import (
with_attach_debugger,
with_likwid_markers,
with_petsc_event,
)

def _compile_static_hashkey(op: PreprocessedOperation, compiler_parameters: ParsedCompilerParameters) -> Hashable:
return (op.disk_cache_key, compiler_parameters, pyop3.config)

@pyop3.cache.memory_and_disk_cache(
hashkey=_compile_static_hashkey,
get_comm=lambda op, *a, **kw: op.comm,
)
def _compile_static(op: InstructionExecutionContext, compiler_parameters: ParsedCompilerParameters) -> tuple:
"""Compile the operation without regard for specific data values.

This function is therefore suitable for disk caching.
Function passes compilation process to compiler-specified backend

Returns
-------
TU
datamap

"""
insn = op.preprocess()
function_name = "pyop3_loop" # TODO: Provide as kwarg

if isinstance(insn, InstructionList):
cs_expr = insn.instructions
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.

ContextClass = LoopyCodegenContext
elif compiler_parameters.codegen == "mlir":
raise NotImplementedError("Class is still being implemented.")

context = ContextClass(
Comment thread
SamSJackson marked this conversation as resolved.
Outdated
propagate_negatives=compiler_parameters.propagate_negatives,
mask_array_accesses=compiler_parameters.mask_array_accesses,
)
# NOTE: so I think LoopCollection is a better abstraction here - don't want to be
# explicitly dealing with contexts at this point. Can always sniff them out again.
# for context, ex in cs_expr:
for ex in cs_expr:
# ex = expand_implicit_pack_unpack(ex)

# add external loop indices as kernel arguments
# FIXME: removed because cs_expr needs to sniff the context now
loop_indices = {}

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!


if not context.buffer_intents:
raise pyop3.exceptions.EffectlessComputationException(
"The generated kernel does not modify any global data, this may indicate that something has gone wrong"
)

translation_unit = context.finalize_kernel(function_name, compiler_parameters)

# Extra information needed by the code executor
kernel_name_to_buffer_info = utils.invert_mapping(context.kernel_names)
buffer_intents = context.buffer_intents

# Replace buffers with their indices, dropping any temporaries. Also
# match the calling order for the kernel.
kernel_name_to_global_buffer_info = {}
global_buffer_intents = {}
for kernel_arg in translation_unit.default_entrypoint.args:
buf_view = kernel_name_to_buffer_info[kernel_arg.name]
buf_index = op.preprocessed_buffers.index(buf_view.buffer)
kernel_name_to_global_buffer_info[kernel_arg.name] = (buf_index, buf_view.nest_indices)
global_buffer_intents[buf_index] = buffer_intents[buf_view.buffer]

return translation_unit, kernel_name_to_global_buffer_info, global_buffer_intents

Loading
Loading