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 &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 nor valid device name. + return false; +} + void AVRTargetInfo::getTargetDefines(const LangOptions &Opts, MacroBuilder &Builder) const { Builder.defineMacro("AVR"); 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 // 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(CGT)) {} + AVRTargetCodeGenInfo(CodeGenTypes &CGT, unsigned N) + : TargetCodeGenInfo(std::make_unique(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/test/CodeGen/avr/argument.c =================================================================== --- /dev/null +++ clang/test/CodeGen/avr/argument.c @@ -0,0 +1,90 @@ +// 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. So there is one register +// NOTE: that is vacant. +// 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. So there is one register +// NOTE: that is vacant. +// 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. Actually 16 registers are used, while 2 ones +// NOTE: are vacant. +// 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; +}