[llvm] e5822de - [FunctionAttrs] Infer argmemonly .
Florian Hahn via llvm-commits
llvm-commits at lists.llvm.org
Wed Mar 16 03:24:42 PDT 2022
Author: Florian Hahn
Date: 2022-03-16T10:24:33Z
New Revision: e5822ded562fd233ff7468d88da9e2c4f1a96c70
URL: https://github.com/llvm/llvm-project/commit/e5822ded562fd233ff7468d88da9e2c4f1a96c70
DIFF: https://github.com/llvm/llvm-project/commit/e5822ded562fd233ff7468d88da9e2c4f1a96c70.diff
LOG: [FunctionAttrs] Infer argmemonly .
This patch adds initial argmemonly inference, by checking the underlying
objects of locations returned by MemoryLocation.
I think this should cover most cases, except function calls to other
argmemonly functions.
I'm not sure if there's a reason why we don't infer those yet.
Additional argmemonly can improve codegen in some cases. It also makes
it easier to come up with a C reproducer for 7662d1687b09 (already fixed,
but I'm trying to see if C/C++ fuzzing could help to uncover similar
issues.)
Compile-time impact:
NewPM-O3: +0.01%
NewPM-ReleaseThinLTO: +0.03%
NewPM-ReleaseLTO+g: +0.05%
https://llvm-compile-time-tracker.com/compare.php?from=067c035012fc061ad6378458774ac2df117283c6&to=fe209d4aab5b593bd62d18c0876732ddcca1614d&stat=instructions
Reviewed By: nikic
Differential Revision: https://reviews.llvm.org/D121415
Added:
Modified:
llvm/lib/Transforms/IPO/FunctionAttrs.cpp
llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll
llvm/test/CodeGen/AMDGPU/inline-attr.ll
llvm/test/Transforms/FunctionAttrs/argmemonly.ll
llvm/test/Transforms/FunctionAttrs/atomic.ll
llvm/test/Transforms/FunctionAttrs/nofree.ll
llvm/test/Transforms/FunctionAttrs/nosync.ll
llvm/test/Transforms/FunctionAttrs/stats.ll
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index 06aab4d7941c2..f916d184085f6 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -69,6 +69,7 @@ using namespace llvm;
#define DEBUG_TYPE "function-attrs"
+STATISTIC(NumArgMemOnly, "Number of functions marked argmemonly");
STATISTIC(NumReadNone, "Number of functions marked readnone");
STATISTIC(NumReadOnly, "Number of functions marked readonly");
STATISTIC(NumWriteOnly, "Number of functions marked writeonly");
@@ -135,6 +136,14 @@ checkFunctionMemoryAccess(Function &F, bool ThisBody, AAResults &AAR,
// Scan the function body for instructions that may read or write memory.
bool ReadsMemory = false;
bool WritesMemory = false;
+ // Track if the function accesses memory not based on pointer arguments or
+ // allocas.
+ bool AccessesNonArgsOrAlloca = false;
+ // Returns true if Ptr is not based on a function argument.
+ auto IsArgumentOrAlloca = [](const Value *Ptr) {
+ const Value *UO = getUnderlyingObject(Ptr);
+ return isa<Argument>(UO) || isa<AllocaInst>(UO);
+ };
for (Instruction &I : instructions(F)) {
// Some instructions can be ignored even if they read or write memory.
// Detect these now, skipping to the next instruction if one is found.
@@ -167,6 +176,7 @@ checkFunctionMemoryAccess(Function &F, bool ThisBody, AAResults &AAR,
// If it reads, note it.
if (isRefSet(MRI))
ReadsMemory = true;
+ AccessesNonArgsOrAlloca = true;
continue;
}
@@ -179,12 +189,13 @@ checkFunctionMemoryAccess(Function &F, bool ThisBody, AAResults &AAR,
MemoryLocation Loc =
MemoryLocation::getBeforeOrAfter(Arg, I.getAAMetadata());
-
// Skip accesses to local or constant memory as they don't impact the
// externally visible mod/ref behavior.
if (AAR.pointsToConstantMemory(Loc, /*OrLocal=*/true))
continue;
+ AccessesNonArgsOrAlloca |= !IsArgumentOrAlloca(Loc.Ptr);
+
if (isModSet(MRI))
// Writes non-local memory.
WritesMemory = true;
@@ -194,24 +205,29 @@ checkFunctionMemoryAccess(Function &F, bool ThisBody, AAResults &AAR,
}
continue;
} else if (LoadInst *LI = dyn_cast<LoadInst>(&I)) {
+ MemoryLocation Loc = MemoryLocation::get(LI);
// Ignore non-volatile loads from local memory. (Atomic is okay here.)
- if (!LI->isVolatile()) {
- MemoryLocation Loc = MemoryLocation::get(LI);
- if (AAR.pointsToConstantMemory(Loc, /*OrLocal=*/true))
- continue;
- }
+ if (!LI->isVolatile() &&
+ AAR.pointsToConstantMemory(Loc, /*OrLocal=*/true))
+ continue;
+ AccessesNonArgsOrAlloca |= !IsArgumentOrAlloca(Loc.Ptr);
} else if (StoreInst *SI = dyn_cast<StoreInst>(&I)) {
+ MemoryLocation Loc = MemoryLocation::get(SI);
// Ignore non-volatile stores to local memory. (Atomic is okay here.)
- if (!SI->isVolatile()) {
- MemoryLocation Loc = MemoryLocation::get(SI);
- if (AAR.pointsToConstantMemory(Loc, /*OrLocal=*/true))
- continue;
- }
+ if (!SI->isVolatile() &&
+ AAR.pointsToConstantMemory(Loc, /*OrLocal=*/true))
+ continue;
+ AccessesNonArgsOrAlloca |= !IsArgumentOrAlloca(Loc.Ptr);
} else if (VAArgInst *VI = dyn_cast<VAArgInst>(&I)) {
// Ignore vaargs on local memory.
MemoryLocation Loc = MemoryLocation::get(VI);
if (AAR.pointsToConstantMemory(Loc, /*OrLocal=*/true))
continue;
+ AccessesNonArgsOrAlloca |= !IsArgumentOrAlloca(Loc.Ptr);
+ } else {
+ // If AccessesNonArgsOrAlloca has not been updated above, set it
+ // conservatively.
+ AccessesNonArgsOrAlloca |= I.mayReadOrWriteMemory();
}
// Any remaining instructions need to be taken seriously! Check if they
@@ -224,14 +240,17 @@ checkFunctionMemoryAccess(Function &F, bool ThisBody, AAResults &AAR,
ReadsMemory |= I.mayReadFromMemory();
}
- if (WritesMemory) {
- if (!ReadsMemory)
- return FMRB_OnlyWritesMemory;
- else
- return FMRB_UnknownModRefBehavior;
- }
-
- return ReadsMemory ? FMRB_OnlyReadsMemory : FMRB_DoesNotAccessMemory;
+ if (!WritesMemory && !ReadsMemory)
+ return FMRB_DoesNotAccessMemory;
+
+ FunctionModRefBehavior Result = FunctionModRefBehavior(FMRL_Anywhere);
+ if (!AccessesNonArgsOrAlloca)
+ Result = FunctionModRefBehavior(FMRL_ArgumentPointees);
+ if (WritesMemory)
+ Result = FunctionModRefBehavior(Result | static_cast<int>(ModRefInfo::Mod));
+ if (ReadsMemory)
+ Result = FunctionModRefBehavior(Result | static_cast<int>(ModRefInfo::Ref));
+ return Result;
}
FunctionModRefBehavior llvm::computeFunctionBodyMemoryAccess(Function &F,
@@ -247,32 +266,48 @@ static void addMemoryAttrs(const SCCNodeSet &SCCNodes, AARGetterT &&AARGetter,
// write memory then they can't be marked readnone or readonly.
bool ReadsMemory = false;
bool WritesMemory = false;
+ // Check if all functions only access memory through their arguments.
+ bool ArgMemOnly = true;
for (Function *F : SCCNodes) {
// Call the callable parameter to look up AA results for this function.
AAResults &AAR = AARGetter(*F);
-
// Non-exact function definitions may not be selected at link time, and an
// alternative version that writes to memory may be selected. See the
// comment on GlobalValue::isDefinitionExact for more details.
FunctionModRefBehavior FMRB =
checkFunctionMemoryAccess(*F, F->hasExactDefinition(), AAR, SCCNodes);
- if (isModAndRefSet(createModRefInfo(FMRB)))
- return;
if (FMRB == FMRB_DoesNotAccessMemory)
continue;
- ReadsMemory |= AliasAnalysis::onlyReadsMemory(FMRB);
- WritesMemory |= AliasAnalysis::onlyWritesMemory(FMRB);
+ ModRefInfo MR = createModRefInfo(FMRB);
+ ReadsMemory |= isRefSet(MR);
+ WritesMemory |= isModSet(MR);
+ ArgMemOnly &= AliasAnalysis::onlyAccessesArgPointees(FMRB);
+ // Reached neither readnone, readonly, writeonly nor argmemonly can be
+ // inferred. Exit.
+ if (ReadsMemory && WritesMemory && !ArgMemOnly)
+ return;
}
- // If the SCC contains both functions that read and functions that write, then
- // we cannot add readonly attributes.
- if (ReadsMemory && WritesMemory)
- return;
-
- // Success! Functions in this SCC do not access memory, or only read memory.
- // Give them the appropriate attribute.
+ assert((!ReadsMemory || !WritesMemory || ArgMemOnly) &&
+ "no memory attributes can be added for this SCC, should have exited "
+ "earlier");
+ // Success! Functions in this SCC do not access memory, only read memory,
+ // only write memory, or only access memory through its arguments. Give them
+ // the appropriate attribute.
for (Function *F : SCCNodes) {
+ // If possible add argmemonly attribute to F, if it accesses memory.
+ if (ArgMemOnly && !F->onlyAccessesArgMemory() &&
+ (ReadsMemory || WritesMemory)) {
+ NumArgMemOnly++;
+ F->addFnAttr(Attribute::ArgMemOnly);
+ Changed.insert(F);
+ }
+
+ // The SCC contains functions both writing and reading from memory. We
+ // cannot add readonly or writeonline attributes.
+ if (ReadsMemory && WritesMemory)
+ continue;
if (F->doesNotAccessMemory())
// Already perfect!
continue;
diff --git a/llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll b/llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll
index e0c9e45101f33..caad8244c1dca 100644
--- a/llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll
+++ b/llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll
@@ -73,12 +73,12 @@ declare void @callee(i32* %p) nounwind
declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i1) nounwind
; CHECK: attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone willreturn }
-; CHECK: attributes #1 = { mustprogress nofree norecurse nosync nounwind willreturn writeonly }
+; CHECK: attributes #1 = { argmemonly mustprogress nofree norecurse nosync nounwind willreturn writeonly }
; CHECK: attributes #2 = { nofree nounwind readonly }
; CHECK: attributes #3 = { nounwind }
; CHECK: attributes #4 = { mustprogress nofree nosync nounwind readnone willreturn }
-; CHECK: attributes #5 = { mustprogress nofree nosync nounwind willreturn }
-; CHECK: attributes #6 = { mustprogress nofree norecurse nosync nounwind willreturn }
+; CHECK: attributes #5 = { argmemonly mustprogress nofree nosync nounwind willreturn }
+; CHECK: attributes #6 = { argmemonly mustprogress nofree norecurse nosync nounwind willreturn }
; CHECK: attributes #7 = { argmemonly nofree nounwind willreturn }
; Root note.
diff --git a/llvm/test/CodeGen/AMDGPU/inline-attr.ll b/llvm/test/CodeGen/AMDGPU/inline-attr.ll
index a65b9a52612a8..68bc38bd9f4a0 100644
--- a/llvm/test/CodeGen/AMDGPU/inline-attr.ll
+++ b/llvm/test/CodeGen/AMDGPU/inline-attr.ll
@@ -7,13 +7,13 @@
; GCN: %mul.i = fmul float %load, 1.500000e+01
; UNSAFE: attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone willreturn "unsafe-fp-math"="true" }
-; UNSAFE: attributes #1 = { mustprogress nofree norecurse nosync nounwind willreturn "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "unsafe-fp-math"="true" }
+; UNSAFE: attributes #1 = { argmemonly mustprogress nofree norecurse nosync nounwind willreturn "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "unsafe-fp-math"="true" }
; NOINFS: attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone willreturn "no-infs-fp-math"="true" }
-; NOINFS: attributes #1 = { mustprogress nofree norecurse nosync nounwind willreturn "less-precise-fpmad"="false" "no-infs-fp-math"="true" "no-nans-fp-math"="false" "unsafe-fp-math"="false" }
+; NOINFS: attributes #1 = { argmemonly mustprogress nofree norecurse nosync nounwind willreturn "less-precise-fpmad"="false" "no-infs-fp-math"="true" "no-nans-fp-math"="false" "unsafe-fp-math"="false" }
; NONANS: attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone willreturn "no-nans-fp-math"="true" }
-; NONANS: attributes #1 = { mustprogress nofree norecurse nosync nounwind willreturn "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="true" "unsafe-fp-math"="false" }
+; NONANS: attributes #1 = { argmemonly mustprogress nofree norecurse nosync nounwind willreturn "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="true" "unsafe-fp-math"="false" }
define float @foo(float %x) #0 {
entry:
diff --git a/llvm/test/Transforms/FunctionAttrs/argmemonly.ll b/llvm/test/Transforms/FunctionAttrs/argmemonly.ll
index a0dec5c53bf93..000f374d12799 100644
--- a/llvm/test/Transforms/FunctionAttrs/argmemonly.ll
+++ b/llvm/test/Transforms/FunctionAttrs/argmemonly.ll
@@ -14,7 +14,7 @@ entry:
}
define i32 @test_only_read_arg(i32* %ptr) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind readonly willreturn
+; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nosync nounwind readonly willreturn
; CHECK-LABEL: @test_only_read_arg(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[L:%.*]] = load i32, i32* [[PTR:%.*]], align 4
@@ -52,7 +52,7 @@ entry:
}
define void @test_only_write_arg(i32* %ptr) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly
+; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nosync nounwind willreturn writeonly
; CHECK-LABEL: @test_only_write_arg(
; CHECK-NEXT: entry:
; CHECK-NEXT: store i32 0, i32* [[PTR:%.*]], align 4
@@ -91,7 +91,7 @@ entry:
declare i32 @fn_readnone() readnone
define void @test_call_readnone(i32* %ptr) {
-; CHECK: Function Attrs: writeonly
+; CHECK: Function Attrs: argmemonly writeonly
; CHECK-LABEL: @test_call_readnone(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[C:%.*]] = call i32 @fn_readnone()
@@ -118,7 +118,7 @@ entry:
}
define i32 @test_call_fn_where_argmemonly_can_be_inferred(i32* %ptr) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind readonly willreturn
+; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nosync nounwind readonly willreturn
; CHECK-LABEL: @test_call_fn_where_argmemonly_can_be_inferred(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[C:%.*]] = call i32 @test_only_read_arg(i32* [[PTR:%.*]])
@@ -130,7 +130,7 @@ entry:
}
define void @test_memcpy_argonly(i8* %dst, i8* %src) {
-; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn
+; CHECK: Function Attrs: argmemonly mustprogress nofree nosync nounwind willreturn
; CHECK-LABEL: @test_memcpy_argonly(
; CHECK-NEXT: entry:
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[DST:%.*]], i8* [[SRC:%.*]], i64 32, i1 false)
@@ -174,7 +174,7 @@ entry:
}
define i32 @test_read_arg_access_alloca(i32* %ptr) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind readonly willreturn
+; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nosync nounwind readonly willreturn
; CHECK-LABEL: @test_read_arg_access_alloca(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
diff --git a/llvm/test/Transforms/FunctionAttrs/atomic.ll b/llvm/test/Transforms/FunctionAttrs/atomic.ll
index d6d4a9b36b7ce..ff3d4b387c8a3 100644
--- a/llvm/test/Transforms/FunctionAttrs/atomic.ll
+++ b/llvm/test/Transforms/FunctionAttrs/atomic.ll
@@ -21,4 +21,4 @@ entry:
}
; CHECK: attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone ssp willreturn uwtable }
-; CHECK: attributes #1 = { mustprogress nofree norecurse nounwind ssp willreturn uwtable }
+; CHECK: attributes #1 = { argmemonly mustprogress nofree norecurse nounwind ssp willreturn uwtable }
diff --git a/llvm/test/Transforms/FunctionAttrs/nofree.ll b/llvm/test/Transforms/FunctionAttrs/nofree.ll
index bbf346056a7ef..8524046973269 100644
--- a/llvm/test/Transforms/FunctionAttrs/nofree.ll
+++ b/llvm/test/Transforms/FunctionAttrs/nofree.ll
@@ -36,7 +36,7 @@ entry:
declare void @free(i8* nocapture) local_unnamed_addr #2
define i32 @_Z4foo3Pi(i32* nocapture readonly %a) local_unnamed_addr #3 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind readonly willreturn uwtable
+; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nosync nounwind readonly willreturn uwtable
; CHECK-LABEL: @_Z4foo3Pi(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[A:%.*]], align 4
diff --git a/llvm/test/Transforms/FunctionAttrs/nosync.ll b/llvm/test/Transforms/FunctionAttrs/nosync.ll
index eba9213ff3f65..64225dad94fb4 100644
--- a/llvm/test/Transforms/FunctionAttrs/nosync.ll
+++ b/llvm/test/Transforms/FunctionAttrs/nosync.ll
@@ -49,7 +49,7 @@ define i32 @test4(i32 %a, i32 %b) {
; negative case - explicit sync
define void @test5(i8* %p) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn
+; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nounwind willreturn
; CHECK-LABEL: @test5(
; CHECK-NEXT: store atomic i8 0, i8* [[P:%.*]] seq_cst, align 1
; CHECK-NEXT: ret void
@@ -60,7 +60,7 @@ define void @test5(i8* %p) {
; negative case - explicit sync
define i8 @test6(i8* %p) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn
+; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nounwind willreturn
; CHECK-LABEL: @test6(
; CHECK-NEXT: [[V:%.*]] = load atomic i8, i8* [[P:%.*]] seq_cst, align 1
; CHECK-NEXT: ret i8 [[V]]
@@ -104,7 +104,7 @@ define void @test9(i8* %p) {
; atomic load with monotonic ordering
define i32 @load_monotonic(i32* nocapture readonly %0) norecurse nounwind uwtable {
-; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn uwtable
+; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nounwind willreturn uwtable
; CHECK-LABEL: @load_monotonic(
; CHECK-NEXT: [[TMP2:%.*]] = load atomic i32, i32* [[TMP0:%.*]] monotonic, align 4
; CHECK-NEXT: ret i32 [[TMP2]]
@@ -115,7 +115,7 @@ define i32 @load_monotonic(i32* nocapture readonly %0) norecurse nounwind uwtabl
; atomic store with monotonic ordering.
define void @store_monotonic(i32* nocapture %0) norecurse nounwind uwtable {
-; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn uwtable
+; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nounwind willreturn uwtable
; CHECK-LABEL: @store_monotonic(
; CHECK-NEXT: store atomic i32 10, i32* [[TMP0:%.*]] monotonic, align 4
; CHECK-NEXT: ret void
@@ -127,7 +127,7 @@ define void @store_monotonic(i32* nocapture %0) norecurse nounwind uwtable {
; negative, should not deduce nosync
; atomic load with acquire ordering.
define i32 @load_acquire(i32* nocapture readonly %0) norecurse nounwind uwtable {
-; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn uwtable
+; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nounwind willreturn uwtable
; CHECK-LABEL: @load_acquire(
; CHECK-NEXT: [[TMP2:%.*]] = load atomic i32, i32* [[TMP0:%.*]] acquire, align 4
; CHECK-NEXT: ret i32 [[TMP2]]
@@ -137,7 +137,7 @@ define i32 @load_acquire(i32* nocapture readonly %0) norecurse nounwind uwtable
}
define i32 @load_unordered(i32* nocapture readonly %0) norecurse nounwind uwtable {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind readonly willreturn uwtable
+; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nosync nounwind readonly willreturn uwtable
; CHECK-LABEL: @load_unordered(
; CHECK-NEXT: [[TMP2:%.*]] = load atomic i32, i32* [[TMP0:%.*]] unordered, align 4
; CHECK-NEXT: ret i32 [[TMP2]]
@@ -148,7 +148,7 @@ define i32 @load_unordered(i32* nocapture readonly %0) norecurse nounwind uwtabl
; atomic store with unordered ordering.
define void @store_unordered(i32* nocapture %0) norecurse nounwind uwtable {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly uwtable
+; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nosync nounwind willreturn writeonly uwtable
; CHECK-LABEL: @store_unordered(
; CHECK-NEXT: store atomic i32 10, i32* [[TMP0:%.*]] unordered, align 4
; CHECK-NEXT: ret void
@@ -161,7 +161,7 @@ define void @store_unordered(i32* nocapture %0) norecurse nounwind uwtable {
; negative, should not deduce nosync
; atomic load with release ordering
define void @load_release(i32* nocapture %0) norecurse nounwind uwtable {
-; CHECK: Function Attrs: nofree norecurse nounwind uwtable
+; CHECK: Function Attrs: argmemonly nofree norecurse nounwind uwtable
; CHECK-LABEL: @load_release(
; CHECK-NEXT: store atomic volatile i32 10, i32* [[TMP0:%.*]] release, align 4
; CHECK-NEXT: ret void
@@ -172,7 +172,7 @@ define void @load_release(i32* nocapture %0) norecurse nounwind uwtable {
; negative volatile, relaxed atomic
define void @load_volatile_release(i32* nocapture %0) norecurse nounwind uwtable {
-; CHECK: Function Attrs: nofree norecurse nounwind uwtable
+; CHECK: Function Attrs: argmemonly nofree norecurse nounwind uwtable
; CHECK-LABEL: @load_volatile_release(
; CHECK-NEXT: store atomic volatile i32 10, i32* [[TMP0:%.*]] release, align 4
; CHECK-NEXT: ret void
@@ -183,7 +183,7 @@ define void @load_volatile_release(i32* nocapture %0) norecurse nounwind uwtable
; volatile store.
define void @volatile_store(i32* %0) norecurse nounwind uwtable {
-; CHECK: Function Attrs: nofree norecurse nounwind uwtable
+; CHECK: Function Attrs: argmemonly nofree norecurse nounwind uwtable
; CHECK-LABEL: @volatile_store(
; CHECK-NEXT: store volatile i32 14, i32* [[TMP0:%.*]], align 4
; CHECK-NEXT: ret void
@@ -195,7 +195,7 @@ define void @volatile_store(i32* %0) norecurse nounwind uwtable {
; negative, should not deduce nosync
; volatile load.
define i32 @volatile_load(i32* %0) norecurse nounwind uwtable {
-; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn uwtable
+; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nounwind willreturn uwtable
; CHECK-LABEL: @volatile_load(
; CHECK-NEXT: [[TMP2:%.*]] = load volatile i32, i32* [[TMP0:%.*]], align 4
; CHECK-NEXT: ret i32 [[TMP2]]
@@ -237,7 +237,7 @@ declare void @llvm.memset(i8* %dest, i8 %val, i32 %len, i1 %isvolatile)
; negative, checking volatile intrinsics.
define i32 @memcpy_volatile(i8* %ptr1, i8* %ptr2) {
-; CHECK: Function Attrs: mustprogress nofree nounwind willreturn
+; CHECK: Function Attrs: argmemonly mustprogress nofree nounwind willreturn
; CHECK-LABEL: @memcpy_volatile(
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* [[PTR1:%.*]], i8* [[PTR2:%.*]], i32 8, i1 true)
; CHECK-NEXT: ret i32 4
@@ -248,7 +248,7 @@ define i32 @memcpy_volatile(i8* %ptr1, i8* %ptr2) {
; positive, non-volatile intrinsic.
define i32 @memset_non_volatile(i8* %ptr1, i8 %val) {
-; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn writeonly
+; CHECK: Function Attrs: argmemonly mustprogress nofree nosync nounwind willreturn writeonly
; CHECK-LABEL: @memset_non_volatile(
; CHECK-NEXT: call void @llvm.memset.p0i8.i32(i8* [[PTR1:%.*]], i8 [[VAL:%.*]], i32 8, i1 false)
; CHECK-NEXT: ret i32 4
diff --git a/llvm/test/Transforms/FunctionAttrs/stats.ll b/llvm/test/Transforms/FunctionAttrs/stats.ll
index 4697495de2383..2c26f7e7db05a 100644
--- a/llvm/test/Transforms/FunctionAttrs/stats.ll
+++ b/llvm/test/Transforms/FunctionAttrs/stats.ll
@@ -16,7 +16,8 @@ entry:
ret void
}
-; CHECK: 1 function-attrs - Number of arguments marked nocapture
+; CHECK: 1 function-attrs - Number of functions marked argmemonly
+; CHECK-NEXT: 1 function-attrs - Number of arguments marked nocapture
; CHECK-NEXT: 1 function-attrs - Number of functions marked as nofree
; CHECK-NEXT: 2 function-attrs - Number of functions marked as norecurse
; CHECK-NEXT: 2 function-attrs - Number of functions marked as nosync
More information about the llvm-commits
mailing list