[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