Skip to content

[Transforms] Introduce BuildBuiltins.h atomic helpers #134455

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
80 changes: 80 additions & 0 deletions llvm/include/llvm/Analysis/TargetLibraryInfo.def
Original file line number Diff line number Diff line change
Expand Up @@ -462,11 +462,91 @@ TLI_DEFINE_ENUM_INTERNAL(atomic_load)
TLI_DEFINE_STRING_INTERNAL("__atomic_load")
TLI_DEFINE_SIG_INTERNAL(Void, SizeT, Ptr, Ptr, Int)

/// int8_t __atomic_load_1(void *ptr, int memorder);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the choice to let __atomic_load_N return a sized integer, while __atomic_load return void based on maintaining proximity with GCC's definitions of these builtins? Or is there any other reason which I am missing?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is the GCC-defined ABI, see https://gcc.gnu.org/wiki/Atomic/GCCMM/LIbrary#GCC_intrinsics for reference.

__atomic_load_N returns the load value, possible due to having a known size that fits into a register (or pre-allocated stack slot, depending on calling convention). __atomic_load reads a dynamic number of bytes and hence cannot be pre-allocated/passed by register.

TLI_DEFINE_ENUM_INTERNAL(atomic_load_1)
TLI_DEFINE_STRING_INTERNAL("__atomic_load_1")
TLI_DEFINE_SIG_INTERNAL(Int8, Ptr, Int)

/// int16_t __atomic_load_2(void *ptr, int memorder);
TLI_DEFINE_ENUM_INTERNAL(atomic_load_2)
TLI_DEFINE_STRING_INTERNAL("__atomic_load_2")
TLI_DEFINE_SIG_INTERNAL(Int16, Ptr, Int)

/// int32_t __atomic_load_4(void *ptr, int memorder);
TLI_DEFINE_ENUM_INTERNAL(atomic_load_4)
TLI_DEFINE_STRING_INTERNAL("__atomic_load_4")
TLI_DEFINE_SIG_INTERNAL(Int32, Ptr, Int)

/// int64_t __atomic_load_8(void *ptr int memorder);
TLI_DEFINE_ENUM_INTERNAL(atomic_load_8)
TLI_DEFINE_STRING_INTERNAL("__atomic_load_8")
TLI_DEFINE_SIG_INTERNAL(Int64, Ptr, Int)

/// int128_t __atomic_load_16(void *ptr, int memorder);
TLI_DEFINE_ENUM_INTERNAL(atomic_load_16)
TLI_DEFINE_STRING_INTERNAL("__atomic_load_16")
TLI_DEFINE_SIG_INTERNAL(Int128, Ptr, Int)

/// void __atomic_store(size_t size, void *mptr, void *vptr, int smodel);
TLI_DEFINE_ENUM_INTERNAL(atomic_store)
TLI_DEFINE_STRING_INTERNAL("__atomic_store")
TLI_DEFINE_SIG_INTERNAL(Void, SizeT, Ptr, Ptr, Int)

/// void __atomic_store_1(void *ptr, int8_t val, int smodel);
TLI_DEFINE_ENUM_INTERNAL(atomic_store_1)
TLI_DEFINE_STRING_INTERNAL("__atomic_store_1")
TLI_DEFINE_SIG_INTERNAL(Void, Ptr, Int8, Int)

/// void __atomic_store_2(void *ptr, int16_t val, int smodel);
TLI_DEFINE_ENUM_INTERNAL(atomic_store_2)
TLI_DEFINE_STRING_INTERNAL("__atomic_store_2")
TLI_DEFINE_SIG_INTERNAL(Void, Ptr, Int16, Int)

/// void __atomic_store_4(void *ptr, int32_t val, int smodel);
TLI_DEFINE_ENUM_INTERNAL(atomic_store_4)
TLI_DEFINE_STRING_INTERNAL("__atomic_store_4")
TLI_DEFINE_SIG_INTERNAL(Void, Ptr, Int32, Int)

/// void __atomic_store_8(void *ptr, int64_t val, int smodel);
TLI_DEFINE_ENUM_INTERNAL(atomic_store_8)
TLI_DEFINE_STRING_INTERNAL("__atomic_store_8")
TLI_DEFINE_SIG_INTERNAL(Void, Ptr, Int64, Int)

/// void __atomic_store_16(void *ptr, int128_t val, int smodel);
TLI_DEFINE_ENUM_INTERNAL(atomic_store_16)
TLI_DEFINE_STRING_INTERNAL("__atomic_store_16")
TLI_DEFINE_SIG_INTERNAL(Void, Ptr, Int128, Int)

/// bool __atomic_compare_exchange(size_t size, void *ptr, void *expected, void *desired, int success, int failure);
TLI_DEFINE_ENUM_INTERNAL(atomic_compare_exchange)
TLI_DEFINE_STRING_INTERNAL("__atomic_compare_exchange")
TLI_DEFINE_SIG_INTERNAL(Bool, SizeT, Ptr, Ptr, Ptr, Int, Int)

/// bool __atomic_compare_exchange_1(void *ptr, void *expected, uint8_t desired, int success, int failure);
TLI_DEFINE_ENUM_INTERNAL(atomic_compare_exchange_1)
TLI_DEFINE_STRING_INTERNAL("__atomic_compare_exchange_1")
TLI_DEFINE_SIG_INTERNAL(Bool, Ptr, Ptr, Int8, Int, Int)

/// bool __atomic_compare_exchange_2(void *ptr, void *expected, uint16_t desired, int success, int failure);
TLI_DEFINE_ENUM_INTERNAL(atomic_compare_exchange_2)
TLI_DEFINE_STRING_INTERNAL("__atomic_compare_exchange_2")
TLI_DEFINE_SIG_INTERNAL(Bool, Ptr, Ptr, Int16, Int, Int)

/// bool __atomic_compare_exchange_4(void *ptr, void *expected, uint32_t desired, int success, int failure);
TLI_DEFINE_ENUM_INTERNAL(atomic_compare_exchange_4)
TLI_DEFINE_STRING_INTERNAL("__atomic_compare_exchange_4")
TLI_DEFINE_SIG_INTERNAL(Bool, Ptr, Ptr, Int32, Int, Int)

/// bool __atomic_compare_exchange_8(void *ptr, void *expected, uint64_t desired, int success, int failure);
TLI_DEFINE_ENUM_INTERNAL(atomic_compare_exchange_8)
TLI_DEFINE_STRING_INTERNAL("__atomic_compare_exchange_8")
TLI_DEFINE_SIG_INTERNAL(Bool, Ptr, Ptr, Int64, Int, Int)

/// bool __atomic_compare_exchange_16(void *ptr, void *expected, uint128_t desired, int success, int failure);
TLI_DEFINE_ENUM_INTERNAL(atomic_compare_exchange_16)
TLI_DEFINE_STRING_INTERNAL("__atomic_compare_exchange_16")
TLI_DEFINE_SIG_INTERNAL(Bool, Ptr, Ptr, Int128, Int, Int)

/// double __cosh_finite(double x);
TLI_DEFINE_ENUM_INTERNAL(cosh_finite)
TLI_DEFINE_STRING_INTERNAL("__cosh_finite")
Expand Down
22 changes: 22 additions & 0 deletions llvm/include/llvm/Support/AtomicOrdering.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,28 @@ inline AtomicOrderingCABI toCABI(AtomicOrdering AO) {
return lookup[static_cast<size_t>(AO)];
}

inline AtomicOrdering fromCABI(AtomicOrderingCABI AO) {
// Acquire is the the closest but still stronger ordering of consume.
static const AtomicOrdering lookup[8] = {
/* relaxed */ AtomicOrdering::Monotonic,
/* consume */ AtomicOrdering::Acquire,
/* acquire */ AtomicOrdering::Acquire,
/* release */ AtomicOrdering::Release,
/* acq_rel */ AtomicOrdering::AcquireRelease,
/* acq_seq */ AtomicOrdering::SequentiallyConsistent,
};
return lookup[static_cast<size_t>(AO)];
}

inline AtomicOrdering fromCABI(int64_t AO) {
if (!isValidAtomicOrderingCABI(AO)) {
// This fallback is what CGAtomic does
return AtomicOrdering::Monotonic;
}
assert(isValidAtomicOrderingCABI(AO));
return fromCABI(static_cast<AtomicOrderingCABI>(AO));
}

} // end namespace llvm

#endif // LLVM_SUPPORT_ATOMICORDERING_H
49 changes: 49 additions & 0 deletions llvm/include/llvm/Testing/Support/Error.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,48 @@ class ValueMatchesPoly {
M Matcher;
};

template <typename RefT> class StoreResultMatcher {
class Impl : public testing::MatcherInterface<
const llvm::detail::ExpectedHolder<RefT> &> {
public:
explicit Impl(RefT &Ref) : Ref(Ref) {}

bool
MatchAndExplain(const llvm::detail::ExpectedHolder<RefT> &Holder,
testing::MatchResultListener *listener) const override {
// If failed to get a value, fail the ASSERT/EXPECT and do not store any
// value
if (!Holder.Success())
return false;

// Succeeded with a value, remember it
Ref = *Holder.Exp;

return true;
}

void DescribeTo(std::ostream *OS) const override { *OS << "succeeded"; }

void DescribeNegationTo(std::ostream *OS) const override {
*OS << "failed";
}

private:
RefT &Ref;
};

public:
explicit StoreResultMatcher(RefT &Ref) : Ref(Ref) {}

template <typename T>
operator testing::Matcher<const llvm::detail::ExpectedHolder<T> &>() const {
return MakeMatcher(new Impl(Ref));
}

private:
RefT &Ref;
};

template <typename InfoT>
class ErrorMatchesMono : public testing::MatcherInterface<const ErrorHolder &> {
public:
Expand Down Expand Up @@ -222,6 +264,13 @@ detail::ValueMatchesPoly<M> HasValue(M Matcher) {
return detail::ValueMatchesPoly<M>(Matcher);
}

/// Matches on Expected<T> values that succeed, but also stores its value into a
Copy link
Member

Choose a reason for hiding this comment

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

Maybe this addition to the testing support should go in a different PR?

/// variable.
template <typename RefT>
detail::StoreResultMatcher<RefT> StoreResult(RefT &Ref) {
return detail::StoreResultMatcher<RefT>(Ref);
}

} // namespace llvm

#endif
Loading