Skip to content

Commit 0eb4a79

Browse files
committed
[CHERI] Use separate Pseudo instructions for cmpxchg nodes
Using separate pseudos for exact and inexact comparsions ensures that our lowering does not depend on the MachineMemOperand (after SDAG) since passes could drop it (which means use the most conservative approach). This adds a bit of boilerplate but it's not as bad as I expected and is less fragile than the previous approach.
1 parent b30abe0 commit 0eb4a79

11 files changed

+391
-48
lines changed

llvm/include/llvm/CodeGen/SelectionDAGNodes.h

+6
Original file line numberDiff line numberDiff line change
@@ -1453,6 +1453,12 @@ class AtomicSDNode : public MemSDNode {
14531453
return MMO->getFailureOrdering();
14541454
}
14551455

1456+
/// Return true if the memory operation ordering is Unordered or higher.
1457+
bool isExactCmpXchg() const {
1458+
assert(getMemoryVT().isFatPointer());
1459+
return MMO->isExactCompare();
1460+
}
1461+
14561462
// Methods to support isa and dyn_cast
14571463
static bool classof(const SDNode *N) {
14581464
return N->getOpcode() == ISD::ATOMIC_CMP_SWAP ||

llvm/include/llvm/Target/TargetSelectionDAG.td

+15-3
Original file line numberDiff line numberDiff line change
@@ -1888,10 +1888,21 @@ multiclass binary_atomic_op_cap<SDNode atomic_op> {
18881888
defm NAME : binary_atomic_op_ord;
18891889
}
18901890

1891-
multiclass ternary_atomic_op_cap<SDNode atomic_op> {
1891+
multiclass ternary_atomic_op_cap_inexact<SDNode atomic_op> {
18921892
def "" : PatFrag<(ops node:$ptr, node:$cmp, node:$val),
18931893
(atomic_op node:$ptr, node:$cmp, node:$val), [{
1894-
return cast<AtomicSDNode>(N)->getMemoryVT().isCapability();
1894+
auto AN = cast<AtomicSDNode>(N);
1895+
return AN->getMemoryVT().isCapability() && !AN->isExactCmpXchg();
1896+
}]>;
1897+
1898+
defm NAME : ternary_atomic_op_ord;
1899+
}
1900+
1901+
multiclass ternary_atomic_op_cap_exact<SDNode atomic_op> {
1902+
def "" : PatFrag<(ops node:$ptr, node:$cmp, node:$val),
1903+
(atomic_op node:$ptr, node:$cmp, node:$val), [{
1904+
auto AN = cast<AtomicSDNode>(N);
1905+
return AN->getMemoryVT().isCapability() && AN->isExactCmpXchg();
18951906
}]>;
18961907

18971908
defm NAME : ternary_atomic_op_ord;
@@ -1910,7 +1921,8 @@ defm atomic_load_max_cap : binary_atomic_op_cap<atomic_load_max_cap_node>;
19101921
defm atomic_load_umin_cap : binary_atomic_op_cap<atomic_load_umin_cap_node>;
19111922
defm atomic_load_umax_cap : binary_atomic_op_cap<atomic_load_umax_cap_node>;
19121923
defm atomic_store_cap : binary_atomic_op_cap<atomic_store_cap_node>;
1913-
defm atomic_cmp_swap_cap : ternary_atomic_op_cap<atomic_cmp_swap_cap_node>;
1924+
defm atomic_cmp_swap_cap_addr : ternary_atomic_op_cap_inexact<atomic_cmp_swap_cap_node>;
1925+
defm atomic_cmp_swap_cap_exact : ternary_atomic_op_cap_exact<atomic_cmp_swap_cap_node>;
19141926

19151927
def atomic_load_cap :
19161928
PatFrag<(ops node:$ptr),

llvm/lib/Target/Mips/MipsExpandPseudo.cpp

+7-5
Original file line numberDiff line numberDiff line change
@@ -212,14 +212,18 @@ bool MipsExpandPseudo::expandAtomicCmpSwap(MachineBasicBlock &BB,
212212

213213
unsigned Size = -1;
214214
bool IsCapCmpXchg = false;
215+
bool UseExactEquals = false;
215216
switch(I->getOpcode()) {
216217
case Mips::ATOMIC_CMP_SWAP_I32_POSTRA: Size = 4; break;
217218
case Mips::ATOMIC_CMP_SWAP_I64_POSTRA: Size = 8; break;
218219
case Mips::CAP_ATOMIC_CMP_SWAP_I8_POSTRA: Size = 1; break;
219220
case Mips::CAP_ATOMIC_CMP_SWAP_I16_POSTRA: Size = 2; break;
220221
case Mips::CAP_ATOMIC_CMP_SWAP_I32_POSTRA: Size = 4; break;
221222
case Mips::CAP_ATOMIC_CMP_SWAP_I64_POSTRA: Size = 8; break;
222-
case Mips::CAP_ATOMIC_CMP_SWAP_CAP_POSTRA:
223+
case Mips::CAP_ATOMIC_CMP_SWAP_CAP_EXACT_POSTRA:
224+
UseExactEquals = true;
225+
LLVM_FALLTHROUGH;
226+
case Mips::CAP_ATOMIC_CMP_SWAP_CAP_ADDR_POSTRA:
223227
Size = CAP_ATOMIC_SIZE;
224228
IsCapCmpXchg = true;
225229
break;
@@ -327,9 +331,6 @@ bool MipsExpandPseudo::expandAtomicCmpSwap(MachineBasicBlock &BB,
327331
if (!IsCapOp)
328332
LLOp.addImm(0);
329333
if (IsCapCmpXchg) {
330-
assert(I->hasOneMemOperand());
331-
bool UseExactEquals =
332-
STI->useCheriExactEquals() || I->memoperands()[0]->isExactCompare();
333334
unsigned CapCmp = UseExactEquals ? Mips::CEXEQ : Mips::CEQ;
334335
// load, compare, and exit if not equal
335336
// cllc dest, ptr
@@ -1098,7 +1099,8 @@ bool MipsExpandPseudo::expandMI(MachineBasicBlock &MBB,
10981099
case Mips::CAP_ATOMIC_CMP_SWAP_I16_POSTRA:
10991100
case Mips::CAP_ATOMIC_CMP_SWAP_I32_POSTRA:
11001101
case Mips::CAP_ATOMIC_CMP_SWAP_I64_POSTRA:
1101-
case Mips::CAP_ATOMIC_CMP_SWAP_CAP_POSTRA:
1102+
case Mips::CAP_ATOMIC_CMP_SWAP_CAP_ADDR_POSTRA:
1103+
case Mips::CAP_ATOMIC_CMP_SWAP_CAP_EXACT_POSTRA:
11021104
return expandAtomicCmpSwap(MBB, MBBI, NMBB, /*IsCapOp=*/true);
11031105
case Mips::PseudoPccRelativeAddressPostRA:
11041106
return expandPccRelativeAddr(MBB, MBBI, NMBB);

llvm/lib/Target/Mips/MipsISelLowering.cpp

+8-3
Original file line numberDiff line numberDiff line change
@@ -1837,7 +1837,8 @@ MipsTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
18371837
case Mips::CAP_ATOMIC_CMP_SWAP_I16:
18381838
case Mips::CAP_ATOMIC_CMP_SWAP_I32:
18391839
case Mips::CAP_ATOMIC_CMP_SWAP_I64:
1840-
case Mips::CAP_ATOMIC_CMP_SWAP_CAP:
1840+
case Mips::CAP_ATOMIC_CMP_SWAP_CAP_ADDR:
1841+
case Mips::CAP_ATOMIC_CMP_SWAP_CAP_EXACT:
18411842
return emitAtomicCmpSwap(MI, BB);
18421843

18431844

@@ -2445,8 +2446,12 @@ MipsTargetLowering::emitAtomicCmpSwap(MachineInstr &MI,
24452446
AtomicOp = Mips::CAP_ATOMIC_CMP_SWAP_I64_POSTRA;
24462447
ScratchTy = MVT::i64;
24472448
break;
2448-
case Mips::CAP_ATOMIC_CMP_SWAP_CAP:
2449-
AtomicOp = Mips::CAP_ATOMIC_CMP_SWAP_CAP_POSTRA;
2449+
case Mips::CAP_ATOMIC_CMP_SWAP_CAP_ADDR:
2450+
AtomicOp = Mips::CAP_ATOMIC_CMP_SWAP_CAP_ADDR_POSTRA;
2451+
ScratchTy = MVT::i64;
2452+
break;
2453+
case Mips::CAP_ATOMIC_CMP_SWAP_CAP_EXACT:
2454+
AtomicOp = Mips::CAP_ATOMIC_CMP_SWAP_CAP_EXACT_POSTRA;
24502455
ScratchTy = MVT::i64;
24512456
break;
24522457
default:

llvm/lib/Target/Mips/MipsInstrCheri.td

+8-6
Original file line numberDiff line numberDiff line change
@@ -759,8 +759,9 @@ let usesCustomInserter = 1 in {
759759

760760
// Capability atomics:
761761
// FIXME: this seems wrong it should be CheriGPROrCNULL
762-
def CAP_ATOMIC_SWAP_CAP : CapAtomic2Ops<atomic_swap_cap, CheriOpnd>;
763-
def CAP_ATOMIC_CMP_SWAP_CAP : CapAtomicCmpSwap<atomic_cmp_swap_cap, CheriOpnd>;
762+
def CAP_ATOMIC_SWAP_CAP : CapAtomic2Ops<atomic_swap_cap, CheriOpnd>;
763+
def CAP_ATOMIC_CMP_SWAP_CAP_ADDR : CapAtomicCmpSwap<atomic_cmp_swap_cap_addr, CheriOpnd>;
764+
def CAP_ATOMIC_CMP_SWAP_CAP_EXACT : CapAtomicCmpSwap<atomic_cmp_swap_cap_exact, CheriOpnd>;
764765

765766
// TODO: implement these:
766767
// def ATOMIC_LOAD_ADD_CAP : Atomic2Ops<atomic_load_add_cap, CheriOpnd>;
@@ -812,8 +813,9 @@ def CAP_ATOMIC_CMP_SWAP_I64_POSTRA : CapAtomicCmpSwapPostRA<GPR64Opnd>;
812813
// Capability postra atomics:
813814
// TODO: do we want add/sub/or/xor/nand/and for capabilities?
814815
// I guess add/sub makes sense but the others don't
815-
def CAP_ATOMIC_SWAP_CAP_POSTRA : CapAtomic2OpsPostRA<CheriOpnd>;
816-
def CAP_ATOMIC_CMP_SWAP_CAP_POSTRA : CapAtomicCmpSwapPostRA<CheriOpnd>;
816+
def CAP_ATOMIC_SWAP_CAP_POSTRA : CapAtomic2OpsPostRA<CheriOpnd>;
817+
def CAP_ATOMIC_CMP_SWAP_CAP_ADDR_POSTRA : CapAtomicCmpSwapPostRA<CheriOpnd>;
818+
def CAP_ATOMIC_CMP_SWAP_CAP_EXACT_POSTRA : CapAtomicCmpSwapPostRA<CheriOpnd>;
817819
// TODO:
818820
// def CAP_ATOMIC_LOAD_ADD_CAP_POSTRA : CapAtomic2OpsPostRA<CheriOpnd>;
819821
// def CAP_ATOMIC_LOAD_SUB_CAP_POSTRA : CapAtomic2OpsPostRA<CheriOpnd>;
@@ -849,8 +851,8 @@ def : MipsPat<(atomic_store_cap GPR64Opnd:$a, CheriOpnd:$v),
849851
(STORECAP $v, GPR64Opnd:$a, (i64 0), DDC)>;
850852
def : MipsPat<(atomic_swap_cap GPR64Opnd:$a, CheriOpnd:$swap),
851853
(CAP_ATOMIC_SWAP_CAP (CFromPtr DDC, GPR64Opnd:$a), CheriOpnd:$swap)>;
852-
def : MipsPat<(atomic_cmp_swap_cap GPR64Opnd:$a, CheriOpnd:$cmp, CheriOpnd:$swap),
853-
(CAP_ATOMIC_CMP_SWAP_CAP (CFromPtr DDC, GPR64Opnd:$a), CheriOpnd:$cmp, CheriOpnd:$swap)>;
854+
def : MipsPat<(atomic_cmp_swap_cap_addr GPR64Opnd:$a, CheriOpnd:$cmp, CheriOpnd:$swap),
855+
(CAP_ATOMIC_CMP_SWAP_CAP_ADDR (CFromPtr DDC, GPR64Opnd:$a), CheriOpnd:$cmp, CheriOpnd:$swap)>;
854856
}
855857
////////////////////////////////////////////////////////////////////////////////
856858
// Helpers for capability-using calls and returns

llvm/lib/Target/RISCV/RISCVExpandAtomicPseudoInsts.cpp

+7-4
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,8 @@ bool RISCVExpandAtomicPseudo::expandMI(MachineBasicBlock &MBB,
160160
case RISCV::PseudoAtomicLoadUMinCap:
161161
return expandAtomicMinMaxOp(MBB, MBBI, AtomicRMWInst::UMin, false, CLenVT,
162162
false, NextMBBI);
163-
case RISCV::PseudoCmpXchgCap:
163+
case RISCV::PseudoCmpXchgCapAddr:
164+
case RISCV::PseudoCmpXchgCapExact:
164165
return expandAtomicCmpXchg(MBB, MBBI, false, CLenVT, false, NextMBBI);
165166
case RISCV::PseudoCheriAtomicSwap8:
166167
return expandAtomicBinOp(MBB, MBBI, AtomicRMWInst::Xchg, false, MVT::i8,
@@ -272,7 +273,8 @@ bool RISCVExpandAtomicPseudo::expandMI(MachineBasicBlock &MBB,
272273
case RISCV::PseudoCheriAtomicLoadUMinCap:
273274
return expandAtomicMinMaxOp(MBB, MBBI, AtomicRMWInst::UMin, false, CLenVT,
274275
true, NextMBBI);
275-
case RISCV::PseudoCheriCmpXchgCap:
276+
case RISCV::PseudoCheriCmpXchgCapAddr:
277+
case RISCV::PseudoCheriCmpXchgCapExact:
276278
return expandAtomicCmpXchg(MBB, MBBI, false, CLenVT, true, NextMBBI);
277279
}
278280

@@ -1020,8 +1022,9 @@ bool RISCVExpandAtomicPseudo::expandAtomicCmpXchg(
10201022
BuildMI(LoopHeadMBB, DL, TII->get(getLRForRMW(PtrIsCap, Ordering, VT)),
10211023
DestReg)
10221024
.addReg(AddrReg);
1023-
assert(MI.hasOneMemOperand());
1024-
if (VT.isFatPointer() && MI.memoperands()[0]->isExactCompare()) {
1025+
bool ExactCapCompare = MI.getOpcode() == RISCV::PseudoCmpXchgCapExact ||
1026+
MI.getOpcode() == RISCV::PseudoCheriCmpXchgCapExact;
1027+
if (VT.isFatPointer() && ExactCapCompare) {
10251028
BuildMI(LoopHeadMBB, DL, TII->get(RISCV::CSEQX), ScratchReg)
10261029
.addReg(DestReg, 0)
10271030
.addReg(CmpValReg, 0);

llvm/lib/Target/RISCV/RISCVInstrInfoXCheri.td

+8-4
Original file line numberDiff line numberDiff line change
@@ -1594,7 +1594,8 @@ def PseudoAtomicLoadMinCap : PseudoAMO<GPCR> { let Size = 24; }
15941594
def PseudoAtomicLoadUMaxCap : PseudoAMO<GPCR> { let Size = 24; }
15951595
def PseudoAtomicLoadUMinCap : PseudoAMO<GPCR> { let Size = 24; }
15961596
def PseudoAtomicLoadNandCap : PseudoAMO<GPCR> { let Size = 24; }
1597-
def PseudoCmpXchgCap : PseudoCmpXchg<GPCR> { let Size = 16; }
1597+
def PseudoCmpXchgCapAddr : PseudoCmpXchg<GPCR> { let Size = 16; }
1598+
def PseudoCmpXchgCapExact : PseudoCmpXchg<GPCR> { let Size = 16; }
15981599
} // Predicates = [HasCheri, HasStdExtA]f
15991600

16001601
let Predicates = [HasCheri, HasStdExtA, NotCapMode] in {
@@ -1608,7 +1609,8 @@ defm : PseudoAMOPat<"atomic_load_min_cap", PseudoAtomicLoadMinCap, GPCR>;
16081609
defm : PseudoAMOPat<"atomic_load_umax_cap", PseudoAtomicLoadUMaxCap, GPCR>;
16091610
defm : PseudoAMOPat<"atomic_load_umin_cap", PseudoAtomicLoadUMinCap, GPCR>;
16101611
defm : PseudoAMOPat<"atomic_load_nand_cap", PseudoAtomicLoadNandCap, GPCR>;
1611-
defm : PseudoCmpXchgPat<"atomic_cmp_swap_cap", PseudoCmpXchgCap, GPCR>;
1612+
defm : PseudoCmpXchgPat<"atomic_cmp_swap_cap_addr", PseudoCmpXchgCapAddr, GPCR>;
1613+
defm : PseudoCmpXchgPat<"atomic_cmp_swap_cap_exact", PseudoCmpXchgCapExact, GPCR>;
16121614
} // Predicates = [HasCheri, HasStdExtA, NotCapMode]
16131615

16141616
/// Capability Mode Instructions
@@ -1751,7 +1753,8 @@ def PseudoCheriAtomicLoadMinCap : PseudoCheriAMO<GPCR> { let Size = 24; }
17511753
def PseudoCheriAtomicLoadUMaxCap : PseudoCheriAMO<GPCR> { let Size = 24; }
17521754
def PseudoCheriAtomicLoadUMinCap : PseudoCheriAMO<GPCR> { let Size = 24; }
17531755
def PseudoCheriAtomicLoadNandCap : PseudoCheriAMO<GPCR> { let Size = 24; }
1754-
def PseudoCheriCmpXchgCap : PseudoCheriCmpXchg<GPCR> { let Size = 16; }
1756+
def PseudoCheriCmpXchgCapAddr : PseudoCheriCmpXchg<GPCR> { let Size = 16; }
1757+
def PseudoCheriCmpXchgCapExact : PseudoCheriCmpXchg<GPCR> { let Size = 16; }
17551758
} // Predicates = [HasCheri, HasStdExtA]
17561759

17571760
let Predicates = [HasCheri, HasStdExtA, IsRV64] in {
@@ -1950,7 +1953,8 @@ defm : PseudoCheriCmpXchgPat<"atomic_cmp_swap_8", PseudoCheriCmpXchg8>;
19501953
defm : PseudoCheriCmpXchgPat<"atomic_cmp_swap_16", PseudoCheriCmpXchg16>;
19511954
defm : PseudoCheriCmpXchgPat<"atomic_cmp_swap_32", PseudoCheriCmpXchg32>;
19521955

1953-
defm : PseudoCheriCmpXchgPat<"atomic_cmp_swap_cap", PseudoCheriCmpXchgCap, GPCR>;
1956+
defm : PseudoCheriCmpXchgPat<"atomic_cmp_swap_cap_addr", PseudoCheriCmpXchgCapAddr, GPCR>;
1957+
defm : PseudoCheriCmpXchgPat<"atomic_cmp_swap_cap_exact", PseudoCheriCmpXchgCapExact, GPCR>;
19541958

19551959
} // Predicates = [HasCheri, HasStdExtA, IsCapMode]
19561960

llvm/test/CodeGen/CHERI-Generic/Inputs/cmpxchg-exact-branch-folder.ll

+5-4
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@
22
; CHERI-GENERIC-UTC: mir
33
@IF-RISCV@; RUN: llc @PURECAP_HARDFLOAT_ARGS@ -mattr=+a < %s --stop-after=branch-folder | FileCheck %s --check-prefixes=MIR
44
@IFNOT-RISCV@; RUN: llc @PURECAP_HARDFLOAT_ARGS@ < %s --stop-after=branch-folder --enable-tail-merge | FileCheck %s --check-prefixes=MIR
5-
@IF-RISCV@; RUN: not --crash llc @PURECAP_HARDFLOAT_ARGS@ -mattr=+a < %s
6-
@IFNOT-RISCV@; RUN: not --crash llc @PURECAP_HARDFLOAT_ARGS@ --enable-tail-merge < %s
5+
; Note: cat %s is needed so that update_mir_test_checks.py does not process these RUN lines.
6+
@IF-RISCV@; RUN: cat %s | llc @PURECAP_HARDFLOAT_ARGS@ -mattr=+a | FileCheck %s
7+
@IFNOT-RISCV@; RUN: cat %s | llc @PURECAP_HARDFLOAT_ARGS@ --enable-tail-merge | FileCheck %s
78
; REQUIRES: asserts
89

910
; The branch-folder MIR pass will merge the two blocks inside these functions but
1011
; since the base pointer is distinct it will have two MachineMemOperands.
1112
; The cmpxchg exact logic stored the exact flag in the MachineMemOperand and
1213
; previously assumed there would only ever be one operand, so this test ensures
13-
; we can handle the merged logic.
14+
; we can handle the merged logic by adding separate pseudo instructions (which
15+
; ensures that the branches with different comparisons can no longer be merged).
1416

1517
define dso_local signext i32 @merge_i32(i1 %cond1, ptr addrspace(200) %ptr, i32 %newval, i32 %cmpval) {
1618
entry:
@@ -66,7 +68,6 @@ end:
6668
ret i32 0
6769
}
6870

69-
; FIXME: these two branches should not be merged!
7071
define dso_local signext i32 @merge_ptr_mismatch_exact_flag(i1 %cond1, ptr addrspace(200) %ptr, ptr addrspace(200) %newval, ptr addrspace(200) %cmpval) {
7172
entry:
7273
br i1 %cond1, label %if.then, label %if.else

0 commit comments

Comments
 (0)