Skip to content

Commit 8234c61

Browse files
authored
[LoongArch] Record the special AMO operand constraint with TableGen (#114398)
Depends on #114508 The LoongArch Reference Manual says that the 3-register atomic memory operations cannot have their rd equal to either rj or rk [^1], and both GNU as and LLVM IAS enforce the constraint for non-zero rd. However, currently LoongArch AsmParser is checking for the opcode with a direct numerical comparison on the opcode, which is enum-typed: the fact that all AMO insns have adjacent numerical values is merely a coincidence, and it is better to not rely on the current TableGen implementation behavior. Instead, start to leverage the target-specific flags field of MCInstrDesc, and record the constraint with TableGen, so we can stop treating the opcode value as number. In doing so, we also have to mark whether the instruction is AMCAS, because the operand index of rj and rk for the AMCAS instructions is different. While documenting the new flag, it was found that v1.10 of the Manual did not specify the similar constraint for the AMCAS instructions. Experiments were done on a Loongson 3A6000 (LA664 uarch) and it turned out that at least AMCAS will still signal INE with `rd == rj`. The `rd == rk` case should be a no-op according to the semantics, but as it is meaningless to perform CAS with the "old value" same as the "new value", it is not worth special-casing. So the current behavior of also enforcing the constraint for AMCAS is kept. [^1]: if `rd == rj` an INE would be signaled; if `rd == rk` it is UB.
1 parent 461e58e commit 8234c61

File tree

5 files changed

+67
-15
lines changed

5 files changed

+67
-15
lines changed

llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9+
#include "MCTargetDesc/LoongArchBaseInfo.h"
910
#include "MCTargetDesc/LoongArchInstPrinter.h"
1011
#include "MCTargetDesc/LoongArchMCExpr.h"
1112
#include "MCTargetDesc/LoongArchMCTargetDesc.h"
@@ -1560,18 +1561,14 @@ bool LoongArchAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc,
15601561

15611562
unsigned LoongArchAsmParser::checkTargetMatchPredicate(MCInst &Inst) {
15621563
unsigned Opc = Inst.getOpcode();
1564+
const MCInstrDesc &MCID = MII.get(Opc);
15631565
switch (Opc) {
15641566
default:
1565-
if (Opc >= LoongArch::AMCAS_B && Opc <= LoongArch::AMCAS__DB_W) {
1567+
if (LoongArchII::isSubjectToAMORdConstraint(MCID.TSFlags)) {
1568+
const bool IsAMCAS = LoongArchII::isAMCAS(MCID.TSFlags);
15661569
MCRegister Rd = Inst.getOperand(0).getReg();
1567-
MCRegister Rk = Inst.getOperand(2).getReg();
1568-
MCRegister Rj = Inst.getOperand(3).getReg();
1569-
if ((Rd == Rk || Rd == Rj) && Rd != LoongArch::R0)
1570-
return Match_RequiresAMORdDifferRkRj;
1571-
} else if (Opc >= LoongArch::AMADD_D && Opc <= LoongArch::AMXOR_W) {
1572-
MCRegister Rd = Inst.getOperand(0).getReg();
1573-
MCRegister Rk = Inst.getOperand(1).getReg();
1574-
MCRegister Rj = Inst.getOperand(2).getReg();
1570+
MCRegister Rk = Inst.getOperand(IsAMCAS ? 2 : 1).getReg();
1571+
MCRegister Rj = Inst.getOperand(IsAMCAS ? 3 : 2).getReg();
15751572
if ((Rd == Rk || Rd == Rj) && Rd != LoongArch::R0)
15761573
return Match_RequiresAMORdDifferRkRj;
15771574
}

llvm/lib/Target/LoongArch/LoongArchInstrFormats.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ class LAInst<dag outs, dag ins, string opcstr, string opnstr,
3232
let InOperandList = ins;
3333
let AsmString = opcstr # "\t" # opnstr;
3434
let Pattern = pattern;
35+
36+
// Target-specific instruction info and defaults
37+
38+
bit IsSubjectToAMORdConstraint = 0;
39+
let TSFlags{0} = IsSubjectToAMORdConstraint;
40+
41+
bit IsAMCAS = 0;
42+
let TSFlags{1} = IsAMCAS;
3543
}
3644

3745
// Pseudo instructions

llvm/lib/Target/LoongArch/LoongArchInstrInfo.td

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -711,15 +711,21 @@ class STORE_2RI14<bits<32> op>
711711
} // hasSideEffects = 0, mayLoad = 0, mayStore = 1
712712

713713
let hasSideEffects = 0, mayLoad = 1, mayStore = 1,
714-
Constraints = "@earlyclobber $rd" in class AM_3R<bits<32> op>
714+
IsSubjectToAMORdConstraint = 1 in {
715+
class AM_3R<bits<32> op>
715716
: Fmt3R<op, (outs GPR:$rd), (ins GPR:$rk, GPRMemAtomic:$rj),
716-
"$rd, $rk, $rj">;
717+
"$rd, $rk, $rj"> {
718+
let Constraints = "@earlyclobber $rd";
719+
}
717720

718-
let hasSideEffects = 0, mayLoad = 1, mayStore = 1,
719-
Constraints =
720-
"@earlyclobber $rd_wb, $rd_wb = $rd" in class AMCAS_3R<bits<32> op>
721+
class AMCAS_3R<bits<32> op>
721722
: Fmt3R<op, (outs GPR:$rd_wb), (ins GPR:$rd, GPR:$rk, GPRMemAtomic:$rj),
722-
"$rd, $rk, $rj">;
723+
"$rd, $rk, $rj"> {
724+
let Constraints = "@earlyclobber $rd_wb, $rd_wb = $rd";
725+
let IsAMCAS = 1;
726+
}
727+
} // hasSideEffects = 0, mayLoad = 1, mayStore = 1,
728+
// IsSubjectToAMORdConstraint = 1
723729

724730
let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in {
725731
class LLBase<bits<32> op>

llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchBaseInfo.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,36 @@ enum {
5656
MO_DESC_CALL,
5757
// TODO: Add more flags.
5858
};
59+
60+
// Target-specific flags of LAInst.
61+
// All definitions must match LoongArchInstrFormats.td.
62+
enum {
63+
// Whether the instruction's rd is normally required to differ from rj and
64+
// rk, in the way the 3-register atomic memory operations behave
65+
// (Section 2.2.7.1 and 2.2.7.2, LoongArch Reference Manual Volume 1 v1.10;
66+
// while Section 2.2.7.3 lacked similar description for the AMCAS
67+
// instructions, at least the INE exception is still signaled on Loongson
68+
// 3A6000 when its rd == rj).
69+
//
70+
// Used for generating diagnostics for assembler input that violate the
71+
// constraint. As described on the manual, the covered instructions require
72+
// rd != rj && rd != rk to work as intended.
73+
IsSubjectToAMORdConstraintShift = 0,
74+
IsSubjectToAMORdConstraintMask = 1 << IsSubjectToAMORdConstraintShift,
75+
76+
// Whether the instruction belongs to the AMCAS family.
77+
IsAMCASShift = IsSubjectToAMORdConstraintShift + 1,
78+
IsAMCASMask = 1 << IsAMCASShift,
79+
};
80+
81+
/// \returns true if this instruction's rd is normally required to differ
82+
/// from rj and rk, in the way 3-register atomic memory operations behave.
83+
static inline bool isSubjectToAMORdConstraint(uint64_t TSFlags) {
84+
return TSFlags & IsSubjectToAMORdConstraintMask;
85+
}
86+
87+
/// \returns true if this instruction belongs to the AMCAS family.
88+
static inline bool isAMCAS(uint64_t TSFlags) { return TSFlags & IsAMCASMask; }
5989
} // end namespace LoongArchII
6090

6191
namespace LoongArchABI {

llvm/test/MC/LoongArch/Basic/Integer/invalid64.s

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,14 @@ amxor.w $a0, $a1, $a0
9191
amadd.d $a0, $a1, $a2, $a3
9292
# CHECK: :[[#@LINE+1]]:24: error: optional integer offset must be 0
9393
amadd.d $a0, $a1, $a2, 1
94+
95+
## According to experiment results on real LA664 HW, the AMCAS instructions
96+
## are subject to the same constraint as the other 3-register atomic insns.
97+
## This is undocumented in v1.10 of the LoongArch Reference Manual.
98+
99+
# CHECK: :[[#@LINE+1]]:10: error: $rd must be different from both $rk and $rj
100+
amcas.b $a0, $a0, $a0
101+
# CHECK: :[[#@LINE+1]]:10: error: $rd must be different from both $rk and $rj
102+
amcas.h $a0, $a0, $a1
103+
# CHECK: :[[#@LINE+1]]:13: error: $rd must be different from both $rk and $rj
104+
amcas_db.w $a0, $a1, $a0

0 commit comments

Comments
 (0)