diff --git a/clang/lib/Basic/Targets/AVR.h b/clang/lib/Basic/Targets/AVR.h --- a/clang/lib/Basic/Targets/AVR.h +++ b/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 diff --git a/clang/lib/Basic/Targets/AVR.cpp b/clang/lib/Basic/Targets/AVR.cpp --- a/clang/lib/Basic/Targets/AVR.cpp +++ b/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. }; // NOTE: This list has been synchronized with gcc-avr 5.4.0 and avr-libc 2.0.0. @@ -282,14 +283,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}, @@ -340,6 +341,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"); diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -19,9 +19,9 @@ #include "CodeGenFunction.h" #include "clang/AST/Attr.h" #include "clang/AST/RecordLayout.h" +#include "clang/Basic/Builtins.h" #include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/DiagnosticFrontend.h" -#include "clang/Basic/Builtins.h" #include "clang/CodeGen/CGFunctionInfo.h" #include "clang/CodeGen/SwiftCallingConv.h" #include "llvm/ADT/SmallBitVector.h" @@ -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 @@ -8278,32 +8279,93 @@ namespace { class AVRABIInfo : public DefaultABIInfo { +private: + // The total amount of registers can be used to pass parameters. It is 18 on + // AVR, or 6 on AVRTiny. + const unsigned ParamRegs; + // The total amount of registers can be used to pass return value. It is 8 on + // AVR, or 4 on AVRTiny. + const unsigned RetRegs; + public: - AVRABIInfo(CodeGenTypes &CGT) : DefaultABIInfo(CGT) {} + AVRABIInfo(CodeGenTypes &CGT, unsigned NPR, unsigned NRR) + : DefaultABIInfo(CGT), ParamRegs(NPR), RetRegs(NRR) {} + + ABIArgInfo classifyReturnType(QualType Ty, bool &LargeRet) const { + if (isAggregateTypeForABI(Ty)) { + // On AVR, a return struct with size less than or equals to 8 bytes is + // returned directly via registers R18-R25. On AVRTiny, a return struct + // with size less than or equals to 4 bytes is returned directly via + // registers R22-R25. + if (getContext().getTypeSize(Ty) <= RetRegs * 8) + return ABIArgInfo::getDirect(); + // A return struct with larger size is returned via a stack + // slot, along with a pointer to it as the function's implicit argument. + LargeRet = true; + return getNaturalAlignIndirect(Ty); + } + // Otherwise we follow the default way which is compatible. + return DefaultABIInfo::classifyReturnType(Ty); + } - ABIArgInfo classifyReturnType(QualType Ty) const { - // A return struct with size less than or equal to 8 bytes is returned - // directly via registers R18-R25. - if (isAggregateTypeForABI(Ty) && getContext().getTypeSize(Ty) <= 64) + ABIArgInfo classifyArgumentType(QualType Ty, unsigned &NumRegs) const { + unsigned TySize = getContext().getTypeSize(Ty); + + // An int8 type argument always costs 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(); - else - return DefaultABIInfo::classifyReturnType(Ty); + } + + // 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. + // However we still need to return `ABIArgInfo::getDirect()` other than + // `ABIInfo::getNaturalAlignIndirect(Ty)`, otherwise an extra stack slot + // will be allocated, so the stack frame layout will be incompatible with + // avr-gcc. + NumRegs = 0; + return ABIArgInfo::getDirect(); } - // Just copy the original implementation of DefaultABIInfo::computeInfo(), - // since DefaultABIInfo::classify{Return,Argument}Type() are not virtual. void computeInfo(CGFunctionInfo &FI) const override { + // Decide the return type. + bool LargeRet = false; if (!getCXXABI().classifyReturnType(FI)) - FI.getReturnInfo() = classifyReturnType(FI.getReturnType()); + FI.getReturnInfo() = classifyReturnType(FI.getReturnType(), LargeRet); + + // Decide each argument type. The total number of registers can be used for + // arguments depends on several factors: + // 1. Arguments of varargs functions are passed on the stack. This applies + // even to the named arguments. So no register can be used. + // 2. Total 18 registers can be used on avr and 6 ones on avrtiny. + // 3. If the return type is a struct with too large size, two registers + // (out of 18/6) will be cost as an implicit pointer argument. + unsigned NumRegs = ParamRegs; + if (FI.isVariadic()) + NumRegs = 0; + else if (LargeRet) + NumRegs -= 2; for (auto &I : FI.arguments()) - I.info = classifyArgumentType(I.type); + I.info = classifyArgumentType(I.type, NumRegs); } }; class AVRTargetCodeGenInfo : public TargetCodeGenInfo { public: - AVRTargetCodeGenInfo(CodeGenTypes &CGT) - : TargetCodeGenInfo(std::make_unique(CGT)) {} + AVRTargetCodeGenInfo(CodeGenTypes &CGT, unsigned NPR, unsigned NRR) + : TargetCodeGenInfo(std::make_unique(CGT, NPR, NRR)) {} LangAS getGlobalVarAddressSpace(CodeGenModule &CGM, const VarDecl *D) const override { @@ -11280,8 +11342,14 @@ case llvm::Triple::mips64el: return SetCGInfo(new MIPSTargetCodeGenInfo(Types, false)); - case llvm::Triple::avr: - return SetCGInfo(new AVRTargetCodeGenInfo(Types)); + case llvm::Triple::avr: { + // For passing parameters, R8~R25 are used on avr, and R18~R25 are used + // on avrtiny. For passing return value, R18~R25 are used on avr, and + // R22~R25 are used on avrtiny. + unsigned NPR = getTarget().getABI() == "avrtiny" ? 6 : 18; + unsigned NRR = getTarget().getABI() == "avrtiny" ? 4 : 8; + return SetCGInfo(new AVRTargetCodeGenInfo(Types, NPR, NRR)); + } case llvm::Triple::aarch64: case llvm::Triple::aarch64_32: diff --git a/clang/test/CodeGen/avr/argument.c b/clang/test/CodeGen/avr/argument.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/avr/argument.c @@ -0,0 +1,116 @@ +// 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 via the stack for functions with variable arguments. +// AVR: define {{.*}} i8 @foo0(i8 {{.*}}, i8 {{.*}}, ...) +// TINY: define {{.*}} i8 @foo0(i8 {{.*}}, i8 {{.*}}, ...) +// AVR-NOT: define {{.*}} i8 @foo0(i8 {{.*}} signext {{.*}}, i8 {{.*}} signext {{.*}}, ...) +// TINY-NOT: define {{.*}} i8 @foo0(i8 {{.*}} signext {{.*}}, i8 {{.*}} signext {{.*}}, ...) +char foo0(char a, char b, ...) { + return a + b; +} + +// NOTE: All arguments are passed via registers on both avr and avrtiny. +// AVR: define {{.*}} i8 @foo1(i32 {{.*}}, i8 {{.*}} signext {{.*}}) +// TINY: define {{.*}} i8 @foo1(i32 {{.*}}, i8 {{.*}} signext {{.*}}) +char foo1(long a, char b) { + return a + b; +} + +// NOTE: The argument `char c` is passed via registers on avr, while via the stack on avrtiny. +// The argument `char b` costs 2 registers, so there is no vacant register left for +// `char c` on avrtiny. +// AVR: define {{.*}} i8 @foo2(i32 {{.*}}, i8 {{.*}} signext {{.*}}, i8 {{.*}} signext {{.*}}) +// TINY: define {{.*}} i8 @foo2(i32 {{.*}}, i8 {{.*}} signext {{.*}}, i8 {{.*}}) +// TINY-NOT: define {{.*}} i8 @foo2(i32 {{.*}}, i8 {{.*}} signext {{.*}}, i8 {{.*}} signext {{.*}}) +char foo2(long a, char b, char c) { + return a + b + c; +} + +// NOTE: On avr, the argument `a` costs 16 registers and `b` costs 2 registers, so +// `c` has to be passed via the stack. +// AVR: define {{.*}} i8 @foo3({{.*}}, i8 {{.*}} signext {{.*}}, i8 {{.*}}) +// AVR-NOT: define {{.*}} i8 @foo3({{.*}}, i8 {{.*}} signext {{.*}}, i8 {{.*}} signext {{.*}}) +// TINY: define {{.*}} i8 @foo3({{.*}}, i8 {{.*}}, i8 {{.*}}) +// TINY-NOT: define {{.*}} i8 @foo3({{.*}}, i8 {{.*}} signext {{.*}}, i8 {{.*}} signext {{.*}}) +struct s15 { + char arr[15]; +}; +char foo3(struct s15 a, char b, char c) { + return a.arr[b] + a.arr[c]; +} + +// NOTE: On avr, `a` only costs 16 registers, though there are 2 vacant registers, +// both `b` and `c` have to be passed via the stack. +// AVR: define {{.*}} i8 @foo4({{.*}}, i32 {{.*}}, i8 {{.*}}) +// AVR-NOT: define {{.*}} i8 @foo4({{.*}}, i32 {{.*}}, i8 {{.*}} signext {{.*}}) +// TINY: define {{.*}} i8 @foo4({{.*}}, i32 {{.*}}, i8 {{.*}}) +// TINY-NOT: define {{.*}} i8 @foo4({{.*}}, i32 {{.*}}, i8 {{.*}} signext {{.*}}) +char foo4(struct s15 a, long b, char c) { + return a.arr[c]; +} + +// NOTE: On avrtiny, `a` only costs 4 registers, though there are 2 vacant +// registers, both `b` and `c` are passed via the stack. +// AVR: define {{.*}} i8 @foo5(i32 {{.*}}, i32 {{.*}}, i8 {{.*}} signext {{.*}}) +// TINY: define {{.*}} i8 @foo5(i32 {{.*}}, i32 {{.*}}, i8 {{.*}}) +// TINY-NOT: define {{.*}} i8 @foo5(i32 {{.*}}, i32 {{.*}}, i8 {{.*}} signext {{.*}}) +char foo5(long a, long b, char c) { + return c + 1; +} + +// NOTE: All arguments are passed via the stack, though all registers are vacant. +// AVR: define {{.*}} i8 @foo6({{.*}}, i8 {{.*}}) +// AVR-NOT: define {{.*}} i8 @foo6({{.*}}, i8 {{.*}} signext {{.*}}) +// TINY: define {{.*}} i8 @foo6({{.*}}, i8 {{.*}}) +// TINY-NOT: define {{.*}} i8 @foo6({{.*}}, i8 {{.*}} signext {{.*}}) +struct s32 { + char arr[32]; +}; +char foo6(struct s32 a, char b) { + return a.arr[b]; +} + +// NOTE: All arguments are passed via registers on avr. While all arguments are passed +// via the stack on avrtiny, though all registers are vacant. +// AVR: define {{.*}} i8 @foo7({{.*}}, i8 {{.*}} signext {{.*}}) +// TINY: define {{.*}} i8 @foo7({{.*}}, i8 {{.*}}) +// TINY-NOT: define {{.*}} i8 @foo7({{.*}}, i8 {{.*}} signext {{.*}}) +char foo7(struct s15 a, char b) { + return a.arr[b]; +} + +// NOTE: On avr, though `a` only cost 16 registers, `b` has to be passed via the +// stack, since there is an implicit pointer argument costs 2 registers. +// AVR: define {{.*}} @foo8({{.*}}, {{.*}}, i8 {{.*}}) +// AVR-NOT: define {{.*}} @foo8({{.*}}, {{.*}}, i8 {{.*}} signext {{.*}}) +// TINY: define {{.*}} @foo8({{.*}}, {{.*}}, i8 {{.*}}) +// TINY-NOT: define {{.*}} @foo8({{.*}}, {{.*}}, i8 {{.*}} signext {{.*}}) +struct s15 foo8(struct s15 a, char b) { + a.arr[0] = b; + return a; +} + +// NOTE: On avrtiny, `b` has to be passed via the stack, since there is an +// implicit pointer argument costs 2 registers. +// AVR: define {{.*}} @foo9({{.*}}, i32 {{.*}}, i8 {{.*}} signext {{.*}}) +// TINY: define {{.*}} @foo9({{.*}}, i32 {{.*}}, i8 {{.*}}) +// TINY-NOT: define {{.*}} @foo9({{.*}}, i32 {{.*}}, i8 {{.*}} signext {{.*}}) +struct s15 foo9(long a, char b) { + struct s15 x; + x.arr[0] = b; + return x; +} + +// NOTE: All arguments are passed via registers, though there is an implicit +// pointer argument costs 2 registers. +// AVR: define {{.*}} @fooa({{.*}}, i8 {{.*}} signext {{.*}}, i8 {{.*}} signext {{.*}}) +// TINY: define {{.*}} @fooa({{.*}}, i8 {{.*}} signext {{.*}}, i8 {{.*}} signext {{.*}}) +struct s15 fooa(char a, char b) { + struct s15 x; + x.arr[0] = a; + x.arr[1] = b; + return x; +} diff --git a/clang/test/CodeGen/avr/struct.c b/clang/test/CodeGen/avr/struct.c --- a/clang/test/CodeGen/avr/struct.c +++ b/clang/test/CodeGen/avr/struct.c @@ -1,15 +1,23 @@ -// RUN: %clang_cc1 -triple avr -emit-llvm %s -o - | FileCheck %s +// 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 // Structure that is more than 8 bytes. struct s10 { int a, b, c, d, e; }; -// Structure that is less than 8 bytes. +// Structure that is less than 8 bytes but more than 4 bytes. struct s06 { int a, b, c; }; +// Structure that is less than 4 bytes. +struct s04 { + int a, b; +}; + struct s10 foo10(int a, int b, int c) { struct s10 a0; return a0; @@ -20,7 +28,21 @@ return a0; } -// CHECK: %struct.s10 = type { i16, i16, i16, i16, i16 } -// CHECK: %struct.s06 = type { i16, i16, i16 } -// CHECK: define{{.*}} void @foo10(%struct.s10* {{.*}}, i16 noundef %a, i16 noundef %b, i16 noundef %c) -// CHECK: define{{.*}} %struct.s06 @foo06(i16 noundef %a, i16 noundef %b, i16 noundef %c) +struct s04 foo04(int a, int b) { + struct s04 a0; + return a0; +} + +// AVR: %struct.s10 = type { i16, i16, i16, i16, i16 } +// AVR: %struct.s06 = type { i16, i16, i16 } +// AVR: %struct.s04 = type { i16, i16 } +// AVR: define{{.*}} void @foo10(%struct.s10* {{.*}}, i16 noundef %a, i16 noundef %b, i16 noundef %c) +// AVR: define{{.*}} %struct.s06 @foo06(i16 noundef %a, i16 noundef %b, i16 noundef %c) +// AVR: define{{.*}} %struct.s04 @foo04(i16 noundef %a, i16 noundef %b) + +// TINY: %struct.s10 = type { i16, i16, i16, i16, i16 } +// TINY: %struct.s06 = type { i16, i16, i16 } +// TINY: %struct.s04 = type { i16, i16 } +// TINY: define{{.*}} void @foo10(%struct.s10* {{.*}}, i16 noundef %a, i16 noundef %b, i16 noundef %c) +// TINY: define{{.*}} void @foo06(%struct.s06* {{.*}}, i16 noundef %a, i16 noundef %b, i16 noundef %c) +// TINY: define{{.*}} %struct.s04 @foo04(i16 noundef %a, i16 noundef %b)