Skip to content

Commit

Permalink
[AVR] Reject/Reserve R0~R15 on AVRTiny.
Browse files Browse the repository at this point in the history
Reviewed By: aykevl, dylanmckay

Differential Revision: https://reviews.llvm.org/D121672
  • Loading branch information
benshi001 committed Mar 24, 2022
1 parent 8474668 commit f319c24
Show file tree
Hide file tree
Showing 6 changed files with 320 additions and 18 deletions.
2 changes: 2 additions & 0 deletions llvm/lib/Target/AVR/AVRCallingConv.td
Expand Up @@ -38,4 +38,6 @@ def ArgCC_AVR_Vararg : CallingConv<[
//===----------------------------------------------------------------------===//

def CSR_Normal : CalleeSavedRegs<(add R29, R28, (sequence "R%u", 17, 2))>;
def CSR_NormalTiny : CalleeSavedRegs<(add R29, R28, R19, R18)>;
def CSR_Interrupts : CalleeSavedRegs<(add(sequence "R%u", 31, 2))>;
def CSR_InterruptsTiny : CalleeSavedRegs<(add(sequence "R%u", 31, 18))>;
59 changes: 46 additions & 13 deletions llvm/lib/Target/AVR/AVRISelLowering.cpp
Expand Up @@ -13,6 +13,7 @@

#include "AVRISelLowering.h"

#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/CodeGen/CallingConvLower.h"
Expand Down Expand Up @@ -1023,28 +1024,47 @@ bool AVRTargetLowering::isOffsetFoldingLegal(

/// Registers for calling conventions, ordered in reverse as required by ABI.
/// Both arrays must be of the same length.
static const MCPhysReg RegList8[] = {
static const MCPhysReg RegList8AVR[] = {
AVR::R25, AVR::R24, AVR::R23, AVR::R22, AVR::R21, AVR::R20,
AVR::R19, AVR::R18, AVR::R17, AVR::R16, AVR::R15, AVR::R14,
AVR::R13, AVR::R12, AVR::R11, AVR::R10, AVR::R9, AVR::R8};
static const MCPhysReg RegList16[] = {
static const MCPhysReg RegList8Tiny[] = {AVR::R25, AVR::R24, AVR::R23,
AVR::R22, AVR::R21, AVR::R20};
static const MCPhysReg RegList16AVR[] = {
AVR::R26R25, AVR::R25R24, AVR::R24R23, AVR::R23R22, AVR::R22R21,
AVR::R21R20, AVR::R20R19, AVR::R19R18, AVR::R18R17, AVR::R17R16,
AVR::R16R15, AVR::R15R14, AVR::R14R13, AVR::R13R12, AVR::R12R11,
AVR::R11R10, AVR::R10R9, AVR::R9R8};
static const MCPhysReg RegList16Tiny[] = {AVR::R26R25, AVR::R25R24,
AVR::R24R23, AVR::R23R22,
AVR::R22R21, AVR::R21R20};

static_assert(array_lengthof(RegList8) == array_lengthof(RegList16),
static_assert(array_lengthof(RegList8AVR) == array_lengthof(RegList16AVR),
"8-bit and 16-bit register arrays must be of equal length");
static_assert(array_lengthof(RegList8Tiny) == array_lengthof(RegList16Tiny),
"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.
/// In addition, all pieces of a certain argument have to be passed either
/// using registers or the stack but never mixing both.
template <typename ArgT>
static void
analyzeArguments(TargetLowering::CallLoweringInfo *CLI, const Function *F,
const DataLayout *TD, const SmallVectorImpl<ArgT> &Args,
SmallVectorImpl<CCValAssign> &ArgLocs, CCState &CCInfo) {
static void analyzeArguments(TargetLowering::CallLoweringInfo *CLI,
const Function *F, const DataLayout *TD,
const SmallVectorImpl<ArgT> &Args,
SmallVectorImpl<CCValAssign> &ArgLocs,
CCState &CCInfo, bool Tiny) {
// Choose the proper register list for argument passing according to the ABI.
ArrayRef<MCPhysReg> RegList8;
ArrayRef<MCPhysReg> RegList16;
if (Tiny) {
RegList8 = makeArrayRef(RegList8Tiny, array_lengthof(RegList8Tiny));
RegList16 = makeArrayRef(RegList16Tiny, array_lengthof(RegList16Tiny));
} else {
RegList8 = makeArrayRef(RegList8AVR, array_lengthof(RegList8AVR));
RegList16 = makeArrayRef(RegList16AVR, array_lengthof(RegList16AVR));
}

unsigned NumArgs = Args.size();
// This is the index of the last used register, in RegList*.
// -1 means R26 (R26 is never actually used in CC).
Expand Down Expand Up @@ -1074,7 +1094,7 @@ analyzeArguments(TargetLowering::CallLoweringInfo *CLI, const Function *F,
unsigned RegIdx = RegLastIdx + TotalBytes;
RegLastIdx = RegIdx;
// If there are not enough registers, use the stack
if (RegIdx >= array_lengthof(RegList8)) {
if (RegIdx >= RegList8.size()) {
UseStack = true;
}
for (; i != j; ++i) {
Expand Down Expand Up @@ -1123,13 +1143,24 @@ getTotalArgumentsSizeInBytes(const SmallVectorImpl<ArgT> &Args) {
/// one value, possibly an aggregate, and it is limited to 8 bytes.
template <typename ArgT>
static void analyzeReturnValues(const SmallVectorImpl<ArgT> &Args,
CCState &CCInfo) {
CCState &CCInfo, bool Tiny) {
unsigned NumArgs = Args.size();
unsigned TotalBytes = getTotalArgumentsSizeInBytes(Args);
// CanLowerReturn() guarantees this assertion.
assert(TotalBytes <= 8 &&
"return values greater than 8 bytes cannot be lowered");

// Choose the proper register list for argument passing according to the ABI.
ArrayRef<MCPhysReg> RegList8;
ArrayRef<MCPhysReg> RegList16;
if (Tiny) {
RegList8 = makeArrayRef(RegList8Tiny, array_lengthof(RegList8Tiny));
RegList16 = makeArrayRef(RegList16Tiny, array_lengthof(RegList16Tiny));
} else {
RegList8 = makeArrayRef(RegList8AVR, array_lengthof(RegList8AVR));
RegList16 = makeArrayRef(RegList16AVR, array_lengthof(RegList16AVR));
}

// 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.
if (TotalBytes > 4) {
Expand Down Expand Up @@ -1174,7 +1205,8 @@ SDValue AVRTargetLowering::LowerFormalArguments(
if (isVarArg) {
CCInfo.AnalyzeFormalArguments(Ins, ArgCC_AVR_Vararg);
} else {
analyzeArguments(nullptr, &MF.getFunction(), &DL, Ins, ArgLocs, CCInfo);
analyzeArguments(nullptr, &MF.getFunction(), &DL, Ins, ArgLocs, CCInfo,
Subtarget.hasTinyEncoding());
}

SDValue ArgValue;
Expand Down Expand Up @@ -1299,7 +1331,8 @@ SDValue AVRTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
if (isVarArg) {
CCInfo.AnalyzeCallOperands(Outs, ArgCC_AVR_Vararg);
} else {
analyzeArguments(&CLI, F, &DAG.getDataLayout(), Outs, ArgLocs, CCInfo);
analyzeArguments(&CLI, F, &DAG.getDataLayout(), Outs, ArgLocs, CCInfo,
Subtarget.hasTinyEncoding());
}

// Get a count of how many bytes are to be pushed on the stack.
Expand Down Expand Up @@ -1444,7 +1477,7 @@ SDValue AVRTargetLowering::LowerCallResult(
if (CallConv == CallingConv::AVR_BUILTIN) {
CCInfo.AnalyzeCallResult(Ins, RetCC_AVR_BUILTIN);
} else {
analyzeReturnValues(Ins, CCInfo);
analyzeReturnValues(Ins, CCInfo, Subtarget.hasTinyEncoding());
}

// Copy all of the result registers out of their specified physreg.
Expand Down Expand Up @@ -1495,7 +1528,7 @@ AVRTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv,
if (CallConv == CallingConv::AVR_BUILTIN) {
CCInfo.AnalyzeReturn(Outs, RetCC_AVR_BUILTIN);
} else {
analyzeReturnValues(Outs, CCInfo);
analyzeReturnValues(Outs, CCInfo, Subtarget.hasTinyEncoding());
}

SDValue Flag;
Expand Down
26 changes: 21 additions & 5 deletions llvm/lib/Target/AVR/AVRRegisterInfo.cpp
Expand Up @@ -36,31 +36,47 @@ AVRRegisterInfo::AVRRegisterInfo() : AVRGenRegisterInfo(0) {}
const uint16_t *
AVRRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
const AVRMachineFunctionInfo *AFI = MF->getInfo<AVRMachineFunctionInfo>();

return AFI->isInterruptOrSignalHandler() ? CSR_Interrupts_SaveList
: CSR_Normal_SaveList;
const AVRSubtarget &STI = MF->getSubtarget<AVRSubtarget>();
if (STI.hasTinyEncoding())
return AFI->isInterruptOrSignalHandler() ? CSR_InterruptsTiny_SaveList
: CSR_NormalTiny_SaveList;
else
return AFI->isInterruptOrSignalHandler() ? CSR_Interrupts_SaveList
: CSR_Normal_SaveList;
}

const uint32_t *
AVRRegisterInfo::getCallPreservedMask(const MachineFunction &MF,
CallingConv::ID CC) const {
return CSR_Normal_RegMask;
const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>();
return STI.hasTinyEncoding() ? CSR_NormalTiny_RegMask : CSR_Normal_RegMask;
}

BitVector AVRRegisterInfo::getReservedRegs(const MachineFunction &MF) const {
BitVector Reserved(getNumRegs());

// Reserve the intermediate result registers r1 and r2
// The result of instructions like 'mul' is always stored here.
// R0/R1/R1R0 are always reserved on both avr and avrtiny.
Reserved.set(AVR::R0);
Reserved.set(AVR::R1);
Reserved.set(AVR::R1R0);

// Reserve the stack pointer.
// Reserve the stack pointer.
Reserved.set(AVR::SPL);
Reserved.set(AVR::SPH);
Reserved.set(AVR::SP);

// Reserve R2~R17 only on avrtiny.
if (MF.getSubtarget<AVRSubtarget>().hasTinyEncoding()) {
// Reserve 8-bit registers R2~R15, Rtmp(R16) and Zero(R17).
for (unsigned Reg = AVR::R2; Reg <= AVR::R17; Reg++)
Reserved.set(Reg);
// Reserve 16-bit registers R3R2~R18R17.
for (unsigned Reg = AVR::R3R2; Reg <= AVR::R18R17; Reg++)
Reserved.set(Reg);
}

// We tenatively reserve the frame pointer register r29:r28 because the
// function may require one, but we cannot tell until register allocation
// is complete, which can be too late.
Expand Down
17 changes: 17 additions & 0 deletions llvm/lib/Target/AVR/AsmParser/AVRAsmParser.cpp
Expand Up @@ -43,6 +43,10 @@ class AVRAsmParser : public MCTargetAsmParser {
const MCRegisterInfo *MRI;
const std::string GENERATE_STUBS = "gs";

enum AVRMatchResultTy {
Match_InvalidRegisterOnTiny = FIRST_TARGET_MATCH_RESULT_TY + 1,
};

#define GET_ASSEMBLER_HEADER
#include "AVRGenAsmMatcher.inc"

Expand Down Expand Up @@ -332,6 +336,8 @@ bool AVRAsmParser::MatchAndEmitInstruction(SMLoc Loc, unsigned &Opcode,
return invalidOperand(Loc, Operands, ErrorInfo);
case Match_MnemonicFail:
return Error(Loc, "invalid instruction");
case Match_InvalidRegisterOnTiny:
return Error(Loc, "invalid register on avrtiny");
default:
return true;
}
Expand Down Expand Up @@ -399,6 +405,11 @@ bool AVRAsmParser::tryParseRegisterOperand(OperandVector &Operands) {
if (RegNo == AVR::NoRegister)
return true;

// Reject R0~R15 on avrtiny.
if (AVR::R0 <= RegNo && RegNo <= AVR::R15 &&
STI.hasFeature(AVR::FeatureTinyEncoding))
return Error(Parser.getTok().getLoc(), "invalid register on avrtiny");

AsmToken const &T = Parser.getTok();
Operands.push_back(AVROperand::CreateReg(RegNo, T.getLoc(), T.getEndLoc()));
Parser.Lex(); // Eat register token.
Expand Down Expand Up @@ -726,6 +737,12 @@ unsigned AVRAsmParser::validateTargetOperandClass(MCParsedAsmOperand &AsmOp,
if (Op.isImm()) {
if (MCConstantExpr const *Const = dyn_cast<MCConstantExpr>(Op.getImm())) {
int64_t RegNum = Const->getValue();

// Reject R0~R15 on avrtiny.
if (0 <= RegNum && RegNum <= 15 &&
STI.hasFeature(AVR::FeatureTinyEncoding))
return Match_InvalidRegisterOnTiny;

std::ostringstream RegName;
RegName << "r" << RegNum;
RegNum = MatchRegisterName(RegName.str());
Expand Down

0 comments on commit f319c24

Please sign in to comment.