Page MenuHomePhabricator

[OMPIRBuilder] Add support for simd (loop) directive.

Authored by arnamoy10 on Nov 22 2021, 9:39 AM.



This patch adds OMPIRBuilder support for the simd directive (without any clause). This will be a first step towards lowering simd directive in LLVM_Flang. The patch uses existing CanonicalLoop infrastructure of IRBuilder to add the support. Also adds necessary code to add and llvm.loop metadata wherever needed.

Diff Detail

Event Timeline

arnamoy10 created this revision.Nov 22 2021, 9:39 AM
arnamoy10 requested review of this revision.Nov 22 2021, 9:39 AM
Herald added projects: Restricted Project, Restricted Project. · View Herald Transcript
arnamoy10 edited the summary of this revision. (Show Details)Nov 22 2021, 9:40 AM
tschuett added inline comments.

I believe LLVM uses American English.

tschuett added inline comments.Nov 22 2021, 9:51 AM

Properties is unused in the function?

Meinersbur requested changes to this revision.Nov 22 2021, 9:21 PM

Thanks for helping to complete the OpenMPIRBuilder implementation!


[nit] Unnecessary whitespace


Did you consider adding a check whether OpenMPIRBuilder actually implements all features requested before using it? See isSupportedByOpenMPIRBuilder.


[style] According to the LLVM coding standard, auto is only used in specific cases.

16 ↗(On Diff #388954)

Could you use regexes to match the virtual register names? The script may help with this, see the other tests.

The naming scheme for files testing the OpenMPIRBiulder is usually irbuilder_<constructs>


Capitalizing SIMD leads to long runs of capital letters. While not an LLVM coding style rule, it is still common to title-case acronyms in identifiers if the acronym is ~4 letters or more, such as OMPSimdDirective instead of OMPSIMDDirective.

For methods transformation CanonicalLoopInfo, we don't use createXYZ naming scheme which is reserved for inserting something new. Suggestions: applySimd, vectorizeLoop, or simdizeLoop.


The LLVM coding style still capitalizes parameters and variables.


The property also applies to any memory access, e.g. memset. You could use mayWriteToMemory() and mayReadFromMemory().


[serious] The access group must be unique only to the loop being accessed.


[serious] header and cond don't contain any memory accesses. body may consist of multiple basic blocks, of which this only applies to the first one.

This revision now requires changes to proceed.Nov 22 2021, 9:21 PM
arnamoy10 added inline comments.Dec 1 2021, 7:37 AM

I took a look at isSupportedByOpenMPIRBuilder() and seems like it is more suitable for a check against work-sharing loop construct, not for simd.

And for simd, I am not sure what features I should be checking for. One check I can think of is checking whether any of the not-yet-supported clauses is present in the simd directive or not. Am I on the right track?


I am inspired by existing code though

Meinersbur added inline comments.Dec 1 2021, 9:44 AM

Indeed, isSupportedByOpenMPIRBuilder is for the workshoring-loop, you could introduce another one for simd. Or modify it to accept OMPExecutableDirective as an argument. This might be useful for combined constructs such as for simd.

Best approach IMHO is to whitelist clauses. Reject any clause you did not test/add support yet. This way we can fallback to the non-OpenMPIRBuilder codegen and not break things that worked without -fopenmp-enable-irbuilder (a lot of code already does not work -fopenmp-enable-irbuilder, so don't put too much effort into getting the interaction between OpenMPIRBuilder and current codegen to work; but we need an overview what is already supported and which clauses are still require some work)


There already is a lot of code violating the coding standard, either because the coding standard changed or it slipped through a review. A prominent example is that the methods of the IRBuilder are still PascalCase, although the current standard says they should use camelCase. Same applies to SourceLocToDebugLoc itself (which every time I see it makes me think SourceLocToDebugLoc is a type)

Not a reason to not use the newest coding standard for new code.

arnamoy10 updated this revision to Diff 392189.Dec 6 2021, 2:38 PM

Addressing reviewers comments Major changes are as follows:

  1. Skipping unsupported clauses and skip the case when there is an ordered directive inside the simd construct
  2. Traversing all the blocks in the Canonical loop body (not only the first block of the loop body) to look for locations to insert metadata
  3. Making metadata unique for each Canonical loop
  4. Update test case to reflect changes.
Meinersbur requested changes to this revision.Dec 7 2021, 11:13 AM
Meinersbur added inline comments.

I think it would be better to list the clauses that are supported. In addition to being shorter, it would also reject any clause we forgot about and new ones.


[style] According to the LLVM coding standard, auto is only used in specific cases.


[style] Please use the current LLVM coding style.


[serious] This does not create a LoopID. A LoopID is a bag of properties where the first element points to itself (which automatically makes then distinct). E.g.

!0 = distinct !{!0, !{!"llvm.loop.parallel_accesses", !1}}

where !1 is an access group (eg !1 = distinct !{}).

[serious] The instruction's metadata should not be a LoopID, but a access group (eg !1 for the above example). I think you should not pass an array of properties, but just the access group MDNode.


[serious] The llvm.loop.parallel_accesses property takes an argument: the access group.


[Style] Please use the current LLVM coding standard.


[serious] Using DominatorTree to check whether we passes Exit is redundant: For any block that is dominated by Exit (by definition of dominator) we will have to visited Exit first anyway. Therefore not adding Exit itself would be sufficient.

You actually don't want paths that go to exit, but the basic blocks that come from Body (the successor of Cond) to the Continue/Latch block. By the definition of CanonicalLoopInfo, any block reachable from Body can only go to Continue[1], so it would be sufficient to iterate everything reachable from Body, including Body itself, until we hit the Latch. Header and Cond themselves are not allowed to contain any memory accesses.

The safest way to determine the set of blocks inside the loop would be using LoopInfo, which internally uses DominatorTree, but for checking dominance with the header, not the exit.

[1] There are exceptions: paths can end in an unreachable instruction and I am currently working on defining what happens at cancellation[2]. There will also be a BasicBlock like Latch/Continue that signals the end of the loop. In both cases these still contain blocks that are not inside the loop in LoopInfo's sense. However, marking instructions in them with access groups should is not a problem since the access groups marker only has an effect if inside a loop that has the llvm.loop.parallel_accesses property set to that access group.

[2] I found interesting cases in which Clang re-uses the same basic blocks (cleanup block for destructors and exception handling) for multiple paths by using a PHINode and a switch to branch to the scope it came from. That is, if part of a loop it would always branch back to another block in the loop, but statically any other switch target would be reachable as well. Not sure yet whether this will be a problem that requires us to use LoopInfo.

This revision now requires changes to proceed.Dec 7 2021, 11:13 AM
arnamoy10 updated this revision to Diff 394046.Dec 13 2021, 1:47 PM

Thanks @Meinersbur for the comments. Addressing reviewers comments:

  1. Update code to make the matadata addition correct, as per reviewers comments.
  2. Using LoopInfo to identify BasicBlocks to iterate through.
  3. Updating the test case.
  4. Following coding standard.

[testing] Could you add a test for applySimd into OpenMPIRBuilderTests.cpp, so we have a test that is independent of Clang?


The invocation is identical to the RUN on line 1. CHECKTWOLOOPS is not needed and can also be made as CHECK.


A single expected-no-diagnostics is sufficient


If you added these CHECK lines by hand, remove those that are not relevant


Do not add a regex specification for any but the first occurance. A regex specification will "overwrite" the previous match, i.e. the different occurrences of META4 would not need to be the same.


The instruction may already have an access group assigned e.g. from a nested #pragma clang loop vectorize(assume_safety). This would overwrite the access group. See LoopInfoStack::InsertHelper (CGLoopInfo.cpp) on how to assign multiple access groups, or add a TODO.


Compiler warning: warning C4189: 'DT': local variable is initialized but not referenced


OpenMPIRBuilder is part of the llvm namespace, llvm:: is not necessary.


Could you add a todo note such as:

// TODO: Generalize getting all blocks inside a CanonicalizeLoopInfo, preferably without running any passes.



The loop might already have a llvm.loop.parallel_accesses, in which case those two lists could be combined.

I don't think its very relevant, but maybe add a TODO?

Meinersbur added inline comments.Jan 13 2022, 12:34 PM
arnamoy10 updated this revision to Diff 400631.Jan 17 2022, 1:33 PM

Updating as per reviewers comments.

Adding a test case in OpenMPIRBuilderTest.cpp

Meinersbur accepted this revision.Jan 17 2022, 1:48 PM

LGTM. Thanks for the patch.


It would also be possible to use any_of instead of a loop.

This revision is now accepted and ready to land.Jan 17 2022, 1:48 PM
arnamoy10 updated this revision to Diff 400830.Jan 18 2022, 6:43 AM
  1. Trying to tackle a test failure
  2. Adding any_of() as per reviewer's suggestion.
Meinersbur added inline comments.Jan 18 2022, 8:31 AM

Can make it a one-liner.

Making any_of one-liner.

Meinersbur accepted this revision.Jan 19 2022, 6:51 AM
This revision was automatically updated to reflect the committed changes.