mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-01-31 12:41:49 +01:00
[Attributor] Deduce "nosync" function attribute.
Introduce and deduce "nosync" function attribute to indicate that a function does not synchronize with another thread in a way that other thread might free memory. Reviewers: jdoerfert, jfb, nhaehnle, arsenm Subscribers: wdng, hfinkel, nhaenhle, mehdi_amini, steven_wu, dexonsmith, arsenm, uenoku, hiraditya, jfb, llvm-commits Differential Revision: https://reviews.llvm.org/D62766 llvm-svn: 365830
This commit is contained in:
parent
00406d9acb
commit
c892f2c16e
@ -1493,6 +1493,16 @@ example:
|
||||
Annotated functions may still raise an exception, i.a., ``nounwind`` is not implied.
|
||||
If an invocation of an annotated function does not return control back
|
||||
to a point in the call stack, the behavior is undefined.
|
||||
``nosync``
|
||||
This function attribute indicates that the function does not communicate
|
||||
(synchronize) with another thread through memory or other well-defined means.
|
||||
Synchronization is considered possible in the presence of `atomic` accesses
|
||||
that enforce an order, thus not "unordered" and "monotonic", `volatile` accesses,
|
||||
as well as `convergent` function calls. Note that through `convergent` function calls
|
||||
non-memory communication, e.g., cross-lane operations, are possible and are also
|
||||
considered synchronization. However `convergent` does not contradict `nosync`.
|
||||
If an annotated function does ever synchronize with another thread,
|
||||
the behavior is undefined.
|
||||
``nounwind``
|
||||
This function attribute indicates that the function never raises an
|
||||
exception. If the function does raise an exception, its runtime
|
||||
|
@ -629,7 +629,8 @@ enum AttributeKindCodes {
|
||||
ATTR_KIND_SPECULATIVE_LOAD_HARDENING = 59,
|
||||
ATTR_KIND_IMMARG = 60,
|
||||
ATTR_KIND_WILLRETURN = 61,
|
||||
ATTR_KIND_NOFREE = 62
|
||||
ATTR_KIND_NOFREE = 62,
|
||||
ATTR_KIND_NOSYNC = 63
|
||||
};
|
||||
|
||||
enum ComdatSelectionKindCodes {
|
||||
|
@ -109,6 +109,9 @@ def NoRedZone : EnumAttr<"noredzone">;
|
||||
/// Mark the function as not returning.
|
||||
def NoReturn : EnumAttr<"noreturn">;
|
||||
|
||||
/// Function does not synchronize.
|
||||
def NoSync : EnumAttr<"nosync">;
|
||||
|
||||
/// Disable Indirect Branch Tracking.
|
||||
def NoCfCheck : EnumAttr<"nocf_check">;
|
||||
|
||||
|
@ -377,7 +377,7 @@ struct AbstractState {
|
||||
/// state will catch up with the assumed one, for a pessimistic fixpoint it is
|
||||
/// the other way around.
|
||||
struct IntegerState : public AbstractState {
|
||||
/// Undrlying integer type, we assume 32 bits to be enough.
|
||||
/// Underlying integer type, we assume 32 bits to be enough.
|
||||
using base_t = uint32_t;
|
||||
|
||||
/// Initialize the (best) state.
|
||||
@ -664,20 +664,40 @@ struct AAReturnedValues : public AbstractAttribute {
|
||||
};
|
||||
|
||||
struct AANoUnwind : public AbstractAttribute {
|
||||
/// An abstract interface for all nosync attributes.
|
||||
AANoUnwind(Value &V, InformationCache &InfoCache)
|
||||
: AbstractAttribute(V, InfoCache) {}
|
||||
/// An abstract interface for all nosync attributes.
|
||||
AANoUnwind(Value &V, InformationCache &InfoCache)
|
||||
: AbstractAttribute(V, InfoCache) {}
|
||||
|
||||
/// See AbstractAttribute::getAttrKind()/
|
||||
virtual Attribute::AttrKind getAttrKind() const override { return ID; }
|
||||
/// See AbstractAttribute::getAttrKind()/
|
||||
virtual Attribute::AttrKind getAttrKind() const override { return ID; }
|
||||
|
||||
static constexpr Attribute::AttrKind ID = Attribute::NoUnwind;
|
||||
static constexpr Attribute::AttrKind ID = Attribute::NoUnwind;
|
||||
|
||||
/// Returns true if nounwind is assumed.
|
||||
virtual bool isAssumedNoUnwind() const = 0;
|
||||
/// Returns true if nounwind is assumed.
|
||||
virtual bool isAssumedNoUnwind() const = 0;
|
||||
|
||||
/// Returns true if nounwind is known.
|
||||
virtual bool isKnownNoUnwind() const = 0;
|
||||
/// Returns true if nounwind is known.
|
||||
virtual bool isKnownNoUnwind() const = 0;
|
||||
};
|
||||
|
||||
struct AANoSync : public AbstractAttribute {
|
||||
/// An abstract interface for all nosync attributes.
|
||||
AANoSync(Value &V, InformationCache &InfoCache)
|
||||
: AbstractAttribute(V, InfoCache) {}
|
||||
|
||||
/// See AbstractAttribute::getAttrKind().
|
||||
virtual Attribute::AttrKind getAttrKind() const override {
|
||||
return ID;
|
||||
}
|
||||
|
||||
static constexpr Attribute::AttrKind ID =
|
||||
Attribute::AttrKind(Attribute::NoSync);
|
||||
|
||||
/// Returns true if "nosync" is assumed.
|
||||
virtual bool isAssumedNoSync() const = 0;
|
||||
|
||||
/// Returns true if "nosync" is known.
|
||||
virtual bool isKnownNoSync() const = 0;
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
|
@ -658,6 +658,7 @@ lltok::Kind LLLexer::LexIdentifier() {
|
||||
KEYWORD(nonnull);
|
||||
KEYWORD(noredzone);
|
||||
KEYWORD(noreturn);
|
||||
KEYWORD(nosync);
|
||||
KEYWORD(nocf_check);
|
||||
KEYWORD(nounwind);
|
||||
KEYWORD(optforfuzzing);
|
||||
|
@ -1287,6 +1287,7 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B,
|
||||
case lltok::kw_nonlazybind: B.addAttribute(Attribute::NonLazyBind); break;
|
||||
case lltok::kw_noredzone: B.addAttribute(Attribute::NoRedZone); break;
|
||||
case lltok::kw_noreturn: B.addAttribute(Attribute::NoReturn); break;
|
||||
case lltok::kw_nosync: B.addAttribute(Attribute::NoSync); break;
|
||||
case lltok::kw_nocf_check: B.addAttribute(Attribute::NoCfCheck); break;
|
||||
case lltok::kw_norecurse: B.addAttribute(Attribute::NoRecurse); break;
|
||||
case lltok::kw_nounwind: B.addAttribute(Attribute::NoUnwind); break;
|
||||
|
@ -203,6 +203,7 @@ enum Kind {
|
||||
kw_nonnull,
|
||||
kw_noredzone,
|
||||
kw_noreturn,
|
||||
kw_nosync,
|
||||
kw_nocf_check,
|
||||
kw_nounwind,
|
||||
kw_optforfuzzing,
|
||||
|
@ -1280,6 +1280,9 @@ static uint64_t getRawAttributeMask(Attribute::AttrKind Val) {
|
||||
return 1ULL << 62;
|
||||
case Attribute::NoFree:
|
||||
return 1ULL << 63;
|
||||
case Attribute::NoSync:
|
||||
llvm_unreachable("nosync attribute not supported in raw format");
|
||||
break;
|
||||
case Attribute::Dereferenceable:
|
||||
llvm_unreachable("dereferenceable attribute not supported in raw format");
|
||||
break;
|
||||
@ -1305,7 +1308,8 @@ static void addRawAttributeValue(AttrBuilder &B, uint64_t Val) {
|
||||
if (I == Attribute::Dereferenceable ||
|
||||
I == Attribute::DereferenceableOrNull ||
|
||||
I == Attribute::ArgMemOnly ||
|
||||
I == Attribute::AllocSize)
|
||||
I == Attribute::AllocSize ||
|
||||
I == Attribute::NoSync)
|
||||
continue;
|
||||
if (uint64_t A = (Val & getRawAttributeMask(I))) {
|
||||
if (I == Attribute::Alignment)
|
||||
@ -1466,6 +1470,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
|
||||
return Attribute::NoRedZone;
|
||||
case bitc::ATTR_KIND_NO_RETURN:
|
||||
return Attribute::NoReturn;
|
||||
case bitc::ATTR_KIND_NOSYNC:
|
||||
return Attribute::NoSync;
|
||||
case bitc::ATTR_KIND_NOCF_CHECK:
|
||||
return Attribute::NoCfCheck;
|
||||
case bitc::ATTR_KIND_NO_UNWIND:
|
||||
|
@ -659,6 +659,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
|
||||
return bitc::ATTR_KIND_NO_RED_ZONE;
|
||||
case Attribute::NoReturn:
|
||||
return bitc::ATTR_KIND_NO_RETURN;
|
||||
case Attribute::NoSync:
|
||||
return bitc::ATTR_KIND_NOSYNC;
|
||||
case Attribute::NoCfCheck:
|
||||
return bitc::ATTR_KIND_NOCF_CHECK;
|
||||
case Attribute::NoUnwind:
|
||||
|
@ -335,6 +335,8 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
|
||||
return "noredzone";
|
||||
if (hasAttribute(Attribute::NoReturn))
|
||||
return "noreturn";
|
||||
if (hasAttribute(Attribute::NoSync))
|
||||
return "nosync";
|
||||
if (hasAttribute(Attribute::WillReturn))
|
||||
return "willreturn";
|
||||
if (hasAttribute(Attribute::NoCfCheck))
|
||||
|
@ -1493,6 +1493,7 @@ void Verifier::visitModuleFlagCGProfileEntry(const MDOperand &MDO) {
|
||||
static bool isFuncOnlyAttr(Attribute::AttrKind Kind) {
|
||||
switch (Kind) {
|
||||
case Attribute::NoReturn:
|
||||
case Attribute::NoSync:
|
||||
case Attribute::WillReturn:
|
||||
case Attribute::NoCfCheck:
|
||||
case Attribute::NoUnwind:
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "llvm/IR/Argument.h"
|
||||
#include "llvm/IR/Attributes.h"
|
||||
#include "llvm/IR/InstIterator.h"
|
||||
#include "llvm/IR/IntrinsicInst.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
@ -48,6 +49,7 @@ STATISTIC(NumFnUniqueReturned, "Number of function with unique return");
|
||||
STATISTIC(NumFnKnownReturns, "Number of function with known return values");
|
||||
STATISTIC(NumFnArgumentReturned,
|
||||
"Number of function arguments marked returned");
|
||||
STATISTIC(NumFnNoSync, "Number of functions marked nosync");
|
||||
|
||||
// TODO: Determine a good default value.
|
||||
//
|
||||
@ -99,6 +101,9 @@ static void bookkeeping(AbstractAttribute::ManifestPosition MP,
|
||||
case Attribute::Returned:
|
||||
NumFnArgumentReturned++;
|
||||
return;
|
||||
case Attribute::NoSync:
|
||||
NumFnNoSync++;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
@ -719,6 +724,191 @@ ChangeStatus AAReturnedValuesImpl::updateImpl(Attributor &A) {
|
||||
return Changed;
|
||||
}
|
||||
|
||||
/// ------------------------ NoSync Function Attribute -------------------------
|
||||
|
||||
struct AANoSyncFunction : AANoSync, BooleanState {
|
||||
|
||||
AANoSyncFunction(Function &F, InformationCache &InfoCache)
|
||||
: AANoSync(F, InfoCache) {}
|
||||
|
||||
/// See AbstractAttribute::getState()
|
||||
/// {
|
||||
AbstractState &getState() override { return *this; }
|
||||
const AbstractState &getState() const override { return *this; }
|
||||
/// }
|
||||
|
||||
/// See AbstractAttribute::getManifestPosition().
|
||||
virtual ManifestPosition getManifestPosition() const override {
|
||||
return MP_FUNCTION;
|
||||
}
|
||||
|
||||
virtual const std::string getAsStr() const override {
|
||||
return getAssumed() ? "nosync" : "may-sync";
|
||||
}
|
||||
|
||||
/// See AbstractAttribute::updateImpl(...).
|
||||
virtual ChangeStatus updateImpl(Attributor &A) override;
|
||||
|
||||
/// See AANoSync::isAssumedNoSync()
|
||||
virtual bool isAssumedNoSync() const override { return getAssumed(); }
|
||||
|
||||
/// See AANoSync::isKnownNoSync()
|
||||
virtual bool isKnownNoSync() const override { return getKnown(); }
|
||||
|
||||
/// Helper function used to determine whether an instruction is non-relaxed
|
||||
/// atomic. In other words, if an atomic instruction does not have unordered
|
||||
/// or monotonic ordering
|
||||
static bool isNonRelaxedAtomic(Instruction *I);
|
||||
|
||||
/// Helper function used to determine whether an instruction is volatile.
|
||||
static bool isVolatile(Instruction *I);
|
||||
|
||||
/// Helper function uset to check if intrinsic is volatile (memcpy, memmove, memset).
|
||||
static bool isNoSyncIntrinsic(Instruction *I);
|
||||
};
|
||||
|
||||
bool AANoSyncFunction::isNonRelaxedAtomic(Instruction *I) {
|
||||
if (!I->isAtomic())
|
||||
return false;
|
||||
|
||||
AtomicOrdering Ordering;
|
||||
switch (I->getOpcode()) {
|
||||
case Instruction::AtomicRMW:
|
||||
Ordering = cast<AtomicRMWInst>(I)->getOrdering();
|
||||
break;
|
||||
case Instruction::Store:
|
||||
Ordering = cast<StoreInst>(I)->getOrdering();
|
||||
break;
|
||||
case Instruction::Load:
|
||||
Ordering = cast<LoadInst>(I)->getOrdering();
|
||||
break;
|
||||
case Instruction::Fence: {
|
||||
auto *FI = cast<FenceInst>(I);
|
||||
if (FI->getSyncScopeID() == SyncScope::SingleThread)
|
||||
return false;
|
||||
Ordering = FI->getOrdering();
|
||||
break;
|
||||
}
|
||||
case Instruction::AtomicCmpXchg: {
|
||||
AtomicOrdering Success = cast<AtomicCmpXchgInst>(I)->getSuccessOrdering();
|
||||
AtomicOrdering Failure = cast<AtomicCmpXchgInst>(I)->getFailureOrdering();
|
||||
// Only if both are relaxed, than it can be treated as relaxed.
|
||||
// Otherwise it is non-relaxed.
|
||||
if (Success != AtomicOrdering::Unordered &&
|
||||
Success != AtomicOrdering::Monotonic)
|
||||
return true;
|
||||
if (Failure != AtomicOrdering::Unordered &&
|
||||
Failure != AtomicOrdering::Monotonic)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
llvm_unreachable(
|
||||
"New atomic operations need to be known in the attributor.");
|
||||
}
|
||||
|
||||
// Relaxed.
|
||||
if (Ordering == AtomicOrdering::Unordered ||
|
||||
Ordering == AtomicOrdering::Monotonic)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Checks if an intrinsic is nosync. Currently only checks mem* intrinsics.
|
||||
/// FIXME: We should ipmrove the handling of intrinsics.
|
||||
bool AANoSyncFunction::isNoSyncIntrinsic(Instruction *I) {
|
||||
if (auto *II = dyn_cast<IntrinsicInst>(I)) {
|
||||
switch (II->getIntrinsicID()) {
|
||||
/// Element wise atomic memory intrinsics are can only be unordered,
|
||||
/// therefore nosync.
|
||||
case Intrinsic::memset_element_unordered_atomic:
|
||||
case Intrinsic::memmove_element_unordered_atomic:
|
||||
case Intrinsic::memcpy_element_unordered_atomic:
|
||||
return true;
|
||||
case Intrinsic::memset:
|
||||
case Intrinsic::memmove:
|
||||
case Intrinsic::memcpy:
|
||||
if (!cast<MemIntrinsic>(II)->isVolatile())
|
||||
return true;
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AANoSyncFunction::isVolatile(Instruction *I) {
|
||||
assert(!ImmutableCallSite(I) && !isa<CallBase>(I) &&
|
||||
"Calls should not be checked here");
|
||||
|
||||
switch (I->getOpcode()) {
|
||||
case Instruction::AtomicRMW:
|
||||
return cast<AtomicRMWInst>(I)->isVolatile();
|
||||
case Instruction::Store:
|
||||
return cast<StoreInst>(I)->isVolatile();
|
||||
case Instruction::Load:
|
||||
return cast<LoadInst>(I)->isVolatile();
|
||||
case Instruction::AtomicCmpXchg:
|
||||
return cast<AtomicCmpXchgInst>(I)->isVolatile();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ChangeStatus AANoSyncFunction::updateImpl(Attributor &A) {
|
||||
Function &F = getAnchorScope();
|
||||
|
||||
/// We are looking for volatile instructions or Non-Relaxed atomics.
|
||||
/// FIXME: We should ipmrove the handling of intrinsics.
|
||||
for (Instruction *I : InfoCache.getReadOrWriteInstsForFunction(F)) {
|
||||
ImmutableCallSite ICS(I);
|
||||
auto *NoSyncAA = A.getAAFor<AANoSyncFunction>(*this, *I);
|
||||
|
||||
if (isa<IntrinsicInst>(I) && isNoSyncIntrinsic(I))
|
||||
continue;
|
||||
|
||||
if (ICS && (!NoSyncAA || !NoSyncAA->isAssumedNoSync()) &&
|
||||
!ICS.hasFnAttr(Attribute::NoSync)) {
|
||||
indicatePessimisticFixpoint();
|
||||
return ChangeStatus::CHANGED;
|
||||
}
|
||||
|
||||
if(ICS)
|
||||
continue;
|
||||
|
||||
if (!isVolatile(I) && !isNonRelaxedAtomic(I))
|
||||
continue;
|
||||
|
||||
indicatePessimisticFixpoint();
|
||||
return ChangeStatus::CHANGED;
|
||||
}
|
||||
|
||||
auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(F);
|
||||
auto Opcodes = {(unsigned)Instruction::Invoke, (unsigned)Instruction::CallBr,
|
||||
(unsigned)Instruction::Call};
|
||||
|
||||
for (unsigned Opcode : Opcodes) {
|
||||
for (Instruction *I : OpcodeInstMap[Opcode]) {
|
||||
// At this point we handled all read/write effects and they are all
|
||||
// nosync, so they can be skipped.
|
||||
if (I->mayReadOrWriteMemory())
|
||||
continue;
|
||||
|
||||
ImmutableCallSite ICS(I);
|
||||
|
||||
// non-convergent and readnone imply nosync.
|
||||
if (!ICS.isConvergent())
|
||||
continue;
|
||||
|
||||
indicatePessimisticFixpoint();
|
||||
return ChangeStatus::CHANGED;
|
||||
}
|
||||
}
|
||||
|
||||
return ChangeStatus::UNCHANGED;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------------------------------
|
||||
/// Attributor
|
||||
/// ----------------------------------------------------------------------------
|
||||
@ -864,6 +1054,9 @@ void Attributor::identifyDefaultAbstractAttributes(
|
||||
// Every function can be nounwind.
|
||||
registerAA(*new AANoUnwindFunction(F, InfoCache));
|
||||
|
||||
// Every function might be marked "nosync"
|
||||
registerAA(*new AANoSyncFunction(F, InfoCache));
|
||||
|
||||
// Return attributes are only appropriate if the return type is non void.
|
||||
Type *ReturnType = F.getReturnType();
|
||||
if (!ReturnType->isVoidTy()) {
|
||||
|
@ -809,6 +809,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs,
|
||||
case Attribute::NoBuiltin:
|
||||
case Attribute::NoCapture:
|
||||
case Attribute::NoReturn:
|
||||
case Attribute::NoSync:
|
||||
case Attribute::None:
|
||||
case Attribute::NonNull:
|
||||
case Attribute::ReadNone:
|
||||
|
@ -203,8 +203,8 @@ declare void @nobuiltin()
|
||||
define void @f34()
|
||||
; CHECK: define void @f34()
|
||||
{
|
||||
call void @nobuiltin() nobuiltin
|
||||
; CHECK: call void @nobuiltin() #38
|
||||
call void @nobuiltin() nobuiltin
|
||||
; CHECK: call void @nobuiltin() #39
|
||||
ret void;
|
||||
}
|
||||
|
||||
@ -362,6 +362,12 @@ define void @f61() nofree {
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK: define void @f62() #38
|
||||
define void @f62() nosync
|
||||
{
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK: attributes #0 = { noreturn }
|
||||
; CHECK: attributes #1 = { nounwind }
|
||||
; CHECK: attributes #2 = { readnone }
|
||||
@ -400,4 +406,5 @@ define void @f61() nofree {
|
||||
; CHECK: attributes #35 = { shadowcallstack }
|
||||
; CHECK: attributes #36 = { willreturn }
|
||||
; CHECK: attributes #37 = { nofree }
|
||||
; CHECK: attributes #38 = { nobuiltin }
|
||||
; CHECK: attributes #38 = { nosync }
|
||||
; CHECK: attributes #39 = { nobuiltin }
|
||||
|
@ -8,13 +8,13 @@
|
||||
|
||||
; TEST SCC test returning an integer value argument
|
||||
;
|
||||
; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
|
||||
; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
|
||||
; BOTH-NEXT: define i32 @sink_r0(i32 returned %r)
|
||||
; BOTH: Function Attrs: noinline nounwind readnone uwtable
|
||||
; BOTH: Function Attrs: noinline nosync nounwind readnone uwtable
|
||||
; BOTH-NEXT: define i32 @scc_r1(i32 %a, i32 returned %r, i32 %b)
|
||||
; BOTH: Function Attrs: noinline nounwind readnone uwtable
|
||||
; BOTH: Function Attrs: noinline nosync nounwind readnone uwtable
|
||||
; BOTH-NEXT: define i32 @scc_r2(i32 %a, i32 %b, i32 returned %r)
|
||||
; BOTH: Function Attrs: noinline nounwind readnone uwtable
|
||||
; BOTH: Function Attrs: noinline nosync nounwind readnone uwtable
|
||||
; BOTH-NEXT: define i32 @scc_rX(i32 %a, i32 %b, i32 %r)
|
||||
;
|
||||
; FNATTR: define i32 @sink_r0(i32 returned %r)
|
||||
@ -159,20 +159,23 @@ return: ; preds = %cond.end, %if.then3
|
||||
|
||||
; TEST SCC test returning a pointer value argument
|
||||
;
|
||||
; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
|
||||
; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
|
||||
; BOTH-NEXT: define double* @ptr_sink_r0(double* readnone returned %r)
|
||||
; BOTH: Function Attrs: noinline nounwind readnone uwtable
|
||||
; BOTH: Function Attrs: noinline nosync nounwind readnone uwtable
|
||||
; BOTH-NEXT: define double* @ptr_scc_r1(double* %a, double* readnone returned %r, double* nocapture readnone %b)
|
||||
; BOTH: Function Attrs: noinline nounwind readnone uwtable
|
||||
; BOTH: Function Attrs: noinline nosync nounwind readnone uwtable
|
||||
; BOTH-NEXT: define double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone returned %r)
|
||||
;
|
||||
; FNATTR: define double* @ptr_sink_r0(double* readnone returned %r)
|
||||
; FNATTR: define double* @ptr_scc_r1(double* %a, double* readnone %r, double* nocapture readnone %b)
|
||||
; FNATTR: define double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone %r)
|
||||
;
|
||||
; ATTRIBUTOR: define double* @ptr_sink_r0(double* returned %r)
|
||||
; ATTRIBUTOR: define double* @ptr_scc_r1(double* %a, double* returned %r, double* %b)
|
||||
; ATTRIBUTOR: define double* @ptr_scc_r2(double* %a, double* %b, double* returned %r)
|
||||
; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define double* @ptr_sink_r0(double* returned %r)
|
||||
; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define double* @ptr_scc_r1(double* %a, double* returned %r, double* %b)
|
||||
; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define double* @ptr_scc_r2(double* %a, double* %b, double* returned %r)
|
||||
;
|
||||
; double* ptr_scc_r1(double* a, double* b, double* r);
|
||||
; double* ptr_scc_r2(double* a, double* b, double* r);
|
||||
@ -258,7 +261,7 @@ return: ; preds = %cond.end, %if.then3
|
||||
;
|
||||
; FIXME: no-return missing
|
||||
; FNATTR: define i32* @rt0(i32* readonly %a)
|
||||
; BOTH: Function Attrs: noinline nounwind readonly uwtable
|
||||
; BOTH: Function Attrs: noinline nosync nounwind readonly uwtable
|
||||
; BOTH-NEXT: define i32* @rt0(i32* readonly returned %a)
|
||||
define i32* @rt0(i32* %a) #0 {
|
||||
entry:
|
||||
@ -277,7 +280,7 @@ entry:
|
||||
;
|
||||
; FIXME: no-return missing
|
||||
; FNATTR: define noalias i32* @rt1(i32* nocapture readonly %a)
|
||||
; BOTH: Function Attrs: noinline nounwind readonly uwtable
|
||||
; BOTH: Function Attrs: noinline nosync nounwind readonly uwtable
|
||||
; BOTH-NEXT: define noalias i32* @rt1(i32* nocapture readonly %a)
|
||||
define i32* @rt1(i32* %a) #0 {
|
||||
entry:
|
||||
@ -438,11 +441,12 @@ entry:
|
||||
; return b == 0? b : x;
|
||||
; }
|
||||
;
|
||||
; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
|
||||
; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
|
||||
; BOTH-NEXT: define double @select_and_phi(double returned %b)
|
||||
;
|
||||
; FNATTR: define double @select_and_phi(double %b)
|
||||
; ATTRIBUTOR: define double @select_and_phi(double returned %b)
|
||||
; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define double @select_and_phi(double returned %b)
|
||||
define double @select_and_phi(double %b) #0 {
|
||||
entry:
|
||||
%cmp = fcmp ogt double %b, 0.000000e+00
|
||||
@ -468,11 +472,13 @@ if.end: ; preds = %if.then, %entry
|
||||
; return b == 0? b : x;
|
||||
; }
|
||||
;
|
||||
; BOTH: Function Attrs: noinline nounwind readnone uwtable
|
||||
; BOTH: Function Attrs: noinline nosync nounwind readnone uwtable
|
||||
; BOTH-NEXT: define double @recursion_select_and_phi(i32 %a, double returned %b)
|
||||
;
|
||||
; FNATTR: define double @recursion_select_and_phi(i32 %a, double %b)
|
||||
; ATTRIBUTOR: define double @recursion_select_and_phi(i32 %a, double returned %b)
|
||||
;
|
||||
; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define double @recursion_select_and_phi(i32 %a, double returned %b)
|
||||
define double @recursion_select_and_phi(i32 %a, double %b) #0 {
|
||||
entry:
|
||||
%dec = add nsw i32 %a, -1
|
||||
@ -497,11 +503,13 @@ if.end: ; preds = %if.then, %entry
|
||||
; return (double*)b;
|
||||
; }
|
||||
;
|
||||
; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
|
||||
; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
|
||||
; BOTH-NEXT: define double* @bitcast(i32* readnone returned %b)
|
||||
;
|
||||
; FNATTR: define double* @bitcast(i32* readnone %b)
|
||||
; ATTRIBUTOR: define double* @bitcast(i32* returned %b)
|
||||
;
|
||||
; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define double* @bitcast(i32* returned %b)
|
||||
define double* @bitcast(i32* %b) #0 {
|
||||
entry:
|
||||
%bc0 = bitcast i32* %b to double*
|
||||
@ -518,11 +526,13 @@ entry:
|
||||
; return b != 0 ? b : x;
|
||||
; }
|
||||
;
|
||||
; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
|
||||
; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
|
||||
; BOTH-NEXT: define double* @bitcasts_select_and_phi(i32* readnone returned %b)
|
||||
;
|
||||
; FNATTR: define double* @bitcasts_select_and_phi(i32* readnone %b)
|
||||
; ATTRIBUTOR: define double* @bitcasts_select_and_phi(i32* returned %b)
|
||||
;
|
||||
; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define double* @bitcasts_select_and_phi(i32* returned %b)
|
||||
define double* @bitcasts_select_and_phi(i32* %b) #0 {
|
||||
entry:
|
||||
%bc0 = bitcast i32* %b to double*
|
||||
@ -554,11 +564,13 @@ if.end: ; preds = %if.then, %entry
|
||||
; /* return undef */
|
||||
; }
|
||||
;
|
||||
; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
|
||||
; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
|
||||
; BOTH-NEXT: define double* @ret_arg_arg_undef(i32* readnone returned %b)
|
||||
;
|
||||
; FNATTR: define double* @ret_arg_arg_undef(i32* readnone %b)
|
||||
; ATTRIBUTOR: define double* @ret_arg_arg_undef(i32* returned %b)
|
||||
;
|
||||
; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define double* @ret_arg_arg_undef(i32* returned %b)
|
||||
define double* @ret_arg_arg_undef(i32* %b) #0 {
|
||||
entry:
|
||||
%bc0 = bitcast i32* %b to double*
|
||||
@ -590,11 +602,13 @@ ret_undef:
|
||||
; /* return undef */
|
||||
; }
|
||||
;
|
||||
; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
|
||||
; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
|
||||
; BOTH-NEXT: define double* @ret_undef_arg_arg(i32* readnone returned %b)
|
||||
;
|
||||
; FNATTR: define double* @ret_undef_arg_arg(i32* readnone %b)
|
||||
; ATTRIBUTOR: define double* @ret_undef_arg_arg(i32* returned %b)
|
||||
;
|
||||
; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define double* @ret_undef_arg_arg(i32* returned %b)
|
||||
define double* @ret_undef_arg_arg(i32* %b) #0 {
|
||||
entry:
|
||||
%bc0 = bitcast i32* %b to double*
|
||||
@ -626,7 +640,7 @@ ret_arg1:
|
||||
; /* return undef */
|
||||
; }
|
||||
;
|
||||
; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
|
||||
; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
|
||||
; BOTH-NEXT: define double* @ret_undef_arg_undef(i32* readnone returned %b)
|
||||
;
|
||||
; FNATTR: define double* @ret_undef_arg_undef(i32* readnone %b)
|
||||
@ -730,8 +744,8 @@ unreachableblock2:
|
||||
attributes #0 = { noinline nounwind uwtable }
|
||||
|
||||
; BOTH-NOT: attributes #
|
||||
; BOTH-DAG: attributes #{{[0-9]*}} = { noinline norecurse nounwind readnone uwtable }
|
||||
; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nounwind readnone uwtable }
|
||||
; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nounwind readonly uwtable }
|
||||
; BOTH-DAG: attributes #{{[0-9]*}} = { noinline norecurse nosync nounwind readnone uwtable }
|
||||
; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nosync nounwind readnone uwtable }
|
||||
; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nosync nounwind readonly uwtable }
|
||||
; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nounwind uwtable }
|
||||
; BOTH-NOT: attributes #
|
||||
|
@ -20,7 +20,7 @@ target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
; }
|
||||
;
|
||||
; FIXME: no-return missing
|
||||
; CHECK: Function Attrs: noinline nounwind readnone uwtable
|
||||
; CHECK: Function Attrs: noinline nosync nounwind readnone uwtable
|
||||
; CHECK: define void @srec0()
|
||||
;
|
||||
define void @srec0() #0 {
|
||||
@ -37,7 +37,7 @@ entry:
|
||||
; }
|
||||
;
|
||||
; FIXME: no-return missing
|
||||
; CHECK: Function Attrs: noinline nounwind readnone uwtable
|
||||
; CHECK: Function Attrs: noinline nosync nounwind readnone uwtable
|
||||
; CHECK: define i32 @srec16(i32 %a)
|
||||
;
|
||||
define i32 @srec16(i32 %a) #0 {
|
||||
@ -69,7 +69,7 @@ entry:
|
||||
; }
|
||||
;
|
||||
; FIXME: no-return missing
|
||||
; CHECK: Function Attrs: noinline norecurse nounwind readnone uwtable
|
||||
; CHECK: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
|
||||
; CHECK: define i32 @endless_loop(i32 %a)
|
||||
;
|
||||
define i32 @endless_loop(i32 %a) #0 {
|
||||
@ -89,7 +89,7 @@ while.body: ; preds = %entry, %while.body
|
||||
; }
|
||||
;
|
||||
; FIXME: no-return missing
|
||||
; CHECK: Function Attrs: noinline norecurse nounwind readnone uwtable
|
||||
; CHECK: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
|
||||
; CHECK: define i32 @dead_return(i32 returned %a)
|
||||
;
|
||||
define i32 @dead_return(i32 %a) #0 {
|
||||
@ -111,7 +111,7 @@ return: ; No predecessors!
|
||||
; }
|
||||
;
|
||||
; FIXME: no-return missing
|
||||
; CHECK: Function Attrs: noinline nounwind readnone uwtable
|
||||
; CHECK: Function Attrs: noinline nosync nounwind readnone uwtable
|
||||
; CHECK: define i32 @multiple_noreturn_calls(i32 %a)
|
||||
;
|
||||
define i32 @multiple_noreturn_calls(i32 %a) #0 {
|
||||
|
352
test/Transforms/FunctionAttrs/nosync.ll
Normal file
352
test/Transforms/FunctionAttrs/nosync.ll
Normal file
@ -0,0 +1,352 @@
|
||||
; RUN: opt -functionattrs -S < %s | FileCheck %s --check-prefix=FNATTR
|
||||
; RUN: opt -attributor -attributor-disable=false -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
|
||||
; Test cases designed for the nosync function attribute.
|
||||
; FIXME's are used to indicate problems and missing attributes.
|
||||
|
||||
; struct RT {
|
||||
; char A;
|
||||
; int B[10][20];
|
||||
; char C;
|
||||
; };
|
||||
; struct ST {
|
||||
; int X;
|
||||
; double Y;
|
||||
; struct RT Z;
|
||||
; };
|
||||
;
|
||||
; int *foo(struct ST *s) {
|
||||
; return &s[1].Z.B[5][13];
|
||||
; }
|
||||
|
||||
; TEST 1
|
||||
; non-convergent and readnone implies nosync
|
||||
%struct.RT = type { i8, [10 x [20 x i32]], i8 }
|
||||
%struct.ST = type { i32, double, %struct.RT }
|
||||
|
||||
; FNATTR: Function Attrs: norecurse nounwind optsize readnone ssp uwtable
|
||||
; FNATTR-NEXT: define nonnull i32* @foo(%struct.ST* readnone %s)
|
||||
; ATTRIBUTOR: Function Attrs: nosync nounwind optsize readnone ssp uwtable
|
||||
; ATTRIBUTOR-NEXT: define i32* @foo(%struct.ST* %s)
|
||||
define i32* @foo(%struct.ST* %s) nounwind uwtable readnone optsize ssp {
|
||||
entry:
|
||||
%arrayidx = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 1, i32 2, i32 1, i64 5, i64 13
|
||||
ret i32* %arrayidx
|
||||
}
|
||||
|
||||
; TEST 2
|
||||
; atomic load with monotonic ordering
|
||||
; int load_monotonic(_Atomic int *num) {
|
||||
; int n = atomic_load_explicit(num, memory_order_relaxed);
|
||||
; return n;
|
||||
; }
|
||||
|
||||
; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
|
||||
; FNATTR-NEXT: define i32 @load_monotonic(i32* nocapture readonly)
|
||||
; ATTRIBUTOR: Function Attrs: norecurse nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define i32 @load_monotonic(i32* nocapture readonly)
|
||||
define i32 @load_monotonic(i32* nocapture readonly) norecurse nounwind uwtable {
|
||||
%2 = load atomic i32, i32* %0 monotonic, align 4
|
||||
ret i32 %2
|
||||
}
|
||||
|
||||
|
||||
; TEST 3
|
||||
; atomic store with monotonic ordering.
|
||||
; void store_monotonic(_Atomic int *num) {
|
||||
; atomic_load_explicit(num, memory_order_relaxed);
|
||||
; }
|
||||
|
||||
; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
|
||||
; FNATTR-NEXT: define void @store_monotonic(i32* nocapture)
|
||||
; ATTRIBUTOR: Function Attrs: norecurse nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define void @store_monotonic(i32* nocapture)
|
||||
define void @store_monotonic(i32* nocapture) norecurse nounwind uwtable {
|
||||
store atomic i32 10, i32* %0 monotonic, align 4
|
||||
ret void
|
||||
}
|
||||
|
||||
; TEST 4 - negative, should not deduce nosync
|
||||
; atomic load with acquire ordering.
|
||||
; int load_acquire(_Atomic int *num) {
|
||||
; int n = atomic_load_explicit(num, memory_order_acquire);
|
||||
; return n;
|
||||
; }
|
||||
|
||||
; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
|
||||
; FNATTR-NEXT: define i32 @load_acquire(i32* nocapture readonly)
|
||||
; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable
|
||||
; ATTRIBUTOR-NOT: nosync
|
||||
; ATTRIBUTOR-NEXT: define i32 @load_acquire(i32* nocapture readonly)
|
||||
define i32 @load_acquire(i32* nocapture readonly) norecurse nounwind uwtable {
|
||||
%2 = load atomic i32, i32* %0 acquire, align 4
|
||||
ret i32 %2
|
||||
}
|
||||
|
||||
; TEST 5 - negative, should not deduce nosync
|
||||
; atomic load with release ordering
|
||||
; void load_release(_Atomic int *num) {
|
||||
; atomic_store_explicit(num, 10, memory_order_release);
|
||||
; }
|
||||
|
||||
; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
|
||||
; FNATTR-NEXT: define void @load_release(i32* nocapture)
|
||||
; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable
|
||||
; ATTRIBUTOR-NOT: nosync
|
||||
; ATTRIBUTOR-NEXT: define void @load_release(i32* nocapture)
|
||||
define void @load_release(i32* nocapture) norecurse nounwind uwtable {
|
||||
store atomic volatile i32 10, i32* %0 release, align 4
|
||||
ret void
|
||||
}
|
||||
|
||||
; TEST 6 - negative volatile, relaxed atomic
|
||||
|
||||
; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
|
||||
; FNATTR-NEXT: define void @load_volatile_release(i32* nocapture)
|
||||
; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable
|
||||
; ATTRIBUTOR-NOT: nosync
|
||||
; ATTRIBUTOR-NEXT: define void @load_volatile_release(i32* nocapture)
|
||||
define void @load_volatile_release(i32* nocapture) norecurse nounwind uwtable {
|
||||
store atomic volatile i32 10, i32* %0 release, align 4
|
||||
ret void
|
||||
}
|
||||
|
||||
; TEST 7 - negative, should not deduce nosync
|
||||
; volatile store.
|
||||
; void volatile_store(volatile int *num) {
|
||||
; *num = 14;
|
||||
; }
|
||||
|
||||
; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
|
||||
; FNATTR-NEXT: define void @volatile_store(i32*)
|
||||
; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable
|
||||
; ATTRIBUTOR-NOT: nosync
|
||||
; ATTRIBUTOR-NEXT: define void @volatile_store(i32*)
|
||||
define void @volatile_store(i32*) norecurse nounwind uwtable {
|
||||
store volatile i32 14, i32* %0, align 4
|
||||
ret void
|
||||
}
|
||||
|
||||
; TEST 8 - negative, should not deduce nosync
|
||||
; volatile load.
|
||||
; int volatile_load(volatile int *num) {
|
||||
; int n = *num;
|
||||
; return n;
|
||||
; }
|
||||
|
||||
; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
|
||||
; FNATTR-NEXT: define i32 @volatile_load(i32*)
|
||||
; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable
|
||||
; ATTRIBUTOR-NOT: nosync
|
||||
; ATTRIBUTOR-NEXT: define i32 @volatile_load(i32*)
|
||||
define i32 @volatile_load(i32*) norecurse nounwind uwtable {
|
||||
%2 = load volatile i32, i32* %0, align 4
|
||||
ret i32 %2
|
||||
}
|
||||
|
||||
; TEST 9
|
||||
|
||||
; FNATTR: Function Attrs: noinline nosync nounwind uwtable
|
||||
; FNATTR-NEXT: declare void @nosync_function()
|
||||
; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: declare void @nosync_function()
|
||||
declare void @nosync_function() noinline nounwind uwtable nosync
|
||||
|
||||
; FNATTR: Function Attrs: noinline nounwind uwtable
|
||||
; FNATTR-NEXT: define void @call_nosync_function()
|
||||
; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-next: define void @call_nosync_function()
|
||||
define void @call_nosync_function() nounwind uwtable noinline {
|
||||
tail call void @nosync_function() noinline nounwind uwtable
|
||||
ret void
|
||||
}
|
||||
|
||||
; TEST 10 - negative, should not deduce nosync
|
||||
|
||||
; FNATTR: Function Attrs: noinline nounwind uwtable
|
||||
; FNATTR-NEXT: declare void @might_sync()
|
||||
; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: declare void @might_sync()
|
||||
declare void @might_sync() noinline nounwind uwtable
|
||||
|
||||
; FNATTR: Function Attrs: noinline nounwind uwtable
|
||||
; FNATTR-NEXT: define void @call_might_sync()
|
||||
; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
|
||||
; ATTRIBUTOR-NOT: nosync
|
||||
; ATTRIBUTOR-NEXT: define void @call_might_sync()
|
||||
define void @call_might_sync() nounwind uwtable noinline {
|
||||
tail call void @might_sync() noinline nounwind uwtable
|
||||
ret void
|
||||
}
|
||||
|
||||
; TEST 11 - negative, should not deduce nosync
|
||||
; volatile operation in same scc. Call volatile_load defined in TEST 8.
|
||||
|
||||
; FNATTR: Function Attrs: nofree noinline nounwind uwtable
|
||||
; FNATTR-NEXT: define i32 @scc1(i32*)
|
||||
; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
|
||||
; ATTRIBUTOR-NOT: nosync
|
||||
; ATTRIBUTOR-NEXT: define i32 @scc1(i32*)
|
||||
define i32 @scc1(i32*) noinline nounwind uwtable {
|
||||
tail call void @scc2(i32* %0);
|
||||
%val = tail call i32 @volatile_load(i32* %0);
|
||||
ret i32 %val;
|
||||
}
|
||||
|
||||
; FNATTR: Function Attrs: nofree noinline nounwind uwtable
|
||||
; FNATTR-NEXT: define void @scc2(i32*)
|
||||
; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
|
||||
; ATTRIBUTOR-NOT: nosync
|
||||
; ATTRIBUTOR-NEXT: define void @scc2(i32*)
|
||||
define void @scc2(i32*) noinline nounwind uwtable {
|
||||
tail call i32 @scc1(i32* %0);
|
||||
ret void;
|
||||
}
|
||||
|
||||
; TEST 12 - fences, negative
|
||||
;
|
||||
; void foo1(int *a, std::atomic<bool> flag){
|
||||
; *a = 100;
|
||||
; atomic_thread_fence(std::memory_order_release);
|
||||
; flag.store(true, std::memory_order_relaxed);
|
||||
; }
|
||||
;
|
||||
; void bar(int *a, std::atomic<bool> flag){
|
||||
; while(!flag.load(std::memory_order_relaxed))
|
||||
; ;
|
||||
;
|
||||
; atomic_thread_fence(std::memory_order_acquire);
|
||||
; int b = *a;
|
||||
; }
|
||||
|
||||
%"struct.std::atomic" = type { %"struct.std::__atomic_base" }
|
||||
%"struct.std::__atomic_base" = type { i8 }
|
||||
|
||||
; FNATTR: Function Attrs: nofree norecurse nounwind
|
||||
; FNATTR-NEXT: define void @foo1(i32* nocapture, %"struct.std::atomic"* nocapture)
|
||||
; ATTRIBUTOR-NOT: nosync
|
||||
; ATTRIBUTOR: define void @foo1(i32*, %"struct.std::atomic"*)
|
||||
define void @foo1(i32*, %"struct.std::atomic"*) {
|
||||
store i32 100, i32* %0, align 4
|
||||
fence release
|
||||
%3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0
|
||||
store atomic i8 1, i8* %3 monotonic, align 1
|
||||
ret void
|
||||
}
|
||||
|
||||
; FNATTR: Function Attrs: nofree norecurse nounwind
|
||||
; FNATTR-NEXT: define void @bar(i32* nocapture readnone, %"struct.std::atomic"* nocapture readonly)
|
||||
; ATTRIBUTOR-NOT: nosync
|
||||
; ATTRIBUTOR: define void @bar(i32*, %"struct.std::atomic"*)
|
||||
define void @bar(i32 *, %"struct.std::atomic"*) {
|
||||
%3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0
|
||||
br label %4
|
||||
|
||||
4: ; preds = %4, %2
|
||||
%5 = load atomic i8, i8* %3 monotonic, align 1
|
||||
%6 = and i8 %5, 1
|
||||
%7 = icmp eq i8 %6, 0
|
||||
br i1 %7, label %4, label %8
|
||||
|
||||
8: ; preds = %4
|
||||
fence acquire
|
||||
ret void
|
||||
}
|
||||
|
||||
; TEST 13 - Fence syncscope("singlethread") seq_cst
|
||||
; FNATTR: Function Attrs: nofree norecurse nounwind
|
||||
; FNATTR-NEXT: define void @foo1_singlethread(i32* nocapture, %"struct.std::atomic"* nocapture)
|
||||
; ATTRIBUTOR: Function Attrs: nosync
|
||||
; ATTRIBUTOR: define void @foo1_singlethread(i32*, %"struct.std::atomic"*)
|
||||
define void @foo1_singlethread(i32*, %"struct.std::atomic"*) {
|
||||
store i32 100, i32* %0, align 4
|
||||
fence syncscope("singlethread") release
|
||||
%3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0
|
||||
store atomic i8 1, i8* %3 monotonic, align 1
|
||||
ret void
|
||||
}
|
||||
|
||||
; FNATTR: Function Attrs: nofree norecurse nounwind
|
||||
; FNATTR-NEXT: define void @bar_singlethread(i32* nocapture readnone, %"struct.std::atomic"* nocapture readonly)
|
||||
; ATTRIBUTOR: Function Attrs: nosync
|
||||
; ATTRIBUTOR: define void @bar_singlethread(i32*, %"struct.std::atomic"*)
|
||||
define void @bar_singlethread(i32 *, %"struct.std::atomic"*) {
|
||||
%3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0
|
||||
br label %4
|
||||
|
||||
4: ; preds = %4, %2
|
||||
%5 = load atomic i8, i8* %3 monotonic, align 1
|
||||
%6 = and i8 %5, 1
|
||||
%7 = icmp eq i8 %6, 0
|
||||
br i1 %7, label %4, label %8
|
||||
|
||||
8: ; preds = %4
|
||||
fence syncscope("singlethread") acquire
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @llvm.memcpy(i8* %dest, i8* %src, i32 %len, i1 %isvolatile)
|
||||
declare void @llvm.memset(i8* %dest, i8 %val, i32 %len, i1 %isvolatile)
|
||||
|
||||
; TEST 14 - negative, checking volatile intrinsics.
|
||||
|
||||
; ATTRIBUTOR: Function Attrs: nounwind
|
||||
; ATTRIBUTOR-NOT: nosync
|
||||
; ATTRIBUTOR-NEXT: define i32 @memcpy_volatile(i8* %ptr1, i8* %ptr2)
|
||||
define i32 @memcpy_volatile(i8* %ptr1, i8* %ptr2) {
|
||||
call void @llvm.memcpy(i8* %ptr1, i8* %ptr2, i32 8, i1 1)
|
||||
ret i32 4
|
||||
}
|
||||
|
||||
; TEST 15 - positive, non-volatile intrinsic.
|
||||
|
||||
; ATTRIBUTOR: Function Attrs: nosync
|
||||
; ATTRIBUTOR-NEXT: define i32 @memset_non_volatile(i8* %ptr1, i8 %val)
|
||||
define i32 @memset_non_volatile(i8* %ptr1, i8 %val) {
|
||||
call void @llvm.memset(i8* %ptr1, i8 %val, i32 8, i1 0)
|
||||
ret i32 4
|
||||
}
|
||||
|
||||
; TEST 16 - negative, inline assembly.
|
||||
|
||||
; ATTRIBUTOR: define i32 @inline_asm_test(i32 %x)
|
||||
define i32 @inline_asm_test(i32 %x) {
|
||||
call i32 asm "bswap $0", "=r,r"(i32 %x)
|
||||
ret i32 4
|
||||
}
|
||||
|
||||
declare void @readnone_test() convergent readnone
|
||||
|
||||
; ATTRIBUTOR: define void @convergent_readnone()
|
||||
; TEST 17 - negative. Convergent
|
||||
define void @convergent_readnone(){
|
||||
call void @readnone_test()
|
||||
ret void
|
||||
}
|
||||
|
||||
; ATTRIBUTOR: Function Attrs: nounwind
|
||||
; ATTRIBUTOR-NEXT: declare void @llvm.x86.sse2.clflush(i8*)
|
||||
declare void @llvm.x86.sse2.clflush(i8*)
|
||||
@a = common global i32 0, align 4
|
||||
|
||||
; TEST 18 - negative. Synchronizing intrinsic
|
||||
|
||||
; ATTRIBUTOR: Function Attrs: nounwind
|
||||
; ATTRIBUTOR-NOT: nosync
|
||||
; ATTRIBUTOR-NEXT: define void @i_totally_sync()
|
||||
define void @i_totally_sync() {
|
||||
tail call void @llvm.x86.sse2.clflush(i8* bitcast (i32* @a to i8*))
|
||||
ret void
|
||||
}
|
||||
|
||||
declare float @llvm.cos(float %val) readnone
|
||||
|
||||
; TEST 19 - positive, readnone & non-convergent intrinsic.
|
||||
|
||||
; ATTRIBUTOR: Function Attrs: nosync nounwind
|
||||
; ATTRIBUTOR-NEXT: define i32 @cos_test(float %x)
|
||||
define i32 @cos_test(float %x) {
|
||||
call float @llvm.cos(float %x)
|
||||
ret i32 4
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
; TEST 1
|
||||
; CHECK: Function Attrs: norecurse nounwind readnone
|
||||
; CHECK-NEXT: define i32 @foo1()
|
||||
; ATTRIBUTOR: Function Attrs: nounwind
|
||||
; ATTRIBUTOR: Function Attrs: nosync nounwind
|
||||
; ATTRIBUTOR-NEXT: define i32 @foo1()
|
||||
define i32 @foo1() {
|
||||
ret i32 1
|
||||
@ -13,7 +13,7 @@ define i32 @foo1() {
|
||||
; TEST 2
|
||||
; CHECK: Function Attrs: nounwind readnone
|
||||
; CHECK-NEXT: define i32 @scc1_foo()
|
||||
; ATTRIBUTOR: Function Attrs: nounwind
|
||||
; ATTRIBUTOR: Function Attrs: nosync nounwind
|
||||
; ATTRIBUTOR-NEXT: define i32 @scc1_foo()
|
||||
define i32 @scc1_foo() {
|
||||
%1 = call i32 @scc1_bar()
|
||||
@ -24,7 +24,7 @@ define i32 @scc1_foo() {
|
||||
; TEST 3
|
||||
; CHECK: Function Attrs: nounwind readnone
|
||||
; CHECK-NEXT: define i32 @scc1_bar()
|
||||
; ATTRIBUTOR: Function Attrs: nounwind
|
||||
; ATTRIBUTOR: Function Attrs: nosync nounwind
|
||||
; ATTRIBUTOR-NEXT: define i32 @scc1_bar()
|
||||
define i32 @scc1_bar() {
|
||||
%1 = call i32 @scc1_foo()
|
||||
|
@ -30,7 +30,7 @@
|
||||
;
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
|
||||
; CHECK: Function Attrs: nofree nounwind
|
||||
; CHECK: Function Attrs: nofree nosync nounwind
|
||||
; CHECK-NEXT: define i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* returned %w0)
|
||||
define i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
|
||||
entry:
|
||||
@ -41,7 +41,7 @@ entry:
|
||||
ret i32* %call3
|
||||
}
|
||||
|
||||
; CHECK: Function Attrs: nofree nounwind
|
||||
; CHECK: Function Attrs: nofree nosync nounwind
|
||||
; CHECK-NEXT: define internal i32* @internal_ret0_nw(i32* returned %n0, i32* %w0)
|
||||
define internal i32* @internal_ret0_nw(i32* %n0, i32* %w0) {
|
||||
entry:
|
||||
@ -70,7 +70,7 @@ return: ; preds = %if.end, %if.then
|
||||
ret i32* %retval.0
|
||||
}
|
||||
|
||||
; CHECK: Function Attrs: nofree nounwind
|
||||
; CHECK: Function Attrs: nofree nosync nounwind
|
||||
; CHECK-NEXT: define internal i32* @internal_ret1_rrw(i32* %r0, i32* returned %r1, i32* %w0)
|
||||
define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) {
|
||||
entry:
|
||||
@ -102,7 +102,7 @@ return: ; preds = %if.end, %if.then
|
||||
ret i32* %retval.0
|
||||
}
|
||||
|
||||
; CHECK: Function Attrs: nofree norecurse nounwind
|
||||
; CHECK: Function Attrs: nofree norecurse nosync nounwind
|
||||
; CHECK-NEXT: define i32* @external_sink_ret2_nrw(i32* readnone %n0, i32* nocapture readonly %r0, i32* returned %w0)
|
||||
define i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
|
||||
entry:
|
||||
@ -121,7 +121,7 @@ return: ; preds = %if.end, %if.then
|
||||
ret i32* %w0
|
||||
}
|
||||
|
||||
; CHECK: Function Attrs: nofree nounwind
|
||||
; CHECK: Function Attrs: nofree nosync nounwind
|
||||
; CHECK-NEXT: define internal i32* @internal_ret1_rw(i32* %r0, i32* returned %w0)
|
||||
define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) {
|
||||
entry:
|
||||
@ -147,7 +147,7 @@ return: ; preds = %if.end, %if.then
|
||||
ret i32* %retval.0
|
||||
}
|
||||
|
||||
; CHECK: Function Attrs: nofree nounwind
|
||||
; CHECK: Function Attrs: nofree nosync nounwind
|
||||
; CHECK-NEXT: define i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* returned %w0)
|
||||
define i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
|
||||
entry:
|
||||
@ -160,6 +160,6 @@ entry:
|
||||
; for a subset relation.
|
||||
;
|
||||
; CHECK-NOT: attributes #
|
||||
; CHECK: attributes #{{.*}} = { nofree nounwind }
|
||||
; CHECK: attributes #{{.*}} = { nofree norecurse nounwind }
|
||||
; CHECK: attributes #{{.*}} = { nofree nosync nounwind }
|
||||
; CHECK: attributes #{{.*}} = { nofree norecurse nosync nounwind }
|
||||
; CHECK-NOT: attributes #
|
||||
|
Loading…
x
Reference in New Issue
Block a user