[Incubation] Request to incubate mlir-npcomp

Per the recent (seeming) consensus regarding incubating new projects under the LLVM organization, I would like to trial the process by requesting to incubate mlir-npcomp. The project is still quite young and has been primarily developed part time by myself and Sean Silva over the last ~2 months. We set it up following discussion of a Numpy/Scipy op set and also in conjunction with a proving ground for high level dialects/transforms for lowering from “numpy-aligned” frontends (e.g. sometimes labeled TCF/TCP).

When I obtained permission from my employer (Google) to open source the effort, it was with the understanding that it was being developed with an eye towards eventual inclusion (in some fashion) under the LLVM umbrella. As such, we set up licensing and repository organization in such a way as to facilitate this down the road. We originally started it as a fork of the LLVM repository, but transitioned to the MLIR standalone template, and we found it more productive to iterate out of tree in this fashion, bumping to the latest LLVM version every week or so as needed (note: the ability to exist out of tree for MLIR dependent projects is actually quite good, and the more of us who do it, the better it becomes).

As noted in the README, there are multiple directions that this project could go in the future, possibly growing to be a full python/numpy compiler frontend or just being a source of dialects, transforms and interface code: we just need to keep building it to find out, and I am open to multiple outcomes as things progress. In addition, there has been some inquiries from the community regarding aligning this work with the LLVM foundation explicitly, as it is an easier entity for contributors to align with.

So, as the first one walking through the door, what is the process we would like to follow? I’m happy to provide more information/discussion, but I’d also be happy if with just an LGTM and someone creating an “mlir-npcomp” repository under the LLVM GitHub organization and working the rest out as we go.

Thanks.

  • Stella

Personally, I’m very excited about this as it will make it much easier for us to contribute to npcomp and I see the TCF/TCP dialects as being very important for MLIR.

+1

Steve

Per the recent (seeming) consensus regarding incubating new projects under the LLVM organization, I would like to trial the process by requesting to incubate mlir-npcomp. The project is still quite young and has been primarily developed part time by myself and Sean Silva over the last ~2 months. We set it up following discussion of a Numpy/Scipy op set and also in conjunction with a proving ground for high level dialects/transforms for lowering from “numpy-aligned” frontends (e.g. sometimes labeled TCF/TCP).

Awesome.

So, as the first one walking through the door, what is the process we would like to follow? I’m happy to provide more information/discussion, but I’d also be happy if with just an LGTM and someone creating an “mlir-npcomp” repository under the LLVM GitHub organization and working the rest out as we go.

I feel like we have general consensus that something like an incubator process is a good idea, but I think we should converge on adding a new section to the LLVM Developer Policy that describe what an incubator project is, and outline the requirements (following the policy etc).

Could you help put together a draft of a patch? If not, I can take a look at this this weekend.

-Chris

Per the recent (seeming) consensus regarding incubating new projects under the LLVM organization, I would like to trial the process by requesting to incubate mlir-npcomp. The project is still quite young and has been primarily developed part time by myself and Sean Silva over the last ~2 months. We set it up following discussion of a Numpy/Scipy op set and also in conjunction with a proving ground for high level dialects/transforms for lowering from “numpy-aligned” frontends (e.g. sometimes labeled TCF/TCP).

Awesome.

So, as the first one walking through the door, what is the process we would like to follow? I’m happy to provide more information/discussion, but I’d also be happy if with just an LGTM and someone creating an “mlir-npcomp” repository under the LLVM GitHub organization and working the rest out as we go.

I feel like we have general consensus that something like an incubator process is a good idea, but I think we should converge on adding a new section to the LLVM Developer Policy that describe what an incubator project is, and outline the requirements (following the policy etc).

Could you help put together a draft of a patch? If not, I can take a look at this this weekend.

Yes, I can do that and can likely get a draft out before the weekend (but may do it this weekend).

How do you deal with the problem of using the "right" LLVM version? As
somebody who spends a significant amount of time on a project that is
open-source but out-of-tree -- and for good reasons that mean we're
unlikely to want to incubate in this fashion -- I find this to be a
major problem.

If the goal of incubation is to eventually become part of the
llvm-project monorepo, I feel that being inside the monorepo should be
a goal early on. This would make your project more inclusive, as
others will automatically have the right LLVM version -- they don't
have to follow some syncing mechanism that you may have tooling for
inside of Google but which isn't available outside. You can always
"bump to the latest LLVM version every week or so" by doing a merge
commit.

Cheers,
Nicolai

We originally started it as a fork of the LLVM repository, but transitioned to the MLIR standalone template, and we found it more productive to iterate out of tree in this fashion, bumping to the latest LLVM version every week or so as needed (note: the ability to exist out of tree for MLIR dependent projects is actually quite good, and the more of us who do it, the better it becomes).

How do you deal with the problem of using the “right” LLVM version? As
somebody who spends a significant amount of time on a project that is
open-source but out-of-tree – and for good reasons that mean we’re
unlikely to want to incubate in this fashion – I find this to be a
major problem.

If the goal of incubation is to eventually become part of the
llvm-project monorepo, I feel that being inside the monorepo should be
a goal early on. This would make your project more inclusive, as
others will automatically have the right LLVM version – they don’t
have to follow some syncing mechanism that you may have tooling for
inside of Google but which isn’t available outside.

There is nothing specific to Google, quite the opposite actually: the “update once a week” here is purely OSS and does not work inside Google.

You can always
“bump to the latest LLVM version every week or so” by doing a merge
commit.

I’m not sure I perceive how having code that is a “read-only” mirror (the LLVM monorepo) mixed within the project repo would be helping to be “more inclusive”?
Not duplicating the monorepo helps to ensure that you don’t diverge from the rest of LLVM by patching it (you’re losing flexibility in the development of course, but then shouldn’t this just be in the monorepo in the first place?)

But it also seems to me that we’re spreading the discussion on the “right template” between the incubator thread and this particular proposal, which does not help to keep track.

We originally started it as a fork of the LLVM repository, but transitioned to the MLIR standalone template, and we found it more productive to iterate out of tree in this fashion, bumping to the latest LLVM version every week or so as needed (note: the ability to exist out of tree for MLIR dependent projects is actually quite good, and the more of us who do it, the better it becomes).

How do you deal with the problem of using the “right” LLVM version? As
somebody who spends a significant amount of time on a project that is
open-source but out-of-tree – and for good reasons that mean we’re
unlikely to want to incubate in this fashion – I find this to be a
major problem.

If the goal of incubation is to eventually become part of the
llvm-project monorepo, I feel that being inside the monorepo should be
a goal early on.

Actually that would be a big problem in practice, because it means that either:

  1. random changes in the monorepo can put the incubator into an unbuildable state
  2. people changing the monorepo need to somehow build and test and fix incubator projects

Currently, in npcomp, we have a monorepo hash that we bump periodically. That means that people can follow our README and build our project at any point by checking out the right monorepo revision. Npcomp developers have the responsibility of fixing our own code as LLVM updates.

– Sean Silva

> We originally started it as a fork of the LLVM repository, but transitioned to the MLIR standalone template, and we found it more productive to iterate out of tree in this fashion, bumping to the latest LLVM version every week or so as needed (note: the ability to exist out of tree for MLIR dependent projects is actually quite good, and the more of us who do it, the better it becomes).

How do you deal with the problem of using the "right" LLVM version? As
somebody who spends a significant amount of time on a project that is
open-source but out-of-tree -- and for good reasons that mean we're
unlikely to want to incubate in this fashion -- I find this to be a
major problem.

If the goal of incubation is to eventually become part of the
llvm-project monorepo, I feel that being inside the monorepo should be
a goal early on.

Actually that would be a big problem in practice, because it means that either:
1. random changes in the monorepo can put the incubator into an unbuildable state
2. people changing the monorepo need to somehow build and test and fix incubator projects

I think you misunderstood. The idea isn't to have the incubated
project in github.com/llvm/llvm-project. It's that the incubator
project is a _fork_ of llvm-project.

Currently, in npcomp, we have a monorepo hash that we bump periodically. That means that people can follow our README and build our project at any point by checking out the right monorepo revision. Npcomp developers have the responsibility of fixing our own code as LLVM updates.

I suppose this works, though it seems to me that this is strictly less
convenient than having the project be a fork and just merging the
llvm-project master periodically instead of changing the README and
forcing everybody to update their llvm-project checkout associated to
the project manually.

Not duplicating the monorepo helps to ensure that you don't diverge from the rest of LLVM by patching it (you're losing flexibility in the development of course, but then shouldn't this just be in the monorepo in the first place?)

The point of incubation is to have a path for getting into
llvm-project, right? At which point you have that flexibility, but we
don't give out that flexibility immediately as a free-for-all. Having
the incubated project be an llvm-project fork gives you the "training
wheels" for working in a way where you consider co-development of both
the incubated project and core LLVM. I agree that there'd need to be
guidelines about keeping the "local" changes to the code from
llvm-project small.

Really, my main motivation for this though is to make day-to-day
development simpler for the incubated project as per my replies above
:slight_smile:

Cheers,
Nicolai

I think the main consideration here should be what happens when it becomes time to integrate into LLVM.
There are several possibilities:

  1. do a merge commit, preserving the history correctly, including any modifications to LLVM. This is probably not a good idea because: 1) we suddenly get lots of non-linear history and 2) the modifications to LLVM over time probably need to get reviewed. This is only really possible with a ‘fork’
  2. Use branch rewriting like MLIR/Flang. This keeps the commit history of the incubator, but loses the ability to bisect the incubator before the merge. This is only really possible if things start out of tree.
  3. Squash the history of the incubator and apply it as a single commit. This obviously loses the history. This is probably possible developing as a fork or out of tree, but might require a significantly large integration patch. It can also be used to consolidate changes to LLVM, but those would still require review which might be difficult.

Generally, I think #2 has been a reasonably successful model. MLIR did not store a hash, but attempted to stay at head, which made it somewhat more difficult to keep up with the pace of development. I would recommend working out of tree for most incubated projects and encourage teams to push modifications to the LLVM core upstream early rather than later.

Steve

We originally started it as a fork of the LLVM repository, but transitioned to the MLIR standalone template, and we found it more productive to iterate out of tree in this fashion, bumping to the latest LLVM version every week or so as needed (note: the ability to exist out of tree for MLIR dependent projects is actually quite good, and the more of us who do it, the better it becomes).

How do you deal with the problem of using the “right” LLVM version? As
somebody who spends a significant amount of time on a project that is
open-source but out-of-tree – and for good reasons that mean we’re
unlikely to want to incubate in this fashion – I find this to be a
major problem.

If the goal of incubation is to eventually become part of the
llvm-project monorepo, I feel that being inside the monorepo should be
a goal early on.

Actually that would be a big problem in practice, because it means that either:

  1. random changes in the monorepo can put the incubator into an unbuildable state
  2. people changing the monorepo need to somehow build and test and fix incubator projects

I think you misunderstood. The idea isn’t to have the incubated
project in github.com/llvm/llvm-project. It’s that the incubator
project is a fork of llvm-project.

Currently, in npcomp, we have a monorepo hash that we bump periodically. That means that people can follow our README and build our project at any point by checking out the right monorepo revision. Npcomp developers have the responsibility of fixing our own code as LLVM updates.

I suppose this works, though it seems to me that this is strictly less
convenient than having the project be a fork and just merging the
llvm-project master periodically instead of changing the README and
forcing everybody to update their llvm-project checkout associated to
the project manually.

Not duplicating the monorepo helps to ensure that you don’t diverge from the rest of LLVM by patching it (you’re losing flexibility in the development of course, but then shouldn’t this just be in the monorepo in the first place?)

The point of incubation is to have a path for getting into
llvm-project, right? At which point you have that flexibility, but we
don’t give out that flexibility immediately as a free-for-all. Having
the incubated project be an llvm-project fork gives you the “training
wheels” for working in a way where you consider co-development of both
the incubated project and core LLVM. I agree that there’d need to be
guidelines about keeping the “local” changes to the code from
llvm-project small.

Really, my main motivation for this though is to make day-to-day
development simpler for the incubated project as per my replies above
:slight_smile:

I suspect this is going to be a case by case, based on which other top level projects are a primary dependency.

Before the repo was open, we tried it both ways, originally starting with a fork. Then, on the advice of a collaborator who had worked on the MLIR out of tree template, I set some time aside to give it a try. I was expecting to need to reorganize things but was pleasantly surprised: a couple of top level cmake changes were all that was needed. Along the way, there were a couple of other patches to the main repo cmake files to include missing things in the installed target, but that is WAI in my opinion: using them is how such things get fixed.

What I wasn’t ready for was the subtle efficiency boost of working in this way, and I expect that to be quite case by case: the smaller footprint of being able to work through structural things common to early projects is just a lot easier in a repo that has ~100s of files where complete reconfigure/build time is measured in seconds.

Early on, we were thinking we would need to maintain a lot more non-upstreamed patches to the existing core projects, which is clearly easier in a fork, but we found the layering of MLIR to make the inverse easy and efficient. Plus there are some fringe benefits to stricter layering:

  • it drives improvements back to the core projects, making them easier to use in this fashion.
  • in the case of MLIR, it got us out of the bad habit of just shoving more dialects into mlir/Dialects and friends, instead building out our own tree for local dialects, transforms and conversions. With a fork, it is almost too easy to just put things in the easiest place, and for something you actually want to grow up some day, better organization early can be pretty important.
  • it gave us more license to think about the identity of this project as a distinct entity, which, again, was a subtle pressure but, I think, a positive one.
  • (minor) the repo has its own readme at the top level, helping visually distinguish it from all of the forks without staring at the directory tree to see if it had an “npcomp” directory.

I suspect there are hurdles we haven’t faced yet but it’s been a good experience so far :slight_smile: it seems like at some point, a project will pass a critical mass, where transitioning it back to a fork will be important, but that can also be just a git-surgery script that merges it back together that we iterate on until it works.

At the outset, I didn’t think I’d be advocating for out of tree as a starting point. I expect that projects that are depending on parts of llvm that more force you into a long term development branch mode will have an entirely different experience, and would likely benefit from choosing a different starting point.

Another thing: not that long ago, all the LLVM subprojects were in separate repos and it was “standard” to clone them separately (you would clone llvm and then clone clang into llvm/tools/clang) before building. You would also maintain the revision consistency manually (most folks having scripts for their subset of the subprojects, for example to switch branches in all of the repo in sync).

We originally started it as a fork of the LLVM repository, but transitioned to the MLIR standalone template, and we found it more productive to iterate out of tree in this fashion, bumping to the latest LLVM version every week or so as needed (note: the ability to exist out of tree for MLIR dependent projects is actually quite good, and the more of us who do it, the better it becomes).

How do you deal with the problem of using the “right” LLVM version? As
somebody who spends a significant amount of time on a project that is
open-source but out-of-tree – and for good reasons that mean we’re
unlikely to want to incubate in this fashion – I find this to be a
major problem.

If the goal of incubation is to eventually become part of the
llvm-project monorepo, I feel that being inside the monorepo should be
a goal early on.

Actually that would be a big problem in practice, because it means that either:

  1. random changes in the monorepo can put the incubator into an unbuildable state
  2. people changing the monorepo need to somehow build and test and fix incubator projects

I think you misunderstood. The idea isn’t to have the incubated
project in github.com/llvm/llvm-project. It’s that the incubator
project is a fork of llvm-project.

Currently, in npcomp, we have a monorepo hash that we bump periodically. That means that people can follow our README and build our project at any point by checking out the right monorepo revision. Npcomp developers have the responsibility of fixing our own code as LLVM updates.

I suppose this works, though it seems to me that this is strictly less
convenient than having the project be a fork and just merging the
llvm-project master periodically instead of changing the README and
forcing everybody to update their llvm-project checkout associated to
the project manually.

Not duplicating the monorepo helps to ensure that you don’t diverge from the rest of LLVM by patching it (you’re losing flexibility in the development of course, but then shouldn’t this just be in the monorepo in the first place?)

The point of incubation is to have a path for getting into
llvm-project, right? At which point you have that flexibility, but we
don’t give out that flexibility immediately as a free-for-all. Having
the incubated project be an llvm-project fork gives you the “training
wheels” for working in a way where you consider co-development of both
the incubated project and core LLVM. I agree that there’d need to be
guidelines about keeping the “local” changes to the code from
llvm-project small.

Really, my main motivation for this though is to make day-to-day
development simpler for the incubated project as per my replies above
:slight_smile:

I suspect this is going to be a case by case, based on which other top level projects are a primary dependency.

Before the repo was open, we tried it both ways, originally starting with a fork. Then, on the advice of a collaborator who had worked on the MLIR out of tree template, I set some time aside to give it a try. I was expecting to need to reorganize things but was pleasantly surprised: a couple of top level cmake changes were all that was needed. Along the way, there were a couple of other patches to the main repo cmake files to include missing things in the installed target, but that is WAI in my opinion: using them is how such things get fixed.

What I wasn’t ready for was the subtle efficiency boost of working in this way, and I expect that to be quite case by case: the smaller footprint of being able to work through structural things common to early projects is just a lot easier in a repo that has ~100s of files where complete reconfigure/build time is measured in seconds.

Early on, we were thinking we would need to maintain a lot more non-upstreamed patches to the existing core projects, which is clearly easier in a fork, but we found the layering of MLIR to make the inverse easy and efficient. Plus there are some fringe benefits to stricter layering:

  • it drives improvements back to the core projects, making them easier to use in this fashion.
  • in the case of MLIR, it got us out of the bad habit of just shoving more dialects into mlir/Dialects and friends, instead building out our own tree for local dialects, transforms and conversions. With a fork, it is almost too easy to just put things in the easiest place, and for something you actually want to grow up some day, better organization early can be pretty important.
  • it gave us more license to think about the identity of this project as a distinct entity, which, again, was a subtle pressure but, I think, a positive one.
  • (minor) the repo has its own readme at the top level, helping visually distinguish it from all of the forks without staring at the directory tree to see if it had an “npcomp” directory.

+1, these have all been majorly helpful, though subtle.

Strictly speaking, we could have done it having npcomp as a “standalone” directory in a monorepo fork. The only effective difference is that our README (and we could override the top-level README just fine in such a fork) wouldn’t have a “checkout LLVM monorepo at this hash” step; it would just already be there in the checkout. Fairly minor distinction though.

– Sean Silva

I’m contributing to an external project based on MLIR (which should become public soon). That project is using the LLVM monorepo as a git submodule, allowing us to update it and track at project-specific times. It seems to work well.

-Chris

We originally started it as a fork of the LLVM repository, but transitioned to the MLIR standalone template, and we found it more productive to iterate out of tree in this fashion, bumping to the latest LLVM version every week or so as needed (note: the ability to exist out of tree for MLIR dependent projects is actually quite good, and the more of us who do it, the better it becomes).

How do you deal with the problem of using the “right” LLVM version? As
somebody who spends a significant amount of time on a project that is
open-source but out-of-tree – and for good reasons that mean we’re
unlikely to want to incubate in this fashion – I find this to be a
major problem.

I’m contributing to an external project based on MLIR (which should become public soon). That project is using the LLVM monorepo as a git submodule, allowing us to update it and track at project-specific times. It seems to work well.

The only problem I’ve had with that approach (other than the well-known usability issues of git submodules in general) is when you end up with more complicated dependency structures that bottom out on LLVM. In these diamond scenarios, it is really easy to end up with a ton of LLVM repo clones on your workstation. In general, though, I do prefer the simplicity of clone, submodule init/update and build.

Sure - no doubt. I’m not a fan of submodules either in practice, but having one that you manage seems to be working ok. It is just a balance between all the other suboptimal answers to this problem.

-Chris

Now that the proposal landed, we could resume the discussion on this proposal?

I haven’t been involved with mlir-npcomp directly so far, but I’m very interested in seeing this moving forward: it has a lot of potential as an incubator project!

Thanks, Mehdi for bringing this back up. I am indeed still interested in seeing if we can incubate this project. Especially now that we have the topic of MLIR python bindings upstream open with commits flowing, this gives us an outlet to (eventually) factor out the bindings I built in npcomp and have a non trivial consumer of them in the ecosystem.

This project is earlier phase than the other proposal (circt) and essentially has so far been two of us doing virtual pair programming via a discord channel and f2f meetings to get it bootstrapped (more slowly than we would have liked, amid our regular jobs).

Just yesterday we hit the milestone where:

  • we can import python function ASTs to our basicpy and numpy dialects (currently some simple scalar code, control flow and numpy ufunc calls like np.add)
  • minimal frontend pipeline to lower to our “TCF” dialect, intended to represent dynamic language originating tensor programs
  • pluggable backend pipeline that compiles to an in-tree reference JIT runtime and to IREE
  • enough plumbing to be able to use a decorator to compile a python function and produce a stub that will run it on one of the backends in place of the python func.

With the e2e spike in place, we’d like to collect and start disseminating our learnings as we continue. Specifically, there are a number of things that may help us think about evolving the MLIR frontend story, and having built a reference runtime, I think we’ve developed some opinions about what really should be in MLIR core on that axis (versus re-invented by every full featured backend like our npcomprt, IREE, and things like TFRT). Having the bare minimum in those areas may help us reason about those topics in a way that is hard to do with the more fully built projects.

In any case, I’d love to move this project into the incubator and continue to work on it there in closer proximity to the community. That may also help us with collaborations that are structurally harder to initiate with the current project placement.