[llvm] 899226a - [LoongArch] Add custom parser for atomic instructions' memory operand

via llvm-commits llvm-commits at lists.llvm.org
Mon Dec 12 19:47:04 PST 2022


Author: wanglei
Date: 2022-12-13T11:46:53+08:00
New Revision: 899226adac2cd07938b318e100fdca1eb9c4b1e1

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

LOG: [LoongArch] Add custom parser for atomic instructions' memory operand

In order to be compatible with the form of the atomic instruction in
GAS that accepts the fourth operand as 0 (i.e. `am* $rd, $rk, $rj, 0`),
we need to treat `$rj, 0` as one operand, but only print `$rj`.

For this, the number of result operands of inline assembly memory
operand `ZB` constraint is modified to 2 (reg + 0).

Restrictions on register usage in `am*` instructions have also been
adjusted. When `$rd` is equal to `$r0`, the instruction must be
considered legal, because of some special usage like `PseudoUNIMP`.

Reviewed By: SixWeining, xen0n

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

Added: 
    

Modified: 
    llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
    llvm/lib/Target/LoongArch/LoongArchAsmPrinter.cpp
    llvm/lib/Target/LoongArch/LoongArchISelDAGToDAG.cpp
    llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
    llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchInstPrinter.cpp
    llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchInstPrinter.h
    llvm/test/CodeGen/LoongArch/inline-asm-constraint-ZB.ll
    llvm/test/MC/LoongArch/Basic/Integer/atomic.s
    llvm/test/MC/LoongArch/Basic/Integer/invalid64.s

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
index 43941fbf49401..8806e13d589b9 100644
--- a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
+++ b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
@@ -78,6 +78,7 @@ class LoongArchAsmParser : public MCTargetAsmParser {
   OperandMatchResultTy parseImmediate(OperandVector &Operands);
   OperandMatchResultTy parseOperandWithModifier(OperandVector &Operands);
   OperandMatchResultTy parseSImm26Operand(OperandVector &Operands);
+  OperandMatchResultTy parseAtomicMemOp(OperandVector &Operands);
 
   bool parseOperand(OperandVector &Operands, StringRef Mnemonic);
 
@@ -180,6 +181,11 @@ class LoongArchOperand : public MCParsedAsmOperand {
   bool isImm() const override { return Kind == KindTy::Immediate; }
   bool isMem() const override { return false; }
   void setReg(MCRegister PhysReg) { Reg.RegNum = PhysReg; }
+  bool isGPR() const {
+    return Kind == KindTy::Register &&
+           LoongArchMCRegisterClasses[LoongArch::GPRRegClassID].contains(
+               Reg.RegNum);
+  }
 
   static bool evaluateConstantImm(const MCExpr *Expr, int64_t &Imm,
                                   LoongArchMCExpr::VariantKind &VK) {
@@ -674,6 +680,28 @@ LoongArchAsmParser::parseSImm26Operand(OperandVector &Operands) {
   return MatchOperand_Success;
 }
 
+OperandMatchResultTy
+LoongArchAsmParser::parseAtomicMemOp(OperandVector &Operands) {
+  // Parse "$r*".
+  if (parseRegister(Operands) != MatchOperand_Success)
+    return MatchOperand_NoMatch;
+
+  // If there is a next operand and it is 0, ignore it. Otherwise print a
+  // diagnostic message.
+  if (getLexer().is(AsmToken::Comma)) {
+    getLexer().Lex(); // Consume comma token.
+    int64_t ImmVal;
+    SMLoc ImmStart = getLoc();
+    if (getParser().parseIntToken(ImmVal, "expected optional integer offset"))
+      return MatchOperand_ParseFail;
+    if (ImmVal) {
+      Error(ImmStart, "optional integer offset must be 0");
+      return MatchOperand_ParseFail;
+    }
+  }
+
+  return MatchOperand_Success;
+}
 /// Looks at a token type and creates the relevant operand from this
 /// information, adding to Operands. Return true upon an error.
 bool LoongArchAsmParser::parseOperand(OperandVector &Operands,
@@ -1149,7 +1177,8 @@ unsigned LoongArchAsmParser::checkTargetMatchPredicate(MCInst &Inst) {
       unsigned Rk = Inst.getOperand(1).getReg();
       unsigned Rj = Inst.getOperand(2).getReg();
       if (Rd == Rk || Rd == Rj)
-        return Match_RequiresAMORdDifferRkRj;
+        return Rd == LoongArch::R0 ? Match_Success
+                                   : Match_RequiresAMORdDifferRkRj;
     }
     break;
   case LoongArch::PseudoLA_PCREL_LARGE:

diff  --git a/llvm/lib/Target/LoongArch/LoongArchAsmPrinter.cpp b/llvm/lib/Target/LoongArch/LoongArchAsmPrinter.cpp
index a974bb81b8dcd..6d9cb5e174d9a 100644
--- a/llvm/lib/Target/LoongArch/LoongArchAsmPrinter.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchAsmPrinter.cpp
@@ -90,22 +90,23 @@ bool LoongArchAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
   if (ExtraCode)
     return true;
 
+  // We only support memory operands like "Base + Offset", where base must be a
+  // register, and offset can be a register or an immediate value.
   const MachineOperand &BaseMO = MI->getOperand(OpNo);
   // Base address must be a register.
   if (!BaseMO.isReg())
     return true;
   // Print the base address register.
   OS << "$" << LoongArchInstPrinter::getRegisterName(BaseMO.getReg());
-  // Print the offset register or immediate if has.
-  if (OpNo + 1 < MI->getNumOperands()) {
-    const MachineOperand &OffsetMO = MI->getOperand(OpNo + 1);
-    if (OffsetMO.isReg())
-      OS << ", $" << LoongArchInstPrinter::getRegisterName(OffsetMO.getReg());
-    else if (OffsetMO.isImm())
-      OS << ", " << OffsetMO.getImm();
-    else
-      return true;
-  }
+  // Print the offset operand.
+  const MachineOperand &OffsetMO = MI->getOperand(OpNo + 1);
+  if (OffsetMO.isReg())
+    OS << ", $" << LoongArchInstPrinter::getRegisterName(OffsetMO.getReg());
+  else if (OffsetMO.isImm())
+    OS << ", " << OffsetMO.getImm();
+  else
+    return true;
+
   return false;
 }
 

diff  --git a/llvm/lib/Target/LoongArch/LoongArchISelDAGToDAG.cpp b/llvm/lib/Target/LoongArch/LoongArchISelDAGToDAG.cpp
index 8ba1f9c1b27f6..49684b911cc63 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelDAGToDAG.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchISelDAGToDAG.cpp
@@ -79,19 +79,19 @@ void LoongArchDAGToDAGISel::Select(SDNode *Node) {
 
 bool LoongArchDAGToDAGISel::SelectInlineAsmMemoryOperand(
     const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) {
+  SDValue Base = Op;
+  SDValue Offset =
+      CurDAG->getTargetConstant(0, SDLoc(Op), Subtarget->getGRLenVT());
   switch (ConstraintID) {
   default:
     llvm_unreachable("unexpected asm memory constraint");
   // Reg+Reg addressing.
   case InlineAsm::Constraint_k:
-    OutOps.push_back(Op.getOperand(0));
-    OutOps.push_back(Op.getOperand(1));
-    return false;
+    Base = Op.getOperand(0);
+    Offset = Op.getOperand(1);
+    break;
   // Reg+simm12 addressing.
-  case InlineAsm::Constraint_m: {
-    SDValue Base = Op;
-    SDValue Offset =
-        CurDAG->getTargetConstant(0, SDLoc(Op), Subtarget->getGRLenVT());
+  case InlineAsm::Constraint_m:
     if (CurDAG->isBaseWithConstantOffset(Op)) {
       ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Op.getOperand(1));
       if (isIntN(12, CN->getSExtValue())) {
@@ -100,19 +100,12 @@ bool LoongArchDAGToDAGISel::SelectInlineAsmMemoryOperand(
                                            Op.getValueType());
       }
     }
-    OutOps.push_back(Base);
-    OutOps.push_back(Offset);
-    return false;
-  }
+    break;
+  // Reg+0 addressing.
   case InlineAsm::Constraint_ZB:
-    OutOps.push_back(Op);
-    // No offset.
-    return false;
+    break;
   // Reg+(simm14<<2) addressing.
-  case InlineAsm::Constraint_ZC: {
-    SDValue Base = Op;
-    SDValue Offset =
-        CurDAG->getTargetConstant(0, SDLoc(Op), Subtarget->getGRLenVT());
+  case InlineAsm::Constraint_ZC:
     if (CurDAG->isBaseWithConstantOffset(Op)) {
       ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Op.getOperand(1));
       if (isIntN(16, CN->getSExtValue()) &&
@@ -122,12 +115,11 @@ bool LoongArchDAGToDAGISel::SelectInlineAsmMemoryOperand(
                                            Op.getValueType());
       }
     }
-    OutOps.push_back(Base);
-    OutOps.push_back(Offset);
-    return false;
-  }
+    break;
   }
-  return true;
+  OutOps.push_back(Base);
+  OutOps.push_back(Offset);
+  return false;
 }
 
 bool LoongArchDAGToDAGISel::SelectBaseAddr(SDValue Addr, SDValue &Base) {

diff  --git a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
index 43cab04dd2120..e139ceeaba11c 100644
--- a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
+++ b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
@@ -151,6 +151,20 @@ class UImmAsmOperand<int width, string suffix = "">
     : ImmAsmOperand<"U", width, suffix> {
 }
 
+// A parse method for "$r*" or "$r*, 0", where the 0 is be silently ignored.
+// Only used for "AM*" instructions, in order to be compatible with GAS.
+def AtomicMemAsmOperand : AsmOperandClass {
+  let Name = "AtomicMemAsmOperand";
+  let RenderMethod = "addRegOperands";
+  let PredicateMethod = "isGPR";
+  let ParserMethod = "parseAtomicMemOp";
+}
+
+def GPRMemAtomic : RegisterOperand<GPR> {
+  let ParserMatchClass = AtomicMemAsmOperand;
+  let PrintMethod = "printAtomicMemOp";
+}
+
 // A parameterized register class alternative to i32imm/i64imm from Target.td.
 def grlenimm : Operand<GRLenVT>;
 def imm32 : Operand<GRLenVT> {
@@ -434,7 +448,8 @@ class STORE_2RI14<bits<8> op, string opstr>
 
 let mayLoad = 1, mayStore = 1, Constraints = "@earlyclobber $rd" in
 class AM_3R<bits<17> op, string opstr>
-    : Fmt3R<op, (outs GPR:$rd), (ins GPR:$rk, GPR:$rj), opstr, "$rd, $rk, $rj">;
+    : Fmt3R<op, (outs GPR:$rd), (ins GPR:$rk, GPRMemAtomic:$rj), opstr,
+            "$rd, $rk, $rj">;
 
 let mayLoad = 1 in
 class LLBase<bits<8> op, string opstr>
@@ -1312,17 +1327,17 @@ defm : AtomicStPat<atomic_store_16, ST_H, GPR, GRLenVT>;
 defm : AtomicStPat<atomic_store_unordered_monotonic_32, ST_W, GPR, i32>,
                    Requires<[IsLA32]>;
 
-def PseudoAtomicStoreW : Pseudo<(outs GPR:$dst), (ins GPR:$rj, GPR:$rk)>,
-                                 PseudoInstExpansion<(AMSWAP_DB_W R0,
-                                                      GPR:$rk, GPR:$rj)>;
+def PseudoAtomicStoreW
+  : Pseudo<(outs GPR:$dst), (ins GPR:$rj, GPR:$rk)>,
+           PseudoInstExpansion<(AMSWAP_DB_W R0, GPR:$rk, GPRMemAtomic:$rj)>;
 
 def : Pat<(atomic_store_release_seqcst_32 GPR:$rj, GPR:$rk),
           (PseudoAtomicStoreW GPR:$rj, GPR:$rk)>;
 
 let Predicates = [IsLA64] in {
-def PseudoAtomicStoreD : Pseudo<(outs GPR:$dst), (ins GPR:$rj, GPR:$rk)>,
-                                 PseudoInstExpansion<(AMSWAP_DB_D R0,
-                                                      GPR:$rk, GPR:$rj)>;
+def PseudoAtomicStoreD
+  : Pseudo<(outs GPR:$dst), (ins GPR:$rj, GPR:$rk)>,
+           PseudoInstExpansion<(AMSWAP_DB_D R0, GPR:$rk, GPRMemAtomic:$rj)>;
 
 def : Pat<(atomic_store_release_seqcst_64 GPR:$rj, GPR:$rk),
           (PseudoAtomicStoreD GPR:$rj, GPR:$rk)>;

diff  --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchInstPrinter.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchInstPrinter.cpp
index 66183868f4681..3e22f78ec6102 100644
--- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchInstPrinter.cpp
+++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchInstPrinter.cpp
@@ -57,6 +57,14 @@ void LoongArchInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
   MO.getExpr()->print(O, &MAI);
 }
 
+void LoongArchInstPrinter::printAtomicMemOp(const MCInst *MI, unsigned OpNo,
+                                            const MCSubtargetInfo &STI,
+                                            raw_ostream &O) {
+  const MCOperand &MO = MI->getOperand(OpNo);
+  assert(MO.isReg() && "printAtomicMemOp can only print register operands");
+  printRegName(O, MO.getReg());
+}
+
 const char *LoongArchInstPrinter::getRegisterName(unsigned RegNo) {
   // Default print reg alias name
   return getRegisterName(RegNo, LoongArch::RegAliasName);

diff  --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchInstPrinter.h b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchInstPrinter.h
index 0cbb3d73cd03b..137c16406785c 100644
--- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchInstPrinter.h
+++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchInstPrinter.h
@@ -27,6 +27,8 @@ class LoongArchInstPrinter : public MCInstPrinter {
   void printInst(const MCInst *MI, uint64_t Address, StringRef Annot,
                  const MCSubtargetInfo &STI, raw_ostream &O) override;
   void printRegName(raw_ostream &O, unsigned RegNo) const override;
+  void printAtomicMemOp(const MCInst *MI, unsigned OpNo,
+                        const MCSubtargetInfo &STI, raw_ostream &O);
 
   // Autogenerated by tblgen.
   std::pair<const char *, uint64_t> getMnemonic(const MCInst *MI) override;

diff  --git a/llvm/test/CodeGen/LoongArch/inline-asm-constraint-ZB.ll b/llvm/test/CodeGen/LoongArch/inline-asm-constraint-ZB.ll
index 4373855e4c008..1a8f50abb6588 100644
--- a/llvm/test/CodeGen/LoongArch/inline-asm-constraint-ZB.ll
+++ b/llvm/test/CodeGen/LoongArch/inline-asm-constraint-ZB.ll
@@ -47,3 +47,16 @@ define void @ZB_variable_offset(ptr %p, i32 signext %idx) nounwind {
   call void asm "amswap.w $$r12, $$r13, $0", "*^ZB"(ptr elementtype(i32) %1)
   ret void
 }
+
+define void @ZB_Input_Output(ptr %p) nounwind {
+; ASM-LABEL: ZB_Input_Output:
+; ASM:       # %bb.0:
+; ASM-NEXT:    #APP
+; ASM-NEXT:    amadd_db.d $zero, $t1, $a0
+; ASM-NEXT:    #NO_APP
+; ASM-NEXT:    ret
+;; Make sure machine instr with this "ZB" constraint is printed correctly.
+; MACHINE-INSTR: INLINEASM{{.*}}[mem:ZB], %0:gpr, 0
+  call void asm "amadd_db.d $$zero, $$r13, $0", "=*^ZB,*^ZB,~{memory}"(ptr elementtype(i64) %p, ptr elementtype(i64) %p)
+  ret void
+}

diff  --git a/llvm/test/MC/LoongArch/Basic/Integer/atomic.s b/llvm/test/MC/LoongArch/Basic/Integer/atomic.s
index 64274018081ca..a35211db88514 100644
--- a/llvm/test/MC/LoongArch/Basic/Integer/atomic.s
+++ b/llvm/test/MC/LoongArch/Basic/Integer/atomic.s
@@ -29,6 +29,18 @@ sc.w $t7, $t2, 56
 
 .ifdef LA64
 
+# CHECK64-ASM-AND-OBJ: amswap.w $a2, $t0, $s1
+# CHECK64-ASM: encoding: [0x06,0x33,0x60,0x38]
+amswap.w $a2, $t0, $s1, 0
+
+# CHECK64-ASM-AND-OBJ: amswap.w $zero, $t0, $zero
+# CHECK64-ASM: encoding: [0x00,0x30,0x60,0x38]
+amswap.w $zero, $t0, $zero
+
+# CHECK64-ASM-AND-OBJ: amadd_db.w $zero, $zero, $a1
+# CHECK64-ASM: encoding: [0xa0,0x00,0x6a,0x38]
+amadd_db.w $zero, $zero, $a1
+
 # CHECK64-ASM-AND-OBJ: amswap.w $a2, $t0, $s1
 # CHECK64-ASM: encoding: [0x06,0x33,0x60,0x38]
 amswap.w $a2, $t0, $s1

diff  --git a/llvm/test/MC/LoongArch/Basic/Integer/invalid64.s b/llvm/test/MC/LoongArch/Basic/Integer/invalid64.s
index 7e67a19e917bc..acddca9432a69 100644
--- a/llvm/test/MC/LoongArch/Basic/Integer/invalid64.s
+++ b/llvm/test/MC/LoongArch/Basic/Integer/invalid64.s
@@ -80,16 +80,14 @@ bstrins.d $a0, $a0, 1, 2
 bstrpick.d $a0, $a0, 32, 63
 # CHECK:             ^~~~~~
 
-# CHECK: :[[#@LINE+1]]:10: error: $rd must be 
diff erent from both $rk and $rj
-amadd.d $zero, $zero, $zero
-# CHECK: :[[#@LINE+1]]:10: error: $rd must be 
diff erent from both $rk and $rj
-ammin.w $zero, $zero, $a0
-# CHECK: :[[#@LINE+1]]:10: error: $rd must be 
diff erent from both $rk and $rj
-amxor.w $zero, $a0, $zero
-
 # CHECK: :[[#@LINE+1]]:10: error: $rd must be 
diff erent from both $rk and $rj
 amadd.d $a0, $a0, $a0
 # CHECK: :[[#@LINE+1]]:10: error: $rd must be 
diff erent from both $rk and $rj
 ammin.w $a0, $a0, $a1
 # CHECK: :[[#@LINE+1]]:10: error: $rd must be 
diff erent from both $rk and $rj
 amxor.w $a0, $a1, $a0
+
+# CHECK: :[[#@LINE+1]]:24: error: expected optional integer offset
+amadd.d $a0, $a1, $a2, $a3
+# CHECK: :[[#@LINE+1]]:24: error: optional integer offset must be 0
+amadd.d $a0, $a1, $a2, 1


        


More information about the llvm-commits mailing list