[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