[LoongArch] Add custom parser for atomic instructions' memory operand
authorwanglei <wanglei@loongson.cn>
Tue, 13 Dec 2022 03:32:10 +0000 (11:32 +0800)
committerwanglei <wanglei@loongson.cn>
Tue, 13 Dec 2022 03:46:53 +0000 (11:46 +0800)
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

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

index 43941fb..8806e13 100644 (file)
@@ -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 @@ public:
   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:
index a974bb8..6d9cb5e 100644 (file)
@@ -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;
 }
 
index 8ba1f9c..49684b9 100644 (file)
@@ -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) {
index 43cab04..e139cee 100644 (file)
@@ -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)>;
index 6618386..3e22f78 100644 (file)
@@ -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);
index 0cbb3d7..137c164 100644 (file)
@@ -27,6 +27,8 @@ public:
   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;
index 4373855..1a8f50a 100644 (file)
@@ -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
+}
index 6427401..a35211d 100644 (file)
@@ -31,6 +31,18 @@ sc.w $t7, $t2, 56
 
 # 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
 
 # CHECK64-ASM-AND-OBJ: amswap.d $tp, $t2, $fp
index 7e67a19..acddca9 100644 (file)
@@ -81,15 +81,13 @@ bstrpick.d $a0, $a0, 32, 63
 # CHECK:             ^~~~~~
 
 # CHECK: :[[#@LINE+1]]:10: error: $rd must be different from both $rk and $rj
-amadd.d $zero, $zero, $zero
-# CHECK: :[[#@LINE+1]]:10: error: $rd must be different from both $rk and $rj
-ammin.w $zero, $zero, $a0
-# CHECK: :[[#@LINE+1]]:10: error: $rd must be different from both $rk and $rj
-amxor.w $zero, $a0, $zero
-
-# CHECK: :[[#@LINE+1]]:10: error: $rd must be different from both $rk and $rj
 amadd.d $a0, $a0, $a0
 # CHECK: :[[#@LINE+1]]:10: error: $rd must be different from both $rk and $rj
 ammin.w $a0, $a0, $a1
 # CHECK: :[[#@LINE+1]]:10: error: $rd must be different 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