Skip to content

Commit 645ce61

Browse files
alessandroddmakarov
authored andcommitted
[SOL] add support for (pseudo) atomics to SBF (rust-lang#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 4205085 commit 645ce61

File tree

4 files changed

+470
-12
lines changed

4 files changed

+470
-12
lines changed

llvm/lib/Target/BPF/BPFISelLowering.cpp

+181-12
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,30 @@ BPFTargetLowering::BPFTargetLowering(const TargetMachine &TM,
7979
setOperationAction(ISD::STACKSAVE, MVT::Other, Expand);
8080
setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand);
8181

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

213-
bool BPFTargetLowering::isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const {
233+
bool BPFTargetLowering::lowerAtomicStoreAsStoreSDNode(
234+
const StoreInst &SI) const {
235+
return Subtarget->isSolana();
236+
}
237+
238+
bool BPFTargetLowering::lowerAtomicLoadAsLoadSDNode(const LoadInst &LI) const {
239+
return Subtarget->isSolana();
240+
}
241+
242+
bool BPFTargetLowering::isOffsetFoldingLegal(
243+
const GlobalAddressSDNode *GA) const {
214244
return false;
215245
}
216246

@@ -280,19 +310,31 @@ BPFTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
280310
return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT);
281311
}
282312

283-
void BPFTargetLowering::ReplaceNodeResults(
284-
SDNode *N, SmallVectorImpl<SDValue> &Results, SelectionDAG &DAG) const {
313+
void BPFTargetLowering::ReplaceNodeResults(SDNode *N,
314+
SmallVectorImpl<SDValue> &Results,
315+
SelectionDAG &DAG) const {
285316
const char *err_msg;
286317
uint32_t Opcode = N->getOpcode();
287318
switch (Opcode) {
288319
default:
289320
report_fatal_error("Unhandled custom legalization");
321+
case ISD::ATOMIC_SWAP:
322+
case ISD::ATOMIC_CMP_SWAP_WITH_SUCCESS:
290323
case ISD::ATOMIC_LOAD_ADD:
291324
case ISD::ATOMIC_LOAD_AND:
325+
case ISD::ATOMIC_LOAD_MAX:
326+
case ISD::ATOMIC_LOAD_MIN:
327+
case ISD::ATOMIC_LOAD_NAND:
292328
case ISD::ATOMIC_LOAD_OR:
329+
case ISD::ATOMIC_LOAD_SUB:
330+
case ISD::ATOMIC_LOAD_UMAX:
331+
case ISD::ATOMIC_LOAD_UMIN:
293332
case ISD::ATOMIC_LOAD_XOR:
294-
case ISD::ATOMIC_SWAP:
295-
case ISD::ATOMIC_CMP_SWAP_WITH_SUCCESS:
333+
if (Subtarget->isSolana()) {
334+
// We do lowering during legalization, see LowerOperation()
335+
return;
336+
}
337+
296338
if (HasAlu32 || Opcode == ISD::ATOMIC_LOAD_ADD)
297339
err_msg = "Unsupported atomic operations, please use 32/64 bit version";
298340
else
@@ -312,10 +354,23 @@ SDValue BPFTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const {
312354
return LowerGlobalAddress(Op, DAG);
313355
case ISD::SELECT_CC:
314356
return LowerSELECT_CC(Op, DAG);
357+
case ISD::ATOMIC_SWAP:
358+
case ISD::ATOMIC_CMP_SWAP_WITH_SUCCESS:
359+
case ISD::ATOMIC_LOAD_ADD:
360+
case ISD::ATOMIC_LOAD_AND:
361+
case ISD::ATOMIC_LOAD_MAX:
362+
case ISD::ATOMIC_LOAD_MIN:
363+
case ISD::ATOMIC_LOAD_NAND:
364+
case ISD::ATOMIC_LOAD_OR:
365+
case ISD::ATOMIC_LOAD_SUB:
366+
case ISD::ATOMIC_LOAD_UMAX:
367+
case ISD::ATOMIC_LOAD_UMIN:
368+
case ISD::ATOMIC_LOAD_XOR:
369+
return LowerATOMICRMW(Op, DAG);
315370
case ISD::DYNAMIC_STACKALLOC:
316371
report_fatal_error("Unsupported dynamic stack allocation");
317372
default:
318-
llvm_unreachable("unimplemented operand");
373+
llvm_unreachable("unimplemented atomic operand");
319374
}
320375
}
321376

@@ -411,7 +466,6 @@ SDValue BPFTargetLowering::LowerFormalArguments(
411466
fail(DL, DAG, "functions with VarArgs or StructRet are not supported");
412467
}
413468

414-
415469
return Chain;
416470
}
417471

@@ -738,6 +792,114 @@ SDValue BPFTargetLowering::LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const {
738792
return DAG.getNode(BPFISD::SELECT_CC, DL, VTs, Ops);
739793
}
740794

795+
SDValue BPFTargetLowering::LowerATOMICRMW(SDValue Op, SelectionDAG &DAG) const {
796+
SDLoc DL(Op);
797+
AtomicSDNode *AN = cast<AtomicSDNode>(Op);
798+
assert(AN && "Expected custom lowering of an atomic load node");
799+
800+
SDValue Chain = AN->getChain();
801+
SDValue Ptr = AN->getBasePtr();
802+
EVT PtrVT = AN->getMemoryVT();
803+
EVT RetVT = Op.getValueType();
804+
805+
// Load the current value
806+
SDValue Load =
807+
DAG.getExtLoad(ISD::EXTLOAD, DL, RetVT, Chain, Ptr, MachinePointerInfo(),
808+
PtrVT, AN->getAlignment());
809+
Chain = Load.getValue(1);
810+
811+
// Most ops return the current value, except CMP_SWAP_WITH_SUCCESS see below
812+
SDValue Ret = Load;
813+
SDValue RetFlag;
814+
815+
// Val contains the new value we want to set. For CMP_SWAP, Cmp contains the
816+
// expected current value.
817+
SDValue Cmp, Val;
818+
if (AN->isCompareAndSwap()) {
819+
Cmp = Op.getOperand(2);
820+
Val = Op.getOperand(3);
821+
822+
// The Cmp value must match the pointer type
823+
EVT CmpVT = Cmp->getValueType(0);
824+
if (CmpVT != RetVT) {
825+
Cmp = RetVT.bitsGT(CmpVT) ? DAG.getNode(ISD::SIGN_EXTEND, DL, RetVT, Cmp)
826+
: DAG.getNode(ISD::TRUNCATE, DL, RetVT, Cmp);
827+
}
828+
} else {
829+
Val = AN->getVal();
830+
}
831+
832+
// The new value type must match the pointer type
833+
EVT ValVT = Val->getValueType(0);
834+
if (ValVT != RetVT) {
835+
Val = RetVT.bitsGT(ValVT) ? DAG.getNode(ISD::SIGN_EXTEND, DL, RetVT, Val)
836+
: DAG.getNode(ISD::TRUNCATE, DL, RetVT, Val);
837+
ValVT = Val->getValueType(0);
838+
}
839+
840+
SDValue NewVal;
841+
switch (Op.getOpcode()) {
842+
case ISD::ATOMIC_SWAP:
843+
NewVal = Val;
844+
break;
845+
case ISD::ATOMIC_CMP_SWAP_WITH_SUCCESS: {
846+
EVT RetFlagVT = AN->getValueType(1);
847+
NewVal = DAG.getSelectCC(DL, Load, Cmp, Val, Load, ISD::SETEQ);
848+
RetFlag = DAG.getSelectCC(
849+
DL, Load, Cmp, DAG.getBoolConstant(true, DL, RetFlagVT, RetFlagVT),
850+
DAG.getBoolConstant(false, DL, RetFlagVT, RetFlagVT), ISD::SETEQ);
851+
break;
852+
}
853+
case ISD::ATOMIC_LOAD_ADD:
854+
NewVal = DAG.getNode(ISD::ADD, DL, ValVT, Load, Val);
855+
break;
856+
case ISD::ATOMIC_LOAD_SUB:
857+
NewVal = DAG.getNode(ISD::SUB, DL, ValVT, Load, Val);
858+
break;
859+
case ISD::ATOMIC_LOAD_AND:
860+
NewVal = DAG.getNode(ISD::AND, DL, ValVT, Load, Val);
861+
break;
862+
case ISD::ATOMIC_LOAD_NAND: {
863+
NewVal =
864+
DAG.getNOT(DL, DAG.getNode(ISD::AND, DL, ValVT, Load, Val), ValVT);
865+
break;
866+
}
867+
case ISD::ATOMIC_LOAD_OR:
868+
NewVal = DAG.getNode(ISD::OR, DL, ValVT, Load, Val);
869+
break;
870+
case ISD::ATOMIC_LOAD_XOR:
871+
NewVal = DAG.getNode(ISD::XOR, DL, ValVT, Load, Val);
872+
break;
873+
case ISD::ATOMIC_LOAD_MIN:
874+
NewVal = DAG.getNode(ISD::SMIN, DL, ValVT, Load, Val);
875+
break;
876+
case ISD::ATOMIC_LOAD_UMIN:
877+
NewVal = DAG.getNode(ISD::UMIN, DL, ValVT, Load, Val);
878+
break;
879+
case ISD::ATOMIC_LOAD_MAX:
880+
NewVal = DAG.getNode(ISD::SMAX, DL, ValVT, Load, Val);
881+
break;
882+
case ISD::ATOMIC_LOAD_UMAX:
883+
NewVal = DAG.getNode(ISD::UMAX, DL, ValVT, Load, Val);
884+
break;
885+
default:
886+
llvm_unreachable("unknown atomicrmw op");
887+
}
888+
889+
Chain =
890+
DAG.getTruncStore(Chain, DL, NewVal, Ptr, MachinePointerInfo(), PtrVT);
891+
892+
if (RetFlag) {
893+
// CMP_SWAP_WITH_SUCCESS returns {value, success, chain}
894+
Ret = DAG.getMergeValues({Ret, RetFlag, Chain}, DL);
895+
} else {
896+
// All the other ops return {value, chain}
897+
Ret = DAG.getMergeValues({Ret, Chain}, DL);
898+
}
899+
900+
return Ret;
901+
}
902+
741903
const char *BPFTargetLowering::getTargetNodeName(unsigned Opcode) const {
742904
switch ((BPFISD::NodeType)Opcode) {
743905
case BPFISD::FIRST_NUMBER:
@@ -841,6 +1003,7 @@ BPFTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
8411003
Opc == BPF::Select_32_64);
8421004

8431005
bool isMemcpyOp = Opc == BPF::MEMCPY;
1006+
bool isAtomicFence = Opc == BPF::ATOMIC_FENCE;
8441007

8451008
#ifndef NDEBUG
8461009
bool isSelectRIOp = (Opc == BPF::Select_Ri ||
@@ -849,13 +1012,19 @@ BPFTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
8491012
Opc == BPF::Select_Ri_32_64);
8501013

8511014

852-
assert((isSelectRROp || isSelectRIOp || isMemcpyOp) &&
1015+
assert((isSelectRROp || isSelectRIOp || isMemcpyOp || isAtomicFence) &&
8531016
"Unexpected instr type to insert");
8541017
#endif
8551018

8561019
if (isMemcpyOp)
8571020
return EmitInstrWithCustomInserterMemcpy(MI, BB);
8581021

1022+
if (isAtomicFence) {
1023+
// this is currently a nop
1024+
MI.eraseFromParent();
1025+
return BB;
1026+
}
1027+
8591028
bool is32BitCmp = (Opc == BPF::Select_32 ||
8601029
Opc == BPF::Select_32_64 ||
8611030
Opc == BPF::Select_Ri_32 ||

llvm/lib/Target/BPF/BPFISelLowering.h

+4
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;
@@ -80,6 +83,7 @@ class BPFTargetLowering : public TargetLowering {
8083
SDValue LowerBR_CC(SDValue Op, SelectionDAG &DAG) const;
8184
SDValue LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const;
8285
SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const;
86+
SDValue LowerATOMICRMW(SDValue Op, SelectionDAG &DAG) const;
8387

8488
// Lower the result values of a call, copying them out of physregs into vregs
8589
SDValue LowerCallResult(SDValue Chain, SDValue InFlag,

llvm/lib/Target/BPF/BPFInstrInfo.td

+9
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ def BPFIsLittleEndian : Predicate<"CurDAG->getDataLayout().isLittleEndian()">;
5353
def BPFIsBigEndian : Predicate<"!CurDAG->getDataLayout().isLittleEndian()">;
5454
def BPFHasALU32 : Predicate<"Subtarget->getHasAlu32()">;
5555
def BPFNoALU32 : Predicate<"!Subtarget->getHasAlu32()">;
56+
def BPFSubtargetSolana : Predicate<"Subtarget->isSolana()">;
5657

5758
def brtarget : Operand<OtherVT> {
5859
let PrintMethod = "printBrTargetOperand";
@@ -745,6 +746,14 @@ def : Pat<(atomic_load_sub_32 ADDRri:$addr, GPR32:$val),
745746
def : Pat<(atomic_load_sub_64 ADDRri:$addr, GPR:$val),
746747
(XFADDD ADDRri:$addr, (NEG_64 GPR:$val))>;
747748

749+
let Predicates = [BPFSubtargetSolana], usesCustomInserter = 1, isCodeGenOnly = 1 in {
750+
def ATOMIC_FENCE : Pseudo<
751+
(outs),
752+
(ins),
753+
"#atomic_fence",
754+
[(atomic_fence timm, timm)]>;
755+
}
756+
748757
// Atomic Exchange
749758
class XCHG<BPFWidthModifer SizeOp, string OpcodeStr, PatFrag OpNode>
750759
: TYPE_LD_ST<BPF_ATOMIC.Value, SizeOp.Value,

0 commit comments

Comments
 (0)