OperandMatchResultTy parseImmediate(OperandVector &Operands);
OperandMatchResultTy parseOperandWithModifier(OperandVector &Operands);
OperandMatchResultTy parseSImm26Operand(OperandVector &Operands);
+ OperandMatchResultTy parseAtomicMemOp(OperandVector &Operands);
bool parseOperand(OperandVector &Operands, StringRef Mnemonic);
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) {
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,
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:
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;
}
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())) {
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()) &&
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) {
: 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> {
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>
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)>;
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);
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;
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
+}
# 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
# 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