The omp::ParallelOp is translated to a callback that is called for each thread. It uses varargs, but the parameter passing is not working properly with SSE(UP) parameter types. Thus, the need to capture the parameters into an alloca-ed struct and pass that to the callback.
Diff Detail
- Repository
- rG LLVM Github Monorepo
Unit Tests
Event Timeline
mlir/lib/Target/LLVMIR/ModuleTranslation.cpp | ||
---|---|---|
837 ↗ | (On Diff #305566) | I would not expect the translation to modify the input module. |
mlir/lib/Target/LLVMIR/ModuleTranslation.cpp | ||
---|---|---|
837 ↗ | (On Diff #305566) | Thanks! That makes sense - I just never thought that a translator is actually translating, input and not modifying it. Moving this code to OpenMPToLLVM converter. |
mlir/lib/Target/LLVMIR/ModuleTranslation.cpp | ||
---|---|---|
837 ↗ | (On Diff #305566) | Translation is supposed to be as simple as possible. We have a dedicated pass preparing an MLIR module in the LLVM dialect for translation - LegalizeForExport. Something similar can be introduced on the OpenMP dialect, and the translator just reject the inputs it cannot handle. (On a side note, we'd better refactor the translator in such a way that it no longer needs to know about OpenMP) |
Thanks @llitchev for this patch.
FYI @jdoerfert was suggesting to fix this issue in the OpenMPIRBuilder by setting /* AggregateArgs */ to true in the CodeExtractor in /llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp.
This hit couple of issues,
- When the ArtificialEntryBlock is removed in OpenMPIRBuilder::finalize() there are some uses that are left hanging.
- Once the above was fixed it hit an assertion in the following place.
OpenMPIRBuilder::createParallel
assert(OutlinedFn.arg_size() >= 2 && "Expected at least tid and bounded tid as arguments");
mlir/lib/Target/LLVMIR/ModuleTranslation.cpp | ||
---|---|---|
837 ↗ | (On Diff #305566) | I created a simple pass that goes after the ConvertOpenMPToLLVM one. It looks like a better place for this than the translator. |
Thanks, this looks better! I have a couple of further comments.
mlir/include/mlir/Conversion/OpenMPToLLVM/ConvertOpenMPToLLVM.h | ||
---|---|---|
30 ↗ | (On Diff #305963) | This could be a function pass instead. |
mlir/include/mlir/Conversion/Passes.td | ||
227 ↗ | (On Diff #305963) | Please try to fit 80 cols. |
228 ↗ | (On Diff #305963) | Triple backslashes? We could just use single quotes inside or the code-block syntax outside... |
mlir/lib/Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp | ||
27 ↗ | (On Diff #305963) | Why is this necessary? |
59 ↗ | (On Diff #305963) | Nit: elide trivial braces |
64 ↗ | (On Diff #305963) | Nit: MLIR uses auto when it improves readability, e.g. if the type is already mentioned on the RHS (casts) or if it's too long to spell out. LLVMType looks just fine here. |
mlir/lib/Target/LLVMIR/ModuleTranslation.cpp | ||
20 ↗ | (On Diff #305963) | Please drop these |
mlir/test/Conversion/OpenMPToLLVM/openmp_float-parallel_param.mlir | ||
3 | Could we please make this test minimal and only exercise the functionality that the patch is adding? I don't think we need anything about main or _mlir_ciface or the entire initialization block here. We can use the Test dialect that supports unregistered ops as opaque producers or users of values. | |
42 | The check pattern doesn't look like valid MLIR, I am surprised pre-merge checks haven't complained. | |
47 | Prefer CHECK over CHECK-NEXT unless the semantics of the IR changes when two operations are not adjacent. The need to change another, unrelated test in these patch is the a good illustration why :) |
mlir/lib/Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp | ||
---|---|---|
24 ↗ | (On Diff #305963) | Use the auto generated base class instead of PassWrapper. |
Addressed some CR feedback on this diff.
mlir/include/mlir/Conversion/OpenMPToLLVM/ConvertOpenMPToLLVM.h | ||
---|---|---|
30 ↗ | (On Diff #305963) | This pass operates on LLVM Dialect IR. I tried to have it as a function pass (from the very beginning), but the function pass doesn't take the LLVMFuncOp (maybe I am missing something, though). Thanks! |
mlir/include/mlir/Conversion/Passes.td | ||
227 ↗ | (On Diff #305963) | I ran the git clang-format origin/master. I thought it should have fix it. Thanks! |
mlir/test/Conversion/OpenMPToLLVM/openmp_float-parallel_param.mlir | ||
42 | I have no idea why it didn't get caught. |
It doesn't make sense to do this here. The OpenMPIRBuilder is used in other places that have the same problem, especially OpenMPOpt. The logic needs to be part of the OpenMPIRBuilder.
@jdoerfert That makes sense. I looked at the code and that is LLVMIR, so Ill start moving things over.
Just finished significant testing ofg this with our AI codegen. The fix in https://reviews.llvm.org/D92189 whows some issues with number of arguments passed as varargs. The number of varargs that starts showing problems is different for different HW (it seems different for SpirV, Vulcan, MultiCore). I will update this PR.
Implemented the closure approach in the OMPIRBuilder only.
The implementation is fully encapsulated in the OMPIRBuilder only (no changes outside of this file).
It also addresses the issue/limitation of the current implementation with having more than 15 upward defined Values, passed to the parallel region as varargs.
I suspect there will be conflicts at this time. Posting the source here, so some of the previous commenters could look at it.
I think the overall approach is good to solve the problem with max 16 arguments. Is this based on current master? I left remarks wrt style and other things below, I will need to go over the logic again after those are addressed.
llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h | ||
---|---|---|
666 | Nit: use SmallVectorImpl w/o the size. No const on the pointers (*const); doesn't mean much anyway. | |
llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp | ||
446 | If it has to be an instruction, use cast. If it might no be, simply get the current instruction point from the builder. | |
692 | This should make some of the code introduced by D92189 obsolete, right? | |
838 | Use range loops above whenever possible. Use LLVM naming style for variables please, so first letter capitalized. Also no llvm::. | |
mlir/lib/Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp | ||
17 ↗ | (On Diff #311301) | leftover. |
Addressed CR feedback.
llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h | ||
---|---|---|
666 | Using the Impl now. | |
llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp | ||
446 | Using the current position from the Builder (moving one forward, so it is not the end() ). | |
692 | Yes! Most of it. Now the data is just wrapped into a capture struct before calling the synthetic ..._fork function. | |
838 | You are right ... That was one of the changes I wanted to make to the Diff. Now iterate over the parallel region blocks only. |
Fixed a failing Windows test. The issue is that that the order of the operands for add operation has changed. I cant see how these changes could cause the issue, but it is a failing test that blocks push of this Diff.
Added separate tests for Windows for specific tests.
On Windows the registers for the add operation are swapped. This test is completely unrelated to this change, but it fails.
Thanks for continuing to work on this.
Change the commit message as it is working for non integer types now, just not for "too many". Some more comments below.
llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h | ||
---|---|---|
124 | wasn't that a spelling error before? and one \param is enough ;) | |
126 | I believe we need to keep the Inner parameter here. The reasons can be found in the discussion of D92476. Short story: The callback might have the original value in a map with information attached. If we pass in only the "reloaded" value the callback cannot determine what the original was. | |
llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp | ||
441 | How do we know that -- is valid here? Couldn't Loc point to the begin of a function? If possible, let's just use Loc.IP. |
Thanks! I have a couple of comments, but I will defer to @jdoerfert for approval in any case.
llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h | ||
---|---|---|
678 | ||
680 | Nit: it looks like this file uses IP rather than InsPoint for names related to insertion points | |
llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp | ||
754 | Nit: I think https://llvm.org/docs/CodingStandards.html#don-t-evaluate-end-every-time-through-a-loop applies to .size() the same way it applies to .end() | |
789 | Nit: https://llvm.org/docs/CodingStandards.html#use-early-exits-and-continue-to-simplify-code if (CapturedValues.empty()) return; | |
790 | Nit: trailing dot | |
793 | Nit: please reserve before pushing back in a loop | |
801–802 | Nit: Builder.restoreIP(CaptureAllocaInsPoint) looks shorter | |
804–809 | I suppose you may want to have alloca inserted in a block (function entry) different from the one where you store into the memory. You need to store just before calling the fork function (or, at least, so that the store postdominates all stored values). Looking at the function API, I would have assumed CaptureAllocaInsPoint to be an insertion point at the function entry block specifically for allocas, where these insertvalues are invalid. | |
820–823 | Can we rather take each captured value and enumerate its uses, replacing those within the parallel block set? | |
llvm/test/CodeGen/XCore/threads.ll | ||
84–140 | These look irrelevant to the patch, but seem to fix a breakage upstream. Would you mind committing this separately? | |
mlir/test/Conversion/OpenMPToLLVM/openmp_float-parallel_param.mlir | ||
2 | Changes to MLIR are no longer necessary |
llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h | ||
---|---|---|
124 | Fixed. | |
680 | No need to store this value anymore. Used the InsertBB->getTerminator(), thus guaranteeing the alloca and stores are just before the fork call (they were before that call too, since the ThreadID was called last), so even if more codegen is introduced in the future the logic deals with it. | |
llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp | ||
441 | There is always an instruction before - the ThreadID was always generated, and that is what -- points to. Changed it to use InsertBB->getTerminator(). It is much more sturdy this way. Even if the codegen is changed, the alloca, insert and store will be generated always just before the forkCall. | |
754 | Fixed. | |
801–802 | Refactored. No need to store the InsertPoint. | |
804–809 | Now it is guaranteed that. the codegen of the alloca, insert, and stores are done just before the forkCall. Even if the codegen changes in the future. It was the case before because the code was generated after the ThreadID getting call (which was just before the fork). | |
820–823 | That was the first implementation I had. The issues was that the uses() was not returning all the uses (particularly the ones introduced by the loop unroller - spent bunch of time debugging it). Iterating to all the instruction parameters of the parallelRegions just works. | |
llvm/test/CodeGen/XCore/threads.ll | ||
84–140 | OK | |
mlir/test/Conversion/OpenMPToLLVM/openmp_float-parallel_param.mlir | ||
2 | Yes. This just exposes the original issue I had. I thought it is useful to have a test that verifies the underlined functionality works for MLIR. |
llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp | ||
---|---|---|
784 |
| |
795 | ||
806 | The alloca needs to go in the OuterAllocaIP passed in by the caller of CreateParallel. | |
821 | I'm not too happy with this insert/extract value scheme. Without further optimization (-O0) this might not be lowered properly. Why don't we create a GEP and load/store to the appropriate location instead? | |
831 | Instead of doing this, unpack/load the location in the PrivHelper like we did before. Also, pass the loaded value as Inner to the PrivCB so that the callback has both the original value V and the reload Inner. |
I think @ggeorgakoudis is working on an alternative API solution, we might need to pick up the MLIR parts though.
Yes, I have a solution for OMPIRBuilder. It hinges on https://reviews.llvm.org/D96854 to use the CodeExtractor for building the aggregate.
wasn't that a spelling error before? and one \param is enough ;)