Skip to content

Commit 51585aa

Browse files
committed
[clang][AVR] Implement standard calling convention for AVR and AVRTiny
This patch implements avr-gcc's calling convention: https://gcc.gnu.org/wiki/avr-gcc#Calling_Convention Reviewed By: aykevl Differential Revision: https://reviews.llvm.org/D120720
1 parent cf198e9 commit 51585aa

File tree

5 files changed

+263
-39
lines changed

5 files changed

+263
-39
lines changed

clang/lib/Basic/Targets/AVR.cpp

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ namespace targets {
2424
struct LLVM_LIBRARY_VISIBILITY MCUInfo {
2525
const char *Name;
2626
const char *DefineName;
27-
const int NumFlashBanks; // -1 means the device does not support LPM/ELPM.
27+
const int NumFlashBanks; // Set to 0 for the devices do not support LPM/ELPM.
28+
bool IsTiny; // Set to true for the devices belong to the avrtiny family.
2829
};
2930

3031
// NOTE: This list has been synchronized with gcc-avr 5.4.0 and avr-libc 2.0.0.
@@ -282,14 +283,14 @@ static MCUInfo AVRMcus[] = {
282283
{"atxmega128a1", "__AVR_ATxmega128A1__", 2},
283284
{"atxmega128a1u", "__AVR_ATxmega128A1U__", 2},
284285
{"atxmega128a4u", "__AVR_ATxmega128A4U__", 2},
285-
{"attiny4", "__AVR_ATtiny4__", 0},
286-
{"attiny5", "__AVR_ATtiny5__", 0},
287-
{"attiny9", "__AVR_ATtiny9__", 0},
288-
{"attiny10", "__AVR_ATtiny10__", 0},
289-
{"attiny20", "__AVR_ATtiny20__", 0},
290-
{"attiny40", "__AVR_ATtiny40__", 0},
291-
{"attiny102", "__AVR_ATtiny102__", 0},
292-
{"attiny104", "__AVR_ATtiny104__", 0},
286+
{"attiny4", "__AVR_ATtiny4__", 0, true},
287+
{"attiny5", "__AVR_ATtiny5__", 0, true},
288+
{"attiny9", "__AVR_ATtiny9__", 0, true},
289+
{"attiny10", "__AVR_ATtiny10__", 0, true},
290+
{"attiny20", "__AVR_ATtiny20__", 0, true},
291+
{"attiny40", "__AVR_ATtiny40__", 0, true},
292+
{"attiny102", "__AVR_ATtiny102__", 0, true},
293+
{"attiny104", "__AVR_ATtiny104__", 0, true},
293294
{"attiny202", "__AVR_ATtiny202__", 1},
294295
{"attiny402", "__AVR_ATtiny402__", 1},
295296
{"attiny204", "__AVR_ATtiny204__", 1},
@@ -340,6 +341,27 @@ void AVRTargetInfo::fillValidCPUList(SmallVectorImpl<StringRef> &Values) const {
340341
Values.push_back(Info.Name);
341342
}
342343

344+
bool AVRTargetInfo::setCPU(const std::string &Name) {
345+
// Set the ABI and CPU fields if parameter Name is a family name.
346+
if (llvm::is_contained(ValidFamilyNames, Name)) {
347+
CPU = Name;
348+
ABI = Name == "avrtiny" ? "avrtiny" : "avr";
349+
return true;
350+
}
351+
352+
// Set the ABI field if parameter Name is a device name.
353+
auto It = llvm::find_if(
354+
AVRMcus, [&](const MCUInfo &Info) { return Info.Name == Name; });
355+
if (It != std::end(AVRMcus)) {
356+
CPU = Name;
357+
ABI = It->IsTiny ? "avrtiny" : "avr";
358+
return true;
359+
}
360+
361+
// Parameter Name is neither valid family name nor valid device name.
362+
return false;
363+
}
364+
343365
void AVRTargetInfo::getTargetDefines(const LangOptions &Opts,
344366
MacroBuilder &Builder) const {
345367
Builder.defineMacro("AVR");

clang/lib/Basic/Targets/AVR.h

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,7 @@ class LLVM_LIBRARY_VISIBILITY AVRTargetInfo : public TargetInfo {
7474
static const char *const GCCRegNames[] = {
7575
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9",
7676
"r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19",
77-
"r20", "r21", "r22", "r23", "r24", "r25", "X", "Y", "Z", "SP"
78-
};
77+
"r20", "r21", "r22", "r23", "r24", "r25", "X", "Y", "Z", "SP"};
7978
return llvm::makeArrayRef(GCCRegNames);
8079
}
8180

@@ -169,15 +168,12 @@ class LLVM_LIBRARY_VISIBILITY AVRTargetInfo : public TargetInfo {
169168

170169
bool isValidCPUName(StringRef Name) const override;
171170
void fillValidCPUList(SmallVectorImpl<StringRef> &Values) const override;
172-
bool setCPU(const std::string &Name) override {
173-
bool isValid = isValidCPUName(Name);
174-
if (isValid)
175-
CPU = Name;
176-
return isValid;
177-
}
171+
bool setCPU(const std::string &Name) override;
172+
StringRef getABI() const override { return ABI; }
178173

179174
protected:
180175
std::string CPU;
176+
StringRef ABI;
181177
};
182178

183179
} // namespace targets

clang/lib/CodeGen/TargetInfo.cpp

Lines changed: 84 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919
#include "CodeGenFunction.h"
2020
#include "clang/AST/Attr.h"
2121
#include "clang/AST/RecordLayout.h"
22+
#include "clang/Basic/Builtins.h"
2223
#include "clang/Basic/CodeGenOptions.h"
2324
#include "clang/Basic/DiagnosticFrontend.h"
24-
#include "clang/Basic/Builtins.h"
2525
#include "clang/CodeGen/CGFunctionInfo.h"
2626
#include "clang/CodeGen/SwiftCallingConv.h"
2727
#include "llvm/ADT/SmallBitVector.h"
@@ -33,6 +33,7 @@
3333
#include "llvm/IR/IntrinsicsNVPTX.h"
3434
#include "llvm/IR/IntrinsicsS390.h"
3535
#include "llvm/IR/Type.h"
36+
#include "llvm/Support/MathExtras.h"
3637
#include "llvm/Support/raw_ostream.h"
3738
#include <algorithm> // std::sort
3839

@@ -8278,32 +8279,93 @@ void M68kTargetCodeGenInfo::setTargetAttributes(
82788279

82798280
namespace {
82808281
class AVRABIInfo : public DefaultABIInfo {
8282+
private:
8283+
// The total amount of registers can be used to pass parameters. It is 18 on
8284+
// AVR, or 6 on AVRTiny.
8285+
const unsigned ParamRegs;
8286+
// The total amount of registers can be used to pass return value. It is 8 on
8287+
// AVR, or 4 on AVRTiny.
8288+
const unsigned RetRegs;
8289+
82818290
public:
8282-
AVRABIInfo(CodeGenTypes &CGT) : DefaultABIInfo(CGT) {}
8291+
AVRABIInfo(CodeGenTypes &CGT, unsigned NPR, unsigned NRR)
8292+
: DefaultABIInfo(CGT), ParamRegs(NPR), RetRegs(NRR) {}
8293+
8294+
ABIArgInfo classifyReturnType(QualType Ty, bool &LargeRet) const {
8295+
if (isAggregateTypeForABI(Ty)) {
8296+
// On AVR, a return struct with size less than or equals to 8 bytes is
8297+
// returned directly via registers R18-R25. On AVRTiny, a return struct
8298+
// with size less than or equals to 4 bytes is returned directly via
8299+
// registers R22-R25.
8300+
if (getContext().getTypeSize(Ty) <= RetRegs * 8)
8301+
return ABIArgInfo::getDirect();
8302+
// A return struct with larger size is returned via a stack
8303+
// slot, along with a pointer to it as the function's implicit argument.
8304+
LargeRet = true;
8305+
return getNaturalAlignIndirect(Ty);
8306+
}
8307+
// Otherwise we follow the default way which is compatible.
8308+
return DefaultABIInfo::classifyReturnType(Ty);
8309+
}
82838310

8284-
ABIArgInfo classifyReturnType(QualType Ty) const {
8285-
// A return struct with size less than or equal to 8 bytes is returned
8286-
// directly via registers R18-R25.
8287-
if (isAggregateTypeForABI(Ty) && getContext().getTypeSize(Ty) <= 64)
8311+
ABIArgInfo classifyArgumentType(QualType Ty, unsigned &NumRegs) const {
8312+
unsigned TySize = getContext().getTypeSize(Ty);
8313+
8314+
// An int8 type argument always costs two registers like an int16.
8315+
if (TySize == 8 && NumRegs >= 2) {
8316+
NumRegs -= 2;
8317+
return ABIArgInfo::getExtend(Ty);
8318+
}
8319+
8320+
// If the argument size is an odd number of bytes, round up the size
8321+
// to the next even number.
8322+
TySize = llvm::alignTo(TySize, 16);
8323+
8324+
// Any type including an array/struct type can be passed in rgisters,
8325+
// if there are enough registers left.
8326+
if (TySize <= NumRegs * 8) {
8327+
NumRegs -= TySize / 8;
82888328
return ABIArgInfo::getDirect();
8289-
else
8290-
return DefaultABIInfo::classifyReturnType(Ty);
8329+
}
8330+
8331+
// An argument is passed either completely in registers or completely in
8332+
// memory. Since there are not enough registers left, current argument
8333+
// and all other unprocessed arguments should be passed in memory.
8334+
// However we still need to return `ABIArgInfo::getDirect()` other than
8335+
// `ABIInfo::getNaturalAlignIndirect(Ty)`, otherwise an extra stack slot
8336+
// will be allocated, so the stack frame layout will be incompatible with
8337+
// avr-gcc.
8338+
NumRegs = 0;
8339+
return ABIArgInfo::getDirect();
82918340
}
82928341

8293-
// Just copy the original implementation of DefaultABIInfo::computeInfo(),
8294-
// since DefaultABIInfo::classify{Return,Argument}Type() are not virtual.
82958342
void computeInfo(CGFunctionInfo &FI) const override {
8343+
// Decide the return type.
8344+
bool LargeRet = false;
82968345
if (!getCXXABI().classifyReturnType(FI))
8297-
FI.getReturnInfo() = classifyReturnType(FI.getReturnType());
8346+
FI.getReturnInfo() = classifyReturnType(FI.getReturnType(), LargeRet);
8347+
8348+
// Decide each argument type. The total number of registers can be used for
8349+
// arguments depends on several factors:
8350+
// 1. Arguments of varargs functions are passed on the stack. This applies
8351+
// even to the named arguments. So no register can be used.
8352+
// 2. Total 18 registers can be used on avr and 6 ones on avrtiny.
8353+
// 3. If the return type is a struct with too large size, two registers
8354+
// (out of 18/6) will be cost as an implicit pointer argument.
8355+
unsigned NumRegs = ParamRegs;
8356+
if (FI.isVariadic())
8357+
NumRegs = 0;
8358+
else if (LargeRet)
8359+
NumRegs -= 2;
82988360
for (auto &I : FI.arguments())
8299-
I.info = classifyArgumentType(I.type);
8361+
I.info = classifyArgumentType(I.type, NumRegs);
83008362
}
83018363
};
83028364

83038365
class AVRTargetCodeGenInfo : public TargetCodeGenInfo {
83048366
public:
8305-
AVRTargetCodeGenInfo(CodeGenTypes &CGT)
8306-
: TargetCodeGenInfo(std::make_unique<AVRABIInfo>(CGT)) {}
8367+
AVRTargetCodeGenInfo(CodeGenTypes &CGT, unsigned NPR, unsigned NRR)
8368+
: TargetCodeGenInfo(std::make_unique<AVRABIInfo>(CGT, NPR, NRR)) {}
83078369

83088370
LangAS getGlobalVarAddressSpace(CodeGenModule &CGM,
83098371
const VarDecl *D) const override {
@@ -11280,8 +11342,14 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() {
1128011342
case llvm::Triple::mips64el:
1128111343
return SetCGInfo(new MIPSTargetCodeGenInfo(Types, false));
1128211344

11283-
case llvm::Triple::avr:
11284-
return SetCGInfo(new AVRTargetCodeGenInfo(Types));
11345+
case llvm::Triple::avr: {
11346+
// For passing parameters, R8~R25 are used on avr, and R18~R25 are used
11347+
// on avrtiny. For passing return value, R18~R25 are used on avr, and
11348+
// R22~R25 are used on avrtiny.
11349+
unsigned NPR = getTarget().getABI() == "avrtiny" ? 6 : 18;
11350+
unsigned NRR = getTarget().getABI() == "avrtiny" ? 4 : 8;
11351+
return SetCGInfo(new AVRTargetCodeGenInfo(Types, NPR, NRR));
11352+
}
1128511353

1128611354
case llvm::Triple::aarch64:
1128711355
case llvm::Triple::aarch64_32:

clang/test/CodeGen/avr/argument.c

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// RUN: %clang_cc1 -triple avr -target-cpu atmega328 -emit-llvm %s -o - \
2+
// RUN: | FileCheck %s --check-prefix AVR
3+
// RUN: %clang_cc1 -triple avr -target-cpu attiny40 -emit-llvm %s -o - \
4+
// RUN: | FileCheck %s --check-prefix TINY
5+
6+
// NOTE: All arguments are passed via the stack for functions with variable arguments.
7+
// AVR: define {{.*}} i8 @foo0(i8 {{.*}}, i8 {{.*}}, ...)
8+
// TINY: define {{.*}} i8 @foo0(i8 {{.*}}, i8 {{.*}}, ...)
9+
// AVR-NOT: define {{.*}} i8 @foo0(i8 {{.*}} signext {{.*}}, i8 {{.*}} signext {{.*}}, ...)
10+
// TINY-NOT: define {{.*}} i8 @foo0(i8 {{.*}} signext {{.*}}, i8 {{.*}} signext {{.*}}, ...)
11+
char foo0(char a, char b, ...) {
12+
return a + b;
13+
}
14+
15+
// NOTE: All arguments are passed via registers on both avr and avrtiny.
16+
// AVR: define {{.*}} i8 @foo1(i32 {{.*}}, i8 {{.*}} signext {{.*}})
17+
// TINY: define {{.*}} i8 @foo1(i32 {{.*}}, i8 {{.*}} signext {{.*}})
18+
char foo1(long a, char b) {
19+
return a + b;
20+
}
21+
22+
// NOTE: The argument `char c` is passed via registers on avr, while via the stack on avrtiny.
23+
// The argument `char b` costs 2 registers, so there is no vacant register left for
24+
// `char c` on avrtiny.
25+
// AVR: define {{.*}} i8 @foo2(i32 {{.*}}, i8 {{.*}} signext {{.*}}, i8 {{.*}} signext {{.*}})
26+
// TINY: define {{.*}} i8 @foo2(i32 {{.*}}, i8 {{.*}} signext {{.*}}, i8 {{.*}})
27+
// TINY-NOT: define {{.*}} i8 @foo2(i32 {{.*}}, i8 {{.*}} signext {{.*}}, i8 {{.*}} signext {{.*}})
28+
char foo2(long a, char b, char c) {
29+
return a + b + c;
30+
}
31+
32+
// NOTE: On avr, the argument `a` costs 16 registers and `b` costs 2 registers, so
33+
// `c` has to be passed via the stack.
34+
// AVR: define {{.*}} i8 @foo3({{.*}}, i8 {{.*}} signext {{.*}}, i8 {{.*}})
35+
// AVR-NOT: define {{.*}} i8 @foo3({{.*}}, i8 {{.*}} signext {{.*}}, i8 {{.*}} signext {{.*}})
36+
// TINY: define {{.*}} i8 @foo3({{.*}}, i8 {{.*}}, i8 {{.*}})
37+
// TINY-NOT: define {{.*}} i8 @foo3({{.*}}, i8 {{.*}} signext {{.*}}, i8 {{.*}} signext {{.*}})
38+
struct s15 {
39+
char arr[15];
40+
};
41+
char foo3(struct s15 a, char b, char c) {
42+
return a.arr[b] + a.arr[c];
43+
}
44+
45+
// NOTE: On avr, `a` only costs 16 registers, though there are 2 vacant registers,
46+
// both `b` and `c` have to be passed via the stack.
47+
// AVR: define {{.*}} i8 @foo4({{.*}}, i32 {{.*}}, i8 {{.*}})
48+
// AVR-NOT: define {{.*}} i8 @foo4({{.*}}, i32 {{.*}}, i8 {{.*}} signext {{.*}})
49+
// TINY: define {{.*}} i8 @foo4({{.*}}, i32 {{.*}}, i8 {{.*}})
50+
// TINY-NOT: define {{.*}} i8 @foo4({{.*}}, i32 {{.*}}, i8 {{.*}} signext {{.*}})
51+
char foo4(struct s15 a, long b, char c) {
52+
return a.arr[c];
53+
}
54+
55+
// NOTE: On avrtiny, `a` only costs 4 registers, though there are 2 vacant
56+
// registers, both `b` and `c` are passed via the stack.
57+
// AVR: define {{.*}} i8 @foo5(i32 {{.*}}, i32 {{.*}}, i8 {{.*}} signext {{.*}})
58+
// TINY: define {{.*}} i8 @foo5(i32 {{.*}}, i32 {{.*}}, i8 {{.*}})
59+
// TINY-NOT: define {{.*}} i8 @foo5(i32 {{.*}}, i32 {{.*}}, i8 {{.*}} signext {{.*}})
60+
char foo5(long a, long b, char c) {
61+
return c + 1;
62+
}
63+
64+
// NOTE: All arguments are passed via the stack, though all registers are vacant.
65+
// AVR: define {{.*}} i8 @foo6({{.*}}, i8 {{.*}})
66+
// AVR-NOT: define {{.*}} i8 @foo6({{.*}}, i8 {{.*}} signext {{.*}})
67+
// TINY: define {{.*}} i8 @foo6({{.*}}, i8 {{.*}})
68+
// TINY-NOT: define {{.*}} i8 @foo6({{.*}}, i8 {{.*}} signext {{.*}})
69+
struct s32 {
70+
char arr[32];
71+
};
72+
char foo6(struct s32 a, char b) {
73+
return a.arr[b];
74+
}
75+
76+
// NOTE: All arguments are passed via registers on avr. While all arguments are passed
77+
// via the stack on avrtiny, though all registers are vacant.
78+
// AVR: define {{.*}} i8 @foo7({{.*}}, i8 {{.*}} signext {{.*}})
79+
// TINY: define {{.*}} i8 @foo7({{.*}}, i8 {{.*}})
80+
// TINY-NOT: define {{.*}} i8 @foo7({{.*}}, i8 {{.*}} signext {{.*}})
81+
char foo7(struct s15 a, char b) {
82+
return a.arr[b];
83+
}
84+
85+
// NOTE: On avr, though `a` only cost 16 registers, `b` has to be passed via the
86+
// stack, since there is an implicit pointer argument costs 2 registers.
87+
// AVR: define {{.*}} @foo8({{.*}}, {{.*}}, i8 {{.*}})
88+
// AVR-NOT: define {{.*}} @foo8({{.*}}, {{.*}}, i8 {{.*}} signext {{.*}})
89+
// TINY: define {{.*}} @foo8({{.*}}, {{.*}}, i8 {{.*}})
90+
// TINY-NOT: define {{.*}} @foo8({{.*}}, {{.*}}, i8 {{.*}} signext {{.*}})
91+
struct s15 foo8(struct s15 a, char b) {
92+
a.arr[0] = b;
93+
return a;
94+
}
95+
96+
// NOTE: On avrtiny, `b` has to be passed via the stack, since there is an
97+
// implicit pointer argument costs 2 registers.
98+
// AVR: define {{.*}} @foo9({{.*}}, i32 {{.*}}, i8 {{.*}} signext {{.*}})
99+
// TINY: define {{.*}} @foo9({{.*}}, i32 {{.*}}, i8 {{.*}})
100+
// TINY-NOT: define {{.*}} @foo9({{.*}}, i32 {{.*}}, i8 {{.*}} signext {{.*}})
101+
struct s15 foo9(long a, char b) {
102+
struct s15 x;
103+
x.arr[0] = b;
104+
return x;
105+
}
106+
107+
// NOTE: All arguments are passed via registers, though there is an implicit
108+
// pointer argument costs 2 registers.
109+
// AVR: define {{.*}} @fooa({{.*}}, i8 {{.*}} signext {{.*}}, i8 {{.*}} signext {{.*}})
110+
// TINY: define {{.*}} @fooa({{.*}}, i8 {{.*}} signext {{.*}}, i8 {{.*}} signext {{.*}})
111+
struct s15 fooa(char a, char b) {
112+
struct s15 x;
113+
x.arr[0] = a;
114+
x.arr[1] = b;
115+
return x;
116+
}

0 commit comments

Comments
 (0)