benshi001 created this revision.
benshi001 added reviewers: aykevl, dylanmckay.
Herald added a subscriber: Jim.
benshi001 requested review of this revision.
Herald added subscribers: cfe-commits, jacquesguan.
Herald added a project: clang.

The standard calling convention of AVR is documented at
https://gcc.gnu.org/wiki/avr-gcc#Calling_Convention


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D120475

Files:
  clang/lib/Basic/Targets/AVR.cpp
  clang/lib/Basic/Targets/AVR.h
  clang/lib/CodeGen/TargetInfo.cpp
  clang/test/CodeGen/avr/argument.c

Index: clang/test/CodeGen/avr/argument.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/avr/argument.c
@@ -0,0 +1,87 @@
+// RUN: %clang_cc1 -triple avr -target-cpu atmega328 -emit-llvm %s -o - \
+// RUN:     | FileCheck %s --check-prefix AVR
+// RUN: %clang_cc1 -triple avr -target-cpu attiny40 -emit-llvm %s -o - \
+// RUN:     | FileCheck %s --check-prefix TINY
+
+// NOTE: All arguments are passed in memory for functions with variable arguments.
+// AVR:  define {{.*}} i16 @foo_varargs(i16* {{.*}} %0, i16* {{.*}} %1, ...)
+// TINY: define {{.*}} i16 @foo_varargs(i16* {{.*}} %0, i16* {{.*}} %1, ...)
+int foo_varargs(int a, int b, ...) {
+  return a + b;
+}
+
+// NOTE: A 10-byte argument is passed in registers on avr but in memory on avrtiny.
+// AVR:  define {{.*}} i16 @foo0([10 x i8]    {{.*}})
+// TINY: define {{.*}} i16 @foo0(%struct.s10* {{.*}})
+struct s10 {
+  char arr[10];
+};
+int foo0(struct s10 v) {
+  return v.arr[0] + v.arr[1];
+}
+
+// NOTE: The 8-byte argument is always passed in registers. The other arguments are passed
+// NOTE: in registers on avr but in memory on avrtiny.
+// AVR:  define {{.*}} i16 @foo1([8 x i8] {{.*}}, i16  {{.*}}, i32  {{.*}})
+// TINY: define {{.*}} i16 @foo1([8 x i8] {{.*}}, i16* {{.*}}, i32* {{.*}})
+struct s8 {
+  char arr[8];
+};
+int foo1(struct s8 a, int b, long c) {
+  return a.arr[b + c];
+}
+
+// NOTE: All arguments are passed in registers.
+// AVR:  define {{.*}} i32 @foo2(i32 {{.*}}, i16 {{.*}}, i16 {{.*}})
+// TINY: define {{.*}} i32 @foo2(i32 {{.*}}, i16 {{.*}}, i16 {{.*}})
+long foo2(long a, int b, int c) {
+  return a + b + c;
+}
+
+// NOTE: The arguments a&b are passed in registers. The argument c is passed
+// NOTE: in register on avr but in memory on avrtiny.
+// AVR:  define {{.*}} i32 @foo3(i32 {{.*}}, i16 {{.*}}, i32  {{.*}})
+// TINY: define {{.*}} i32 @foo3(i32 {{.*}}, i16 {{.*}}, i32* {{.*}})
+long foo3(long a, int b, long c) {
+  return a + b + c;
+}
+
+// NOTE: An int8 arguments will be extended to i16 if passed in registers.
+// AVR:  define {{.*}} i16 @foo4(i8 {{.*}} signext {{.*}}, i8 {{.*}} zeroext {{.*}})
+// TINY: define {{.*}} i16 @foo4(i8 {{.*}} signext {{.*}}, i8 {{.*}} zeroext {{.*}})
+int foo4(char a, unsigned char b) {
+  return (int)a + (int)b;
+}
+
+// NOTE: On avrtiny, although the first argument only costs 7 registers, the second
+// NOTE: argument (with i8 type) is passed in memory.
+// TINY: define {{.*}} i16 @foo5([7 x i8] {{.*}}, i8* {{.*}})
+struct s7 {
+  char arr[7];
+};
+int foo5(struct s7 a, char b) {
+  return a.arr[b];
+}
+
+// NOTE: On avr, although the first argument only costs 17 registers, the second
+// NOTE: argument (with i8 type) is passed in memory.
+// AVR: define {{.*}} i16 @foo6([17 x i8] {{.*}}, i8* {{.*}})
+struct s17 {
+  char arr[17];
+};
+int foo6(struct s17 a, char b) {
+  return a.arr[b];
+}
+
+// NOTE: On avr, all arguments are passed in registers.
+// AVR: define {{.*}} i32 @foo7(i32 {{.*}}, i32 {{.*}}, i32 {{.*}}, i32 {{.*}}, i16 {{.*}})
+long foo7(long a, long b, long c, long d, int e) {
+  return a + b + c + d + e;
+}
+
+// NOTE: On avr, the arguments a&b&c&d are passed in registers, but e is
+// NOTE: passed in memory.
+// AVR: define {{.*}} i32 @foo8(i32 {{.*}}, i32 {{.*}}, i32 {{.*}}, i32 {{.*}}, i32* {{.*}})
+long foo8(long a, long b, long c, long d, long e) {
+  return a + b + c + d + e;
+}
Index: clang/lib/CodeGen/TargetInfo.cpp
===================================================================
--- clang/lib/CodeGen/TargetInfo.cpp
+++ clang/lib/CodeGen/TargetInfo.cpp
@@ -33,6 +33,7 @@
 #include "llvm/IR/IntrinsicsNVPTX.h"
 #include "llvm/IR/IntrinsicsS390.h"
 #include "llvm/IR/Type.h"
+#include "llvm/Support/MathExtras.h"
 #include "llvm/Support/raw_ostream.h"
 #include <algorithm> // std::sort
 
@@ -8273,8 +8274,14 @@
 
 namespace {
 class AVRABIInfo : public DefaultABIInfo {
+private:
+  // The total amount of registers can be used to pass parameters. It is 18 on
+  // AVR, or 8 on AVRTiny.
+  unsigned ParamRegs;
+
 public:
-  AVRABIInfo(CodeGenTypes &CGT) : DefaultABIInfo(CGT) {}
+  AVRABIInfo(CodeGenTypes &CGT, unsigned N)
+      : DefaultABIInfo(CGT), ParamRegs(N) {}
 
   ABIArgInfo classifyReturnType(QualType Ty) const {
     // A return struct with size less than or equal to 8 bytes is returned
@@ -8285,20 +8292,55 @@
       return DefaultABIInfo::classifyReturnType(Ty);
   }
 
-  // Just copy the original implementation of DefaultABIInfo::computeInfo(),
-  // since DefaultABIInfo::classify{Return,Argument}Type() are not virtual.
+  ABIArgInfo classifyArgumentType(QualType Ty, unsigned &NumRegs) const {
+    unsigned TySize = getContext().getTypeSize(Ty);
+
+    // An int8 type argument always occupies two registers like an int16.
+    if (TySize == 8 && NumRegs >= 2) {
+      NumRegs -= 2;
+      return ABIArgInfo::getExtend(Ty);
+    }
+
+    // If the argument size is an odd number of bytes, round up the size
+    // to the next even number.
+    TySize = llvm::alignTo(TySize, 16);
+
+    // Any type including an array/struct type can be passed in rgisters,
+    // if there are enough registers left.
+    if (TySize <= NumRegs * 8) {
+      NumRegs -= TySize / 8;
+      return ABIArgInfo::getDirect();
+    }
+
+    // An argument is passed either completely in registers or completely in
+    // memory. Since there are not enough registers left, current argument
+    // and all other unprocessed arguments should be passed in memory.
+    NumRegs = 0;
+    return ABIInfo::getNaturalAlignIndirect(Ty);
+  }
+
   void computeInfo(CGFunctionInfo &FI) const override {
+    // Decide the return type.
     if (!getCXXABI().classifyReturnType(FI))
       FI.getReturnInfo() = classifyReturnType(FI.getReturnType());
+    // Decide each argument type.
+    unsigned NumRegs = ParamRegs;
     for (auto &I : FI.arguments())
-      I.info = classifyArgumentType(I.type);
+      if (FI.isVariadic())
+        // Arguments of varargs functions are passed on the stack.
+        // This applies even to the named arguments.
+        I.info = ABIInfo::getNaturalAlignIndirect(I.type);
+      else
+        // Process each argument according to the ABI is avr or avrtiny.
+        // Total 18 registers can be used on avr and 8 ones on avrtiny.
+        I.info = classifyArgumentType(I.type, NumRegs);
   }
 };
 
 class AVRTargetCodeGenInfo : public TargetCodeGenInfo {
 public:
-  AVRTargetCodeGenInfo(CodeGenTypes &CGT)
-      : TargetCodeGenInfo(std::make_unique<AVRABIInfo>(CGT)) {}
+  AVRTargetCodeGenInfo(CodeGenTypes &CGT, unsigned N)
+      : TargetCodeGenInfo(std::make_unique<AVRABIInfo>(CGT, N)) {}
 
   LangAS getGlobalVarAddressSpace(CodeGenModule &CGM,
                                   const VarDecl *D) const override {
@@ -11271,8 +11313,12 @@
   case llvm::Triple::mips64el:
     return SetCGInfo(new MIPSTargetCodeGenInfo(Types, false));
 
-  case llvm::Triple::avr:
-    return SetCGInfo(new AVRTargetCodeGenInfo(Types));
+  case llvm::Triple::avr: {
+    // R8 ~ R25 are used to pass parameters on avr, while R18 ~ R25
+    // are used on avrtiny.
+    unsigned N = getTarget().getABI() == "avrtiny" ? 8 : 18;
+    return SetCGInfo(new AVRTargetCodeGenInfo(Types, N));
+  }
 
   case llvm::Triple::aarch64:
   case llvm::Triple::aarch64_32:
Index: clang/lib/Basic/Targets/AVR.h
===================================================================
--- clang/lib/Basic/Targets/AVR.h
+++ clang/lib/Basic/Targets/AVR.h
@@ -74,8 +74,7 @@
     static const char *const GCCRegNames[] = {
         "r0",  "r1",  "r2",  "r3",  "r4",  "r5",  "r6",  "r7",  "r8",  "r9",
         "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19",
-        "r20", "r21", "r22", "r23", "r24", "r25", "X",   "Y",   "Z",   "SP"
-    };
+        "r20", "r21", "r22", "r23", "r24", "r25", "X",   "Y",   "Z",   "SP"};
     return llvm::makeArrayRef(GCCRegNames);
   }
 
@@ -169,15 +168,12 @@
 
   bool isValidCPUName(StringRef Name) const override;
   void fillValidCPUList(SmallVectorImpl<StringRef> &Values) const override;
-  bool setCPU(const std::string &Name) override {
-    bool isValid = isValidCPUName(Name);
-    if (isValid)
-      CPU = Name;
-    return isValid;
-  }
+  bool setCPU(const std::string &Name) override;
+  StringRef getABI() const override { return ABI; }
 
 protected:
   std::string CPU;
+  StringRef ABI;
 };
 
 } // namespace targets
Index: clang/lib/Basic/Targets/AVR.cpp
===================================================================
--- clang/lib/Basic/Targets/AVR.cpp
+++ clang/lib/Basic/Targets/AVR.cpp
@@ -24,7 +24,8 @@
 struct LLVM_LIBRARY_VISIBILITY MCUInfo {
   const char *Name;
   const char *DefineName;
-  const int NumFlashBanks; // -1 means the device does not support LPM/ELPM.
+  const int NumFlashBanks; // Set to 0 for the devices do not support LPM/ELPM.
+  bool IsTiny; // Set to true for the devices belong to the avrtiny family.
 };
 
 // This list should be kept up-to-date with AVRDevices.td in LLVM.
@@ -267,14 +268,14 @@
     {"atxmega128a1", "__AVR_ATxmega128A1__", 2},
     {"atxmega128a1u", "__AVR_ATxmega128A1U__", 2},
     {"atxmega128a4u", "__AVR_ATxmega128A4U__", 2},
-    {"attiny4", "__AVR_ATtiny4__", 0},
-    {"attiny5", "__AVR_ATtiny5__", 0},
-    {"attiny9", "__AVR_ATtiny9__", 0},
-    {"attiny10", "__AVR_ATtiny10__", 0},
-    {"attiny20", "__AVR_ATtiny20__", 0},
-    {"attiny40", "__AVR_ATtiny40__", 0},
-    {"attiny102", "__AVR_ATtiny102__", 0},
-    {"attiny104", "__AVR_ATtiny104__", 0},
+    {"attiny4", "__AVR_ATtiny4__", 0, true},
+    {"attiny5", "__AVR_ATtiny5__", 0, true},
+    {"attiny9", "__AVR_ATtiny9__", 0, true},
+    {"attiny10", "__AVR_ATtiny10__", 0, true},
+    {"attiny20", "__AVR_ATtiny20__", 0, true},
+    {"attiny40", "__AVR_ATtiny40__", 0, true},
+    {"attiny102", "__AVR_ATtiny102__", 0, true},
+    {"attiny104", "__AVR_ATtiny104__", 0, true},
     {"attiny202", "__AVR_ATtiny202__", 1},
     {"attiny402", "__AVR_ATtiny402__", 1},
     {"attiny204", "__AVR_ATtiny204__", 1},
@@ -325,6 +326,27 @@
     Values.push_back(Info.Name);
 }
 
+bool AVRTargetInfo::setCPU(const std::string &Name) {
+  // Set the ABI and CPU fields if parameter Name is a family name.
+  if (llvm::is_contained(ValidFamilyNames, Name)) {
+    CPU = Name;
+    ABI = Name == "avrtiny" ? "avrtiny" : "avr";
+    return true;
+  }
+
+  // Set the ABI field if parameter Name is a device name.
+  auto It = llvm::find_if(
+      AVRMcus, [&](const MCUInfo &Info) { return Info.Name == Name; });
+  if (It != std::end(AVRMcus)) {
+    CPU = Name;
+    ABI = It->IsTiny ? "avrtiny" : "avr";
+    return true;
+  }
+
+  // Parameter Name is neither valid family name or valid device name.
+  return false;
+}
+
 void AVRTargetInfo::getTargetDefines(const LangOptions &Opts,
                                      MacroBuilder &Builder) const {
   Builder.defineMacro("AVR");
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to