[llvm] f319c24 - [AVR] Reject/Reserve R0~R15 on AVRTiny.

Ben Shi via llvm-commits llvm-commits at lists.llvm.org
Wed Mar 23 19:34:06 PDT 2022


Author: Ben Shi
Date: 2022-03-24T02:33:51Z
New Revision: f319c24570f93c8a5a4401768b3ecfba9ef2fdba

URL: https://github.com/llvm/llvm-project/commit/f319c24570f93c8a5a4401768b3ecfba9ef2fdba
DIFF: https://github.com/llvm/llvm-project/commit/f319c24570f93c8a5a4401768b3ecfba9ef2fdba.diff

LOG: [AVR] Reject/Reserve R0~R15 on AVRTiny.

Reviewed By: aykevl, dylanmckay

Differential Revision: https://reviews.llvm.org/D121672

Added: 
    llvm/test/CodeGen/AVR/calling-conv/c/tiny.ll
    llvm/test/MC/AVR/error.s

Modified: 
    llvm/lib/Target/AVR/AVRCallingConv.td
    llvm/lib/Target/AVR/AVRISelLowering.cpp
    llvm/lib/Target/AVR/AVRRegisterInfo.cpp
    llvm/lib/Target/AVR/AsmParser/AVRAsmParser.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/AVR/AVRCallingConv.td b/llvm/lib/Target/AVR/AVRCallingConv.td
index 0fae61fb55c5f..314d59bc2a59b 100644
--- a/llvm/lib/Target/AVR/AVRCallingConv.td
+++ b/llvm/lib/Target/AVR/AVRCallingConv.td
@@ -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))>;

diff  --git a/llvm/lib/Target/AVR/AVRISelLowering.cpp b/llvm/lib/Target/AVR/AVRISelLowering.cpp
index a3c1a6aabe10d..252331e88812e 100644
--- 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 @@ 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
@@ -1041,10 +1049,22 @@ static_assert(array_lengthof(RegList8) == array_lengthof(RegList16),
 /// 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).
@@ -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) {
@@ -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) {
@@ -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;
@@ -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.
@@ -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.
@@ -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;

diff  --git a/llvm/lib/Target/AVR/AVRRegisterInfo.cpp b/llvm/lib/Target/AVR/AVRRegisterInfo.cpp
index c34cf7480ae59..87e6558c12c22 100644
--- a/llvm/lib/Target/AVR/AVRRegisterInfo.cpp
+++ b/llvm/lib/Target/AVR/AVRRegisterInfo.cpp
@@ -36,15 +36,20 @@ 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 {
@@ -52,15 +57,26 @@ BitVector AVRRegisterInfo::getReservedRegs(const MachineFunction &MF) const {
 
   // 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.

diff  --git a/llvm/lib/Target/AVR/AsmParser/AVRAsmParser.cpp b/llvm/lib/Target/AVR/AsmParser/AVRAsmParser.cpp
index f19e7840eb310..9e1c7b781f0f4 100644
--- a/llvm/lib/Target/AVR/AsmParser/AVRAsmParser.cpp
+++ b/llvm/lib/Target/AVR/AsmParser/AVRAsmParser.cpp
@@ -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"
 
@@ -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;
   }
@@ -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.
@@ -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());

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
index 0000000000000..2c6e97e6221a3
--- /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
index 0000000000000..b9f53dc3d86da
--- /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+


        


More information about the llvm-commits mailing list