[llvm] a190fcd - [CSKY] Add inline asm constraints and related codegen support
Zi Xuan Wu via llvm-commits
llvm-commits at lists.llvm.org
Mon Feb 7 01:47:49 PST 2022
Author: Zi Xuan Wu
Date: 2022-02-07T17:45:37+08:00
New Revision: a190fcdfcc54587f8dc3b574e0f4ebb7e6c67000
URL: https://github.com/llvm/llvm-project/commit/a190fcdfcc54587f8dc3b574e0f4ebb7e6c67000
DIFF: https://github.com/llvm/llvm-project/commit/a190fcdfcc54587f8dc3b574e0f4ebb7e6c67000.diff
LOG: [CSKY] Add inline asm constraints and related codegen support
There are kinds of inline asm constraints and corresponding register class or register as following.
'b': mGPRRegClass
'v': sGPRRegClass
'w': sFPR32RegClass or sFPR64RegClass
'c': C register
'z': R14 register
'h': HI register
'l': LO register
'y': HI or LO register
It also adds codegen test for inline-asm including constraints, clobbers and abi names.
Added:
llvm/test/CodeGen/CSKY/inline-asm-abi-names.ll
llvm/test/CodeGen/CSKY/inline-asm-clobbers.ll
llvm/test/CodeGen/CSKY/inline-asm-d-constraint-f.ll
llvm/test/CodeGen/CSKY/inline-asm-f-constraint-f.ll
llvm/test/CodeGen/CSKY/inline-asm-float-abi-names.ll
llvm/test/CodeGen/CSKY/inline-asm-invalid.ll
llvm/test/CodeGen/CSKY/inline-asm.ll
Modified:
llvm/lib/Target/CSKY/AsmParser/CSKYAsmParser.cpp
llvm/lib/Target/CSKY/CSKYAsmPrinter.cpp
llvm/lib/Target/CSKY/CSKYAsmPrinter.h
llvm/lib/Target/CSKY/CSKYISelDAGToDAG.cpp
llvm/lib/Target/CSKY/CSKYISelLowering.cpp
llvm/lib/Target/CSKY/CSKYISelLowering.h
llvm/lib/Target/CSKY/CSKYRegisterInfo.cpp
Removed:
################################################################################
diff --git a/llvm/lib/Target/CSKY/AsmParser/CSKYAsmParser.cpp b/llvm/lib/Target/CSKY/AsmParser/CSKYAsmParser.cpp
index a62bd111cba95..74c462dbd52fb 100644
--- a/llvm/lib/Target/CSKY/AsmParser/CSKYAsmParser.cpp
+++ b/llvm/lib/Target/CSKY/AsmParser/CSKYAsmParser.cpp
@@ -52,6 +52,9 @@ class CSKYAsmParser : public MCTargetAsmParser {
const MCRegisterInfo *MRI;
+ unsigned validateTargetOperandClass(MCParsedAsmOperand &Op,
+ unsigned Kind) override;
+
bool generateImmOutOfRangeError(OperandVector &Operands, uint64_t ErrorInfo,
int64_t Lower, int64_t Upper, Twine Msg);
@@ -612,6 +615,11 @@ struct CSKYOperand : public MCParsedAsmOperand {
#define GET_MNEMONIC_SPELL_CHECKER
#include "CSKYGenAsmMatcher.inc"
+static MCRegister convertFPR32ToFPR64(MCRegister Reg) {
+ assert(Reg >= CSKY::F0_32 && Reg <= CSKY::F31_32 && "Invalid register");
+ return Reg - CSKY::F0_32 + CSKY::F0_64;
+}
+
static std::string CSKYMnemonicSpellCheck(StringRef S, const FeatureBitset &FBS,
unsigned VariantID = 0);
@@ -1473,6 +1481,40 @@ OperandMatchResultTy CSKYAsmParser::tryParseRegister(unsigned &RegNo,
bool CSKYAsmParser::ParseDirective(AsmToken DirectiveID) { return true; }
+unsigned CSKYAsmParser::validateTargetOperandClass(MCParsedAsmOperand &AsmOp,
+ unsigned Kind) {
+ CSKYOperand &Op = static_cast<CSKYOperand &>(AsmOp);
+
+ if (!Op.isReg())
+ return Match_InvalidOperand;
+
+ MCRegister Reg = Op.getReg();
+
+ if (CSKYMCRegisterClasses[CSKY::FPR32RegClassID].contains(Reg)) {
+ // As the parser couldn't
diff erentiate an FPR64 from an FPR32, coerce the
+ // register from FPR32 to FPR64 if necessary.
+ if (Kind == MCK_FPR64 || Kind == MCK_sFPR64_V) {
+ Op.Reg.RegNum = convertFPR32ToFPR64(Reg);
+ if (Kind == MCK_sFPR64_V &&
+ (Op.Reg.RegNum < CSKY::F0_64 || Op.Reg.RegNum > CSKY::F15_64))
+ return Match_InvalidRegOutOfRange;
+ if (Kind == MCK_FPR64 &&
+ (Op.Reg.RegNum < CSKY::F0_64 || Op.Reg.RegNum > CSKY::F31_64))
+ return Match_InvalidRegOutOfRange;
+ return Match_Success;
+ }
+ }
+
+ if (CSKYMCRegisterClasses[CSKY::GPRRegClassID].contains(Reg)) {
+ if (Kind == MCK_GPRPair) {
+ Op.Reg.RegNum = MRI->getEncodingValue(Reg) + CSKY::R0_R1;
+ return Match_Success;
+ }
+ }
+
+ return Match_InvalidOperand;
+}
+
void CSKYAsmParser::emitToStreamer(MCStreamer &S, const MCInst &Inst) {
MCInst CInst;
bool Res = false;
diff --git a/llvm/lib/Target/CSKY/CSKYAsmPrinter.cpp b/llvm/lib/Target/CSKY/CSKYAsmPrinter.cpp
index c8269eeacfdb2..0c317c9f56288 100644
--- a/llvm/lib/Target/CSKY/CSKYAsmPrinter.cpp
+++ b/llvm/lib/Target/CSKY/CSKYAsmPrinter.cpp
@@ -218,6 +218,69 @@ void CSKYAsmPrinter::emitMachineConstantPoolValue(
OutStreamer->emitValue(Expr, Size);
}
+bool CSKYAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
+ const char *ExtraCode, raw_ostream &OS) {
+ // First try the generic code, which knows about modifiers like 'c' and 'n'.
+ if (!AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, OS))
+ return false;
+
+ const MachineOperand &MO = MI->getOperand(OpNo);
+ if (ExtraCode && ExtraCode[0]) {
+ if (ExtraCode[1] != 0)
+ return true; // Unknown modifier.
+
+ switch (ExtraCode[0]) {
+ default:
+ return true; // Unknown modifier.
+ case 'R':
+ if (MO.getType() == MachineOperand::MO_Register) {
+ OS << CSKYInstPrinter::getRegisterName(MO.getReg() + 1);
+ return false;
+ }
+ }
+ }
+
+ switch (MO.getType()) {
+ case MachineOperand::MO_Immediate:
+ OS << MO.getImm();
+ return false;
+ case MachineOperand::MO_Register:
+ if (MO.getReg() == CSKY::C)
+ return false;
+ OS << CSKYInstPrinter::getRegisterName(MO.getReg());
+ return false;
+ case MachineOperand::MO_GlobalAddress:
+ PrintSymbolOperand(MO, OS);
+ return false;
+ case MachineOperand::MO_BlockAddress: {
+ MCSymbol *Sym = GetBlockAddressSymbol(MO.getBlockAddress());
+ Sym->print(OS, MAI);
+ return false;
+ }
+ default:
+ break;
+ }
+
+ return true;
+}
+
+bool CSKYAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
+ unsigned OpNo, const char *ExtraCode,
+ raw_ostream &OS) {
+ if (!ExtraCode) {
+ const MachineOperand &MO = MI->getOperand(OpNo);
+ // For now, we only support register memory operands in registers and
+ // assume there is no addend
+ if (!MO.isReg())
+ return true;
+
+ OS << "(" << CSKYInstPrinter::getRegisterName(MO.getReg()) << ", 0)";
+ return false;
+ }
+
+ return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, ExtraCode, OS);
+}
+
extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeCSKYAsmPrinter() {
RegisterAsmPrinter<CSKYAsmPrinter> X(getTheCSKYTarget());
}
diff --git a/llvm/lib/Target/CSKY/CSKYAsmPrinter.h b/llvm/lib/Target/CSKY/CSKYAsmPrinter.h
index 04a253d349c80..c626fea15262a 100644
--- a/llvm/lib/Target/CSKY/CSKYAsmPrinter.h
+++ b/llvm/lib/Target/CSKY/CSKYAsmPrinter.h
@@ -52,6 +52,12 @@ class LLVM_LIBRARY_VISIBILITY CSKYAsmPrinter : public AsmPrinter {
// we emit constant pools customly!
void emitConstantPool() override{};
+
+ bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
+ const char *ExtraCode, raw_ostream &OS) override;
+
+ bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
+ const char *ExtraCode, raw_ostream &OS) override;
};
} // end namespace llvm
diff --git a/llvm/lib/Target/CSKY/CSKYISelDAGToDAG.cpp b/llvm/lib/Target/CSKY/CSKYISelDAGToDAG.cpp
index d58f9095aa0df..3dc272074401b 100644
--- a/llvm/lib/Target/CSKY/CSKYISelDAGToDAG.cpp
+++ b/llvm/lib/Target/CSKY/CSKYISelDAGToDAG.cpp
@@ -42,6 +42,12 @@ class CSKYDAGToDAGISel : public SelectionDAGISel {
void Select(SDNode *N) override;
bool selectAddCarry(SDNode *N);
bool selectSubCarry(SDNode *N);
+ bool selectInlineAsm(SDNode *N);
+
+ SDNode *createGPRPairNode(EVT VT, SDValue V0, SDValue V1);
+
+ bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID,
+ std::vector<SDValue> &OutOps) override;
#include "CSKYGenDAGISel.inc"
};
@@ -86,6 +92,10 @@ void CSKYDAGToDAGISel::Select(SDNode *N) {
IsSelected = true;
break;
}
+ case ISD::INLINEASM:
+ case ISD::INLINEASM_BR:
+ IsSelected = selectInlineAsm(N);
+ break;
}
if (IsSelected)
@@ -95,6 +105,167 @@ void CSKYDAGToDAGISel::Select(SDNode *N) {
SelectCode(N);
}
+bool CSKYDAGToDAGISel::selectInlineAsm(SDNode *N) {
+ std::vector<SDValue> AsmNodeOperands;
+ unsigned Flag, Kind;
+ bool Changed = false;
+ unsigned NumOps = N->getNumOperands();
+
+ // Normally, i64 data is bounded to two arbitrary GRPs for "%r" constraint.
+ // However, some instructions (e.g. mula.s32) require GPR pair.
+ // Since there is no constraint to explicitly specify a
+ // reg pair, we use GPRPair reg class for "%r" for 64-bit data.
+
+ SDLoc dl(N);
+ SDValue Glue =
+ N->getGluedNode() ? N->getOperand(NumOps - 1) : SDValue(nullptr, 0);
+
+ SmallVector<bool, 8> OpChanged;
+ // Glue node will be appended late.
+ for (unsigned i = 0, e = N->getGluedNode() ? NumOps - 1 : NumOps; i < e;
+ ++i) {
+ SDValue op = N->getOperand(i);
+ AsmNodeOperands.push_back(op);
+
+ if (i < InlineAsm::Op_FirstOperand)
+ continue;
+
+ if (ConstantSDNode *C = dyn_cast<ConstantSDNode>(N->getOperand(i))) {
+ Flag = C->getZExtValue();
+ Kind = InlineAsm::getKind(Flag);
+ } else
+ continue;
+
+ // Immediate operands to inline asm in the SelectionDAG are modeled with
+ // two operands. The first is a constant of value InlineAsm::Kind_Imm, and
+ // the second is a constant with the value of the immediate. If we get here
+ // and we have a Kind_Imm, skip the next operand, and continue.
+ if (Kind == InlineAsm::Kind_Imm) {
+ SDValue op = N->getOperand(++i);
+ AsmNodeOperands.push_back(op);
+ continue;
+ }
+
+ unsigned NumRegs = InlineAsm::getNumOperandRegisters(Flag);
+ if (NumRegs)
+ OpChanged.push_back(false);
+
+ unsigned DefIdx = 0;
+ bool IsTiedToChangedOp = false;
+ // If it's a use that is tied with a previous def, it has no
+ // reg class constraint.
+ if (Changed && InlineAsm::isUseOperandTiedToDef(Flag, DefIdx))
+ IsTiedToChangedOp = OpChanged[DefIdx];
+
+ // Memory operands to inline asm in the SelectionDAG are modeled with two
+ // operands: a constant of value InlineAsm::Kind_Mem followed by the input
+ // operand. If we get here and we have a Kind_Mem, skip the next operand (so
+ // it doesn't get misinterpreted), and continue. We do this here because
+ // it's important to update the OpChanged array correctly before moving on.
+ if (Kind == InlineAsm::Kind_Mem) {
+ SDValue op = N->getOperand(++i);
+ AsmNodeOperands.push_back(op);
+ continue;
+ }
+
+ if (Kind != InlineAsm::Kind_RegUse && Kind != InlineAsm::Kind_RegDef &&
+ Kind != InlineAsm::Kind_RegDefEarlyClobber)
+ continue;
+
+ unsigned RC;
+ bool HasRC = InlineAsm::hasRegClassConstraint(Flag, RC);
+ if ((!IsTiedToChangedOp && (!HasRC || RC != CSKY::GPRRegClassID)) ||
+ NumRegs != 2)
+ continue;
+
+ assert((i + 2 < NumOps) && "Invalid number of operands in inline asm");
+ SDValue V0 = N->getOperand(i + 1);
+ SDValue V1 = N->getOperand(i + 2);
+ unsigned Reg0 = cast<RegisterSDNode>(V0)->getReg();
+ unsigned Reg1 = cast<RegisterSDNode>(V1)->getReg();
+ SDValue PairedReg;
+ MachineRegisterInfo &MRI = MF->getRegInfo();
+
+ if (Kind == InlineAsm::Kind_RegDef ||
+ Kind == InlineAsm::Kind_RegDefEarlyClobber) {
+ // Replace the two GPRs with 1 GPRPair and copy values from GPRPair to
+ // the original GPRs.
+
+ Register GPVR = MRI.createVirtualRegister(&CSKY::GPRPairRegClass);
+ PairedReg = CurDAG->getRegister(GPVR, MVT::i64);
+ SDValue Chain = SDValue(N, 0);
+
+ SDNode *GU = N->getGluedUser();
+ SDValue RegCopy =
+ CurDAG->getCopyFromReg(Chain, dl, GPVR, MVT::i64, Chain.getValue(1));
+
+ // Extract values from a GPRPair reg and copy to the original GPR reg.
+ SDValue Sub0 =
+ CurDAG->getTargetExtractSubreg(CSKY::sub32_0, dl, MVT::i32, RegCopy);
+ SDValue Sub1 =
+ CurDAG->getTargetExtractSubreg(CSKY::sub32_32, dl, MVT::i32, RegCopy);
+ SDValue T0 =
+ CurDAG->getCopyToReg(Sub0, dl, Reg0, Sub0, RegCopy.getValue(1));
+ SDValue T1 = CurDAG->getCopyToReg(Sub1, dl, Reg1, Sub1, T0.getValue(1));
+
+ // Update the original glue user.
+ std::vector<SDValue> Ops(GU->op_begin(), GU->op_end() - 1);
+ Ops.push_back(T1.getValue(1));
+ CurDAG->UpdateNodeOperands(GU, Ops);
+ } else {
+ // For Kind == InlineAsm::Kind_RegUse, we first copy two GPRs into a
+ // GPRPair and then pass the GPRPair to the inline asm.
+ SDValue Chain = AsmNodeOperands[InlineAsm::Op_InputChain];
+
+ // As REG_SEQ doesn't take RegisterSDNode, we copy them first.
+ SDValue T0 =
+ CurDAG->getCopyFromReg(Chain, dl, Reg0, MVT::i32, Chain.getValue(1));
+ SDValue T1 =
+ CurDAG->getCopyFromReg(Chain, dl, Reg1, MVT::i32, T0.getValue(1));
+ SDValue Pair = SDValue(createGPRPairNode(MVT::i64, T0, T1), 0);
+
+ // Copy REG_SEQ into a GPRPair-typed VR and replace the original two
+ // i32 VRs of inline asm with it.
+ Register GPVR = MRI.createVirtualRegister(&CSKY::GPRPairRegClass);
+ PairedReg = CurDAG->getRegister(GPVR, MVT::i64);
+ Chain = CurDAG->getCopyToReg(T1, dl, GPVR, Pair, T1.getValue(1));
+
+ AsmNodeOperands[InlineAsm::Op_InputChain] = Chain;
+ Glue = Chain.getValue(1);
+ }
+
+ Changed = true;
+
+ if (PairedReg.getNode()) {
+ OpChanged[OpChanged.size() - 1] = true;
+ Flag = InlineAsm::getFlagWord(Kind, 1 /* RegNum*/);
+ if (IsTiedToChangedOp)
+ Flag = InlineAsm::getFlagWordForMatchingOp(Flag, DefIdx);
+ else
+ Flag = InlineAsm::getFlagWordForRegClass(Flag, CSKY::GPRPairRegClassID);
+ // Replace the current flag.
+ AsmNodeOperands[AsmNodeOperands.size() - 1] =
+ CurDAG->getTargetConstant(Flag, dl, MVT::i32);
+ // Add the new register node and skip the original two GPRs.
+ AsmNodeOperands.push_back(PairedReg);
+ // Skip the next two GPRs.
+ i += 2;
+ }
+ }
+
+ if (Glue.getNode())
+ AsmNodeOperands.push_back(Glue);
+ if (!Changed)
+ return false;
+
+ SDValue New = CurDAG->getNode(N->getOpcode(), SDLoc(N),
+ CurDAG->getVTList(MVT::Other, MVT::Glue),
+ AsmNodeOperands);
+ New->setNodeId(-1);
+ ReplaceNode(N, New.getNode());
+ return true;
+}
+
bool CSKYDAGToDAGISel::selectAddCarry(SDNode *N) {
MachineSDNode *NewNode = nullptr;
auto Type0 = N->getValueType(0);
@@ -175,6 +346,31 @@ bool CSKYDAGToDAGISel::selectSubCarry(SDNode *N) {
return true;
}
+SDNode *CSKYDAGToDAGISel::createGPRPairNode(EVT VT, SDValue V0, SDValue V1) {
+ SDLoc dl(V0.getNode());
+ SDValue RegClass =
+ CurDAG->getTargetConstant(CSKY::GPRPairRegClassID, dl, MVT::i32);
+ SDValue SubReg0 = CurDAG->getTargetConstant(CSKY::sub32_0, dl, MVT::i32);
+ SDValue SubReg1 = CurDAG->getTargetConstant(CSKY::sub32_32, dl, MVT::i32);
+ const SDValue Ops[] = {RegClass, V0, SubReg0, V1, SubReg1};
+ return CurDAG->getMachineNode(TargetOpcode::REG_SEQUENCE, dl, VT, Ops);
+}
+
+bool CSKYDAGToDAGISel::SelectInlineAsmMemoryOperand(
+ const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) {
+ switch (ConstraintID) {
+ case InlineAsm::Constraint_m:
+ // We just support simple memory operands that have a single address
+ // operand and need no special handling.
+ OutOps.push_back(Op);
+ return false;
+ default:
+ break;
+ }
+
+ return true;
+}
+
FunctionPass *llvm::createCSKYISelDag(CSKYTargetMachine &TM) {
return new CSKYDAGToDAGISel(TM);
}
diff --git a/llvm/lib/Target/CSKY/CSKYISelLowering.cpp b/llvm/lib/Target/CSKY/CSKYISelLowering.cpp
index 0b589e3d3e4f7..bacbdcfa18408 100644
--- a/llvm/lib/Target/CSKY/CSKYISelLowering.cpp
+++ b/llvm/lib/Target/CSKY/CSKYISelLowering.cpp
@@ -784,6 +784,175 @@ SDValue CSKYTargetLowering::getTargetConstantPoolValue(GlobalAddressSDNode *N,
return DAG.getTargetConstantPool(CPV, Ty);
}
+CSKYTargetLowering::ConstraintType
+CSKYTargetLowering::getConstraintType(StringRef Constraint) const {
+ if (Constraint.size() == 1) {
+ switch (Constraint[0]) {
+ default:
+ break;
+ case 'a':
+ case 'b':
+ case 'v':
+ case 'w':
+ case 'y':
+ return C_RegisterClass;
+ case 'c':
+ case 'l':
+ case 'h':
+ case 'z':
+ return C_Register;
+ }
+ }
+ return TargetLowering::getConstraintType(Constraint);
+}
+
+std::pair<unsigned, const TargetRegisterClass *>
+CSKYTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
+ StringRef Constraint,
+ MVT VT) const {
+ if (Constraint.size() == 1) {
+ switch (Constraint[0]) {
+ case 'r':
+ return std::make_pair(0U, &CSKY::GPRRegClass);
+ case 'a':
+ return std::make_pair(0U, &CSKY::mGPRRegClass);
+ case 'b':
+ return std::make_pair(0U, &CSKY::sGPRRegClass);
+ case 'z':
+ return std::make_pair(CSKY::R14, &CSKY::GPRRegClass);
+ case 'c':
+ return std::make_pair(CSKY::C, &CSKY::CARRYRegClass);
+ case 'w':
+ if ((Subtarget.hasFPUv2SingleFloat() ||
+ Subtarget.hasFPUv3SingleFloat()) &&
+ VT == MVT::f32)
+ return std::make_pair(0U, &CSKY::sFPR32RegClass);
+ if ((Subtarget.hasFPUv2DoubleFloat() ||
+ Subtarget.hasFPUv3DoubleFloat()) &&
+ VT == MVT::f64)
+ return std::make_pair(0U, &CSKY::sFPR64RegClass);
+ break;
+ case 'v':
+ if (Subtarget.hasFPUv2SingleFloat() && VT == MVT::f32)
+ return std::make_pair(0U, &CSKY::sFPR32RegClass);
+ if (Subtarget.hasFPUv3SingleFloat() && VT == MVT::f32)
+ return std::make_pair(0U, &CSKY::FPR32RegClass);
+ if (Subtarget.hasFPUv2DoubleFloat() && VT == MVT::f64)
+ return std::make_pair(0U, &CSKY::sFPR64RegClass);
+ if (Subtarget.hasFPUv3DoubleFloat() && VT == MVT::f64)
+ return std::make_pair(0U, &CSKY::FPR64RegClass);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (Constraint == "{c}")
+ return std::make_pair(CSKY::C, &CSKY::CARRYRegClass);
+
+ // Clang will correctly decode the usage of register name aliases into their
+ // official names. However, other frontends like `rustc` do not. This allows
+ // users of these frontends to use the ABI names for registers in LLVM-style
+ // register constraints.
+ unsigned XRegFromAlias = StringSwitch<unsigned>(Constraint.lower())
+ .Case("{a0}", CSKY::R0)
+ .Case("{a1}", CSKY::R1)
+ .Case("{a2}", CSKY::R2)
+ .Case("{a3}", CSKY::R3)
+ .Case("{l0}", CSKY::R4)
+ .Case("{l1}", CSKY::R5)
+ .Case("{l2}", CSKY::R6)
+ .Case("{l3}", CSKY::R7)
+ .Case("{l4}", CSKY::R8)
+ .Case("{l5}", CSKY::R9)
+ .Case("{l6}", CSKY::R10)
+ .Case("{l7}", CSKY::R11)
+ .Case("{t0}", CSKY::R12)
+ .Case("{t1}", CSKY::R13)
+ .Case("{sp}", CSKY::R14)
+ .Case("{lr}", CSKY::R15)
+ .Case("{l8}", CSKY::R16)
+ .Case("{l9}", CSKY::R17)
+ .Case("{t2}", CSKY::R18)
+ .Case("{t3}", CSKY::R19)
+ .Case("{t4}", CSKY::R20)
+ .Case("{t5}", CSKY::R21)
+ .Case("{t6}", CSKY::R22)
+ .Cases("{t7}", "{fp}", CSKY::R23)
+ .Cases("{t8}", "{top}", CSKY::R24)
+ .Cases("{t9}", "{bsp}", CSKY::R25)
+ .Case("{r26}", CSKY::R26)
+ .Case("{r27}", CSKY::R27)
+ .Cases("{gb}", "{rgb}", "{rdb}", CSKY::R28)
+ .Cases("{tb}", "{rtb}", CSKY::R29)
+ .Case("{svbr}", CSKY::R30)
+ .Case("{tls}", CSKY::R31)
+ .Default(CSKY::NoRegister);
+
+ if (XRegFromAlias != CSKY::NoRegister)
+ return std::make_pair(XRegFromAlias, &CSKY::GPRRegClass);
+
+ // Since TargetLowering::getRegForInlineAsmConstraint uses the name of the
+ // TableGen record rather than the AsmName to choose registers for InlineAsm
+ // constraints, plus we want to match those names to the widest floating point
+ // register type available, manually select floating point registers here.
+ //
+ // The second case is the ABI name of the register, so that frontends can also
+ // use the ABI names in register constraint lists.
+ if (Subtarget.useHardFloat()) {
+ unsigned FReg = StringSwitch<unsigned>(Constraint.lower())
+ .Cases("{fr0}", "{vr0}", CSKY::F0_32)
+ .Cases("{fr1}", "{vr1}", CSKY::F1_32)
+ .Cases("{fr2}", "{vr2}", CSKY::F2_32)
+ .Cases("{fr3}", "{vr3}", CSKY::F3_32)
+ .Cases("{fr4}", "{vr4}", CSKY::F4_32)
+ .Cases("{fr5}", "{vr5}", CSKY::F5_32)
+ .Cases("{fr6}", "{vr6}", CSKY::F6_32)
+ .Cases("{fr7}", "{vr7}", CSKY::F7_32)
+ .Cases("{fr8}", "{vr8}", CSKY::F8_32)
+ .Cases("{fr9}", "{vr9}", CSKY::F9_32)
+ .Cases("{fr10}", "{vr10}", CSKY::F10_32)
+ .Cases("{fr11}", "{vr11}", CSKY::F11_32)
+ .Cases("{fr12}", "{vr12}", CSKY::F12_32)
+ .Cases("{fr13}", "{vr13}", CSKY::F13_32)
+ .Cases("{fr14}", "{vr14}", CSKY::F14_32)
+ .Cases("{fr15}", "{vr15}", CSKY::F15_32)
+ .Cases("{fr16}", "{vr16}", CSKY::F16_32)
+ .Cases("{fr17}", "{vr17}", CSKY::F17_32)
+ .Cases("{fr18}", "{vr18}", CSKY::F18_32)
+ .Cases("{fr19}", "{vr19}", CSKY::F19_32)
+ .Cases("{fr20}", "{vr20}", CSKY::F20_32)
+ .Cases("{fr21}", "{vr21}", CSKY::F21_32)
+ .Cases("{fr22}", "{vr22}", CSKY::F22_32)
+ .Cases("{fr23}", "{vr23}", CSKY::F23_32)
+ .Cases("{fr24}", "{vr24}", CSKY::F24_32)
+ .Cases("{fr25}", "{vr25}", CSKY::F25_32)
+ .Cases("{fr26}", "{vr26}", CSKY::F26_32)
+ .Cases("{fr27}", "{vr27}", CSKY::F27_32)
+ .Cases("{fr28}", "{vr28}", CSKY::F28_32)
+ .Cases("{fr29}", "{vr29}", CSKY::F29_32)
+ .Cases("{fr30}", "{vr30}", CSKY::F30_32)
+ .Cases("{fr31}", "{vr31}", CSKY::F31_32)
+ .Default(CSKY::NoRegister);
+ if (FReg != CSKY::NoRegister) {
+ assert(CSKY::F0_32 <= FReg && FReg <= CSKY::F31_32 && "Unknown fp-reg");
+ unsigned RegNo = FReg - CSKY::F0_32;
+ unsigned DReg = CSKY::F0_64 + RegNo;
+
+ if (Subtarget.hasFPUv2DoubleFloat())
+ return std::make_pair(DReg, &CSKY::sFPR64RegClass);
+ else if (Subtarget.hasFPUv3DoubleFloat())
+ return std::make_pair(DReg, &CSKY::FPR64RegClass);
+ else if (Subtarget.hasFPUv2SingleFloat())
+ return std::make_pair(FReg, &CSKY::sFPR32RegClass);
+ else if (Subtarget.hasFPUv3SingleFloat())
+ return std::make_pair(FReg, &CSKY::FPR32RegClass);
+ }
+ }
+
+ return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT);
+}
+
static MachineBasicBlock *
emitSelectPseudo(MachineInstr &MI, MachineBasicBlock *BB, unsigned Opcode) {
diff --git a/llvm/lib/Target/CSKY/CSKYISelLowering.h b/llvm/lib/Target/CSKY/CSKYISelLowering.h
index e1744d5ce2203..1cd0f99b17bc2 100644
--- a/llvm/lib/Target/CSKY/CSKYISelLowering.h
+++ b/llvm/lib/Target/CSKY/CSKYISelLowering.h
@@ -88,6 +88,12 @@ class CSKYTargetLowering : public TargetLowering {
return (Kind != ScalarCondVectorVal);
}
+ ConstraintType getConstraintType(StringRef Constraint) const override;
+
+ std::pair<unsigned, const TargetRegisterClass *>
+ getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
+ StringRef Constraint, MVT VT) const override;
+
MachineBasicBlock *
EmitInstrWithCustomInserter(MachineInstr &MI,
MachineBasicBlock *BB) const override;
diff --git a/llvm/lib/Target/CSKY/CSKYRegisterInfo.cpp b/llvm/lib/Target/CSKY/CSKYRegisterInfo.cpp
index 57b6ae3c27b59..0e42c878ade8b 100644
--- a/llvm/lib/Target/CSKY/CSKYRegisterInfo.cpp
+++ b/llvm/lib/Target/CSKY/CSKYRegisterInfo.cpp
@@ -29,6 +29,10 @@ const uint32_t *
CSKYRegisterInfo::getCallPreservedMask(const MachineFunction &MF,
CallingConv::ID Id) const {
const CSKYSubtarget &STI = MF.getSubtarget<CSKYSubtarget>();
+ if (STI.hasFPUv2DoubleFloat() || STI.hasFPUv3DoubleFloat())
+ return CSR_GPR_FPR64_RegMask;
+ if (STI.hasFPUv2SingleFloat() || STI.hasFPUv3SingleFloat())
+ return CSR_GPR_FPR32_RegMask;
return CSR_I32_RegMask;
}
@@ -82,9 +86,21 @@ const MCPhysReg *
CSKYRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
const CSKYSubtarget &STI = MF->getSubtarget<CSKYSubtarget>();
if (MF->getFunction().hasFnAttribute("interrupt")) {
+ if (STI.hasFPUv3DoubleFloat())
+ return CSR_GPR_FPR64v3_ISR_SaveList;
+ if (STI.hasFPUv3SingleFloat())
+ return CSR_GPR_FPR32v3_ISR_SaveList;
+ if (STI.hasFPUv2DoubleFloat())
+ return CSR_GPR_FPR64_ISR_SaveList;
+ if (STI.hasFPUv2SingleFloat())
+ return CSR_GPR_FPR32_ISR_SaveList;
return CSR_GPR_ISR_SaveList;
}
+ if (STI.hasFPUv2DoubleFloat() || STI.hasFPUv3DoubleFloat())
+ return CSR_GPR_FPR64_SaveList;
+ if (STI.hasFPUv2SingleFloat() || STI.hasFPUv3SingleFloat())
+ return CSR_GPR_FPR32_SaveList;
return CSR_I32_SaveList;
}
diff --git a/llvm/test/CodeGen/CSKY/inline-asm-abi-names.ll b/llvm/test/CodeGen/CSKY/inline-asm-abi-names.ll
new file mode 100644
index 0000000000000..58e86ed2d3fdc
--- /dev/null
+++ b/llvm/test/CodeGen/CSKY/inline-asm-abi-names.ll
@@ -0,0 +1,1007 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=csky -verify-machineinstrs -csky-no-aliases -no-integrated-as < %s \
+; RUN: | FileCheck -check-prefix=CSKY %s
+
+; These test that we can use both the architectural names (r*) and the ABI names
+; (a*, l* etc) to refer to registers in inline asm constraint lists. In each
+; case, the named register should be used for the source register of the `addi`.
+;
+; The inline assembly will, by default, contain the ABI names for the registers.
+;
+; Parenthesised registers in comments are the other aliases for this register.
+
+; NOTE: This test uses `r0` (`a0`) as an input, so it should be saved.
+define i32 @explicit_register_r0(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r0:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, a0, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r0}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r0` (`a0`) as an input, so it should be saved.
+define i32 @explicit_register_a0(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_a0:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, a0, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{a0}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r1` (`a1`) as an input, so it should be saved.
+define i32 @explicit_register_r1(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r1:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 a1, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, a1, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r1}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_a1(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_a1:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 a1, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, a1, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{a1}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r2` (`a2`) as an input, so it should be saved.
+define i32 @explicit_register_r2(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r2:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 a2, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, a2, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r2}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_a2(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_a2:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 a2, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, a2, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{a2}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r3` (`a3`) as an input, so it should be saved.
+define i32 @explicit_register_r3(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r3:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 a3, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, a3, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r3}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_a3(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_a3:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 a3, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, a3, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{a3}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r4` (`l0`) as an input, so it should be saved.
+define i32 @explicit_register_r4(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r4:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: st16.w l0, (sp, 0) # 4-byte Folded Spill
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 l0, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, l0, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: ld16.w l0, (sp, 0) # 4-byte Folded Reload
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r4}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_l0(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_l0:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: st16.w l0, (sp, 0) # 4-byte Folded Spill
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 l0, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, l0, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: ld16.w l0, (sp, 0) # 4-byte Folded Reload
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{l0}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r5` (`l1`) as an input, so it should be saved.
+define i32 @explicit_register_r5(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r5:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: st16.w l1, (sp, 0) # 4-byte Folded Spill
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 l1, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, l1, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: ld16.w l1, (sp, 0) # 4-byte Folded Reload
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r5}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_l1(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_l1:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: st16.w l1, (sp, 0) # 4-byte Folded Spill
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 l1, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, l1, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: ld16.w l1, (sp, 0) # 4-byte Folded Reload
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{l1}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r6` (`l2`) as an input, so it should be saved.
+define i32 @explicit_register_r6(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r6:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: st16.w l2, (sp, 0) # 4-byte Folded Spill
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 l2, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, l2, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: ld16.w l2, (sp, 0) # 4-byte Folded Reload
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r6}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_l2(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_l2:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: st16.w l2, (sp, 0) # 4-byte Folded Spill
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 l2, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, l2, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: ld16.w l2, (sp, 0) # 4-byte Folded Reload
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{l2}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r7` (`l3`) as an input, so it should be saved.
+define i32 @explicit_register_r7(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r7:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: st16.w l3, (sp, 0) # 4-byte Folded Spill
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 l3, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, l3, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: ld16.w l3, (sp, 0) # 4-byte Folded Reload
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r7}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_l3(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_l3:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: st16.w l3, (sp, 0) # 4-byte Folded Spill
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 l3, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, l3, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: ld16.w l3, (sp, 0) # 4-byte Folded Reload
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{l3}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r8` (`l4`) as an input, so it should be saved.
+define i32 @explicit_register_r8(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r8:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: st32.w l4, (sp, 0) # 4-byte Folded Spill
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 l4, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, l4, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: ld32.w l4, (sp, 0) # 4-byte Folded Reload
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r8}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_l4(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_l4:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: st32.w l4, (sp, 0) # 4-byte Folded Spill
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 l4, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, l4, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: ld32.w l4, (sp, 0) # 4-byte Folded Reload
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{l4}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r9` (`l5`) as an input, so it should be saved.
+define i32 @explicit_register_r9(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r9:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: st32.w l5, (sp, 0) # 4-byte Folded Spill
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 l5, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, l5, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: ld32.w l5, (sp, 0) # 4-byte Folded Reload
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r9}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_l5(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_l5:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: st32.w l5, (sp, 0) # 4-byte Folded Spill
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 l5, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, l5, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: ld32.w l5, (sp, 0) # 4-byte Folded Reload
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{l5}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r10` (`l6`) as an input, so it should be saved.
+define i32 @explicit_register_r10(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r10:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: st32.w l6, (sp, 0) # 4-byte Folded Spill
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 l6, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, l6, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: ld32.w l6, (sp, 0) # 4-byte Folded Reload
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r10}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_l6(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_l6:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: st32.w l6, (sp, 0) # 4-byte Folded Spill
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 l6, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, l6, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: ld32.w l6, (sp, 0) # 4-byte Folded Reload
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{l6}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r11` (`l7`) as an input, so it should be saved.
+define i32 @explicit_register_r11(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r11:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: st32.w l7, (sp, 0) # 4-byte Folded Spill
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 l7, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, l7, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: ld32.w l7, (sp, 0) # 4-byte Folded Reload
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r11}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_l7(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_l7:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: st32.w l7, (sp, 0) # 4-byte Folded Spill
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 l7, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, l7, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: ld32.w l7, (sp, 0) # 4-byte Folded Reload
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{l7}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r12` (`t0`) as an input, so it should be saved.
+define i32 @explicit_register_r12(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r12:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 t0, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, t0, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r12}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_t0(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_t0:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 t0, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, t0, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{t0}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r13` (`t1`) as an input, so it should be saved.
+define i32 @explicit_register_r13(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r13:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 t1, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, t1, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r13}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_t1(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_t1:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 t1, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, t1, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{t1}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r14` (`sp`) as an input, so it should be saved.
+define i32 @explicit_register_r14(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r14:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 sp, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, sp, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r14}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_sp(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_sp:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 sp, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, sp, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{sp}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r15` (`lr`) as an input, so it should be saved.
+define i32 @explicit_register_r15(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r15:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: st32.w lr, (sp, 0) # 4-byte Folded Spill
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 lr, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, lr, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: ld32.w lr, (sp, 0) # 4-byte Folded Reload
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r15}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_lr(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_lr:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: st32.w lr, (sp, 0) # 4-byte Folded Spill
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 lr, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, lr, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: ld32.w lr, (sp, 0) # 4-byte Folded Reload
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{lr}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r16` (`l8`) as an input, so it should be saved.
+define i32 @explicit_register_r16(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r16:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: st32.w l8, (sp, 0) # 4-byte Folded Spill
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 l8, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, l8, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: ld32.w l8, (sp, 0) # 4-byte Folded Reload
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r16}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_l8(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_l8:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: st32.w l8, (sp, 0) # 4-byte Folded Spill
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 l8, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, l8, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: ld32.w l8, (sp, 0) # 4-byte Folded Reload
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{l8}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r17` (`l9`) as an input, so it should be saved.
+define i32 @explicit_register_r17(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r17:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: st32.w l9, (sp, 0) # 4-byte Folded Spill
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 l9, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, l9, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: ld32.w l9, (sp, 0) # 4-byte Folded Reload
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r17}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_l9(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_l9:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: st32.w l9, (sp, 0) # 4-byte Folded Spill
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 l9, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, l9, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: ld32.w l9, (sp, 0) # 4-byte Folded Reload
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{l9}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r18` (`t2`) as an input, so it should be saved.
+define i32 @explicit_register_r18(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r18:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 t2, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, t2, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r18}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_t2(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_t2:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 t2, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, t2, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{t2}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r19` (`t3`) as an input, so it should be saved.
+define i32 @explicit_register_r19(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r19:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 t3, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, t3, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r19}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_t3(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_t3:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 t3, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, t3, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{t3}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r20` (`t4`) as an input, so it should be saved.
+define i32 @explicit_register_r20(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r20:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 t4, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, t4, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r20}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_t4(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_t4:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 t4, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, t4, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{t4}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r21` (`t5`) as an input, so it should be saved.
+define i32 @explicit_register_r21(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r21:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 t5, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, t5, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r21}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_t5(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_t5:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 t5, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, t5, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{t5}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r22` (`t6`) as an input, so it should be saved.
+define i32 @explicit_register_r22(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r22:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 t6, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, t6, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r22}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_t6(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_t6:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 t6, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, t6, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{t6}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r23` (`t7`) as an input, so it should be saved.
+define i32 @explicit_register_r23(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r23:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 t7, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, t7, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r23}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_t7(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_t7:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 t7, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, t7, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{t7}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r24` (`t8`) as an input, so it should be saved.
+define i32 @explicit_register_r24(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r24:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 t8, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, t8, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r24}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_t8(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_t8:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 t8, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, t8, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{t8}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r25` (`t9`) as an input, so it should be saved.
+define i32 @explicit_register_r25(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r25:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 t9, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, t9, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r25}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_t9(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_t9:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 t9, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, t9, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{t9}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r26` (`r26`) as an input, so it should be saved.
+define i32 @explicit_register_r26(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r26:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 r26, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, r26, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r26}"(i32 %a)
+ ret i32 %1
+}
+
+
+; NOTE: This test uses `r27` (`r27`) as an input, so it should be saved.
+define i32 @explicit_register_r27(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r27:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 r27, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, r27, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r27}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r28` (`rgb`) as an input, so it should be saved.
+define i32 @explicit_register_r28(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r28:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: st32.w rgb, (sp, 0) # 4-byte Folded Spill
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 rgb, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, rgb, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: ld32.w rgb, (sp, 0) # 4-byte Folded Reload
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r28}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_rgb(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_rgb:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: st32.w rgb, (sp, 0) # 4-byte Folded Spill
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 rgb, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, rgb, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: ld32.w rgb, (sp, 0) # 4-byte Folded Reload
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{rgb}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r29` (`rtb`) as an input, so it should be saved.
+define i32 @explicit_register_r29(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r29:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 rtb, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, rtb, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r29}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_rtb(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_rtb:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 rtb, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, rtb, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{rtb}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r30` (`svbr`) as an input, so it should be saved.
+define i32 @explicit_register_r30(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r30:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 svbr, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, svbr, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r30}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_svbr(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_svbr:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 svbr, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, svbr, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{svbr}"(i32 %a)
+ ret i32 %1
+}
+
+; NOTE: This test uses `r31` (`tls`) as an input, so it should be saved.
+define i32 @explicit_register_r31(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_r31:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 tls, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, tls, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{r31}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @explicit_register_tls(i32 %a) nounwind {
+; CSKY-LABEL: explicit_register_tls:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov32 tls, a0
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, tls, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, 1", "=r,{tls}"(i32 %a)
+ ret i32 %1
+}
diff --git a/llvm/test/CodeGen/CSKY/inline-asm-clobbers.ll b/llvm/test/CodeGen/CSKY/inline-asm-clobbers.ll
new file mode 100644
index 0000000000000..12a4bd4e33e86
--- /dev/null
+++ b/llvm/test/CodeGen/CSKY/inline-asm-clobbers.ll
@@ -0,0 +1,62 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=csky -verify-machineinstrs -csky-no-aliases < %s \
+; RUN: | FileCheck -check-prefix=CSKYI %s
+; RUN: llc -mtriple=csky -mattr=+fpuv2_sf -mattr=+hard-float -float-abi=hard -verify-machineinstrs -csky-no-aliases < %s \
+; RUN: | FileCheck -check-prefix=CSKYIF %s
+; RUN: llc -mtriple=csky -mattr=+fpuv2_sf -mattr=+fpuv2_df -mattr=+hard-float -float-abi=hard -verify-machineinstrs -csky-no-aliases < %s \
+; RUN: | FileCheck -check-prefix=CSKYID %s
+
+define void @testcase() nounwind {
+; CSKYI-LABEL: testcase:
+; CSKYI: # %bb.0:
+; CSKYI-NEXT: subi16 sp, sp, 8
+; CSKYI-NEXT: st16.w l3, (sp, 4) # 4-byte Folded Spill
+; CSKYI-NEXT: st16.w l0, (sp, 0) # 4-byte Folded Spill
+; CSKYI-NEXT: subi16 sp, sp, 4
+; CSKYI-NEXT: #APP
+; CSKYI-NEXT: #NO_APP
+; CSKYI-NEXT: addi16 sp, sp, 4
+; CSKYI-NEXT: ld16.w l0, (sp, 0) # 4-byte Folded Reload
+; CSKYI-NEXT: ld16.w l3, (sp, 4) # 4-byte Folded Reload
+; CSKYI-NEXT: addi16 sp, sp, 8
+; CSKYI-NEXT: rts16
+;
+; CSKYIF-LABEL: testcase:
+; CSKYIF: # %bb.0:
+; CSKYIF-NEXT: subi16 sp, sp, 16
+; CSKYIF-NEXT: fsts vr9, (sp, 12) # 4-byte Folded Spill
+; CSKYIF-NEXT: fsts vr8, (sp, 8) # 4-byte Folded Spill
+; CSKYIF-NEXT: st16.w l3, (sp, 4) # 4-byte Folded Spill
+; CSKYIF-NEXT: st16.w l0, (sp, 0) # 4-byte Folded Spill
+; CSKYIF-NEXT: subi16 sp, sp, 4
+; CSKYIF-NEXT: #APP
+; CSKYIF-NEXT: #NO_APP
+; CSKYIF-NEXT: addi16 sp, sp, 4
+; CSKYIF-NEXT: ld16.w l0, (sp, 0) # 4-byte Folded Reload
+; CSKYIF-NEXT: ld16.w l3, (sp, 4) # 4-byte Folded Reload
+; CSKYIF-NEXT: flds vr8, (sp, 8) # 4-byte Folded Reload
+; CSKYIF-NEXT: flds vr9, (sp, 12) # 4-byte Folded Reload
+; CSKYIF-NEXT: addi16 sp, sp, 16
+; CSKYIF-NEXT: rts16
+;
+; CSKYID-LABEL: testcase:
+; CSKYID: # %bb.0:
+; CSKYID-NEXT: subi16 sp, sp, 24
+; CSKYID-NEXT: fstd vr9, (sp, 16) # 8-byte Folded Spill
+; CSKYID-NEXT: fstd vr8, (sp, 8) # 8-byte Folded Spill
+; CSKYID-NEXT: st16.w l3, (sp, 4) # 4-byte Folded Spill
+; CSKYID-NEXT: st16.w l0, (sp, 0) # 4-byte Folded Spill
+; CSKYID-NEXT: subi16 sp, sp, 4
+; CSKYID-NEXT: #APP
+; CSKYID-NEXT: #NO_APP
+; CSKYID-NEXT: addi16 sp, sp, 4
+; CSKYID-NEXT: ld16.w l0, (sp, 0) # 4-byte Folded Reload
+; CSKYID-NEXT: ld16.w l3, (sp, 4) # 4-byte Folded Reload
+; CSKYID-NEXT: fldd vr8, (sp, 8) # 8-byte Folded Reload
+; CSKYID-NEXT: fldd vr9, (sp, 16) # 8-byte Folded Reload
+; CSKYID-NEXT: addi16 sp, sp, 24
+; CSKYID-NEXT: rts16
+
+ tail call void asm sideeffect "", "~{fr8},~{fr9},~{r4},~{r7}"()
+ ret void
+}
diff --git a/llvm/test/CodeGen/CSKY/inline-asm-d-constraint-f.ll b/llvm/test/CodeGen/CSKY/inline-asm-d-constraint-f.ll
new file mode 100644
index 0000000000000..09efc7cf804c5
--- /dev/null
+++ b/llvm/test/CodeGen/CSKY/inline-asm-d-constraint-f.ll
@@ -0,0 +1,51 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=csky -mattr=+fpuv2_sf,+fpuv2_df,+hard-float -float-abi=hard -verify-machineinstrs -csky-no-aliases < %s \
+; RUN: | FileCheck -check-prefix=CSKYF %s
+
+ at gd = external global double
+
+define double @constraint_f_double(double %a) nounwind {
+; CSKYF-LABEL: constraint_f_double:
+; CSKYF: # %bb.0:
+; CSKYF-NEXT: subi16 sp, sp, 4
+; CSKYF-NEXT: lrw32 a0, [.LCPI0_0]
+; CSKYF-NEXT: fldd vr1, (a0, 0)
+; CSKYF-NEXT: #APP
+; CSKYF-NEXT: faddd vr0, vr0, vr1
+; CSKYF-NEXT: #NO_APP
+; CSKYF-NEXT: addi16 sp, sp, 4
+; CSKYF-NEXT: rts16
+; CSKYF-NEXT: .p2align 1
+; CSKYF-NEXT: # %bb.1:
+; CSKYF-NEXT: .p2align 2
+; CSKYF-NEXT: .LCPI0_0:
+; CSKYF-NEXT: .long gd
+
+ %1 = load double, double* @gd
+ %2 = tail call double asm "faddd $0, $1, $2", "=v,v,v"(double %a, double %1)
+ ret double %2
+}
+
+define double @constraint_f_double_abi_name(double %a) nounwind {
+; CSKYF-LABEL: constraint_f_double_abi_name:
+; CSKYF: # %bb.0:
+; CSKYF-NEXT: subi16 sp, sp, 4
+; CSKYF-NEXT: lrw32 a0, [.LCPI1_0]
+; CSKYF-NEXT: fldd vr3, (a0, 0)
+; CSKYF-NEXT: fmovd vr2, vr0
+; CSKYF-NEXT: #APP
+; CSKYF-NEXT: faddd vr1, vr2, vr3
+; CSKYF-NEXT: #NO_APP
+; CSKYF-NEXT: fmovd vr0, vr1
+; CSKYF-NEXT: addi16 sp, sp, 4
+; CSKYF-NEXT: rts16
+; CSKYF-NEXT: .p2align 1
+; CSKYF-NEXT: # %bb.1:
+; CSKYF-NEXT: .p2align 2
+; CSKYF-NEXT: .LCPI1_0:
+; CSKYF-NEXT: .long gd
+
+ %1 = load double, double* @gd
+ %2 = tail call double asm "faddd $0, $1, $2", "={fr1},{fr2},{fr3}"(double %a, double %1)
+ ret double %2
+}
diff --git a/llvm/test/CodeGen/CSKY/inline-asm-f-constraint-f.ll b/llvm/test/CodeGen/CSKY/inline-asm-f-constraint-f.ll
new file mode 100644
index 0000000000000..cf97a8477bd4e
--- /dev/null
+++ b/llvm/test/CodeGen/CSKY/inline-asm-f-constraint-f.ll
@@ -0,0 +1,49 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=csky -mattr=+fpuv2_sf,+hard-float -float-abi=hard -verify-machineinstrs -csky-no-aliases < %s \
+; RUN: | FileCheck -check-prefix=CSKYF %s
+
+ at gf = external global float
+
+define float @constraint_f_float(float %a) nounwind {
+; CSKYF-LABEL: constraint_f_float:
+; CSKYF: # %bb.0:
+; CSKYF-NEXT: subi16 sp, sp, 4
+; CSKYF-NEXT: lrw32 a0, [.LCPI0_0]
+; CSKYF-NEXT: flds vr1, (a0, 0)
+; CSKYF-NEXT: #APP
+; CSKYF-NEXT: fadds vr0, vr0, vr1
+; CSKYF-NEXT: #NO_APP
+; CSKYF-NEXT: addi16 sp, sp, 4
+; CSKYF-NEXT: rts16
+; CSKYF-NEXT: .p2align 1
+; CSKYF-NEXT: # %bb.1:
+; CSKYF-NEXT: .p2align 2
+; CSKYF-NEXT: .LCPI0_0:
+; CSKYF-NEXT: .long gf
+ %1 = load float, float* @gf
+ %2 = tail call float asm "fadds $0, $1, $2", "=v,v,v"(float %a, float %1)
+ ret float %2
+}
+
+define float @constraint_f_float_abi_name(float %a) nounwind {
+; CSKYF-LABEL: constraint_f_float_abi_name:
+; CSKYF: # %bb.0:
+; CSKYF-NEXT: subi16 sp, sp, 4
+; CSKYF-NEXT: lrw32 a0, [.LCPI1_0]
+; CSKYF-NEXT: flds vr2, (a0, 0)
+; CSKYF-NEXT: fmovs vr1, vr0
+; CSKYF-NEXT: #APP
+; CSKYF-NEXT: fadds vr0, vr1, vr2
+; CSKYF-NEXT: #NO_APP
+; CSKYF-NEXT: addi16 sp, sp, 4
+; CSKYF-NEXT: rts16
+; CSKYF-NEXT: .p2align 1
+; CSKYF-NEXT: # %bb.1:
+; CSKYF-NEXT: .p2align 2
+; CSKYF-NEXT: .LCPI1_0:
+; CSKYF-NEXT: .long gf
+
+ %1 = load float, float* @gf
+ %2 = tail call float asm "fadds $0, $1, $2", "={fr0},{fr1},{fr2}"(float %a, float %1)
+ ret float %2
+}
diff --git a/llvm/test/CodeGen/CSKY/inline-asm-float-abi-names.ll b/llvm/test/CodeGen/CSKY/inline-asm-float-abi-names.ll
new file mode 100644
index 0000000000000..d5dd33ebd2e84
--- /dev/null
+++ b/llvm/test/CodeGen/CSKY/inline-asm-float-abi-names.ll
@@ -0,0 +1,67 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=csky -mattr=+fpuv2_sf,+fpuv2_df,+hard-float -float-abi=hard -verify-machineinstrs -csky-no-aliases < %s \
+; RUN: | FileCheck -check-prefix=CSKYIFD %s
+
+; These test that we can use both the architectural names (r*) and the ABI names
+; (a*, l* etc) to refer to registers in inline asm constraint lists. In each
+; case, the named register should be used for the source register of the `addi`.
+;
+; The inline assembly will, by default, contain the ABI names for the registers.
+;
+; Parenthesised registers in comments are the other aliases for this register.
+
+define double @explicit_register_fr0_d(double %a, double %b) nounwind {
+; CSKYIFD-LABEL: explicit_register_fr0_d:
+; CSKYIFD: # %bb.0:
+; CSKYIFD-NEXT: subi16 sp, sp, 4
+; CSKYIFD-NEXT: fmovd vr0, vr1
+; CSKYIFD-NEXT: #APP
+; CSKYIFD-NEXT: faddd vr0, vr0, vr0
+; CSKYIFD-NEXT: #NO_APP
+; CSKYIFD-NEXT: addi16 sp, sp, 4
+; CSKYIFD-NEXT: rts16
+ %1 = tail call double asm "faddd $0, $1, $2", "=v,{fr0},{fr0}"(double %a, double %b)
+ ret double %1
+}
+
+define double @explicit_register_vr0_d(double %a, double %b) nounwind {
+; CSKYIFD-LABEL: explicit_register_vr0_d:
+; CSKYIFD: # %bb.0:
+; CSKYIFD-NEXT: subi16 sp, sp, 4
+; CSKYIFD-NEXT: fmovd vr0, vr1
+; CSKYIFD-NEXT: #APP
+; CSKYIFD-NEXT: faddd vr0, vr0, vr0
+; CSKYIFD-NEXT: #NO_APP
+; CSKYIFD-NEXT: addi16 sp, sp, 4
+; CSKYIFD-NEXT: rts16
+ %1 = tail call double asm "faddd $0, $1, $2", "=v,{vr0},{vr0}"(double %a, double %b)
+ ret double %1
+}
+
+define float @explicit_register_fr0_s(float %a, float %b) nounwind {
+; CSKYIFD-LABEL: explicit_register_fr0_s:
+; CSKYIFD: # %bb.0:
+; CSKYIFD-NEXT: subi16 sp, sp, 4
+; CSKYIFD-NEXT: fstod vr0, vr1
+; CSKYIFD-NEXT: #APP
+; CSKYIFD-NEXT: fadds vr0, vr0, vr0
+; CSKYIFD-NEXT: #NO_APP
+; CSKYIFD-NEXT: addi16 sp, sp, 4
+; CSKYIFD-NEXT: rts16
+ %1 = tail call float asm "fadds $0, $1, $2", "=v,{fr0},{fr0}"(float %a, float %b)
+ ret float %1
+}
+
+define float @explicit_register_vr0_s(float %a, float %b) nounwind {
+; CSKYIFD-LABEL: explicit_register_vr0_s:
+; CSKYIFD: # %bb.0:
+; CSKYIFD-NEXT: subi16 sp, sp, 4
+; CSKYIFD-NEXT: fstod vr0, vr1
+; CSKYIFD-NEXT: #APP
+; CSKYIFD-NEXT: fadds vr0, vr0, vr0
+; CSKYIFD-NEXT: #NO_APP
+; CSKYIFD-NEXT: addi16 sp, sp, 4
+; CSKYIFD-NEXT: rts16
+ %1 = tail call float asm "fadds $0, $1, $2", "=v,{vr0},{vr0}"(float %a, float %b)
+ ret float %1
+}
diff --git a/llvm/test/CodeGen/CSKY/inline-asm-invalid.ll b/llvm/test/CodeGen/CSKY/inline-asm-invalid.ll
new file mode 100644
index 0000000000000..511c4c2ab6490
--- /dev/null
+++ b/llvm/test/CodeGen/CSKY/inline-asm-invalid.ll
@@ -0,0 +1,10 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: not llc -mtriple=csky < %s 2>&1 | FileCheck %s
+
+define void @constraint_f() nounwind {
+; CHECK: error: couldn't allocate input reg for constraint 'v'
+ tail call void asm "fadds fa0, fa0, $0", "v"(float 0.0)
+; CHECK: error: couldn't allocate input reg for constraint 'v'
+ tail call void asm "faddd fa0, fa0, $0", "v"(double 0.0)
+ ret void
+}
diff --git a/llvm/test/CodeGen/CSKY/inline-asm.ll b/llvm/test/CodeGen/CSKY/inline-asm.ll
new file mode 100644
index 0000000000000..4e7cb8e98e748
--- /dev/null
+++ b/llvm/test/CodeGen/CSKY/inline-asm.ll
@@ -0,0 +1,225 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=csky -verify-machineinstrs -csky-no-aliases -no-integrated-as < %s \
+; RUN: | FileCheck -check-prefix=CSKY %s
+
+ at gi = external global i32
+ at mi = external global i64
+
+define i32 @constraint_r(i32 %a) nounwind {
+; CSKY-LABEL: constraint_r:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: lrw32 a1, [.LCPI0_0]
+; CSKY-NEXT: ld16.w a1, (a1, 0)
+; CSKY-NEXT: #APP
+; CSKY-NEXT: add a0, a0, a1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+; CSKY-NEXT: .p2align 1
+; CSKY-NEXT: # %bb.1:
+; CSKY-NEXT: .p2align 2
+; CSKY-NEXT: .LCPI0_0:
+; CSKY-NEXT: .long gi
+ %1 = load i32, i32* @gi
+ %2 = tail call i32 asm "add $0, $1, $2", "=r,r,r"(i32 %a, i32 %1)
+ ret i32 %2
+}
+
+define i64 @constraint_r_i64(i32 %a) nounwind {
+; CSKY-LABEL: constraint_r_i64:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: lrw32 a3, [.LCPI1_0]
+; CSKY-NEXT: ld16.w a1, (a3, 0)
+; CSKY-NEXT: ld16.w a2, (a3, 4)
+; CSKY-NEXT: #APP
+; CSKY-NEXT: mula.s32 a1, a0, a0
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: mov16 a0, a1
+; CSKY-NEXT: mov16 a1, a2
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+; CSKY-NEXT: .p2align 1
+; CSKY-NEXT: # %bb.1:
+; CSKY-NEXT: .p2align 2
+; CSKY-NEXT: .LCPI1_0:
+; CSKY-NEXT: .long mi
+ %1 = load i64, i64* @mi
+ %2 = call i64 asm "mula.s32 $0, $1, $2", "=r,r,r,0"(i32 %a, i32 %a, i64 %1)
+ ret i64 %2
+}
+
+define i32 @constraint_a(i32 %a) nounwind {
+; CSKY-LABEL: constraint_a:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: lrw32 a1, [.LCPI2_0]
+; CSKY-NEXT: ld16.w a1, (a1, 0)
+; CSKY-NEXT: #APP
+; CSKY-NEXT: add a0, a0, a1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+; CSKY-NEXT: .p2align 1
+; CSKY-NEXT: # %bb.1:
+; CSKY-NEXT: .p2align 2
+; CSKY-NEXT: .LCPI2_0:
+; CSKY-NEXT: .long gi
+ %1 = load i32, i32* @gi
+ %2 = tail call i32 asm "add $0, $1, $2", "=a,a,a"(i32 %a, i32 %1)
+ ret i32 %2
+}
+
+define i32 @constraint_b(i32 %a) nounwind {
+; CSKY-LABEL: constraint_b:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: lrw32 a1, [.LCPI3_0]
+; CSKY-NEXT: ld16.w a1, (a1, 0)
+; CSKY-NEXT: #APP
+; CSKY-NEXT: add a0, a0, a1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+; CSKY-NEXT: .p2align 1
+; CSKY-NEXT: # %bb.1:
+; CSKY-NEXT: .p2align 2
+; CSKY-NEXT: .LCPI3_0:
+; CSKY-NEXT: .long gi
+ %1 = load i32, i32* @gi
+ %2 = tail call i32 asm "add $0, $1, $2", "=b,b,b"(i32 %a, i32 %1)
+ ret i32 %2
+}
+
+define i32 @constraint_z(i32 %a) nounwind {
+; CSKY-LABEL: constraint_z:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: mov16 sp, a0
+; CSKY-NEXT: lrw32 a0, [.LCPI4_0]
+; CSKY-NEXT: ld16.w a0, (a0, 0)
+; CSKY-NEXT: #APP
+; CSKY-NEXT: add a0, sp, a0
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+; CSKY-NEXT: .p2align 1
+; CSKY-NEXT: # %bb.1:
+; CSKY-NEXT: .p2align 2
+; CSKY-NEXT: .LCPI4_0:
+; CSKY-NEXT: .long gi
+ %1 = load i32, i32* @gi
+ %2 = tail call i32 asm "add $0, $1, $2", "=r,z,r"(i32 %a, i32 %1)
+ ret i32 %2
+}
+
+define i32 @constraint_c(i32 %a, i32 %b) nounwind {
+; CSKY-LABEL: constraint_c:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: lrw32 a1, [.LCPI5_0]
+; CSKY-NEXT: ld16.w a1, (a1, 0)
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addc a0, a0, a1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+; CSKY-NEXT: .p2align 1
+; CSKY-NEXT: # %bb.1:
+; CSKY-NEXT: .p2align 2
+; CSKY-NEXT: .LCPI5_0:
+; CSKY-NEXT: .long gi
+ %1 = load i32, i32* @gi
+ %2 = tail call i32 asm "addc $0, $1, $2", "=r,r,r,~{c}"(i32 %a, i32 %1)
+ ret i32 %2
+}
+
+define i32 @constraint_i(i32 %a) nounwind {
+; CSKY-LABEL: constraint_i:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, a0, 113
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+
+ %1 = load i32, i32* @gi
+ %2 = tail call i32 asm "addi $0, $1, $2", "=r,r,i"(i32 %a, i32 113)
+ ret i32 %2
+}
+
+define void @constraint_m(i32* %a) nounwind {
+; CSKY-LABEL: constraint_m:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: #APP
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+
+ call void asm sideeffect "", "=*m"(i32* elementtype(i32) %a)
+ ret void
+}
+
+define i32 @constraint_m2(i32* %a) nounwind {
+; CSKY-LABEL: constraint_m2:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: #APP
+; CSKY-NEXT: ld.w a0, (a0, 0)
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+
+ %1 = tail call i32 asm "ld.w $0, $1", "=r,*m"(i32* elementtype(i32) %a)
+ ret i32 %1
+}
+
+define i32 @modifier_i_imm(i32 %a) nounwind {
+; CSKY-LABEL: modifier_i_imm:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: #APP
+; CSKY-NEXT: addi a0, a0, 1
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+ %1 = tail call i32 asm "addi $0, $1, $2", "=r,r,ri"(i32 %a, i32 1)
+ ret i32 %1
+}
+
+define void @operand_global() nounwind {
+; CSKY-LABEL: operand_global:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: #APP
+; CSKY-NEXT: .4byte gi
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+
+ tail call void asm sideeffect ".4byte $0", "i"(i32* @gi)
+ ret void
+}
+
+define void @operand_block_address() nounwind {
+; CSKY-LABEL: operand_block_address:
+; CSKY: # %bb.0:
+; CSKY-NEXT: subi16 sp, sp, 4
+; CSKY-NEXT: #APP
+; CSKY-NEXT: br32 .Ltmp0
+; CSKY-NEXT: #NO_APP
+; CSKY-NEXT: .Ltmp0: # Block address taken
+; CSKY-NEXT: # %bb.1: # %bb
+; CSKY-NEXT: addi16 sp, sp, 4
+; CSKY-NEXT: rts16
+
+ call void asm sideeffect "br32 $0", "i"(i8* blockaddress(@operand_block_address, %bb))
+ br label %bb
+bb:
+ ret void
+}
+
+; TODO: expand tests for more complex constraints, out of range immediates etc
More information about the llvm-commits
mailing list