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
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 {
/// 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">;
/// 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.
};
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;
+
+ /// Returns true if nounwind is assumed.
+ virtual bool isAssumedNoUnwind() 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::NoUnwind;
+ static constexpr Attribute::AttrKind ID =
+ Attribute::AttrKind(Attribute::NoSync);
- /// Returns true if nounwind is assumed.
- virtual bool isAssumedNoUnwind() const = 0;
+ /// Returns true if "nosync" is assumed.
+ virtual bool isAssumedNoSync() const = 0;
- /// Returns true if nounwind is known.
- virtual bool isKnownNoUnwind() const = 0;
+ /// Returns true if "nosync" is known.
+ virtual bool isKnownNoSync() const = 0;
};
} // end namespace llvm
KEYWORD(nonnull);
KEYWORD(noredzone);
KEYWORD(noreturn);
+ KEYWORD(nosync);
KEYWORD(nocf_check);
KEYWORD(nounwind);
KEYWORD(optforfuzzing);
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;
kw_nonnull,
kw_noredzone,
kw_noreturn,
+ kw_nosync,
kw_nocf_check,
kw_nounwind,
kw_optforfuzzing,
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;
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)
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:
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:
return "noredzone";
if (hasAttribute(Attribute::NoReturn))
return "noreturn";
+ if (hasAttribute(Attribute::NoSync))
+ return "nosync";
if (hasAttribute(Attribute::WillReturn))
return "willreturn";
if (hasAttribute(Attribute::NoCfCheck))
static bool isFuncOnlyAttr(Attribute::AttrKind Kind) {
switch (Kind) {
case Attribute::NoReturn:
+ case Attribute::NoSync:
case Attribute::WillReturn:
case Attribute::NoCfCheck:
case Attribute::NoUnwind:
#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"
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.
//
case Attribute::Returned:
NumFnArgumentReturned++;
return;
+ case Attribute::NoSync:
+ NumFnNoSync++;
+ break;
default:
return;
}
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
/// ----------------------------------------------------------------------------
// 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()) {
case Attribute::NoBuiltin:
case Attribute::NoCapture:
case Attribute::NoReturn:
+ case Attribute::NoSync:
case Attribute::None:
case Attribute::NonNull:
case Attribute::ReadNone:
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;
}
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 }
; CHECK: attributes #35 = { shadowcallstack }
; CHECK: attributes #36 = { willreturn }
; CHECK: attributes #37 = { nofree }
-; CHECK: attributes #38 = { nobuiltin }
+; CHECK: attributes #38 = { nosync }
+; CHECK: attributes #39 = { nobuiltin }
; 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)
; 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);
;
; 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:
;
; 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:
; 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
; 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
; 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*
; 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*
; /* 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*
; /* 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*
; /* 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)
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 #
; }
;
; 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 {
; }
;
; 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 {
; }
;
; 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 {
; }
;
; 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 {
; }
;
; 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 {
--- /dev/null
+; 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
+}
; 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
; 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()
; 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()
;
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:
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:
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:
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:
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:
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:
; 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 #