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

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

Recommend using alignTo from Support/MathExtras.h

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

935

s/no/not

936

Add explicit #include for ADT/STLExtras.h

960

s/sort/sorted

981

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

Same comment as above re. alignTo

1321

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

Good catch, thanks for the bugfix!

lib/Target/AVR/AVRRegisterInfo.td
107

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

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

935

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

981

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

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

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

Forgot about that one, nice.

935

Agree.

lib/Target/AVR/AVRRegisterInfo.td
107

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
901 ↗(On Diff #231323)

16-bit

979 ↗(On Diff #231323)

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

995 ↗(On Diff #231323)

s/greter/greater

llvm/lib/Target/AVR/AVRRegisterInfo.td
106 ↗(On Diff #231323)

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

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

llvm/lib/Target/AVR/AVRISelLowering.cpp
901 ↗(On Diff #231323)

Good catch, have fixed.

979 ↗(On Diff #231323)

Good idea, I agree, fixed.

995 ↗(On Diff #231323)

Fixed

llvm/lib/Target/AVR/AVRRegisterInfo.td
106 ↗(On Diff #231323)

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!