Skip to content

[mlir] Initial patch to add an MPI dialect #68892

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 1 commit into from
Feb 15, 2024

Conversation

AntonLydike
Copy link
Contributor

@AntonLydike AntonLydike commented Oct 12, 2023

This patch introduces the new MPI dialect into MLIR. The Message Passing Interface (MPI) is a widely-used standard for distributed programs to exchange data. This PR goes together with a talk later at today's LLVM Dev Meeting.

This is just a first, small patch to get going and add the necessary base files, so that we can add more operations in further patches.

Here's the documentation as generated by ninja mlir-doc:

'mpi' Dialect

This dialect models the Message Passing Interface (MPI), version
4.0. It is meant to serve as an interfacing dialect that is targeted
by higher-level dialects. The MPI dialect itself can be lowered to
multiple MPI implementations and hide differences in ABI. The dialect
models the functions of the MPI specification as close to 1:1 as possible
while preserving SSA value semantics where it makes sense, and uses
memref types instead of bare pointers.

This dialect is under active development, and while stability is an
eventual goal, it is not guaranteed at this juncture. Given the early
state, it is recommended to inquire further prior to using this dialect.

For an in-depth documentation of the MPI library interface, please refer
to official documentation such as the
OpenMPI online documentation.

[TOC]

Operation definition

mpi.comm_rank (mpi::CommRankOp)

Get the current rank, equivalent to MPI_Comm_rank(MPI_COMM_WORLD, &rank)

Syntax:

operation ::= `mpi.comm_rank` attr-dict `:` type(results)

Communicators other than MPI_COMM_WORLD are not supported for now.

This operation can optionally return an !mpi.retval value that can be used
to check for errors.

Results:

Result Description
retval MPI function call return value
rank 32-bit signless integer

mpi.error_class (mpi::ErrorClassOp)

Get the error class from an error code, equivalent to the MPI_Error_class function

Syntax:

operation ::= `mpi.error_class` $val attr-dict `:` type($val)

MPI_Error_class maps return values from MPI calls to a set of well-known
MPI error classes.

Operands:

Operand Description
val MPI function call return value

Results:

Result Description
errclass MPI function call return value

mpi.finalize (mpi::FinalizeOp)

Finalize the MPI library, equivalent to MPI_Finalize()

Syntax:

operation ::= `mpi.finalize` attr-dict (`:` type($retval)^)?

This function cleans up the MPI state. Afterwards, no MPI methods may
be invoked (excpet for MPI_Get_version, MPI_Initialized, and MPI_Finalized).
Notably, MPI_Init cannot be called again in the same program.

This operation can optionally return an !mpi.retval value that can be used
to check for errors.

Results:

Result Description
retval MPI function call return value

mpi.init (mpi::InitOp)

Initialize the MPI library, equivalent to MPI_Init(NULL, NULL)

Syntax:

operation ::= `mpi.init` attr-dict (`:` type($retval)^)?

This operation must preceed most MPI calls (except for very few exceptions,
please consult with the MPI specification on these).

Passing &argc, &argv is not supported currently.

This operation can optionally return an !mpi.retval value that can be used
to check for errors.

Results:

Result Description
retval MPI function call return value

mpi.recv (mpi::RecvOp)

Equivalent to MPI_Recv(ptr, size, dtype, dest, tag, MPI_COMM_WORLD, MPI_STATUS_IGNORE)

Syntax:

operation ::= `mpi.recv` `(` $ref `,` $tag `,` $rank `)` attr-dict `:` type($ref) `,` type($tag) `,` type($rank)(`->` type($retval)^)?

MPI_Recv performs a blocking receive of size elements of type dtype
from rank dest. The tag value and communicator enables the library to
determine the matching of multiple sends and receives between the same
ranks.

Communicators other than MPI_COMM_WORLD are not supprted for now.
The MPI_Status is set to MPI_STATUS_IGNORE, as the status object
is not yet ported to MLIR.

This operation can optionally return an !mpi.retval value that can be used
to check for errors.

Operands:

Operand Description
ref memref of any type values
tag 32-bit signless integer
rank 32-bit signless integer

Results:

Result Description
retval MPI function call return value

mpi.retval_check (mpi::RetvalCheckOp)

Check an MPI return value against an error class

Syntax:

operation ::= `mpi.retval_check` $val `=` $errclass attr-dict `:` type($res)

This operation compares MPI status codes to known error class
constants such as MPI_SUCCESS, or MPI_ERR_COMM.

Attributes:

AttributeMLIR TypeDescription
errclass::mlir::mpi::MPI_ErrorClassEnumAttr
MPI error class name{{% markdown %}}Enum cases: * MPI_SUCCESS (`MPI_SUCCESS`) * MPI_ERR_ACCESS (`MPI_ERR_ACCESS`) * MPI_ERR_AMODE (`MPI_ERR_AMODE`) * MPI_ERR_ARG (`MPI_ERR_ARG`) * MPI_ERR_ASSERT (`MPI_ERR_ASSERT`) * MPI_ERR_BAD_FILE (`MPI_ERR_BAD_FILE`) * MPI_ERR_BASE (`MPI_ERR_BASE`) * MPI_ERR_BUFFER (`MPI_ERR_BUFFER`) * MPI_ERR_COMM (`MPI_ERR_COMM`) * MPI_ERR_CONVERSION (`MPI_ERR_CONVERSION`) * MPI_ERR_COUNT (`MPI_ERR_COUNT`) * MPI_ERR_DIMS (`MPI_ERR_DIMS`) * MPI_ERR_DISP (`MPI_ERR_DISP`) * MPI_ERR_DUP_DATAREP (`MPI_ERR_DUP_DATAREP`) * MPI_ERR_ERRHANDLER (`MPI_ERR_ERRHANDLER`) * MPI_ERR_FILE (`MPI_ERR_FILE`) * MPI_ERR_FILE_EXISTS (`MPI_ERR_FILE_EXISTS`) * MPI_ERR_FILE_IN_USE (`MPI_ERR_FILE_IN_USE`) * MPI_ERR_GROUP (`MPI_ERR_GROUP`) * MPI_ERR_INFO (`MPI_ERR_INFO`) * MPI_ERR_INFO_KEY (`MPI_ERR_INFO_KEY`) * MPI_ERR_INFO_NOKEY (`MPI_ERR_INFO_NOKEY`) * MPI_ERR_INFO_VALUE (`MPI_ERR_INFO_VALUE`) * MPI_ERR_IN_STATUS (`MPI_ERR_IN_STATUS`) * MPI_ERR_INTERN (`MPI_ERR_INTERN`) * MPI_ERR_IO (`MPI_ERR_IO`) * MPI_ERR_KEYVAL (`MPI_ERR_KEYVAL`) * MPI_ERR_LOCKTYPE (`MPI_ERR_LOCKTYPE`) * MPI_ERR_NAME (`MPI_ERR_NAME`) * MPI_ERR_NO_MEM (`MPI_ERR_NO_MEM`) * MPI_ERR_NO_SPACE (`MPI_ERR_NO_SPACE`) * MPI_ERR_NO_SUCH_FILE (`MPI_ERR_NO_SUCH_FILE`) * MPI_ERR_NOT_SAME (`MPI_ERR_NOT_SAME`) * MPI_ERR_OP (`MPI_ERR_OP`) * MPI_ERR_OTHER (`MPI_ERR_OTHER`) * MPI_ERR_PENDING (`MPI_ERR_PENDING`) * MPI_ERR_PORT (`MPI_ERR_PORT`) * MPI_ERR_PROC_ABORTED (`MPI_ERR_PROC_ABORTED`) * MPI_ERR_QUOTA (`MPI_ERR_QUOTA`) * MPI_ERR_RANK (`MPI_ERR_RANK`) * MPI_ERR_READ_ONLY (`MPI_ERR_READ_ONLY`) * MPI_ERR_REQUEST (`MPI_ERR_REQUEST`) * MPI_ERR_RMA_ATTACH (`MPI_ERR_RMA_ATTACH`) * MPI_ERR_RMA_CONFLICT (`MPI_ERR_RMA_CONFLICT`) * MPI_ERR_RMA_FLAVOR (`MPI_ERR_RMA_FLAVOR`) * MPI_ERR_RMA_RANGE (`MPI_ERR_RMA_RANGE`) * MPI_ERR_RMA_SHARED (`MPI_ERR_RMA_SHARED`) * MPI_ERR_RMA_SYNC (`MPI_ERR_RMA_SYNC`) * MPI_ERR_ROOT (`MPI_ERR_ROOT`) * MPI_ERR_SERVICE (`MPI_ERR_SERVICE`) * MPI_ERR_SESSION (`MPI_ERR_SESSION`) * MPI_ERR_SIZE (`MPI_ERR_SIZE`) * MPI_ERR_SPAWN (`MPI_ERR_SPAWN`) * MPI_ERR_TAG (`MPI_ERR_TAG`) * MPI_ERR_TOPOLOGY (`MPI_ERR_TOPOLOGY`) * MPI_ERR_TRUNCATE (`MPI_ERR_TRUNCATE`) * MPI_ERR_TYPE (`MPI_ERR_TYPE`) * MPI_ERR_UNKNOWN (`MPI_ERR_UNKNOWN`) * MPI_ERR_UNSUPPORTED_DATAREP (`MPI_ERR_UNSUPPORTED_DATAREP`) * MPI_ERR_UNSUPPORTED_OPERATION (`MPI_ERR_UNSUPPORTED_OPERATION`) * MPI_ERR_VALUE_TOO_LARGE (`MPI_ERR_VALUE_TOO_LARGE`) * MPI_ERR_WIN (`MPI_ERR_WIN`) * MPI_ERR_LASTCODE (`MPI_ERR_LASTCODE`){{% /markdown %}}

Operands:

Operand Description
val MPI function call return value

Results:

Result Description
res 1-bit signless integer

mpi.send (mpi::SendOp)

Equivalent to MPI_Send(ptr, size, dtype, dest, tag, MPI_COMM_WORLD)

Syntax:

operation ::= `mpi.send` `(` $ref `,` $tag `,` $rank `)` attr-dict `:` type($ref) `,` type($tag) `,` type($rank)(`->` type($retval)^)?

MPI_Send performs a blocking send of size elements of type dtype to rank
dest. The tag value and communicator enables the library to determine
the matching of multiple sends and receives between the same ranks.

Communicators other than MPI_COMM_WORLD are not supprted for now.

This operation can optionally return an !mpi.retval value that can be used
to check for errors.

Operands:

Operand Description
ref memref of any type values
tag 32-bit signless integer
rank 32-bit signless integer

Results:

Result Description
retval MPI function call return value

Attribute definition

MPI_ErrorClassEnumAttr

MPI error class name

Syntax:

#mpi.errclass<
  ::mlir::mpi::MPI_ErrorClassEnum   # value
>

Enum cases:

  • MPI_SUCCESS (MPI_SUCCESS)
  • MPI_ERR_ACCESS (MPI_ERR_ACCESS)
  • MPI_ERR_AMODE (MPI_ERR_AMODE)
  • ... all other MPI error codes

Parameters:

Parameter C++ type Description
value ::mlir::mpi::MPI_ErrorClassEnum an enum of type MPI_ErrorClassEnum

Type definition

RetvalType

MPI function call return value

Syntax: !mpi.retval

This type represents a return value from an MPI function vall.
This value can be MPI_SUCCESS, MPI_ERR_IN_STATUS, or any error code.

This return value can be compared agains the known MPI error classes
represented by #mpi.errclass using the mpi.retval_check operation.

@AntonLydike AntonLydike marked this pull request as draft October 12, 2023 13:49
@llvmbot llvmbot added the mlir label Oct 12, 2023
@AntonLydike AntonLydike force-pushed the anton/initial-mpi-dialect branch from 7b3e9f5 to a6358c2 Compare October 12, 2023 13:56
@luisacicolini
Copy link

This looks interesting! I'm curious to see how it works in detail.

@kiranchandramohan
Copy link
Contributor

The Flang community is also interested in this work. Would you be interested in presenting at our regular calls? We have calls every week (The next occurrences are on Monday 16th and Wednesday 25th). Thanks in advance.

@fabianmcg
Copy link
Contributor

This looks great, thank you for working on this!
I think an RFC would be appreciated, especially to discuss future development. In this PR adding more test cases, including invalid syntax ones would be great.
Great work again!

@clementval
Copy link
Contributor

clementval commented Oct 16, 2023

I think an RFC would be appreciated, especially to discuss future development.

+1

@AntonLydike AntonLydike force-pushed the anton/initial-mpi-dialect branch from a6358c2 to 855f178 Compare October 17, 2023 12:50
@AntonLydike
Copy link
Contributor Author

@fabianmcg @clementval, the next ODM no Nov. 2nd will have a presentation and discussion round on the MPI dialect!

@clementval
Copy link
Contributor

A RFC on discord would still be appreciated to discuss the advantage of having a MPI dialect.

@AntonLydike AntonLydike force-pushed the anton/initial-mpi-dialect branch 2 times, most recently from 5e0ff46 to 4ed4b25 Compare November 6, 2023 15:33
@AntonLydike
Copy link
Contributor Author

RFC has been posted here: https://discourse.llvm.org/t/rfc-mpi-dialect/74705

@AntonLydike AntonLydike marked this pull request as ready for review November 11, 2023 22:01
@fschlimb
Copy link
Contributor

fschlimb commented Dec 1, 2023

+1
Excellent!
I suggest adding returning error codes right away.

@AntonLydike
Copy link
Contributor Author

Thanks for the feedback @fschlimb, I'll see that I get an updated PR ready for review ASAP!

@AntonLydike AntonLydike force-pushed the anton/initial-mpi-dialect branch 3 times, most recently from c9470d7 to a199f3a Compare December 14, 2023 17:07
@rengolin
Copy link
Member

The failure in Lower/OpenMP/parallel-private-clause-fixes.f90 seems unrelated and looks just like bad test (CHECK-DAG instead of CHECK). I think I've seen this error before in other unrelated PRs: @llvm/pr-subscribers-flang-fir-hlfir

@kiranchandramohan
Copy link
Contributor

The failure in Lower/OpenMP/parallel-private-clause-fixes.f90 seems unrelated and looks just like bad test (CHECK-DAG instead of CHECK). I think I've seen this error before in other unrelated PRs: @llvm/pr-subscribers-flang-fir-hlfir

Will push a fix soon. Thanks for bringing it to our attention.

@AntonLydike AntonLydike force-pushed the anton/initial-mpi-dialect branch 2 times, most recently from cecde9c to 171caa3 Compare December 23, 2023 10:16
@AntonLydike
Copy link
Contributor Author

Thank you @Groverkss for catching these! I've updated the PR to reflect your feedback!

@AntonLydike AntonLydike force-pushed the anton/initial-mpi-dialect branch from 171caa3 to fdfa330 Compare December 23, 2023 10:19
@AntonLydike AntonLydike force-pushed the anton/initial-mpi-dialect branch 2 times, most recently from 1415888 to 9e697ff Compare January 4, 2024 13:40
Copy link
Contributor

@Dinistro Dinistro left a comment

Choose a reason for hiding this comment

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

Dropped a bunch of nit comments, the general design makes a lot of sense to me.

One concern I'm having is the restriction to only work with memref types. While I understand that this is easier to work with, some users might not be able to work with memrefs and thus cannot use this dialect. AFAIK, flang doesn't use memrefs, and neither does code that will come from some lower level input (or the LLVM import).
Note that this can also be resolved once this has been landed, though.

I think this is looking good to me, but I don't have the authority to accept this, I think. @kiranchandramohan @rengolin any idea who has the authority?

@AntonLydike AntonLydike force-pushed the anton/initial-mpi-dialect branch from 9e697ff to 17f7fe0 Compare January 6, 2024 13:54
@AntonLydike AntonLydike force-pushed the anton/initial-mpi-dialect branch from 17f7fe0 to 958f37a Compare January 6, 2024 14:12
@Groverkss
Copy link
Member

This LGTM. Let's wait another week since I expect folks are still returning from vacation.

There seems to be enough interest in the MPI Dialect, and I don't see any outstanding concerns, so I'm happy to accept it after a week.

Tagging people who initially showed interest to see if there are any outstanding concerns @rengolin @nicolasvasilache @kiranchandramohan @clementval @fschlimb @fabianmcg @sogartar @tschuett @yaochengji

@kiranchandramohan
Copy link
Contributor

Could you summarise the answers to the questions in the guidelines on contributing a new dialect in the RFC? Apologies, if this has been done already, then a link will be useful.

https://mlir.llvm.org/getting_started/DeveloperGuide/#guidelines-on-contributing-a-new-dialect-or-important-components

One concern I'm having is the restriction to only work with memref types. While I understand that this is easier to work with, some users might not be able to work with memrefs and thus cannot use this dialect. AFAIK, flang doesn't use memrefs, and neither does code that will come from some lower level input (or the LLVM import).

Yes, a memref only approach will make it not immediately useful in Flang. But that need not be a blocker. Fortran has co-arrays which can potentially lower to MPI.

The failure in Lower/OpenMP/parallel-private-clause-fixes.f90 seems unrelated and looks just like bad test (CHECK-DAG instead of CHECK).

Is this still failing? I thought I had fixed in a4deb14.

@AntonLydike AntonLydike force-pushed the anton/initial-mpi-dialect branch from 958f37a to 9e28910 Compare January 12, 2024 10:33
@AntonLydike
Copy link
Contributor Author

AntonLydike commented Jan 12, 2024

Thank you @kiranchandramohan for your input!

Could you summarise the answers to the questions in the guidelines on contributing a new dialect in the RFC? Apologies, if this has been done already, then a link will be useful.

https://mlir.llvm.org/getting_started/DeveloperGuide/#guidelines-on-contributing-a-new-dialect-or-important-components

I'll see that I get good answers to these questions, especially in regards to use-case and external support!

The failure in Lower/OpenMP/parallel-private-clause-fixes.f90 seems unrelated and looks just like bad test (CHECK-DAG instead of CHECK).

Is this still failing? I thought I had fixed in a4deb14.

I rebased the PR, it seems to all be okay locally

@AntonLydike
Copy link
Contributor Author

As @kiranchandramohan said:

Could you summarise the answers to the questions in the guidelines on contributing a new dialect in the RFC? Apologies, if this has been done already, then a link will be useful.

https://mlir.llvm.org/getting_started/DeveloperGuide/#guidelines-on-contributing-a-new-dialect-or-important-components

Here are our answers as requested:

What is the overall goal of the dialect?

An MPI dialect will provide first-class message-passing primitives that can be used both as a target by higher-level code generation and as a fixed point for other lower-level efforts such as MPI-level optimizations and rewrites.

What is the first implementation milestone?

As a first implementation milestone, we will introduce ops for blocking communication and a lowering to llvm.

How does it fit into the MLIR dialect ecosystem?

It fits in as a target-able dialect that abstracts away the messy MPI ABI and helps bridge the gap between pointers and MLIR's memref.

Connection: how does it connect to the existing dialects in a compilation pipeline(s)?

The MPI dialect will work as a target for higher-level message-passing abstractions such as the sharding dialect. The lower end of a sharded linalg flow would encompass (with more intermediate steps) (linalg + sharding) -> (scf + memref + MPI) -> (llvm). We have demonstrated this approach by building a stencil compiler on a slightly different higher level message passing abstraction that goes (stencil) -> (stencil + message passing) -> (scf + memref + MPI) -> (llvm).

Consolidation: is there already a dialect with a similar goal or matching abstractions; if so, can it be improved instead of adding a new one?

We are unaware of any other dialect in MLIR filling a similar role to this one.

Reuse: how does it generalize to similar but slightly different use cases?

While we focus on a target dialect for HPC code generation, other groups are interested in exploring a different space, such as MPI-level optimizations or translating point-to-point MPI calls to other forms of communication. We are confident that our abstractions can generalize to these use cases without a significant effort. We have collaborated with Jeff Hammond from the MPI ABI standardization body to ensure that our abstractions are a sensible expression of the MPI Interface.

What is the community of users that it is serving?

The proposed MPI dialect makes MLIR accessible to the HPC community and provides MLIR's AI compiler community access to HPC-style distributed computing. MPI is a broadly established communication standard used in HPC and almost uniformly used as its communication abstraction.

Who are the future contributors/maintainers beyond those who propose the dialect?

HPC DSL developers such as Frank Schlimbach at Intel are actively looking into MLIR, and Renato Golin at Intel agreed to co-maintain the dialect with us. Unlocking access to the MLIR ecosystem through the MPI dialect will ensure they can contribute to this dialect as part of their work, ensuring that the dialect will receive the needed support.

@kiranchandramohan
Copy link
Contributor

As @kiranchandramohan said:

Could you summarise the answers to the questions in the guidelines on contributing a new dialect in the RFC? Apologies, if this has been done already, then a link will be useful.
https://mlir.llvm.org/getting_started/DeveloperGuide/#guidelines-on-contributing-a-new-dialect-or-important-components

Here are our answers as requested:

Thanks for the detailed answer. Could you post a copy of this in the RFC? Also, tag the people whom you have mentioned will help maintain.

@AntonLydike AntonLydike force-pushed the anton/initial-mpi-dialect branch from 9e28910 to 10c3699 Compare February 2, 2024 10:15
Copy link
Contributor

@sjw36 sjw36 left a comment

Choose a reason for hiding this comment

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

Thanks for this effort! Looks like a good start.
Looking forward to the non-blocking PR! Thanks.

def MPI_Retval : MPI_Type<"Retval", "retval"> {
let summary = "MPI function call return value";
let description = [{
This type represents a return value from an MPI function vall.
Copy link
Contributor

Choose a reason for hiding this comment

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

typo: vall -> call

Copy link
Collaborator

@joker-eph joker-eph left a comment

Choose a reason for hiding this comment

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

LG, please rebase and re-run the CI though.

[OpenMPI online documentation](https://www.open-mpi.org/doc/current/).
}];

let usePropertiesForAttributes = 1;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: this is the default.

@AntonLydike AntonLydike force-pushed the anton/initial-mpi-dialect branch from 10c3699 to a904552 Compare February 15, 2024 14:10
@AntonLydike
Copy link
Contributor Author

Thank you @sjw36 and @joker-eph for your reviews, I addressed your feedback in the latest update to this PR

@Groverkss
Copy link
Member

@AntonLydike I reverted this for now #81884 . There seem to be buildbot failures.

#include "mlir/Dialect/MPI/IR/MPIAttrDefs.cpp.inc"

#define GET_OP_CLASSES
#include "mlir/Dialect/MPI/IR/MPIOps.cpp.inc"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Seems like these two lines should be removed

@AntonLydike
Copy link
Contributor Author

Thank you @Groverkss and @joker-eph for the quick revert and tips on how to fix. I have re-opened a PR with the fix incorporated in #81975

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

Successfully merging this pull request may close these issues.