1
0
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:
Stefan Stipanovic 2019-07-11 21:37:40 +00:00
parent 00406d9acb
commit c892f2c16e
19 changed files with 675 additions and 60 deletions

View File

@ -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

View File

@ -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 {

View File

@ -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">;

View File

@ -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.
@ -680,6 +680,26 @@ struct AANoUnwind : public AbstractAttribute {
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
#endif // LLVM_TRANSFORMS_IPO_FUNCTIONATTRS_H

View File

@ -658,6 +658,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(nonnull);
KEYWORD(noredzone);
KEYWORD(noreturn);
KEYWORD(nosync);
KEYWORD(nocf_check);
KEYWORD(nounwind);
KEYWORD(optforfuzzing);

View File

@ -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;

View File

@ -203,6 +203,7 @@ enum Kind {
kw_nonnull,
kw_noredzone,
kw_noreturn,
kw_nosync,
kw_nocf_check,
kw_nounwind,
kw_optforfuzzing,

View File

@ -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:

View File

@ -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:

View File

@ -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))

View File

@ -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:

View File

@ -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()) {

View File

@ -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:

View File

@ -204,7 +204,7 @@ define void @f34()
; CHECK: define void @f34()
{
call void @nobuiltin() nobuiltin
; CHECK: call void @nobuiltin() #38
; 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 }

View File

@ -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 #

View File

@ -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 {

View 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
}

View File

@ -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()

View File

@ -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 #