Skip to content

Commit

Permalink
[RISCV][ISEL] Lowering to load-acquire/store-release for RISCV Zalasr…
Browse files Browse the repository at this point in the history
… (#82914)

Lowering to load-acquire/store-release for RISCV Zalasr.

Currently uses the psABI lowerings for WMO load-acquire/store-release
(which are identical to A.7). These are incompatable with the A.6
lowerings currently used by LLVM. This should be OK for now since Zalasr
is behind the enable experimental extensions flag, but needs to be fixed
before it is removed from that.

For TSO, it uses the standard Ztso mappings except for lowering seq_cst
loads/store to load-acquire/store-release, I had Andrea review that.
  • Loading branch information
mehnadnerd authored Dec 17, 2024
1 parent 300deeb commit bfe8a21
Show file tree
Hide file tree
Showing 5 changed files with 359 additions and 11 deletions.
29 changes: 29 additions & 0 deletions llvm/lib/Target/RISCV/RISCVISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22291,6 +22291,35 @@ unsigned RISCVTargetLowering::getCustomCtpopCost(EVT VT,
return isCtpopFast(VT) ? 0 : 1;
}

bool RISCVTargetLowering::shouldInsertFencesForAtomic(
const Instruction *I) const {
if (Subtarget.hasStdExtZalasr()) {
if (Subtarget.hasStdExtZtso()) {
// Zalasr + TSO means that atomic_load_acquire and atomic_store_release
// should be lowered to plain load/store. The easiest way to do this is
// to say we should insert fences for them, and the fence insertion code
// will just not insert any fences
auto *LI = dyn_cast<LoadInst>(I);
auto *SI = dyn_cast<StoreInst>(I);
if ((LI &&
(LI->getOrdering() == AtomicOrdering::SequentiallyConsistent)) ||
(SI &&
(SI->getOrdering() == AtomicOrdering::SequentiallyConsistent))) {
// Here, this is a load or store which is seq_cst, and needs a .aq or
// .rl therefore we shouldn't try to insert fences
return false;
}
// Here, we are a TSO inst that isn't a seq_cst load/store
return isa<LoadInst>(I) || isa<StoreInst>(I);
}
return false;
}
// Note that one specific case requires fence insertion for an
// AtomicCmpXchgInst but is handled via the RISCVZacasABIFix pass rather
// than this hook due to limitations in the interface here.
return isa<LoadInst>(I) || isa<StoreInst>(I);
}

bool RISCVTargetLowering::fallBackToDAGISel(const Instruction &Inst) const {

// GISel support is in progress or complete for these opcodes.
Expand Down
5 changes: 2 additions & 3 deletions llvm/lib/Target/RISCV/RISCVISelLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -703,9 +703,8 @@ class RISCVTargetLowering : public TargetLowering {
// Note that one specific case requires fence insertion for an
// AtomicCmpXchgInst but is handled via the RISCVZacasABIFix pass rather
// than this hook due to limitations in the interface here.
bool shouldInsertFencesForAtomic(const Instruction *I) const override {
return isa<LoadInst>(I) || isa<StoreInst>(I);
}
bool shouldInsertFencesForAtomic(const Instruction *I) const override;

Instruction *emitLeadingFence(IRBuilderBase &Builder, Instruction *Inst,
AtomicOrdering Ord) const override;
Instruction *emitTrailingFence(IRBuilderBase &Builder, Instruction *Inst,
Expand Down
70 changes: 62 additions & 8 deletions llvm/lib/Target/RISCV/RISCVInstrInfoA.td
Original file line number Diff line number Diff line change
Expand Up @@ -118,22 +118,65 @@ defm AMOMAXU_D : AMO_rr_aq_rl<0b11100, 0b011, "amomaxu.d">,
// Pseudo-instructions and codegen patterns
//===----------------------------------------------------------------------===//

let IsAtomic = 1 in {
// An atomic load operation that does not need either acquire or release
// semantics.
class relaxed_load<PatFrags base>
: PatFrag<(ops node:$ptr), (base node:$ptr)> {
let IsAtomicOrderingAcquireOrStronger = 0;
}

// A atomic load operation that actually needs acquire semantics.
class acquiring_load<PatFrags base>
: PatFrag<(ops node:$ptr), (base node:$ptr)> {
let IsAtomicOrderingAcquire = 1;
}

// An atomic load operation that needs sequential consistency.
class seq_cst_load<PatFrags base>
: PatFrag<(ops node:$ptr), (base node:$ptr)> {
let IsAtomicOrderingSequentiallyConsistent = 1;
}

// An atomic store operation that does not need either acquire or release
// semantics.
class relaxed_store<PatFrag base>
: PatFrag<(ops node:$val, node:$ptr), (base node:$val, node:$ptr)> {
let IsAtomicOrderingReleaseOrStronger = 0;
}

// A store operation that actually needs release semantics.
class releasing_store<PatFrag base>
: PatFrag<(ops node:$val, node:$ptr), (base node:$val, node:$ptr)> {
let IsAtomicOrderingRelease = 1;
}

// A store operation that actually needs sequential consistency.
class seq_cst_store<PatFrag base>
: PatFrag<(ops node:$val, node:$ptr), (base node:$val, node:$ptr)> {
let IsAtomicOrderingSequentiallyConsistent = 1;
}
} // IsAtomic = 1

// Atomic load/store are available under both +a and +force-atomics.
// Fences will be inserted for atomic load/stores according to the logic in
// RISCVTargetLowering::{emitLeadingFence,emitTrailingFence}.
// The normal loads/stores are relaxed (unordered) loads/stores that don't have
// any ordering. This is necessary because AtomicExpandPass has added fences to
// atomic load/stores and changed them to unordered ones.
let Predicates = [HasAtomicLdSt] in {
def : LdPat<atomic_load_8, LB>;
def : LdPat<atomic_load_16, LH>;
def : LdPat<atomic_load_32, LW>;
def : LdPat<relaxed_load<atomic_load_8>, LB>;
def : LdPat<relaxed_load<atomic_load_16>, LH>;
def : LdPat<relaxed_load<atomic_load_32>, LW>;

def : StPat<atomic_store_8, SB, GPR, XLenVT>;
def : StPat<atomic_store_16, SH, GPR, XLenVT>;
def : StPat<atomic_store_32, SW, GPR, XLenVT>;
def : StPat<relaxed_store<atomic_store_8>, SB, GPR, XLenVT>;
def : StPat<relaxed_store<atomic_store_16>, SH, GPR, XLenVT>;
def : StPat<relaxed_store<atomic_store_32>, SW, GPR, XLenVT>;
}

let Predicates = [HasAtomicLdSt, IsRV64] in {
def : LdPat<atomic_load_64, LD, i64>;
def : StPat<atomic_store_64, SD, GPR, i64>;
def : LdPat<relaxed_load<atomic_load_64>, LD, i64>;
def : StPat<relaxed_store<atomic_store_64>, SD, GPR, i64>;
}

/// AMOs
Expand Down Expand Up @@ -386,3 +429,14 @@ def : Pat<(int_riscv_masked_cmpxchg_i64
(PseudoMaskedCmpXchg32
GPR:$addr, GPR:$cmpval, GPR:$newval, GPR:$mask, timm:$ordering)>;
} // Predicates = [HasStdExtA, IsRV64]

let Predicates = [HasAtomicLdSt] in {
def : LdPat<relaxed_load<atomic_load_8>, LB, i32>;
def : LdPat<relaxed_load<atomic_load_16>, LH, i32>;
def : LdPat<relaxed_load<atomic_load_32>, LW, i32>;

def : StPat<relaxed_store<atomic_store_8>, SB, GPR, i32>;
def : StPat<relaxed_store<atomic_store_16>, SH, GPR, i32>;
def : StPat<relaxed_store<atomic_store_32>, SW, GPR, i32>;
}

47 changes: 47 additions & 0 deletions llvm/lib/Target/RISCV/RISCVInstrInfoZalasr.td
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,50 @@ let Predicates = [HasStdExtZalasr, IsRV64] in {
defm LD : LAQ_r_aq_rl<0b011, "ld">;
defm SD : SRL_r_aq_rl<0b011, "sd">;
} // Predicates = [HasStdExtZalasr, IsRV64]

//===----------------------------------------------------------------------===//
// Pseudo-instructions and codegen patterns
//===----------------------------------------------------------------------===//

class PatLAQ<SDPatternOperator OpNode, RVInst Inst, ValueType vt = XLenVT>
: Pat<(vt (OpNode (vt GPRMemZeroOffset:$rs1))), (Inst GPRMemZeroOffset:$rs1)>;

// n.b. this switches order of arguments
// to deal with the fact that SRL has addr, data
// while atomic_store has data, addr
class PatSRL<SDPatternOperator OpNode, RVInst Inst, ValueType vt = XLenVT>
: Pat<(OpNode (vt GPR:$rs2), (vt GPRMemZeroOffset:$rs1)),
(Inst GPRMemZeroOffset:$rs1, GPR:$rs2)>;


let Predicates = [HasStdExtZalasr] in {
// the sequentially consistent loads use
// .aq instead of .aqrl to match the psABI/A.7
def : PatLAQ<acquiring_load<atomic_load_8>, LB_AQ>;
def : PatLAQ<seq_cst_load<atomic_load_8>, LB_AQ>;

def : PatLAQ<acquiring_load<atomic_load_16>, LH_AQ>;
def : PatLAQ<seq_cst_load<atomic_load_16>, LH_AQ>;

def : PatLAQ<acquiring_load<atomic_load_32>, LW_AQ>;
def : PatLAQ<seq_cst_load<atomic_load_32>, LW_AQ>;

// the sequentially consistent stores use
// .rl instead of .aqrl to match the psABI/A.7
def : PatSRL<releasing_store<atomic_store_8>, SB_RL>;
def : PatSRL<seq_cst_store<atomic_store_8>, SB_RL>;

def : PatSRL<releasing_store<atomic_store_16>, SH_RL>;
def : PatSRL<seq_cst_store<atomic_store_16>, SH_RL>;

def : PatSRL<releasing_store<atomic_store_32>, SW_RL>;
def : PatSRL<seq_cst_store<atomic_store_32>, SW_RL>;
} // Predicates = [HasStdExtZalasr]

let Predicates = [HasStdExtZalasr, IsRV64] in {
def : PatLAQ<acquiring_load<atomic_load_64>, LD_AQ>;
def : PatLAQ<seq_cst_load<atomic_load_64>, LD_AQ>;

def : PatSRL<releasing_store<atomic_store_64>, SD_RL>;
def : PatSRL<seq_cst_store<atomic_store_64>, SD_RL>;
} // Predicates = [HasStdExtZalasr, IsRV64]
Loading

0 comments on commit bfe8a21

Please sign in to comment.