Skip to content

Conversation

ashermancinelli
Copy link
Contributor

Adds initial support for Python bindings to the OpenACC dialect.

  • The bindings do not provide any niceties yet, just the barebones exposure of the dialect to Python. Construction of OpenACC ops is therefore verbose and somewhat inconvenient, as evidenced by the test.
  • The test only constructs one module, but I attempted to use enough operations to be meaningful. It does not test all the ops exposed, but does contain a realistic example of a memcpy idiom.
@llvmbot
Copy link
Member

llvmbot commented Oct 15, 2025

@llvm/pr-subscribers-mlir-openacc

@llvm/pr-subscribers-mlir

Author: Asher Mancinelli (ashermancinelli)

Changes

Adds initial support for Python bindings to the OpenACC dialect.

  • The bindings do not provide any niceties yet, just the barebones exposure of the dialect to Python. Construction of OpenACC ops is therefore verbose and somewhat inconvenient, as evidenced by the test.
  • The test only constructs one module, but I attempted to use enough operations to be meaningful. It does not test all the ops exposed, but does contain a realistic example of a memcpy idiom.

Full diff: https://github.com/llvm/llvm-project/pull/163620.diff

4 Files Affected:

  • (modified) mlir/python/CMakeLists.txt (+9)
  • (added) mlir/python/mlir/dialects/OpenACCOps.td (+14)
  • (added) mlir/python/mlir/dialects/openacc.py (+6)
  • (added) mlir/test/python/dialects/openacc.py (+138)
diff --git a/mlir/python/CMakeLists.txt b/mlir/python/CMakeLists.txt index 9f5246de6bda0..c643d32e22174 100644 --- a/mlir/python/CMakeLists.txt +++ b/mlir/python/CMakeLists.txt @@ -134,6 +134,15 @@ declare_mlir_dialect_python_bindings( dialects/func.py DIALECT_NAME func) +declare_mlir_dialect_python_bindings( + ADD_TO_PARENT MLIRPythonSources.Dialects + ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/mlir" + TD_FILE dialects/OpenACCOps.td + SOURCES + dialects/openacc.py + DIALECT_NAME acc + DEPENDS acc_common_td) + declare_mlir_dialect_python_bindings( ADD_TO_PARENT MLIRPythonSources.Dialects ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/mlir" diff --git a/mlir/python/mlir/dialects/OpenACCOps.td b/mlir/python/mlir/dialects/OpenACCOps.td new file mode 100644 index 0000000000000..69a3002e73b81 --- /dev/null +++ b/mlir/python/mlir/dialects/OpenACCOps.td @@ -0,0 +1,14 @@ +//===-- OpenACCOps.td - Entry point for OpenACCOps bind ------------*- tablegen -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef PYTHON_BINDINGS_OPENACC_OPS +#define PYTHON_BINDINGS_OPENACC_OPS + +include "mlir/Dialect/OpenACC/OpenACCOps.td" + +#endif diff --git a/mlir/python/mlir/dialects/openacc.py b/mlir/python/mlir/dialects/openacc.py new file mode 100644 index 0000000000000..e06830a9c48f3 --- /dev/null +++ b/mlir/python/mlir/dialects/openacc.py @@ -0,0 +1,6 @@ +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +from ._acc_ops_gen import * + diff --git a/mlir/test/python/dialects/openacc.py b/mlir/test/python/dialects/openacc.py new file mode 100644 index 0000000000000..bd607febe21cc --- /dev/null +++ b/mlir/test/python/dialects/openacc.py @@ -0,0 +1,138 @@ +# RUN: python %s | FileCheck %s +from mlir.ir import ( + Context, + FunctionType, + Location, + Module, + InsertionPoint, + IntegerType, + IndexType, + MemRefType, + F32Type, + Block, + ArrayAttr, + Attribute, + UnitAttr, + StringAttr, + DenseI32ArrayAttr, + ShapedType, +) +from mlir.dialects import openacc, func, arith, memref + + +def run(f): + print("\n// TEST:", f.__name__) + with Context(), Location.unknown(): + f() + return f + + +@run +def testManualReconstructedKernel(): + module = Module.create() + + # Add required module attributes + module.operation.attributes["dlti.dl_spec"] = Attribute.parse("#dlti.dl_spec<>") + module.operation.attributes["gpu.container_module"] = UnitAttr.get() + + i32 = IntegerType.get_signless(32) + i64 = IntegerType.get_signless(64) + f32 = F32Type.get() + dynamic = ShapedType.get_dynamic_size() + memref_f32_1d_any = MemRefType.get([dynamic], f32) + + with InsertionPoint(module.body): + function_type = FunctionType.get( + [memref_f32_1d_any, memref_f32_1d_any, i64], [] + ) + f = func.FuncOp( + type=function_type, + name="memcpy_idiom", + ) + f.attributes["sym_visibility"] = StringAttr.get("public") + + with InsertionPoint(f.add_entry_block()): + c1024 = arith.ConstantOp(i32, 1024) + c128 = arith.ConstantOp(i32, 128) + + parallel_op = openacc.ParallelOp( + asyncOperands=[], + waitOperands=[], + numGangs=[c1024], + numWorkers=[], + vectorLength=[c128], + reductionOperands=[], + privateOperands=[], + firstprivateOperands=[], + dataClauseOperands=[], + ) + + # Set required device_type and segment attributes to satisfy verifier + acc_device_none = ArrayAttr.get([Attribute.parse("#acc.device_type<none>")]) + parallel_op.numGangsDeviceType = acc_device_none + parallel_op.numGangsSegments = DenseI32ArrayAttr.get([1]) + parallel_op.vectorLengthDeviceType = acc_device_none + + parallel_block = Block.create_at_start(parent=parallel_op.region, arg_types=[]) + + with InsertionPoint(parallel_block): + c0 = arith.ConstantOp(i64, 0) + c1 = arith.ConstantOp(i64, 1) + + loop_op = openacc.LoopOp( + results_=[], + lowerbound=[c0], + upperbound=[f.arguments[2]], + step=[c1], + gangOperands=[], + workerNumOperands=[], + vectorOperands=[], + tileOperands=[], + cacheOperands=[], + privateOperands=[], + reductionOperands=[], + firstprivateOperands=[], + ) + + # Set loop attributes: gang and independent on device_type<none> + acc_device_none = ArrayAttr.get([Attribute.parse("#acc.device_type<none>")]) + loop_op.gang = acc_device_none + loop_op.independent = acc_device_none + + loop_block = Block.create_at_start(parent=loop_op.region, arg_types=[i64]) + + with InsertionPoint(loop_block): + idx0 = arith.index_cast( + out=IndexType.get(), in_=loop_block.arguments[0] + ) + val = memref.load(memref=f.arguments[1], indices=[idx0]) + idx1 = arith.index_cast( + out=IndexType.get(), in_=loop_block.arguments[0] + ) + memref.store(value=val, memref=f.arguments[0], indices=[idx1]) + openacc.YieldOp([]) + + openacc.YieldOp([]) + + func.ReturnOp([]) + + print(module) + + # CHECK-LABEL: func.func public @memcpy_idiom + # CHECK-SAME: (%[[ARG0:.*]]: memref<?xf32>, %[[ARG1:.*]]: memref<?xf32>, %[[ARG2:.*]]: i64) { + # CHECK: %[[CONSTANT_0:.*]] = arith.constant 1024 : i32 + # CHECK: %[[CONSTANT_1:.*]] = arith.constant 128 : i32 + # CHECK: acc.parallel num_gangs({%[[CONSTANT_0]] : i32}) vector_length(%[[CONSTANT_1]] : i32) { + # CHECK: %[[CONSTANT_2:.*]] = arith.constant 0 : i64 + # CHECK: %[[CONSTANT_3:.*]] = arith.constant 1 : i64 + # CHECK: acc.loop gang control(%[[VAL_0:.*]] : i64) = (%[[CONSTANT_2]] : i64) to (%[[ARG2]] : i64) step (%[[CONSTANT_3]] : i64) { + # CHECK: %[[INDEX_CAST_0:.*]] = arith.index_cast %[[VAL_0]] : i64 to index + # CHECK: %[[LOAD_0:.*]] = memref.load %[[ARG1]][%[[INDEX_CAST_0]]] : memref<?xf32> + # CHECK: %[[INDEX_CAST_1:.*]] = arith.index_cast %[[VAL_0]] : i64 to index + # CHECK: memref.store %[[LOAD_0]], %[[ARG0]][%[[INDEX_CAST_1]]] : memref<?xf32> + # CHECK: acc.yield + # CHECK: } + # CHECK: acc.yield + # CHECK: } + # CHECK: return + # CHECK: } 
Copy link

github-actions bot commented Oct 15, 2025

✅ With the latest revision this PR passed the Python code formatter.

Copy link
Member

@grypp grypp 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. I just left minor comments

Copy link
Contributor

@razvanlupusoru razvanlupusoru left a comment

Choose a reason for hiding this comment

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

Thank you Asher!

Also, do you know what is the process of maintaining and testing the bindings? Let's say I update the builders in acc dialect, do I need to do anything special? Will the python tests automatically be exercised when I build MLIR normally?

@ashermancinelli
Copy link
Contributor Author

ashermancinelli commented Oct 15, 2025

@razvanlupusoru Will the python tests automatically be exercised when I build MLIR normally?

No, you'll need to enable the python bindings for the tests to run - let's chat offline. I believe the builders should catch big stuff in the usual LLVM CI though. Thanks for the review!

@ashermancinelli ashermancinelli enabled auto-merge (squash) October 16, 2025 14:21
@ashermancinelli ashermancinelli merged commit e5825c4 into llvm:main Oct 16, 2025
10 checks passed
@llvm-ci
Copy link
Collaborator

llvm-ci commented Oct 16, 2025

LLVM Buildbot has detected a new failure on builder mlir-nvidia-gcc7 running on mlir-nvidia while building mlir at step 7 "test-build-check-mlir-build-only-check-mlir".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/116/builds/19787

Here is the relevant piece of the build log for the reference
Step 7 (test-build-check-mlir-build-only-check-mlir) failure: test (failure) ******************** TEST 'MLIR :: python/dialects/openacc.py' FAILED ******************** Exit Code: 127 Command Output (stdout): -- # RUN: at line 1 python /vol/worker/mlir-nvidia/mlir-nvidia-gcc7/llvm.src/mlir/test/python/dialects/openacc.py | /vol/worker/mlir-nvidia/mlir-nvidia-gcc7/llvm.obj/bin/FileCheck /vol/worker/mlir-nvidia/mlir-nvidia-gcc7/llvm.src/mlir/test/python/dialects/openacc.py # executed command: python /vol/worker/mlir-nvidia/mlir-nvidia-gcc7/llvm.src/mlir/test/python/dialects/openacc.py # .---command stderr------------ # | 'python': command not found # `----------------------------- # error: command failed with exit status: 127 -- ******************** 
@llvm-ci
Copy link
Collaborator

llvm-ci commented Oct 16, 2025

LLVM Buildbot has detected a new failure on builder mlir-rocm-mi200 running on mi200-buildbot while building mlir at step 7 "test-build-check-mlir-build-only-check-mlir".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/177/builds/22718

Here is the relevant piece of the build log for the reference
Step 7 (test-build-check-mlir-build-only-check-mlir) failure: test (failure) ******************** TEST 'MLIR :: python/dialects/openacc.py' FAILED ******************** Exit Code: 127 Command Output (stdout): -- # RUN: at line 1 python /vol/worker/mi200-buildbot/mlir-rocm-mi200/llvm-project/mlir/test/python/dialects/openacc.py | /vol/worker/mi200-buildbot/mlir-rocm-mi200/build/bin/FileCheck /vol/worker/mi200-buildbot/mlir-rocm-mi200/llvm-project/mlir/test/python/dialects/openacc.py # executed command: python /vol/worker/mi200-buildbot/mlir-rocm-mi200/llvm-project/mlir/test/python/dialects/openacc.py # .---command stderr------------ # | 'python': command not found # `----------------------------- # error: command failed with exit status: 127 -- ******************** 
@ashermancinelli
Copy link
Contributor Author

Investigating

@llvm-ci
Copy link
Collaborator

llvm-ci commented Oct 16, 2025

LLVM Buildbot has detected a new failure on builder mlir-nvidia running on mlir-nvidia while building mlir at step 7 "test-build-check-mlir-build-only-check-mlir".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/138/builds/20517

Here is the relevant piece of the build log for the reference
Step 7 (test-build-check-mlir-build-only-check-mlir) failure: test (failure) ******************** TEST 'MLIR :: python/dialects/openacc.py' FAILED ******************** Exit Code: 127 Command Output (stdout): -- # RUN: at line 1 python /vol/worker/mlir-nvidia/mlir-nvidia/llvm.src/mlir/test/python/dialects/openacc.py | /vol/worker/mlir-nvidia/mlir-nvidia/llvm.obj/bin/FileCheck /vol/worker/mlir-nvidia/mlir-nvidia/llvm.src/mlir/test/python/dialects/openacc.py # executed command: python /vol/worker/mlir-nvidia/mlir-nvidia/llvm.src/mlir/test/python/dialects/openacc.py # .---command stderr------------ # | 'python': command not found # `----------------------------- # error: command failed with exit status: 127 -- ******************** 
ashermancinelli added a commit that referenced this pull request Oct 16, 2025
This test passed locally because I had a python environment with the `python` command available, but I should have used the `%PYTHON` lit command substitution instead. Fixes buildbot failures from #163620.
@cmtice
Copy link
Contributor

cmtice commented Oct 16, 2025

We're seeing a build error with this test:

exit status 2
Traceback (most recent call last):
../llvm-project/mlir/test/python/dialects/openacc.py", line 3, in
from mlir.ir import (
ImportError: No module named mlir.ir
FileCheck error: '' is empty.
../llvm-project/mlir/test/python/dialects/openacc.py

@ashermancinelli
Copy link
Contributor Author

@cmtice Thanks for reporting! Is there a buildbot or log I can look at? I'm surprised you can't find the mlir.ir module.

rupprecht added a commit to rupprecht/llvm-project that referenced this pull request Oct 16, 2025
rupprecht added a commit that referenced this pull request Oct 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

7 participants