This is an archive of the discontinued LLVM Phabricator instance.

[flang] Add TargetRewrite pass
ClosedPublic

Authored by rovka on Nov 1 2021, 2:00 AM.

Details

Summary

This patch adds the basic infrastructure for the TargetRewrite pass,
which rewrites certain FIR dialect operations into target specific
forms. In particular, it converts boxchar function parameters, call
arguments and return values. Other rewritings will be included in
future patches.

This patch is part of the effort for upstreaming the fir-dev branch.

Co-authored-by: Eric Schweitz <eschweitz@nvidia.com>
Co-authored-by: Jean Perier <jperier@nvidia.com>

Diff Detail

Event Timeline

rovka created this revision.Nov 1 2021, 2:00 AM
rovka requested review of this revision.Nov 1 2021, 2:00 AM
Herald added a project: Restricted Project. · View Herald TranscriptNov 1 2021, 2:00 AM

Thanks for sending this @rovka ! Please see my questions/suggestions inline.

flang/lib/Optimizer/CodeGen/Target.h
75

What is sret?

flang/lib/Optimizer/CodeGen/TargetRewrite.cpp
57

[nit] IR --> MLIR (I think that IR is too ambiguous here)

flang/test/Fir/target.fir
6

Could you add comments to highlight which cases from the pass implementation are tested? Based on comments in TargetRewrite.cpp, I'd expect to see

  • type conversion on signatures and call sites.
  • ops conversion in target-specific patterns.
54

This signature is also modified - could you add a CHECK line?

rovka added inline comments.Nov 1 2021, 5:24 AM
flang/lib/Optimizer/CodeGen/Target.h
75

It's an LLVM parameter attribute for a parameter that's actually the return value of the function (probably short for 'struct return'). Would it be clearer if I rephrased the comment to "[...] converted that return value to an argument with the sret attribute"? The attribute is also mentioned in the last sentence in the comment, so I'm not sure if rephrasing really adds anything.

flang/test/Fir/target.fir
54

Good point, thanks!

rovka updated this revision to Diff 383784.Nov 1 2021, 5:25 AM

Fixup comments based on feedback from Andrzej.

rovka updated this revision to Diff 383786.Nov 1 2021, 6:01 AM

Fixup test (didn't rename function everywhere).

rovka updated this revision to Diff 383790.Nov 1 2021, 6:14 AM
rovka updated this revision to Diff 383791.Nov 1 2021, 6:25 AM

Thanks for addressing my comments! I've added a few more suggestions, but nothing major.

flang/include/flang/Optimizer/CodeGen/CodeGen.h
31

[nit] In line with the change in TargetRewrite.cpp, IR --> FIR

flang/lib/Optimizer/CodeGen/Target.cpp
39

I think that I understand why append=!sret, but why is sret initialized with {}rather than the input argument?

flang/lib/Optimizer/CodeGen/Target.h
71

nit

75

Ah, thanks! I think that context is key here :) How about:

/// A function that returns a `boxchar<n>` type value must already have
/// converted that return value to an argument decorated with LLVM's `sret` attribute.

?

I don't know what Attribute is L78 referring to TBH. I'm guessing fir::details::Attributes?

flang/lib/Optimizer/CodeGen/TargetRewrite.cpp
26–30

[nit] Why not move this to the top of the file? It looks like a comment that will apply to everything in this file.

flang/test/Fir/target.fir
34

Would it make sense to add a few more cases here?

// More than one `sret` arg (verify that the array length is stored next to the `sret` arg)
func @boxcharsret_1(%arg0 : !fir.boxchar<1> {llvm.sret}, %arg1 : !fir.boxchar<1> {llvm.sret}, %arg2 : !fir.boxchar<1>) {
  return
}

// Multiple non `sret` args (verify that the array length is stored at the end, after the array args)
func @boxcharsret_2(%arg0 : !fir.boxchar<1> {llvm.sret}, %arg1 : !fir.boxchar<1>, %arg2 : !fir.boxchar<1>) {
  return
}

You can skip the function body and just verify that the signature is updated.

rovka added inline comments.Nov 2 2021, 2:49 AM
flang/lib/Optimizer/CodeGen/Target.cpp
39

Good catch. That looks like an oversight and it seems we're never actually checking the value of that 'sret' anyway. I'm just going to trim this class and we can add the other bits back when actually needed.

flang/lib/Optimizer/CodeGen/TargetRewrite.cpp
26–30

Fair point

flang/test/Fir/target.fir
34

Sure, I can add a testcase for the latter, but I think if we try to do multiple srets we'll hit the FIXME in this patch at TargetRewrite.cpp line 126. To be honest I'm not sure if multiple srets are possible, my understanding of LLVM IR is that if a function has multiple results they all get bundled up in a single struct and that's the sret argument (there's no use in having more than 1). Personally I would sooner have an llvm_unreachable on that path. But then again my understanding might be blatantly wrong or skewed towards how clang does things. It would be nice to have some input from someone that actually knows FIR :)

rovka updated this revision to Diff 384018.Nov 2 2021, 2:50 AM
rovka marked 5 inline comments as done and an inline comment as not done.Nov 2 2021, 2:52 AM
awarzynski accepted this revision.Nov 2 2021, 7:02 AM

LGTM!

Thank you for addressing my comments :)

flang/test/Fir/target.fir
34

Thanks! I would be in favor of llvm_unreachable on L126 in TargetRewrite.cpp, but will leave it as a nit.

This revision is now accepted and ready to land.Nov 2 2021, 7:02 AM
schweitz added inline comments.Nov 2 2021, 8:17 AM
flang/lib/Optimizer/CodeGen/Target.cpp
39

sret is a parameter to the function as well as a field in the attribute set.

Other targets and data types will need sret support. I don't know that dropping it on the floor will help.

flang/lib/Optimizer/CodeGen/Target.h
75

How about putting a link to the LLVM language reference? No need to restate the LLVM concept again here, as these rewrites are specifically targeting LLVM.

flang/test/Fir/target.fir
34

I don't think llvm_unreachable is what we want here. (Especially introducing undefined behavior for what could become a valid case.)

FIR allows specialization of functions and letting them return anonymous tuples/vectors of results.

That said, the definition for exactly how the ABI should look in LLVM IR for these has not been nailed down. Currently this takes a hands off approach and omits the rewriting. That may be sufficient or not in the long run.

rovka added inline comments.Nov 3 2021, 2:16 AM
flang/lib/Optimizer/CodeGen/Target.cpp
39

I agree that it will probably be needed in the future and we can add it back when that happens. I also removed the byval, which I'll be adding back in the very next patch (rewriting complex stuff). I'd like to avoid having dead/untested code around, since that makes it difficult to know for sure what's properly supported. If somebody did need the sret for something and ran into this bug it would probably take them more time to figure out what's going on than it would take them to add it from scratch.

flang/lib/Optimizer/CodeGen/Target.h
75

Sounds like a good idea, thanks.

flang/test/Fir/target.fir
34

I don't think llvm_unreachable is what we want here. (Especially introducing undefined behavior for what could become a valid case.)

It's only undefined behaviour for release builds :) Otherwise it's a very important tool for finding and debugging issues.

In any case, I only suggested llvm_unreachable because that's what I'm used to from the rest of llvm. What I really meant was something that would either crash or elegantly error out rather than roll along and generate something that we're uncertain about. Do we have something better that we can use? I see this has been discussed before here but I don't know the whole story.

FIR allows specialization of functions and letting them return anonymous tuples/vectors of results.

That said, the definition for exactly how the ABI should look in LLVM IR for these has not been nailed down. Currently this takes a hands off approach and omits the rewriting. That may be sufficient or not in the long run.

I'm not sure that's the best approach. If code outside TargetRewrite doesn't know how to handle a single boxchar sret it seems unlikely that it will know how to handle multiple boxchar srets, so we'll probably either get errors in other parts of the compiler or worse generate wrong code. I think it's better to be conservative and get a hard error here (and in any other places that may touch sret but don't yet handle multiple srets). This will make it easier to figure out which parts need to be updated if we do decide to support multiple srets in the future.

As a sidenote, I also tried to run a test with 2 srets, and the output seemed buggy to me (it left both srets in but also added a rewritten one). I didn't investigate much because if this is not supported in the rest of the pipeline I don't think it's worth putting any effort in it right now.

awarzynski added inline comments.Nov 3 2021, 4:46 AM
flang/lib/Optimizer/CodeGen/Target.cpp
39

+1

flang/test/Fir/target.fir
34

I only suggested llvm_unreachable because that's what I'm used to from the rest of llvm.

+1. llvm_unreachable is a well known and popular mechanism within LLVM. In this case, we would be using it to document that a particular case is not supported _yet_. If we do decide to implement support for more sret arguments, then the compiler will hit this code and llvm_unreachable will contain a helpful message that will add some useful context. This context is quite clear just now (we are discussing it), but it might not be so obvious to our future selves. Or to some other developer that will be adding this support.

schweitz added inline comments.Nov 3 2021, 8:45 AM
flang/lib/Optimizer/CodeGen/Target.cpp
39

I don't see a problem with adding things in small increments, so long as there is a plan to not leave behind holes in the functionality.

flang/test/Fir/target.fir
34

Sorry for the misunderstanding.

Yes, we do not currently return multiple values. And obviously the LLVM-IR dialect isn't going to deal well with FIR types.

Placing a TODO() here is the current approach.

(In Flang, llvm_unreachable is considered bad practice precisely because it gets compiled to undefined behavior in release builds, which some developers prefer.)

mehdi_amini added inline comments.Nov 3 2021, 12:40 PM
flang/test/Fir/target.fir
5

This is running a pass, this should be testable/tested with fir-opt.

If the only thing that tco provides is overriding the triple, this can also be done in a trivial pass that you just apply on the module (strawman, it'd look: pipeline=module(overrideTargetTriple{target=aarch64-unknown-linux-gnu}, target-rewrite)

34

llvm_unreachable isn't meant for things that should work but aren't implemented yet, they are meant for things that really cannot happen from a system point of view: I should be able to plug a Fuzzer in front of a component and never hit an unreachable (assuming you don't break a documented API invariant).
In general the invariant for the IR is "does it pass the verifier": hitting an unreachable on "valid IR" wouldn't seem right to me.
(llvm::report_fatal_error provides with the same "helpful message and context" you referred to, but isn't optimized away in release mode)

rovka updated this revision to Diff 384727.Nov 4 2021, 6:27 AM
rovka edited the summary of this revision. (Show Details)

Replaced all llvm_unreachable with TODOs.
Added a pass option for the target triple.
Switched the tests to fir-opt and removed all changes to tco.

rovka added inline comments.Nov 4 2021, 6:29 AM
flang/test/Fir/target.fir
5

I added a pass option instead since it seemed easier / less boilerplate.

34

Ok, thanks, TODO sounds fine to me.

rovka added a comment.Nov 8 2021, 12:18 AM

@mehdi_amini Does this look ok now?

@mehdi_amini Does this look ok now?

I haven't reviewed the patch deeply, but feel free to move forward with Andrzej's review here! Thanks for checking :)

clementval accepted this revision.Nov 8 2021, 12:14 PM

Thanks for the patch @rovka LG!

This revision was automatically updated to reflect the committed changes.