Skip to content

Commit 2d40886

Browse files
alessandrodLucasSte
authored andcommitted
[SOL] add support for (pseudo) atomics to SBF (#23)
Lower atomic operations to their regular non-atomic equivalents. Lowering for all operations except atomic fence is done at DAG legalization time. Fences are removed at instruction emission time.
1 parent a664dc8 commit 2d40886

File tree

4 files changed

+471
-6
lines changed

4 files changed

+471
-6
lines changed

llvm/lib/Target/BPF/BPFISelLowering.cpp

Lines changed: 182 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,30 @@ BPFTargetLowering::BPFTargetLowering(const TargetMachine &TM,
8181
setOperationAction(ISD::STACKSAVE, MVT::Other, Expand);
8282
setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand);
8383

84-
// Set unsupported atomic operations as Custom so
85-
// we can emit better error messages than fatal error
86-
// from selectiondag.
87-
for (auto VT : {MVT::i8, MVT::i16, MVT::i32}) {
84+
for (auto VT : {MVT::i8, MVT::i16, MVT::i32, MVT::i32, MVT::i64}) {
85+
if (Subtarget->isSolana()) {
86+
// Implement custom lowering for all atomic operations
87+
setOperationAction(ISD::ATOMIC_SWAP, VT, Custom);
88+
setOperationAction(ISD::ATOMIC_CMP_SWAP_WITH_SUCCESS, VT, Custom);
89+
setOperationAction(ISD::ATOMIC_LOAD_ADD, VT, Custom);
90+
setOperationAction(ISD::ATOMIC_LOAD_AND, VT, Custom);
91+
setOperationAction(ISD::ATOMIC_LOAD_MAX, VT, Custom);
92+
setOperationAction(ISD::ATOMIC_LOAD_MIN, VT, Custom);
93+
setOperationAction(ISD::ATOMIC_LOAD_NAND, VT, Custom);
94+
setOperationAction(ISD::ATOMIC_LOAD_OR, VT, Custom);
95+
setOperationAction(ISD::ATOMIC_LOAD_SUB, VT, Custom);
96+
setOperationAction(ISD::ATOMIC_LOAD_UMAX, VT, Custom);
97+
setOperationAction(ISD::ATOMIC_LOAD_UMIN, VT, Custom);
98+
setOperationAction(ISD::ATOMIC_LOAD_XOR, VT, Custom);
99+
continue;
100+
}
101+
102+
if (VT == MVT::i64) {
103+
continue;
104+
}
105+
106+
// Set unsupported atomic operations as Custom so we can emit better error
107+
// messages than fatal error from selectiondag.
88108
if (VT == MVT::i32) {
89109
if (STI.getHasAlu32())
90110
continue;
@@ -222,7 +242,17 @@ bool BPFTargetLowering::allowsMisalignedMemoryAccesses(
222242
return isSolana;
223243
}
224244

225-
bool BPFTargetLowering::isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const {
245+
bool BPFTargetLowering::lowerAtomicStoreAsStoreSDNode(
246+
const StoreInst &SI) const {
247+
return Subtarget->isSolana();
248+
}
249+
250+
bool BPFTargetLowering::lowerAtomicLoadAsLoadSDNode(const LoadInst &LI) const {
251+
return Subtarget->isSolana();
252+
}
253+
254+
bool BPFTargetLowering::isOffsetFoldingLegal(
255+
const GlobalAddressSDNode *GA) const {
226256
return false;
227257
}
228258

@@ -318,6 +348,21 @@ void BPFTargetLowering::ReplaceNodeResults(
318348
case ISD::ATOMIC_LOAD_XOR:
319349
case ISD::ATOMIC_SWAP:
320350
case ISD::ATOMIC_CMP_SWAP_WITH_SUCCESS:
351+
case ISD::ATOMIC_LOAD_ADD:
352+
case ISD::ATOMIC_LOAD_AND:
353+
case ISD::ATOMIC_LOAD_MAX:
354+
case ISD::ATOMIC_LOAD_MIN:
355+
case ISD::ATOMIC_LOAD_NAND:
356+
case ISD::ATOMIC_LOAD_OR:
357+
case ISD::ATOMIC_LOAD_SUB:
358+
case ISD::ATOMIC_LOAD_UMAX:
359+
case ISD::ATOMIC_LOAD_UMIN:
360+
case ISD::ATOMIC_LOAD_XOR:
361+
if (Subtarget->isSolana()) {
362+
// We do lowering during legalization, see LowerOperation()
363+
return;
364+
}
365+
321366
if (HasAlu32 || Opcode == ISD::ATOMIC_LOAD_ADD)
322367
Msg = "unsupported atomic operation, please use 32/64 bit version";
323368
else
@@ -346,6 +391,23 @@ SDValue BPFTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const {
346391
return LowerSDIVSREM(Op, DAG);
347392
case ISD::DYNAMIC_STACKALLOC:
348393
return LowerDYNAMIC_STACKALLOC(Op, DAG);
394+
case ISD::ATOMIC_SWAP:
395+
case ISD::ATOMIC_CMP_SWAP_WITH_SUCCESS:
396+
case ISD::ATOMIC_LOAD_ADD:
397+
case ISD::ATOMIC_LOAD_AND:
398+
case ISD::ATOMIC_LOAD_MAX:
399+
case ISD::ATOMIC_LOAD_MIN:
400+
case ISD::ATOMIC_LOAD_NAND:
401+
case ISD::ATOMIC_LOAD_OR:
402+
case ISD::ATOMIC_LOAD_SUB:
403+
case ISD::ATOMIC_LOAD_UMAX:
404+
case ISD::ATOMIC_LOAD_UMIN:
405+
case ISD::ATOMIC_LOAD_XOR:
406+
return LowerATOMICRMW(Op, DAG);
407+
case ISD::DYNAMIC_STACKALLOC:
408+
report_fatal_error("Unsupported dynamic stack allocation");
409+
default:
410+
llvm_unreachable("unimplemented atomic operand");
349411
}
350412
}
351413

@@ -444,7 +506,6 @@ SDValue BPFTargetLowering::LowerFormalArguments(
444506
fail(DL, DAG, "functions with VarArgs or StructRet are not supported");
445507
}
446508

447-
448509
return Chain;
449510
}
450511

@@ -788,6 +849,114 @@ SDValue BPFTargetLowering::LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const {
788849
return DAG.getNode(BPFISD::SELECT_CC, DL, VTs, Ops);
789850
}
790851

852+
SDValue BPFTargetLowering::LowerATOMICRMW(SDValue Op, SelectionDAG &DAG) const {
853+
SDLoc DL(Op);
854+
AtomicSDNode *AN = cast<AtomicSDNode>(Op);
855+
assert(AN && "Expected custom lowering of an atomic load node");
856+
857+
SDValue Chain = AN->getChain();
858+
SDValue Ptr = AN->getBasePtr();
859+
EVT PtrVT = AN->getMemoryVT();
860+
EVT RetVT = Op.getValueType();
861+
862+
// Load the current value
863+
SDValue Load =
864+
DAG.getExtLoad(ISD::EXTLOAD, DL, RetVT, Chain, Ptr, MachinePointerInfo(),
865+
PtrVT, AN->getAlignment());
866+
Chain = Load.getValue(1);
867+
868+
// Most ops return the current value, except CMP_SWAP_WITH_SUCCESS see below
869+
SDValue Ret = Load;
870+
SDValue RetFlag;
871+
872+
// Val contains the new value we want to set. For CMP_SWAP, Cmp contains the
873+
// expected current value.
874+
SDValue Cmp, Val;
875+
if (AN->isCompareAndSwap()) {
876+
Cmp = Op.getOperand(2);
877+
Val = Op.getOperand(3);
878+
879+
// The Cmp value must match the pointer type
880+
EVT CmpVT = Cmp->getValueType(0);
881+
if (CmpVT != RetVT) {
882+
Cmp = RetVT.bitsGT(CmpVT) ? DAG.getNode(ISD::SIGN_EXTEND, DL, RetVT, Cmp)
883+
: DAG.getNode(ISD::TRUNCATE, DL, RetVT, Cmp);
884+
}
885+
} else {
886+
Val = AN->getVal();
887+
}
888+
889+
// The new value type must match the pointer type
890+
EVT ValVT = Val->getValueType(0);
891+
if (ValVT != RetVT) {
892+
Val = RetVT.bitsGT(ValVT) ? DAG.getNode(ISD::SIGN_EXTEND, DL, RetVT, Val)
893+
: DAG.getNode(ISD::TRUNCATE, DL, RetVT, Val);
894+
ValVT = Val->getValueType(0);
895+
}
896+
897+
SDValue NewVal;
898+
switch (Op.getOpcode()) {
899+
case ISD::ATOMIC_SWAP:
900+
NewVal = Val;
901+
break;
902+
case ISD::ATOMIC_CMP_SWAP_WITH_SUCCESS: {
903+
EVT RetFlagVT = AN->getValueType(1);
904+
NewVal = DAG.getSelectCC(DL, Load, Cmp, Val, Load, ISD::SETEQ);
905+
RetFlag = DAG.getSelectCC(
906+
DL, Load, Cmp, DAG.getBoolConstant(true, DL, RetFlagVT, RetFlagVT),
907+
DAG.getBoolConstant(false, DL, RetFlagVT, RetFlagVT), ISD::SETEQ);
908+
break;
909+
}
910+
case ISD::ATOMIC_LOAD_ADD:
911+
NewVal = DAG.getNode(ISD::ADD, DL, ValVT, Load, Val);
912+
break;
913+
case ISD::ATOMIC_LOAD_SUB:
914+
NewVal = DAG.getNode(ISD::SUB, DL, ValVT, Load, Val);
915+
break;
916+
case ISD::ATOMIC_LOAD_AND:
917+
NewVal = DAG.getNode(ISD::AND, DL, ValVT, Load, Val);
918+
break;
919+
case ISD::ATOMIC_LOAD_NAND: {
920+
NewVal =
921+
DAG.getNOT(DL, DAG.getNode(ISD::AND, DL, ValVT, Load, Val), ValVT);
922+
break;
923+
}
924+
case ISD::ATOMIC_LOAD_OR:
925+
NewVal = DAG.getNode(ISD::OR, DL, ValVT, Load, Val);
926+
break;
927+
case ISD::ATOMIC_LOAD_XOR:
928+
NewVal = DAG.getNode(ISD::XOR, DL, ValVT, Load, Val);
929+
break;
930+
case ISD::ATOMIC_LOAD_MIN:
931+
NewVal = DAG.getNode(ISD::SMIN, DL, ValVT, Load, Val);
932+
break;
933+
case ISD::ATOMIC_LOAD_UMIN:
934+
NewVal = DAG.getNode(ISD::UMIN, DL, ValVT, Load, Val);
935+
break;
936+
case ISD::ATOMIC_LOAD_MAX:
937+
NewVal = DAG.getNode(ISD::SMAX, DL, ValVT, Load, Val);
938+
break;
939+
case ISD::ATOMIC_LOAD_UMAX:
940+
NewVal = DAG.getNode(ISD::UMAX, DL, ValVT, Load, Val);
941+
break;
942+
default:
943+
llvm_unreachable("unknown atomicrmw op");
944+
}
945+
946+
Chain =
947+
DAG.getTruncStore(Chain, DL, NewVal, Ptr, MachinePointerInfo(), PtrVT);
948+
949+
if (RetFlag) {
950+
// CMP_SWAP_WITH_SUCCESS returns {value, success, chain}
951+
Ret = DAG.getMergeValues({Ret, RetFlag, Chain}, DL);
952+
} else {
953+
// All the other ops return {value, chain}
954+
Ret = DAG.getMergeValues({Ret, Chain}, DL);
955+
}
956+
957+
return Ret;
958+
}
959+
791960
const char *BPFTargetLowering::getTargetNodeName(unsigned Opcode) const {
792961
switch ((BPFISD::NodeType)Opcode) {
793962
case BPFISD::FIRST_NUMBER:
@@ -897,6 +1066,7 @@ BPFTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
8971066
Opc == BPF::Select_32_64);
8981067

8991068
bool isMemcpyOp = Opc == BPF::MEMCPY;
1069+
bool isAtomicFence = Opc == BPF::ATOMIC_FENCE;
9001070

9011071
#ifndef NDEBUG
9021072
bool isSelectRIOp = (Opc == BPF::Select_Ri ||
@@ -911,6 +1081,12 @@ BPFTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
9111081
if (isMemcpyOp)
9121082
return EmitInstrWithCustomInserterMemcpy(MI, BB);
9131083

1084+
if (isAtomicFence) {
1085+
// this is currently a nop
1086+
MI.eraseFromParent();
1087+
return BB;
1088+
}
1089+
9141090
bool is32BitCmp = (Opc == BPF::Select_32 ||
9151091
Opc == BPF::Select_32_64 ||
9161092
Opc == BPF::Select_Ri_32 ||

llvm/lib/Target/BPF/BPFISelLowering.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ class BPFTargetLowering : public TargetLowering {
7171

7272
MVT getScalarShiftAmountTy(const DataLayout &, EVT) const override;
7373

74+
bool lowerAtomicStoreAsStoreSDNode(const StoreInst &SI) const override;
75+
bool lowerAtomicLoadAsLoadSDNode(const LoadInst &LI) const override;
76+
7477
private:
7578
// Control Instruction Selection Features
7679
bool HasAlu32;
@@ -83,6 +86,7 @@ class BPFTargetLowering : public TargetLowering {
8386
SDValue LowerBR_CC(SDValue Op, SelectionDAG &DAG) const;
8487
SDValue LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const;
8588
SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const;
89+
SDValue LowerATOMICRMW(SDValue Op, SelectionDAG &DAG) const;
8690

8791
// Lower the result values of a call, copying them out of physregs into vregs
8892
SDValue LowerCallResult(SDValue Chain, SDValue InGlue,

llvm/lib/Target/BPF/BPFInstrInfo.td

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ class ImmediateAsmOperand<string name> : AsmOperandClass {
6868
}
6969

7070
def SImm16AsmOperand : ImmediateAsmOperand<"SImm16">;
71+
def BPFSubtargetSolana : Predicate<"Subtarget->isSolana()">;
7172

7273
def brtarget : Operand<OtherVT> {
7374
let PrintMethod = "printBrTargetOperand";
@@ -873,6 +874,14 @@ def : Pat<(atomic_load_sub_32 ADDRri:$addr, GPR32:$val),
873874
def : Pat<(atomic_load_sub_64 ADDRri:$addr, GPR:$val),
874875
(XFADDD ADDRri:$addr, (NEG_64 GPR:$val))>;
875876

877+
let Predicates = [BPFSubtargetSolana], usesCustomInserter = 1, isCodeGenOnly = 1 in {
878+
def ATOMIC_FENCE : Pseudo<
879+
(outs),
880+
(ins),
881+
"#atomic_fence",
882+
[(atomic_fence timm, timm)]>;
883+
}
884+
876885
// Atomic Exchange
877886
class XCHG<BPFWidthModifer SizeOp, string OpcodeStr, PatFrag OpNode>
878887
: TYPE_LD_ST<BPF_ATOMIC.Value, SizeOp.Value,

0 commit comments

Comments
 (0)