diff --git a/llvm/lib/Target/AVR/AVRCallingConv.td b/llvm/lib/Target/AVR/AVRCallingConv.td --- a/llvm/lib/Target/AVR/AVRCallingConv.td +++ b/llvm/lib/Target/AVR/AVRCallingConv.td @@ -38,4 +38,6 @@ //===----------------------------------------------------------------------===// 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))>; diff --git a/llvm/lib/Target/AVR/AVRISelLowering.cpp b/llvm/lib/Target/AVR/AVRISelLowering.cpp --- a/llvm/lib/Target/AVR/AVRISelLowering.cpp +++ b/llvm/lib/Target/AVR/AVRISelLowering.cpp @@ -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" @@ -1023,17 +1024,24 @@ /// 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 @@ -1041,10 +1049,22 @@ /// In addition, all pieces of a certain argument have to be passed either /// using registers or the stack but never mixing both. template -static void -analyzeArguments(TargetLowering::CallLoweringInfo *CLI, const Function *F, - const DataLayout *TD, const SmallVectorImpl &Args, - SmallVectorImpl &ArgLocs, CCState &CCInfo) { +static void analyzeArguments(TargetLowering::CallLoweringInfo *CLI, + const Function *F, const DataLayout *TD, + const SmallVectorImpl &Args, + SmallVectorImpl &ArgLocs, + CCState &CCInfo, bool Tiny) { + // Choose the proper register list for argument passing according to the ABI. + ArrayRef RegList8; + ArrayRef 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). @@ -1074,7 +1094,7 @@ 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) { @@ -1123,13 +1143,24 @@ /// one value, possibly an aggregate, and it is limited to 8 bytes. template static void analyzeReturnValues(const SmallVectorImpl &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 RegList8; + ArrayRef 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) { @@ -1174,7 +1205,8 @@ 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; @@ -1299,7 +1331,8 @@ 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. @@ -1444,7 +1477,7 @@ 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. @@ -1495,7 +1528,7 @@ if (CallConv == CallingConv::AVR_BUILTIN) { CCInfo.AnalyzeReturn(Outs, RetCC_AVR_BUILTIN); } else { - analyzeReturnValues(Outs, CCInfo); + analyzeReturnValues(Outs, CCInfo, Subtarget.hasTinyEncoding()); } SDValue Flag; diff --git a/llvm/lib/Target/AVR/AVRRegisterInfo.cpp b/llvm/lib/Target/AVR/AVRRegisterInfo.cpp --- a/llvm/lib/Target/AVR/AVRRegisterInfo.cpp +++ b/llvm/lib/Target/AVR/AVRRegisterInfo.cpp @@ -36,15 +36,20 @@ const uint16_t * AVRRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { const AVRMachineFunctionInfo *AFI = MF->getInfo(); - - return AFI->isInterruptOrSignalHandler() ? CSR_Interrupts_SaveList - : CSR_Normal_SaveList; + const AVRSubtarget &STI = MF->getSubtarget(); + 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(); + return STI.hasTinyEncoding() ? CSR_NormalTiny_RegMask : CSR_Normal_RegMask; } BitVector AVRRegisterInfo::getReservedRegs(const MachineFunction &MF) const { @@ -52,15 +57,26 @@ // 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().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. diff --git a/llvm/lib/Target/AVR/AsmParser/AVRAsmParser.cpp b/llvm/lib/Target/AVR/AsmParser/AVRAsmParser.cpp --- a/llvm/lib/Target/AVR/AsmParser/AVRAsmParser.cpp +++ b/llvm/lib/Target/AVR/AsmParser/AVRAsmParser.cpp @@ -43,6 +43,10 @@ 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" @@ -332,6 +336,8 @@ 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; } @@ -399,6 +405,11 @@ 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. @@ -726,6 +737,12 @@ if (Op.isImm()) { if (MCConstantExpr const *Const = dyn_cast(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()); diff --git a/llvm/test/CodeGen/AVR/calling-conv/c/tiny.ll b/llvm/test/CodeGen/AVR/calling-conv/c/tiny.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AVR/calling-conv/c/tiny.ll @@ -0,0 +1,218 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=avr -mcpu=avrtiny | FileCheck %s + +; NOTE: Both %a(i8) and %b(i8) cost two registers. +define i8 @foo0(i8 %a, i8 %b) { +; CHECK-LABEL: foo0: +; CHECK: ; %bb.0: +; CHECK-NEXT: sub r24, r22 +; CHECK-NEXT: ret + %c = sub i8 %a, %b + ret i8 %c +} + +; NOTE: Both %a(i16) and %b(i16) cost two registers. +define i16 @foo1(i16 %a, i16 %b) { +; CHECK-LABEL: foo1: +; CHECK: ; %bb.0: +; CHECK-NEXT: sub r24, r22 +; CHECK-NEXT: sbc r25, r23 +; CHECK-NEXT: ret + %c = sub i16 %a, %b + ret i16 %c +} + +; NOTE: %a(i16), %b(i16) and %c(i16) each costs two registers. +define i16 @foo2(i16 %a, i16 %b, i16 %c) { +; CHECK-LABEL: foo2: +; CHECK: ; %bb.0: +; CHECK-NEXT: sub r22, r24 +; CHECK-NEXT: sbc r23, r25 +; CHECK-NEXT: add r22, r20 +; CHECK-NEXT: adc r23, r21 +; CHECK-NEXT: mov r24, r22 +; CHECK-NEXT: mov r25, r23 +; CHECK-NEXT: ret + %d = sub i16 %a, %b + %e = sub i16 %c, %d + ret i16 %e +} + +; NOTE: %a(i16), %b(i16) and %c(i16) each costs two registers, +; while %d(i16) is passed via the stack. +; FIXME: The `ldd` instruction is invalid on avrtiny, this test just shows +; how arguments are passed. +define i16 @foo3(i16 %a, i16 %b, i16 %c, i16 %d) { +; CHECK-LABEL: foo3: +; CHECK: ; %bb.0: +; CHECK-NEXT: push r28 +; CHECK-NEXT: push r29 +; CHECK-NEXT: in r28, 61 +; CHECK-NEXT: in r29, 62 +; CHECK-NEXT: ldd r30, Y+5 +; CHECK-NEXT: ldd r31, Y+6 +; CHECK-NEXT: sub r20, r30 +; CHECK-NEXT: sbc r21, r31 +; CHECK-NEXT: sub r24, r22 +; CHECK-NEXT: sbc r25, r23 +; CHECK-NEXT: add r24, r20 +; CHECK-NEXT: adc r25, r21 +; CHECK-NEXT: pop r29 +; CHECK-NEXT: pop r28 +; CHECK-NEXT: ret + %e = sub i16 %a, %b + %g = sub i16 %c, %d + %h = add i16 %e, %g + ret i16 %h +} + +; NOTE: %a(i32) costs four registers, while %b(i32) is passed via the stack. +; FIXME: The `ldd` instruction is invalid on avrtiny, this test just shows +; how arguments are passed. +define i32 @foo4(i32 %a, i32 %b) { +; CHECK-LABEL: foo4: +; CHECK: ; %bb.0: +; CHECK-NEXT: push r28 +; CHECK-NEXT: push r29 +; CHECK-NEXT: in r28, 61 +; CHECK-NEXT: in r29, 62 +; CHECK-NEXT: ldd r20, Y+5 +; CHECK-NEXT: ldd r21, Y+6 +; CHECK-NEXT: ldd r30, Y+7 +; CHECK-NEXT: ldd r31, Y+8 +; CHECK-NEXT: sub r20, r22 +; CHECK-NEXT: sbc r21, r23 +; CHECK-NEXT: sbc r30, r24 +; CHECK-NEXT: sbc r31, r25 +; CHECK-NEXT: mov r22, r20 +; CHECK-NEXT: mov r23, r21 +; CHECK-NEXT: mov r24, r30 +; CHECK-NEXT: mov r25, r31 +; CHECK-NEXT: pop r29 +; CHECK-NEXT: pop r28 +; CHECK-NEXT: ret + %c = sub i32 %b, %a + ret i32 %c +} + +; NOTE: %0 costs six registers, while %1 is passed via the stack. +; FIXME: The `ldd` instruction is invalid on avrtiny, this test just shows +; how arguments are passed. +define i8 @foo5([5 x i8] %0, i8 %1) { +; CHECK-LABEL: foo5: +; CHECK: ; %bb.0: +; CHECK-NEXT: push r28 +; CHECK-NEXT: push r29 +; CHECK-NEXT: in r28, 61 +; CHECK-NEXT: in r29, 62 +; CHECK-NEXT: ldd r24, Y+5 +; CHECK-NEXT: add r24, r20 +; CHECK-NEXT: pop r29 +; CHECK-NEXT: pop r28 +; CHECK-NEXT: ret + %3 = extractvalue [5 x i8] %0, 0 + %4 = add i8 %3, %1 + ret i8 %4 +} + +; NOTE: %0 costs two registers and %1 costs four registers. +define i8 @foo6([2 x i8] %0, [4 x i8] %1) { +; CHECK-LABEL: foo6: +; CHECK: ; %bb.0: +; CHECK-NEXT: add r24, r20 +; CHECK-NEXT: ret + %3 = extractvalue [2 x i8] %0, 0 + %4 = extractvalue [4 x i8] %1, 0 + %5 = add i8 %3, %4 + ret i8 %5 +} + +; NOTE: %0 cost four registers, while %1 is passed via the stack, +; though there are two vacant registers. +; FIXME: The `ldd` instruction is invalid on avrtiny, this test just shows +; how arguments are passed. +define i8 @foo7([3 x i8] %0, [3 x i8] %1) { +; CHECK-LABEL: foo7: +; CHECK: ; %bb.0: +; CHECK-NEXT: push r28 +; CHECK-NEXT: push r29 +; CHECK-NEXT: in r28, 61 +; CHECK-NEXT: in r29, 62 +; CHECK-NEXT: ldd r24, Y+5 +; CHECK-NEXT: add r24, r22 +; CHECK-NEXT: pop r29 +; CHECK-NEXT: pop r28 +; CHECK-NEXT: ret + %3 = extractvalue [3 x i8] %0, 0 + %4 = extractvalue [3 x i8] %1, 0 + %5 = add i8 %3, %4 + ret i8 %5 +} + +; NOTE: %0 costs four registers, and %1 costs two registers, while %2 is +; passed via the stack, though there is one vacant register. +; FIXME: The `ldd` instruction is invalid on avrtiny, this test just shows +; how arguments are passed. +define i8 @foo8([3 x i8] %0, i8 %1, i8 %2) { +; CHECK-LABEL: foo8: +; CHECK: ; %bb.0: +; CHECK-NEXT: push r28 +; CHECK-NEXT: push r29 +; CHECK-NEXT: in r28, 61 +; CHECK-NEXT: in r29, 62 +; CHECK-NEXT: add r22, r20 +; CHECK-NEXT: ldd r24, Y+5 +; CHECK-NEXT: sub r24, r22 +; CHECK-NEXT: pop r29 +; CHECK-NEXT: pop r28 +; CHECK-NEXT: ret + %4 = extractvalue [3 x i8] %0, 0 + %5 = add i8 %4, %1 + %6 = sub i8 %2, %5 + ret i8 %6 +} + +; NOTE: %0 is passed via registers, though there are 6 vacant registers. +; FIXME: The `ldd` instruction is invalid on avrtiny, this test just shows +; how arguments are passed. +define i8 @foo9([7 x i8] %0) { +; CHECK-LABEL: foo9: +; CHECK: ; %bb.0: +; CHECK-NEXT: push r28 +; CHECK-NEXT: push r29 +; CHECK-NEXT: in r28, 61 +; CHECK-NEXT: in r29, 62 +; CHECK-NEXT: ldd r25, Y+6 +; CHECK-NEXT: ldd r24, Y+5 +; CHECK-NEXT: add r24, r25 +; CHECK-NEXT: pop r29 +; CHECK-NEXT: pop r28 +; CHECK-NEXT: ret + %2 = extractvalue [7 x i8] %0, 0 + %3 = extractvalue [7 x i8] %0, 1 + %4 = add i8 %2, %3 + ret i8 %4 +} + +; NOTE: %0 costs six registers, while %1 and %2 are passed via the stack. +; FIXME: The `ldd` instruction is invalid on avrtiny, this test just shows +; how arguments are passed. +define i8 @fooa([6 x i8] %0, i8 %1, i8 %2) { +; CHECK-LABEL: fooa: +; CHECK: ; %bb.0: +; CHECK-NEXT: push r28 +; CHECK-NEXT: push r29 +; CHECK-NEXT: in r28, 61 +; CHECK-NEXT: in r29, 62 +; CHECK-NEXT: ldd r25, Y+5 +; CHECK-NEXT: ldd r24, Y+6 +; CHECK-NEXT: sub r24, r25 +; CHECK-NEXT: sub r24, r20 +; CHECK-NEXT: pop r29 +; CHECK-NEXT: pop r28 +; CHECK-NEXT: ret + %4 = extractvalue [6 x i8] %0, 0 + %5 = sub i8 %2, %1 + %6 = sub i8 %5, %4 + ret i8 %6 +} diff --git a/llvm/test/MC/AVR/error.s b/llvm/test/MC/AVR/error.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/AVR/error.s @@ -0,0 +1,16 @@ +# RUN: not llvm-mc -triple avr -mcpu=avrtiny %s 2>&1 \ +# RUN: | FileCheck -check-prefix=AVRTINY %s +# RUN: not llvm-mc -triple avr -mcpu=avr2 %s 2>&1 \ +# RUN: | FileCheck -check-prefix=AVR2 %s + +# AVRTINY: error: invalid register on avrtiny +mov r0, r16 + +# AVRTINY: error: invalid register on avrtiny +mov 1, r16 + +# AVR2: error: invalid operand for instruction +ldi r1, 15 + +# AVR2: error: instruction requires a CPU feature not currently enabled +lpm r8, Z+