This is an archive of the discontinued LLVM Phabricator instance.

[AVR] Rewrite the function calling convention.
ClosedPublic

Authored by rodrigorc on Oct 4 2019, 4:17 PM.

Details

Summary

The previous version relied on the standard calling convention using
std::reverse() to try to force the AVR ABI. But this only works for
simple cases, it fails for example with aggregate types.

This patch rewrites the calling convention with custom C++ code, that
implements the ABI defined in https://gcc.gnu.org/wiki/avr-gcc.

To do that it adds a few 16-bit pseudo registers for unaligned argument
passing, such as R24R23. For example this function:

define void @fun({ i8, i16 } %a)

will pass %a.0 in R22 and %a.1 in R24R23.

There are no instructions that can use these pseudo registers, so a new
register class, DREGSMOVW, is defined to make them apart.

Also the ArgCC_AVR_BUILTIN_DIV is no longer necessary, as it is
identical to the C++ behavior (actually the clobber list is more strict
for __div* functions, but that is currently unimplemented).

Diff Detail

Event Timeline

rodrigorc created this revision.Oct 4 2019, 4:17 PM

This patch is really great!

I have once attempted to rewrite the calling convention implementation in terms of the algorithm in the avr-gcc manual, but never got all the way there. This has been the source of a few problems, so thanks for doing this!

I'm reviewing this now.

I've had a quick first pass, nice work, I will have a deeper look tonight.

lib/Target/AVR/AVRISelLowering.cpp
887 ↗(On Diff #223333)

Can we add a static_assertion that this invariant is upheld?

Something like

static_assert(sizeof(RegList8) * 2 == sizeof(RegList16));

It looks like LLVM supports static_assert Coding Standards

928 ↗(On Diff #223333)

Recommend using alignTo from Support/MathExtras.h

This expression is equivalent to `alignTo(TotalBytes, 2);

935 ↗(On Diff #223333)

s/no/not

936 ↗(On Diff #223333)

Add explicit #include for ADT/STLExtras.h

960 ↗(On Diff #223333)

s/sort/sorted

981 ↗(On Diff #223333)

Where does the 8-byte maximum retval size emerge from? Perhaps you could add some text to the assertion so that there will be more detail if ever hit.

987 ↗(On Diff #223333)

Same comment as above re. alignTo

1321 ↗(On Diff #223333)

This for loop summing up the sizes of all arguments has also exists on line 974 - recommend extracting it into a function, something like unsigned getTotalArgumentsSizeInBytes(args)

lib/Target/AVR/AVRInstrInfo.cpp
51 ↗(On Diff #223333)

Good catch, thanks for the bugfix!

lib/Target/AVR/AVRRegisterInfo.td
107 ↗(On Diff #223333)

Can we give these registers a common prefix to indicate their pseudoness, something like PR26R25?

rodrigorc updated this revision to Diff 231323.Nov 27 2019, 2:01 PM
rodrigorc marked 14 inline comments as done.

I've applied the suggestions in the previous review, except that of the renaming of the new pseudo-registers.

rodrigorc planned changes to this revision.Nov 27 2019, 2:02 PM
rodrigorc added inline comments.
lib/Target/AVR/AVRISelLowering.cpp
887 ↗(On Diff #223333)

Indeed! But I'll use array_lengthof() instead of sizeof(), that looks nicer for this case.

935 ↗(On Diff #223333)

Also s/is/are/, I think...

981 ↗(On Diff #223333)

AFAIUI, when CanLowerReturn() returns false this function will not be called. I'm adding a comment and a string to the assert().

lib/Target/AVR/AVRInstrInfo.cpp
51 ↗(On Diff #223333)

Actually, I'm not sure this bug could be triggered before, but now there are all these new pseudo-registers and this is really needed.

lib/Target/AVR/AVRRegisterInfo.td
107 ↗(On Diff #223333)

I tried that, but then somehow the testcase AVR/trunc.ll fails. I don't know exactly why, but it should generate the instr:

mov r24, r23

but instead it emits:

mov r23, r22
mov r24, r23

Looking around, I noticed that just by renaming these regs, the generated tables are quite different. That is, in AVRGenRegisterInfo.inc, the array AVRRegDiffLists goes from 22 entries to 57! I admit that I don't fully understand the .td format, but I think that just renaming the register should not do this, should it?

rodrigorc requested review of this revision.Nov 27 2019, 2:03 PM
dylanmckay marked 2 inline comments as done.Dec 16 2019, 3:15 AM
dylanmckay added inline comments.
lib/Target/AVR/AVRISelLowering.cpp
887 ↗(On Diff #223333)

Forgot about that one, nice.

935 ↗(On Diff #223333)

Agree.

lib/Target/AVR/AVRRegisterInfo.td
107 ↗(On Diff #223333)

That is indeed a weird one - the answer should lie inside llvm/utils/TableGen/RegisterInfoEmitter.cpp.

The .td format was definitely the hardest thing for me to understand when I first got into LLVM.

I don't think it should do that.

jwagen added a subscriber: jwagen.Jan 20 2020, 3:22 PM
Sh4rK added a subscriber: Sh4rK.Feb 23 2020, 6:43 AM

Hi, sorry for the comments if it is not appropriate for me to review, since I'm not a maintainer, in fact, I know nothing about LLVM internals.

I came across this patch because I'm interested in Rust on AVR, and read it out of curiousity, and couldn't not notice the typos :)

llvm/lib/Target/AVR/AVRISelLowering.cpp
899

16-bit

977

This could be replaced with a range-based for loop, the LLVM coding standards seems to encourage this as well.

991

s/greter/greater

llvm/lib/Target/AVR/AVRRegisterInfo.td
106

I'm not entirely sure, but I think i32 was meant to be i16.

Gaelan added a subscriber: Gaelan.Apr 23 2020, 1:10 PM

This conflicts with master now, he's a rebased patch

dylanmckay marked an inline comment as done.Jun 19 2020, 9:17 AM

@Sh4rK your review is always welcome - I've updated the code with your comments, here's the delta containing just the fixes you've suggested.

patch
diff --git a/llvm/lib/Target/AVR/AVRISelLowering.cpp b/llvm/lib/Target/AVR/AVRISelLowering.cpp
index 20e9d209531..bf9b32e1278 100644
--- a/llvm/lib/Target/AVR/AVRISelLowering.cpp
+++ b/llvm/lib/Target/AVR/AVRISelLowering.cpp
@@ -896,7 +896,7 @@ static const MCPhysReg RegList16[] = {
     AVR::R10R9,  AVR::R9R8};
 
 static_assert(array_lengthof(RegList8) == array_lengthof(RegList16),
-        "8-bit and 15-bit register arrays must be of equal length");
+        "8-bit and 16-bit register arrays must be of equal length");
 
 /// Analyze incoming and outgoing function arguments. We need custom C++ code
 /// to handle special constraints in the ABI.
@@ -972,11 +972,9 @@ analyzeArguments(TargetLowering::CallLoweringInfo *CLI, const Function *F,
 template <typename ArgT>
 static unsigned getTotalArgumentsSizeInBytes(const SmallVectorImpl<ArgT> &Args) {
   unsigned TotalBytes = 0;
-  unsigned NumArgs = Args.size();
 
-  for (unsigned i = 0; i != NumArgs; ++i) {
-    MVT VT = Args[i].VT;
-    TotalBytes += VT.getStoreSize();
+  for (const ArgT& Arg : Args) {
+    TotalBytes += Arg.VT.getStoreSize();
   }
   return TotalBytes;
 }
@@ -990,7 +988,7 @@ static void analyzeReturnValues(const SmallVectorImpl<ArgT> &Args,
   unsigned NumArgs = Args.size();
   unsigned TotalBytes = getTotalArgumentsSizeInBytes(Args);
   // CanLowerReturn() guarantees this assertion.
-  assert(TotalBytes <= 8 && "return values greter than 8 bytes cannot be lowered");
+  assert(TotalBytes <= 8 && "return values greater than 8 bytes cannot be lowered");
 
   // GCC-ABI says that the size is rounded up to the next even number,
   // but actually once it is more than 4 it will always round up to 8.
diff --git a/llvm/lib/Target/AVR/AVRRegisterInfo.td b/llvm/lib/Target/AVR/AVRRegisterInfo.td
index 8e36971dd63..ab5d02356c9 100644
--- a/llvm/lib/Target/AVR/AVRRegisterInfo.td
+++ b/llvm/lib/Target/AVR/AVRRegisterInfo.td
@@ -103,7 +103,8 @@ CoveredBySubRegs = 1 in
   def R5R4   : AVRReg<4,  "r5:r4",   [R4, R5]>,   DwarfRegNum<[4]>;
   def R3R2   : AVRReg<2,  "r3:r2",   [R2, R3]>,   DwarfRegNum<[2]>;
   def R1R0   : AVRReg<0,  "r1:r0",   [R0, R1]>,   DwarfRegNum<[0]>;
-  // Pseudo registers for unaligned i32
+
+  // Pseudo registers for unaligned i16
   def R26R25 : AVRReg<25, "r26:r25", [R25, R26]>, DwarfRegNum<[25]>;
   def R24R23 : AVRReg<23, "r24:r23", [R23, R24]>, DwarfRegNum<[23]>;
   def R22R21 : AVRReg<21, "r22:r21", [R21, R22]>, DwarfRegNum<[21]>;
lib/Target/AVR/AVRRegisterInfo.td
107 ↗(On Diff #223333)

This comment is minor IMO, giving the registers a common prefix is unnecessary for now.

llvm/lib/Target/AVR/AVRISelLowering.cpp
899

Good catch, have fixed.

977

Good idea, I agree, fixed.

991

Fixed

llvm/lib/Target/AVR/AVRRegisterInfo.td
106

I believe you are right, fixed.

Rebased version with @Sh4rK's comments fixed.

I've checked and indeed it does not regress any of the LLVM AVR unit tests.

I'd like to merge it as-is, although I'd like to write a calling convention integration test [here](github.com/dylanmckay/avr-compiler-integration-tests) first. I plan to get to this in the next few days.

This revision was not accepted when it landed; it landed in state Needs Review.Jun 23 2020, 2:36 AM
This revision was automatically updated to reflect the committed changes.

Committed in

b9c26a9cfe53da7ef96e13ef1aa7fe793b1b5d28
commit b9c26a9cfe53da7ef96e13ef1aa7fe793b1b5d28
Author: Dylan McKay <me@dylanmckay.io>
Date:   Fri Jun 19 23:26:00 2020 +1200

    [AVR] Rewrite the function calling convention.
    
    Summary:
    The previous version relied on the standard calling convention using
    std::reverse() to try to force the AVR ABI. But this only works for
    simple cases, it fails for example with aggregate types.
    
    This patch rewrites the calling convention with custom C++ code, that
    implements the ABI defined in https://gcc.gnu.org/wiki/avr-gcc.
    
    To do that it adds a few 16-bit pseudo registers for unaligned argument
    passing, such as R24R23. For example this function:
    
        define void @fun({ i8, i16 } %a)
    
    will pass %a.0 in R22 and %a.1 in R24R23.
    
    There are no instructions that can use these pseudo registers, so a new
    register class, DREGSMOVW, is defined to make them apart.
    
    Also the ArgCC_AVR_BUILTIN_DIV is no longer necessary, as it is
    identical to the C++ behavior (actually the clobber list is more strict
    for __div* functions, but that is currently unimplemented).
    
    Reviewers: dylanmckay
    
    Subscribers: Gaelan, Sh4rK, indirect, jwagen, efriedma, dsprenkels, hiraditya, Jim, llvm-commits
    
    Tags: #llvm
    
    Differential Revision: https://reviews.llvm.org/D68524
    
    Patch by Rodrigo Rivas Costa.

Thanks for the patch @rodrigorc, appreciate it!