[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