Skip to content

[SystemZ] Don't lower float/double ATOMIC_[LOAD|STORE] to [LOAD|STORE] #75879

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions llvm/include/llvm/CodeGen/SelectionDAGNodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,7 @@ BEGIN_TWO_BYTE_PACK()

class LoadSDNodeBitfields {
friend class LoadSDNode;
friend class AtomicSDNode;
friend class VPLoadSDNode;
friend class VPStridedLoadSDNode;
friend class MaskedLoadSDNode;
Expand Down Expand Up @@ -1475,6 +1476,16 @@ class AtomicSDNode : public MemSDNode {
MMO->isAtomic()) && "then why are we using an AtomicSDNode?");
}

void setExtensionType(ISD::LoadExtType ETy) {
assert(getOpcode() == ISD::ATOMIC_LOAD && "Only used for atomic loads.");
LoadSDNodeBits.ExtTy = ETy;
}

ISD::LoadExtType getExtensionType() const {
assert(getOpcode() == ISD::ATOMIC_LOAD && "Only used for atomic loads.");
return static_cast<ISD::LoadExtType>(LoadSDNodeBits.ExtTy);
}

const SDValue &getBasePtr() const {
return getOpcode() == ISD::ATOMIC_STORE ? getOperand(2) : getOperand(1);
}
Expand Down
2 changes: 1 addition & 1 deletion llvm/include/llvm/Target/TargetSelectionDAG.td
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ def SDTAtomicStore : SDTypeProfile<0, 2, [
SDTCisInt<0>, SDTCisPtrTy<1>
]>;
def SDTAtomicLoad : SDTypeProfile<1, 1, [
SDTCisInt<0>, SDTCisPtrTy<1>
SDTCisPtrTy<1>
]>;

class SDCallSeqStart<list<SDTypeConstraint> constraints> :
Expand Down
20 changes: 20 additions & 0 deletions llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,26 @@ SDValue DAGTypeLegalizer::PromoteIntRes_Atomic0(AtomicSDNode *N) {
N->getMemoryVT(), ResVT,
N->getChain(), N->getBasePtr(),
N->getMemOperand());
if (N->getOpcode() == ISD::ATOMIC_LOAD) {
ISD::LoadExtType ETy = cast<AtomicSDNode>(N)->getExtensionType();
if (ETy == ISD::NON_EXTLOAD) {
switch (TLI.getExtendForAtomicOps()) {
case ISD::SIGN_EXTEND:
ETy = ISD::SEXTLOAD;
break;
case ISD::ZERO_EXTEND:
ETy = ISD::ZEXTLOAD;
break;
case ISD::ANY_EXTEND:
ETy = ISD::EXTLOAD;
break;
default:
llvm_unreachable("Invalid atomic op extension");
}
}
cast<AtomicSDNode>(Res)->setExtensionType(ETy);
}

// Legalize the chain result - switch anything that used the old chain to
// use the new one.
ReplaceValueWith(SDValue(N, 1), Res.getValue(1));
Expand Down
10 changes: 10 additions & 0 deletions llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4070,6 +4070,9 @@ KnownBits SelectionDAG::computeKnownBits(SDValue Op, const APInt &DemandedElts,
if (Op.getResNo() == 0) {
if (TLI->getExtendForAtomicOps() == ISD::ZERO_EXTEND)
Known.Zero.setBitsFrom(MemBits);
else if (Op->getOpcode() == ISD::ATOMIC_LOAD &&
cast<AtomicSDNode>(Op)->getExtensionType() == ISD::ZEXTLOAD)
Known.Zero.setBitsFrom(MemBits);
}
break;
}
Expand Down Expand Up @@ -4875,6 +4878,13 @@ unsigned SelectionDAG::ComputeNumSignBits(SDValue Op, const APInt &DemandedElts,
return VTBits - Tmp + 1;
if (TLI->getExtendForAtomicOps() == ISD::ZERO_EXTEND)
return VTBits - Tmp;
if (Op->getOpcode() == ISD::ATOMIC_LOAD) {
ISD::LoadExtType ETy = cast<AtomicSDNode>(Op)->getExtensionType();
if (ETy == ISD::SEXTLOAD)
return VTBits - Tmp + 1;
if (ETy == ISD::ZEXTLOAD)
return VTBits - Tmp;
}
}
break;
}
Expand Down
12 changes: 12 additions & 0 deletions llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,18 @@ void SDNode::print_details(raw_ostream &OS, const SelectionDAG *G) const {
} else if (const MemSDNode *M = dyn_cast<MemSDNode>(this)) {
OS << "<";
printMemOperand(OS, *M->getMemOperand(), G);
if (auto *A = dyn_cast<AtomicSDNode>(M))
if (A->getOpcode() == ISD::ATOMIC_LOAD) {
bool doExt = true;
switch (A->getExtensionType()) {
default: doExt = false; break;
case ISD::EXTLOAD: OS << ", anyext"; break;
case ISD::SEXTLOAD: OS << ", sext"; break;
case ISD::ZEXTLOAD: OS << ", zext"; break;
}
if (doExt)
OS << " from " << A->getMemoryVT();
}
OS << ">";
} else if (const BlockAddressSDNode *BA =
dyn_cast<BlockAddressSDNode>(this)) {
Expand Down
12 changes: 6 additions & 6 deletions llvm/lib/Target/PowerPC/PPCInstrInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -5045,12 +5045,12 @@ defm : TrapExtendedMnemonic<"lng", 6>;
defm : TrapExtendedMnemonic<"u", 31>;

// Atomic loads
def : Pat<(atomic_load_8 DForm:$src), (LBZ memri:$src)>;
def : Pat<(atomic_load_16 DForm:$src), (LHZ memri:$src)>;
def : Pat<(atomic_load_32 DForm:$src), (LWZ memri:$src)>;
def : Pat<(atomic_load_8 XForm:$src), (LBZX memrr:$src)>;
def : Pat<(atomic_load_16 XForm:$src), (LHZX memrr:$src)>;
def : Pat<(atomic_load_32 XForm:$src), (LWZX memrr:$src)>;
def : Pat<(i32 (atomic_load_8 DForm:$src)), (LBZ memri:$src)>;
def : Pat<(i32 (atomic_load_16 DForm:$src)), (LHZ memri:$src)>;
def : Pat<(i32 (atomic_load_32 DForm:$src)), (LWZ memri:$src)>;
def : Pat<(i32 (atomic_load_8 XForm:$src)), (LBZX memrr:$src)>;
def : Pat<(i32 (atomic_load_16 XForm:$src)), (LHZX memrr:$src)>;
def : Pat<(i32 (atomic_load_32 XForm:$src)), (LWZX memrr:$src)>;

// Atomic stores
def : Pat<(atomic_store_8 i32:$val, DForm:$ptr), (STB gprc:$val, memri:$ptr)>;
Expand Down
16 changes: 8 additions & 8 deletions llvm/lib/Target/PowerPC/PPCInstrP10.td
Original file line number Diff line number Diff line change
Expand Up @@ -1289,13 +1289,13 @@ let Predicates = [PCRelativeMemops] in {
(PSTXVpc $XS, $ga, 0)>;

// Atomic Load
def : Pat<(atomic_load_8 (PPCmatpcreladdr PCRelForm:$ga)),
def : Pat<(i32 (atomic_load_8 (PPCmatpcreladdr PCRelForm:$ga))),
(PLBZpc $ga, 0)>;
def : Pat<(atomic_load_16 (PPCmatpcreladdr PCRelForm:$ga)),
def : Pat<(i32 (atomic_load_16 (PPCmatpcreladdr PCRelForm:$ga))),
(PLHZpc $ga, 0)>;
def : Pat<(atomic_load_32 (PPCmatpcreladdr PCRelForm:$ga)),
def : Pat<(i32 (atomic_load_32 (PPCmatpcreladdr PCRelForm:$ga))),
(PLWZpc $ga, 0)>;
def : Pat<(atomic_load_64 (PPCmatpcreladdr PCRelForm:$ga)),
def : Pat<(i64 (atomic_load_64 (PPCmatpcreladdr PCRelForm:$ga))),
(PLDpc $ga, 0)>;

// Atomic Store
Expand Down Expand Up @@ -2347,10 +2347,10 @@ let Predicates = [PrefixInstrs] in {
def : Pat<(store f64:$FRS, PDForm:$dst), (PSTFD $FRS, memri34:$dst)>;

// Atomic Load
def : Pat<(atomic_load_8 PDForm:$src), (PLBZ memri34:$src)>;
def : Pat<(atomic_load_16 PDForm:$src), (PLHZ memri34:$src)>;
def : Pat<(atomic_load_32 PDForm:$src), (PLWZ memri34:$src)>;
def : Pat<(atomic_load_64 PDForm:$src), (PLD memri34:$src)>;
def : Pat<(i32 (atomic_load_8 PDForm:$src)), (PLBZ memri34:$src)>;
def : Pat<(i32 (atomic_load_16 PDForm:$src)), (PLHZ memri34:$src)>;
def : Pat<(i32 (atomic_load_32 PDForm:$src)), (PLWZ memri34:$src)>;
def : Pat<(i64 (atomic_load_64 PDForm:$src)), (PLD memri34:$src)>;

// Atomic Store
def : Pat<(atomic_store_8 i32:$RS, PDForm:$dst), (PSTB $RS, memri34:$dst)>;
Expand Down
40 changes: 38 additions & 2 deletions llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,9 @@ class SystemZDAGToDAGISel : public SelectionDAGISel {
// requirements for a PC-relative access.
bool storeLoadIsAligned(SDNode *N) const;

// Return the load extension type of a load or atomic load.
ISD::LoadExtType getLoadExtType(SDNode *N) const;

// Try to expand a boolean SELECT_CCMASK using an IPM sequence.
SDValue expandSelectBoolean(SDNode *Node);

Expand Down Expand Up @@ -1507,15 +1510,17 @@ bool SystemZDAGToDAGISel::storeLoadCanUseBlockBinary(SDNode *N,

bool SystemZDAGToDAGISel::storeLoadIsAligned(SDNode *N) const {

auto *MemAccess = cast<LSBaseSDNode>(N);
auto *MemAccess = cast<MemSDNode>(N);
auto *LdSt = dyn_cast<LSBaseSDNode>(MemAccess);
TypeSize StoreSize = MemAccess->getMemoryVT().getStoreSize();
SDValue BasePtr = MemAccess->getBasePtr();
MachineMemOperand *MMO = MemAccess->getMemOperand();
assert(MMO && "Expected a memory operand.");

// The memory access must have a proper alignment and no index register.
// Only load and store nodes have the offset operand (atomic loads do not).
if (MemAccess->getAlign().value() < StoreSize ||
!MemAccess->getOffset().isUndef())
(LdSt && !LdSt->getOffset().isUndef()))
return false;

// The MMO must not have an unaligned offset.
Expand Down Expand Up @@ -1545,6 +1550,17 @@ bool SystemZDAGToDAGISel::storeLoadIsAligned(SDNode *N) const {
return true;
}

ISD::LoadExtType SystemZDAGToDAGISel::getLoadExtType(SDNode *N) const {
ISD::LoadExtType ETy;
if (auto *L = dyn_cast<LoadSDNode>(N))
ETy = L->getExtensionType();
else if (auto *AL = dyn_cast<AtomicSDNode>(N))
ETy = AL->getExtensionType();
else
llvm_unreachable("Unkown load node type.");
return ETy;
}

void SystemZDAGToDAGISel::Select(SDNode *Node) {
// If we have a custom node, we already have selected!
if (Node->isMachineOpcode()) {
Expand Down Expand Up @@ -1742,6 +1758,26 @@ void SystemZDAGToDAGISel::Select(SDNode *Node) {
}
break;
}

case ISD::ATOMIC_STORE: {
auto *AtomOp = cast<AtomicSDNode>(Node);
// Replace the atomic_store with a regular store and select it. This is
// ok since we know all store instructions <= 8 bytes are atomic, and the
// 16 byte case is already handled during lowering.
StoreSDNode *St = cast<StoreSDNode>(CurDAG->getTruncStore(
AtomOp->getChain(), SDLoc(AtomOp), AtomOp->getVal(),
AtomOp->getBasePtr(), AtomOp->getMemoryVT(), AtomOp->getMemOperand()));
assert(St->getMemOperand()->isAtomic() && "Broken MMO.");
SDNode *Chain = St;
// We have to enforce sequential consistency by performing a
// serialization operation after the store.
if (AtomOp->getSuccessOrdering() == AtomicOrdering::SequentiallyConsistent)
Chain = CurDAG->getMachineNode(SystemZ::Serialize, SDLoc(AtomOp),
MVT::Other, SDValue(Chain, 0));
ReplaceNode(Node, Chain);
SelectCode(St);
return;
}
}

SelectCode(Node);
Expand Down
102 changes: 62 additions & 40 deletions llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,11 +194,6 @@ SystemZTargetLowering::SystemZTargetLowering(const TargetMachine &TM,
setOperationAction(ISD::UADDO_CARRY, VT, Custom);
setOperationAction(ISD::USUBO_CARRY, VT, Custom);

// Lower ATOMIC_LOAD and ATOMIC_STORE into normal volatile loads and
// stores, putting a serialization instruction after the stores.
setOperationAction(ISD::ATOMIC_LOAD, VT, Custom);
setOperationAction(ISD::ATOMIC_STORE, VT, Custom);

// Lower ATOMIC_LOAD_SUB into ATOMIC_LOAD_ADD if LAA and LAAG are
// available, or if the operand is constant.
setOperationAction(ISD::ATOMIC_LOAD_SUB, VT, Custom);
Expand Down Expand Up @@ -920,6 +915,22 @@ bool SystemZTargetLowering::hasInlineStackProbe(const MachineFunction &MF) const
return false;
}

TargetLowering::AtomicExpansionKind
SystemZTargetLowering::shouldCastAtomicLoadInIR(LoadInst *LI) const {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at this again, I don't think this target hook should exist. Fundamentally AtomicExpandPass exists as a hack because SelectionDAG cannot handle emitting control flow for the cmpxchg blocks. Bitcasting types to match instructions is best done as part of regular legalization. cc @shiltian

// Lower fp128 the same way as i128.
if (LI->getType()->isFP128Ty())
return AtomicExpansionKind::CastToInteger;
return AtomicExpansionKind::None;
}

TargetLowering::AtomicExpansionKind
SystemZTargetLowering::shouldCastAtomicStoreInIR(StoreInst *SI) const {
// Lower fp128 the same way as i128.
if (SI->getValueOperand()->getType()->isFP128Ty())
return AtomicExpansionKind::CastToInteger;
return AtomicExpansionKind::None;
}

TargetLowering::AtomicExpansionKind
SystemZTargetLowering::shouldExpandAtomicRMWInIR(AtomicRMWInst *RMW) const {
// Don't expand subword operations as they require special treatment.
Expand Down Expand Up @@ -4503,40 +4514,14 @@ SDValue SystemZTargetLowering::lowerATOMIC_FENCE(SDValue Op,
return DAG.getNode(ISD::MEMBARRIER, DL, MVT::Other, Op.getOperand(0));
}

// Op is an atomic load. Lower it into a normal volatile load.
SDValue SystemZTargetLowering::lowerATOMIC_LOAD(SDValue Op,
SelectionDAG &DAG) const {
auto *Node = cast<AtomicSDNode>(Op.getNode());
if (Node->getMemoryVT() == MVT::i128) {
// Use same code to handle both legal and non-legal i128 types.
SmallVector<SDValue, 2> Results;
LowerOperationWrapper(Node, Results, DAG);
return DAG.getMergeValues(Results, SDLoc(Op));
}
return DAG.getExtLoad(ISD::EXTLOAD, SDLoc(Op), Op.getValueType(),
Node->getChain(), Node->getBasePtr(),
Node->getMemoryVT(), Node->getMemOperand());
}

// Op is an atomic store. Lower it into a normal volatile store.
SDValue SystemZTargetLowering::lowerATOMIC_STORE(SDValue Op,
SelectionDAG &DAG) const {
SDValue SystemZTargetLowering::lowerATOMIC_LDST_I128(SDValue Op,
SelectionDAG &DAG) const {
auto *Node = cast<AtomicSDNode>(Op.getNode());
if (Node->getMemoryVT() == MVT::i128) {
// Use same code to handle both legal and non-legal i128 types.
SmallVector<SDValue, 1> Results;
LowerOperationWrapper(Node, Results, DAG);
return DAG.getMergeValues(Results, SDLoc(Op));
}
SDValue Chain = DAG.getTruncStore(Node->getChain(), SDLoc(Op), Node->getVal(),
Node->getBasePtr(), Node->getMemoryVT(),
Node->getMemOperand());
// We have to enforce sequential consistency by performing a
// serialization operation after the store.
if (Node->getSuccessOrdering() == AtomicOrdering::SequentiallyConsistent)
Chain = SDValue(DAG.getMachineNode(SystemZ::Serialize, SDLoc(Op),
MVT::Other, Chain), 0);
return Chain;
assert(Node->getMemoryVT() == MVT::i128 && "Only custom lowering i128.");
// Use same code to handle both legal and non-legal i128 types.
SmallVector<SDValue, 2> Results;
LowerOperationWrapper(Node, Results, DAG);
return DAG.getMergeValues(Results, SDLoc(Op));
}

// Prepare for a Compare And Swap for a subword operation. This needs to be
Expand Down Expand Up @@ -5662,6 +5647,9 @@ static SDValue tryBuildVectorShuffle(SelectionDAG &DAG,
bool SystemZTargetLowering::isVectorElementLoad(SDValue Op) const {
if (Op.getOpcode() == ISD::LOAD && cast<LoadSDNode>(Op)->isUnindexed())
return true;
if (auto *AL = dyn_cast<AtomicSDNode>(Op))
if (AL->getOpcode() == ISD::ATOMIC_LOAD)
return true;
if (Subtarget.hasVectorEnhancements2() && Op.getOpcode() == SystemZISD::LRV)
return true;
return false;
Expand Down Expand Up @@ -6138,9 +6126,8 @@ SDValue SystemZTargetLowering::LowerOperation(SDValue Op,
case ISD::ATOMIC_SWAP:
return lowerATOMIC_LOAD_OP(Op, DAG, SystemZISD::ATOMIC_SWAPW);
case ISD::ATOMIC_STORE:
return lowerATOMIC_STORE(Op, DAG);
case ISD::ATOMIC_LOAD:
return lowerATOMIC_LOAD(Op, DAG);
return lowerATOMIC_LDST_I128(Op, DAG);
case ISD::ATOMIC_LOAD_ADD:
return lowerATOMIC_LOAD_OP(Op, DAG, SystemZISD::ATOMIC_LOADW_ADD);
case ISD::ATOMIC_LOAD_SUB:
Expand Down Expand Up @@ -6587,6 +6574,27 @@ SDValue SystemZTargetLowering::combineTruncateExtract(
return SDValue();
}

// Replace ALoad with a new ATOMIC_LOAD with a result that is extended to VT
// per ETy.
static SDValue extendAtomicLoad(AtomicSDNode *ALoad, EVT VT, SelectionDAG &DAG,
ISD::LoadExtType ETy) {
if (VT.getSizeInBits() > 64)
return SDValue();
EVT OrigVT = ALoad->getValueType(0);
assert(OrigVT.getSizeInBits() < VT.getSizeInBits() && "VT should be wider.");
EVT MemoryVT = ALoad->getMemoryVT();
auto *NewALoad = dyn_cast<AtomicSDNode>(DAG.getAtomic(
ISD::ATOMIC_LOAD, SDLoc(ALoad), MemoryVT, VT, ALoad->getChain(),
ALoad->getBasePtr(), ALoad->getMemOperand()));
NewALoad->setExtensionType(ETy);
DAG.ReplaceAllUsesOfValueWith(
SDValue(ALoad, 0),
DAG.getNode(ISD::TRUNCATE, SDLoc(ALoad), OrigVT, SDValue(NewALoad, 0)));
// Update the chain uses.
DAG.ReplaceAllUsesOfValueWith(SDValue(ALoad, 1), SDValue(NewALoad, 1));
return SDValue(NewALoad, 0);
}

SDValue SystemZTargetLowering::combineZERO_EXTEND(
SDNode *N, DAGCombinerInfo &DCI) const {
// Convert (zext (select_ccmask C1, C2)) into (select_ccmask C1', C2')
Expand All @@ -6611,6 +6619,13 @@ SDValue SystemZTargetLowering::combineZERO_EXTEND(
return NewSelect;
}
}

// Fold into ATOMIC_LOAD unless it is already sign extending.
if (auto *ALoad = dyn_cast<AtomicSDNode>(N0))
if (ALoad->getOpcode() == ISD::ATOMIC_LOAD &&
ALoad->getExtensionType() != ISD::SEXTLOAD)
return extendAtomicLoad(ALoad, VT, DAG, ISD::ZEXTLOAD);

return SDValue();
}

Expand Down Expand Up @@ -6662,6 +6677,13 @@ SDValue SystemZTargetLowering::combineSIGN_EXTEND(
}
}
}

// Fold into ATOMIC_LOAD unless it is already zero extending.
if (auto *ALoad = dyn_cast<AtomicSDNode>(N0))
if (ALoad->getOpcode() == ISD::ATOMIC_LOAD &&
ALoad->getExtensionType() != ISD::ZEXTLOAD)
return extendAtomicLoad(ALoad, VT, DAG, ISD::SEXTLOAD);

return SDValue();
}

Expand Down
Loading