Skip to content
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

[BPF] Do atomic_fetch_*() pattern matching with memory ordering #107343

Merged
merged 3 commits into from
Sep 24, 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
1 change: 1 addition & 0 deletions clang/lib/Basic/Targets/BPF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ void BPFTargetInfo::getTargetDefines(const LangOptions &Opts,

Builder.defineMacro("__BPF_FEATURE_ADDR_SPACE_CAST");
Builder.defineMacro("__BPF_FEATURE_MAY_GOTO");
Builder.defineMacro("__BPF_FEATURE_ATOMIC_MEM_ORDERING");

if (CPU.empty())
CPU = "v3";
Expand Down
6 changes: 5 additions & 1 deletion clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1249,8 +1249,12 @@ llvm::DIType *CGDebugInfo::CreatePointerLikeType(llvm::dwarf::Tag Tag,
CGM.getTarget().getDWARFAddressSpace(
CGM.getTypes().getTargetAddressSpace(PointeeTy));

const BTFTagAttributedType *BTFAttrTy;
if (auto *Atomic = PointeeTy->getAs<AtomicType>())
BTFAttrTy = dyn_cast<BTFTagAttributedType>(Atomic->getValueType());
else
BTFAttrTy = dyn_cast<BTFTagAttributedType>(PointeeTy);
SmallVector<llvm::Metadata *, 4> Annots;
auto *BTFAttrTy = dyn_cast<BTFTagAttributedType>(PointeeTy);
while (BTFAttrTy) {
StringRef Tag = BTFAttrTy->getAttr()->getBTFTypeTag();
if (!Tag.empty()) {
Expand Down
16 changes: 16 additions & 0 deletions clang/test/CodeGen/bpf-attr-type-tag-atomic.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// REQUIRES: bpf-registered-target
// RUN: %clang_cc1 -triple bpf -emit-llvm -disable-llvm-passes -debug-info-kind=limited %s -o - | FileCheck %s

#define __tag1 __attribute__((btf_type_tag("tag1")))
int _Atomic __tag1 *g1;
volatile int _Atomic __tag1 *g2;

// CHECK: distinct !DIGlobalVariable(name: "g1", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[PTR1:[0-9]+]]
// CHECK: distinct !DIGlobalVariable(name: "g2", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[PTR2:[0-9]+]]
// CHECK: ![[PTR2]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[BASE2:[0-9]+]], size: [[#]], annotations: ![[ANNOT:[0-9]+]])
// CHECK: ![[BASE2]] = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: ![[BASE1:[0-9]+]])
// CHECK: ![[BASE1]] = !DIDerivedType(tag: DW_TAG_atomic_type, baseType: ![[BASIC:[0-9]+]])
// CHECK: ![[BASIC]] = !DIBasicType(name: "int", size: [[#]], encoding: DW_ATE_signed)
// CHECK: ![[ANNOT]] = !{![[ENTRY:[0-9]+]]}
// CHECK: ![[ENTRY]] = !{!"btf_type_tag", !"tag1"}
// CHECK: ![[PTR1]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[BASE1]], size: [[#]], annotations: ![[ANNOT]])
134 changes: 114 additions & 20 deletions llvm/lib/Target/BPF/BPFInstrInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -826,13 +826,12 @@ let Predicates = [BPFNoALU32] in {
}

// Atomic Fetch-and-<add, and, or, xor> operations
class XFALU64<BPFWidthModifer SizeOp, BPFArithOp Opc, string OpcodeStr,
string OpcStr, PatFrag OpNode>
class XFALU64<BPFWidthModifer SizeOp, BPFArithOp Opc, string OpcodeStr, string OpcStr>
: TYPE_LD_ST<BPF_ATOMIC.Value, SizeOp.Value,
(outs GPR:$dst),
(ins MEMri:$addr, GPR:$val),
"$dst = atomic_fetch_"#OpcStr#"(("#OpcodeStr#" *)($addr), $val)",
[(set GPR:$dst, (OpNode ADDRri:$addr, GPR:$val))]> {
[]> {
bits<4> dst;
bits<20> addr;

Expand All @@ -844,13 +843,12 @@ class XFALU64<BPFWidthModifer SizeOp, BPFArithOp Opc, string OpcodeStr,
let BPFClass = BPF_STX;
}

class XFALU32<BPFWidthModifer SizeOp, BPFArithOp Opc, string OpcodeStr,
string OpcStr, PatFrag OpNode>
class XFALU32<BPFWidthModifer SizeOp, BPFArithOp Opc, string OpcodeStr, string OpcStr>
: TYPE_LD_ST<BPF_ATOMIC.Value, SizeOp.Value,
(outs GPR32:$dst),
(ins MEMri:$addr, GPR32:$val),
"$dst = atomic_fetch_"#OpcStr#"(("#OpcodeStr#" *)($addr), $val)",
[(set GPR32:$dst, (OpNode ADDRri:$addr, GPR32:$val))]> {
[]> {
bits<4> dst;
bits<20> addr;

Expand All @@ -864,26 +862,122 @@ class XFALU32<BPFWidthModifer SizeOp, BPFArithOp Opc, string OpcodeStr,

let Constraints = "$dst = $val" in {
let Predicates = [BPFHasALU32], DecoderNamespace = "BPFALU32" in {
def XFADDW32 : XFALU32<BPF_W, BPF_ADD, "u32", "add", atomic_load_add_i32>;
def XFANDW32 : XFALU32<BPF_W, BPF_AND, "u32", "and", atomic_load_and_i32>;
def XFORW32 : XFALU32<BPF_W, BPF_OR, "u32", "or", atomic_load_or_i32>;
def XFXORW32 : XFALU32<BPF_W, BPF_XOR, "u32", "xor", atomic_load_xor_i32>;
def XFADDW32 : XFALU32<BPF_W, BPF_ADD, "u32", "add">;
def XFANDW32 : XFALU32<BPF_W, BPF_AND, "u32", "and">;
def XFORW32 : XFALU32<BPF_W, BPF_OR, "u32", "or">;
def XFXORW32 : XFALU32<BPF_W, BPF_XOR, "u32", "xor">;
}

let Predicates = [BPFHasALU32] in {
def XFADDD : XFALU64<BPF_DW, BPF_ADD, "u64", "add", atomic_load_add_i64>;
def XFADDD : XFALU64<BPF_DW, BPF_ADD, "u64", "add">;
}
def XFANDD : XFALU64<BPF_DW, BPF_AND, "u64", "and", atomic_load_and_i64>;
def XFORD : XFALU64<BPF_DW, BPF_OR, "u64", "or", atomic_load_or_i64>;
def XFXORD : XFALU64<BPF_DW, BPF_XOR, "u64", "xor", atomic_load_xor_i64>;
def XFANDD : XFALU64<BPF_DW, BPF_AND, "u64", "and">;
def XFORD : XFALU64<BPF_DW, BPF_OR, "u64", "or">;
def XFXORD : XFALU64<BPF_DW, BPF_XOR, "u64", "xor">;
}

// atomic_load_sub can be represented as a neg followed
// by an atomic_load_add.
def : Pat<(atomic_load_sub_i32 ADDRri:$addr, GPR32:$val),
(XFADDW32 ADDRri:$addr, (NEG_32 GPR32:$val))>;
def : Pat<(atomic_load_sub_i64 ADDRri:$addr, GPR:$val),
(XFADDD ADDRri:$addr, (NEG_64 GPR:$val))>;
let Predicates = [BPFHasALU32] in {
foreach P = [// add
[atomic_load_add_i32_monotonic, XADDW32],
[atomic_load_add_i32_acquire, XFADDW32],
[atomic_load_add_i32_release, XFADDW32],
[atomic_load_add_i32_acq_rel, XFADDW32],
[atomic_load_add_i32_seq_cst, XFADDW32],
// and
[atomic_load_and_i32_monotonic, XANDW32],
[atomic_load_and_i32_acquire, XFANDW32],
[atomic_load_and_i32_release, XFANDW32],
[atomic_load_and_i32_acq_rel, XFANDW32],
[atomic_load_and_i32_seq_cst, XFANDW32],
// or
[atomic_load_or_i32_monotonic, XORW32],
[atomic_load_or_i32_acquire, XFORW32],
[atomic_load_or_i32_release, XFORW32],
[atomic_load_or_i32_acq_rel, XFORW32],
[atomic_load_or_i32_seq_cst, XFORW32],
// xor
[atomic_load_xor_i32_monotonic, XXORW32],
[atomic_load_xor_i32_acquire, XFXORW32],
[atomic_load_xor_i32_release, XFXORW32],
[atomic_load_xor_i32_acq_rel, XFXORW32],
[atomic_load_xor_i32_seq_cst, XFXORW32],
] in {
def : Pat<(P[0] ADDRri:$addr, GPR32:$val), (P[1] ADDRri:$addr, GPR32:$val)>;
}

// atomic_load_sub can be represented as a neg followed
// by an atomic_load_add.
foreach P = [[atomic_load_sub_i32_monotonic, XADDW32],
[atomic_load_sub_i32_acquire, XFADDW32],
[atomic_load_sub_i32_release, XFADDW32],
[atomic_load_sub_i32_acq_rel, XFADDW32],
[atomic_load_sub_i32_seq_cst, XFADDW32],
] in {
def : Pat<(P[0] ADDRri:$addr, GPR32:$val), (P[1] ADDRri:$addr, (NEG_32 GPR32:$val))>;
}

foreach P = [// add
[atomic_load_add_i64_monotonic, XADDD],
[atomic_load_add_i64_acquire, XFADDD],
[atomic_load_add_i64_release, XFADDD],
[atomic_load_add_i64_acq_rel, XFADDD],
[atomic_load_add_i64_seq_cst, XFADDD],
] in {
def : Pat<(P[0] ADDRri:$addr, GPR:$val), (P[1] ADDRri:$addr, GPR:$val)>;
}
}

foreach P = [[atomic_load_sub_i64_monotonic, XADDD],
[atomic_load_sub_i64_acquire, XFADDD],
[atomic_load_sub_i64_release, XFADDD],
[atomic_load_sub_i64_acq_rel, XFADDD],
[atomic_load_sub_i64_seq_cst, XFADDD],
] in {
def : Pat<(P[0] ADDRri:$addr, GPR:$val), (P[1] ADDRri:$addr, (NEG_64 GPR:$val))>;
}

// Borrow the idea from X86InstrFragments.td
class binop_no_use<SDPatternOperator operator>
: PatFrag<(ops node:$A, node:$B),
(operator node:$A, node:$B),
[{ return SDValue(N, 0).use_empty(); }]>;

class binop_has_use<SDPatternOperator operator>
: PatFrag<(ops node:$A, node:$B),
(operator node:$A, node:$B),
[{ return !SDValue(N, 0).use_empty(); }]>;

foreach op = [add, and, or, xor] in {
def atomic_load_ # op # _i64_monotonic_nu:
binop_no_use <!cast<SDPatternOperator>("atomic_load_"#op# _i64_monotonic)>;
def atomic_load_ # op # _i64_monotonic_hu:
binop_has_use<!cast<SDPatternOperator>("atomic_load_"#op# _i64_monotonic)>;
}

foreach P = [// and
[atomic_load_and_i64_monotonic_nu, XANDD],
[atomic_load_and_i64_monotonic_hu, XFANDD],
[atomic_load_and_i64_acquire, XFANDD],
[atomic_load_and_i64_release, XFANDD],
[atomic_load_and_i64_acq_rel, XFANDD],
[atomic_load_and_i64_seq_cst, XFANDD],
// or
[atomic_load_or_i64_monotonic_nu, XORD],
[atomic_load_or_i64_monotonic_hu, XFORD],
[atomic_load_or_i64_acquire, XFORD],
[atomic_load_or_i64_release, XFORD],
[atomic_load_or_i64_acq_rel, XFORD],
[atomic_load_or_i64_seq_cst, XFORD],
// xor
[atomic_load_xor_i64_monotonic_nu, XXORD],
[atomic_load_xor_i64_monotonic_hu, XFXORD],
[atomic_load_xor_i64_acquire, XFXORD],
[atomic_load_xor_i64_release, XFXORD],
[atomic_load_xor_i64_acq_rel, XFXORD],
[atomic_load_xor_i64_seq_cst, XFXORD],
] in {
def : Pat<(P[0] ADDRri:$addr, GPR:$val), (P[1] ADDRri:$addr, GPR:$val)>;
}

// Atomic Exchange
class XCHG<BPFWidthModifer SizeOp, string OpcodeStr, PatFrag OpNode>
Expand Down
6 changes: 5 additions & 1 deletion llvm/lib/Target/BPF/BPFMIChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ static bool hasLiveDefs(const MachineInstr &MI, const TargetRegisterInfo *TRI) {

RegIsGPR64 = GPR64RegClass->contains(MO.getReg());
if (!MO.isDead()) {
// It is a GPR64 live Def, we are sure it is live. */
// It is a GPR64 live Def, we are sure it is live.
if (RegIsGPR64)
return true;
// It is a GPR32 live Def, we are unsure whether it is really dead due to
Expand Down Expand Up @@ -153,6 +153,10 @@ static bool hasLiveDefs(const MachineInstr &MI, const TargetRegisterInfo *TRI) {
}

void BPFMIPreEmitChecking::processAtomicInsts() {
if (MF->getSubtarget<BPFSubtarget>().getHasJmp32())
return;

// Only check for cpu version 1 and 2.
for (MachineBasicBlock &MBB : *MF) {
for (MachineInstr &MI : MBB) {
if (MI.getOpcode() != BPF::XADDW && MI.getOpcode() != BPF::XADDD)
Expand Down
29 changes: 22 additions & 7 deletions llvm/lib/Target/BPF/BTFDebug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ static const char *BTFKindStr[] = {
#include "llvm/DebugInfo/BTF/BTF.def"
};

static const DIType *tryRemoveAtomicType(const DIType *Ty) {
eddyz87 marked this conversation as resolved.
Show resolved Hide resolved
if (!Ty)
return Ty;
auto DerivedTy = dyn_cast<DIDerivedType>(Ty);
if (DerivedTy && DerivedTy->getTag() == dwarf::DW_TAG_atomic_type)
return DerivedTy->getBaseType();
return Ty;
}

/// Emit a BTF common type.
void BTFTypeBase::emitType(MCStreamer &OS) {
OS.AddComment(std::string(BTFKindStr[Kind]) + "(id = " + std::to_string(Id) +
Expand Down Expand Up @@ -90,7 +99,7 @@ void BTFTypeDerived::completeType(BTFDebug &BDebug) {
return;

// The base type for PTR/CONST/VOLATILE could be void.
const DIType *ResolvedType = DTy->getBaseType();
const DIType *ResolvedType = tryRemoveAtomicType(DTy->getBaseType());
if (!ResolvedType) {
assert((Kind == BTF::BTF_KIND_PTR || Kind == BTF::BTF_KIND_CONST ||
Kind == BTF::BTF_KIND_VOLATILE) &&
Expand Down Expand Up @@ -305,7 +314,7 @@ void BTFTypeStruct::completeType(BTFDebug &BDebug) {
} else {
BTFMember.Offset = DDTy->getOffsetInBits();
}
const auto *BaseTy = DDTy->getBaseType();
const auto *BaseTy = tryRemoveAtomicType(DDTy->getBaseType());
BTFMember.Type = BDebug.getTypeId(BaseTy);
Members.push_back(BTFMember);
}
Expand Down Expand Up @@ -342,15 +351,15 @@ void BTFTypeFuncProto::completeType(BTFDebug &BDebug) {
IsCompleted = true;

DITypeRefArray Elements = STy->getTypeArray();
auto RetType = Elements[0];
auto RetType = tryRemoveAtomicType(Elements[0]);
BTFType.Type = RetType ? BDebug.getTypeId(RetType) : 0;
BTFType.NameOff = 0;

// For null parameter which is typically the last one
// to represent the vararg, encode the NameOff/Type to be 0.
for (unsigned I = 1, N = Elements.size(); I < N; ++I) {
struct BTF::BTFParam Param;
auto Element = Elements[I];
auto Element = tryRemoveAtomicType(Elements[I]);
if (Element) {
Param.NameOff = BDebug.addString(FuncArgNames[I]);
Param.Type = BDebug.getTypeId(Element);
Expand Down Expand Up @@ -483,7 +492,7 @@ void BTFTypeTypeTag::completeType(BTFDebug &BDebug) {
IsCompleted = true;
BTFType.NameOff = BDebug.addString(Tag);
if (DTy) {
const DIType *ResolvedType = DTy->getBaseType();
const DIType *ResolvedType = tryRemoveAtomicType(DTy->getBaseType());
if (!ResolvedType)
BTFType.Type = 0;
else
Expand Down Expand Up @@ -800,6 +809,10 @@ void BTFDebug::visitDerivedType(const DIDerivedType *DTy, uint32_t &TypeId,
bool CheckPointer, bool SeenPointer) {
unsigned Tag = DTy->getTag();

if (Tag == dwarf::DW_TAG_atomic_type)
return visitTypeEntry(DTy->getBaseType(), TypeId, CheckPointer,
SeenPointer);

/// Try to avoid chasing pointees, esp. structure pointees which may
/// unnecessary bring in a lot of types.
if (CheckPointer && !SeenPointer) {
Expand Down Expand Up @@ -1444,8 +1457,10 @@ void BTFDebug::processGlobals(bool ProcessingMapDef) {
DIGlobal = GVE->getVariable();
if (SecName.starts_with(".maps"))
visitMapDefType(DIGlobal->getType(), GVTypeId);
else
visitTypeEntry(DIGlobal->getType(), GVTypeId, false, false);
else {
const DIType *Ty = tryRemoveAtomicType(DIGlobal->getType());
visitTypeEntry(Ty, GVTypeId, false, false);
}
break;
}

Expand Down
Loading
Loading