[clang] [llvm] [mlir] [clang][CodeGen][AA] Add `!llvm.errno.tbaa` gathering int-compatible TBAA nodes (PR #125258)
Antonio Frighetto via llvm-commits
llvm-commits at lists.llvm.org
Fri Jan 31 09:40:07 PST 2025
https://github.com/antoniofrighetto created https://github.com/llvm/llvm-project/pull/125258
None
>From 54d3ac991abaa341dd4798bbf6aeb3ede4441d64 Mon Sep 17 00:00:00 2001
From: Antonio Frighetto <me at antoniofrighetto.com>
Date: Fri, 20 Dec 2024 19:30:59 +0100
Subject: [PATCH 1/3] [IR][ModRef] Introduce `errno` memory location
Model C/C++ `errno` macro by adding a corresponding `errno`
memory location kind to the IR. Preliminary work to separate
`errno` writes from other memory accesses, to the benefit of
alias analyses and optimization correctness.
Previous discussion: https://discourse.llvm.org/t/rfc-modelling-errno-memory-effects/82972.
---
.../CodeGen/sanitize-metadata-nosanitize.c | 18 +++---
clang/test/CodeGenOpenCL/convergent.cl | 2 +-
llvm/include/llvm/AsmParser/LLToken.h | 4 ++
llvm/include/llvm/Bitcode/LLVMBitCodes.h | 3 +
llvm/include/llvm/IR/Function.h | 15 +++++
llvm/include/llvm/IR/InstrTypes.h | 15 +++++
llvm/include/llvm/Support/ModRef.h | 57 ++++++++++++++++++-
llvm/lib/AsmParser/LLLexer.cpp | 4 ++
llvm/lib/AsmParser/LLParser.cpp | 13 ++++-
llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 28 +++++++++
llvm/lib/IR/Attributes.cpp | 3 +
llvm/lib/IR/Function.cpp | 29 ++++++++++
llvm/lib/IR/Instructions.cpp | 29 ++++++++++
llvm/lib/Support/ModRef.cpp | 3 +
.../test/Assembler/memory-attribute-errors.ll | 6 +-
llvm/test/Assembler/memory-attribute.ll | 12 ++++
.../Transforms/FunctionAttrs/argmemonly.ll | 22 +++----
.../Transforms/FunctionAttrs/nocapture.ll | 30 +++++-----
.../FunctionAttrs/read-write-scc.ll | 4 +-
.../Transforms/FunctionAttrs/readattrs.ll | 2 +-
.../Transforms/FunctionAttrs/writeonly.ll | 4 +-
.../InferFunctionAttrs/norecurse_debug.ll | 2 +-
.../cfi-nounwind-direct-call.ll | 2 +-
.../Transforms/SCCP/ipscp-drop-argmemonly.ll | 12 ++--
llvm/unittests/Support/ModRefTest.cpp | 4 +-
mlir/test/Target/LLVMIR/llvmir.mlir | 10 ++--
26 files changed, 272 insertions(+), 61 deletions(-)
diff --git a/clang/test/CodeGen/sanitize-metadata-nosanitize.c b/clang/test/CodeGen/sanitize-metadata-nosanitize.c
index fd2fdce31b52fb..77dc7d9d492ecc 100644
--- a/clang/test/CodeGen/sanitize-metadata-nosanitize.c
+++ b/clang/test/CodeGen/sanitize-metadata-nosanitize.c
@@ -10,7 +10,7 @@
// CHECK: @llvm.global_ctors = appending global [2 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 2, ptr @__sanitizer_metadata_covered2.module_ctor, ptr @__sanitizer_metadata_covered2.module_ctor }, { i32, ptr, ptr } { i32 2, ptr @__sanitizer_metadata_atomics2.module_ctor, ptr @__sanitizer_metadata_atomics2.module_ctor }]
// CHECK: @llvm.global_dtors = appending global [2 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 2, ptr @__sanitizer_metadata_covered2.module_dtor, ptr @__sanitizer_metadata_covered2.module_dtor }, { i32, ptr, ptr } { i32 2, ptr @__sanitizer_metadata_atomics2.module_dtor, ptr @__sanitizer_metadata_atomics2.module_dtor }]
//.
-// CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none)
+// CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, errnomem: none)
// CHECK-LABEL: define dso_local void @escape
// CHECK-SAME: (ptr noundef [[P:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] !pcsections [[META2:![0-9]+]] {
// CHECK-NEXT: entry:
@@ -21,7 +21,7 @@ __attribute__((noinline, not_tail_called)) void escape(const volatile void *p) {
sink = p;
}
-// CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none)
+// CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, errnomem: none)
// CHECK-LABEL: define dso_local i32 @normal_function
// CHECK-SAME: (ptr noundef [[X:%.*]], ptr nocapture noundef readonly [[Y:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] !pcsections [[META4:![0-9]+]] {
// CHECK-NEXT: entry:
@@ -38,7 +38,7 @@ int normal_function(int *x, int *y) {
return *y;
}
-// CHECK: Function Attrs: disable_sanitizer_instrumentation mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none)
+// CHECK: Function Attrs: disable_sanitizer_instrumentation mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, errnomem: none)
// CHECK-LABEL: define dso_local i32 @test_disable_sanitize_instrumentation
// CHECK-SAME: (ptr noundef [[X:%.*]], ptr nocapture noundef readonly [[Y:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] {
// CHECK-NEXT: entry:
@@ -55,7 +55,7 @@ __attribute__((disable_sanitizer_instrumentation)) int test_disable_sanitize_ins
return *y;
}
-// CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none)
+// CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, errnomem: none)
// CHECK-LABEL: define dso_local i32 @test_no_sanitize_thread
// CHECK-SAME: (ptr noundef [[X:%.*]], ptr nocapture noundef readonly [[Y:%.*]]) local_unnamed_addr #[[ATTR3:[0-9]+]] !pcsections [[META14:![0-9]+]] {
// CHECK-NEXT: entry:
@@ -72,7 +72,7 @@ __attribute__((no_sanitize("thread"))) int test_no_sanitize_thread(int *x, int *
return *y;
}
-// CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none)
+// CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, errnomem: none)
// CHECK-LABEL: define dso_local i32 @test_no_sanitize_all
// CHECK-SAME: (ptr noundef [[X:%.*]], ptr nocapture noundef readonly [[Y:%.*]]) local_unnamed_addr #[[ATTR3]] !pcsections [[META14]] {
// CHECK-NEXT: entry:
@@ -89,10 +89,10 @@ __attribute__((no_sanitize("all"))) int test_no_sanitize_all(int *x, int *y) {
return *y;
}
//.
-// CHECK: attributes #[[ATTR0]] = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
-// CHECK: attributes #[[ATTR1]] = { mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
-// CHECK: attributes #[[ATTR2]] = { disable_sanitizer_instrumentation mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
-// CHECK: attributes #[[ATTR3]] = { mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none) "min-legal-vector-width"="0" "no-trapping-math"="true" "no_sanitize_thread" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
+// CHECK: attributes #[[ATTR0]] = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, errnomem: none) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
+// CHECK: attributes #[[ATTR1]] = { mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, errnomem: none) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
+// CHECK: attributes #[[ATTR2]] = { disable_sanitizer_instrumentation mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, errnomem: none) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
+// CHECK: attributes #[[ATTR3]] = { mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, errnomem: none) "min-legal-vector-width"="0" "no-trapping-math"="true" "no_sanitize_thread" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
// CHECK: attributes #[[ATTR4:[0-9]+]] = { nounwind "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
//.
// CHECK: [[META0:![0-9]+]] = !{i32 1, !"wchar_size", i32 4}
diff --git a/clang/test/CodeGenOpenCL/convergent.cl b/clang/test/CodeGenOpenCL/convergent.cl
index 123adba7b40d2c..d64915205aabf6 100644
--- a/clang/test/CodeGenOpenCL/convergent.cl
+++ b/clang/test/CodeGenOpenCL/convergent.cl
@@ -133,7 +133,7 @@ kernel void assume_convergent_asm()
__asm__ volatile("s_barrier");
}
-// CHECK: attributes #0 = { nofree noinline norecurse nounwind "
+// CHECK: attributes #0 = { nofree noinline norecurse nounwind memory(readwrite, errnomem: none) "
// CHECK: attributes #1 = { {{[^}]*}}convergent{{[^}]*}} }
// CHECK: attributes #2 = { {{[^}]*}}convergent{{[^}]*}} }
// CHECK: attributes #3 = { {{[^}]*}}convergent noduplicate{{[^}]*}} }
diff --git a/llvm/include/llvm/AsmParser/LLToken.h b/llvm/include/llvm/AsmParser/LLToken.h
index 7b47bc88ddb25f..e36ebcb0f13b08 100644
--- a/llvm/include/llvm/AsmParser/LLToken.h
+++ b/llvm/include/llvm/AsmParser/LLToken.h
@@ -201,11 +201,15 @@ enum Kind {
kw_readwrite,
kw_argmem,
kw_inaccessiblemem,
+ kw_errnomem,
// Legacy memory attributes:
kw_argmemonly,
kw_inaccessiblememonly,
kw_inaccessiblemem_or_argmemonly,
+ kw_inaccessiblemem_or_errnomemonly,
+ kw_inaccessiblemem_or_argmem_or_errnomemonly,
+ kw_errnomemonly,
// Captures attribute:
kw_address,
diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index 9eb38c3e448291..4d4911932fb181 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -789,6 +789,9 @@ enum AttributeKindCodes {
ATTR_KIND_NO_DIVERGENCE_SOURCE = 100,
ATTR_KIND_SANITIZE_TYPE = 101,
ATTR_KIND_CAPTURES = 102,
+ ATTR_KIND_ERRNOMEMONLY = 103,
+ ATTR_KIND_INACCESSIBLEMEM_OR_ERRNOMEMONLY = 104,
+ ATTR_KIND_INACCESSIBLEMEM_OR_ARGMEM_OR_ERRNOMEMONLY = 105,
};
enum ComdatSelectionKindCodes {
diff --git a/llvm/include/llvm/IR/Function.h b/llvm/include/llvm/IR/Function.h
index e7afcbd31420c1..847b8367743234 100644
--- a/llvm/include/llvm/IR/Function.h
+++ b/llvm/include/llvm/IR/Function.h
@@ -575,11 +575,26 @@ class LLVM_ABI Function : public GlobalObject, public ilist_node<Function> {
bool onlyAccessesInaccessibleMemory() const;
void setOnlyAccessesInaccessibleMemory();
+ /// Determine if the function may only access errno memory.
+ bool onlyAccessesErrnoMemory() const;
+ void setOnlyAccessesErrnoMemory();
+
/// Determine if the function may only access memory that is
/// either inaccessible from the IR or pointed to by its arguments.
bool onlyAccessesInaccessibleMemOrArgMem() const;
void setOnlyAccessesInaccessibleMemOrArgMem();
+ /// Determine if the function may only access memory that is
+ /// either inaccessible from the IR or errno memory.
+ bool onlyAccessesInaccessibleMemOrErrnoMem() const;
+ void setOnlyAccessesInaccessibleMemOrErrnoMem();
+
+ /// Determine if the function may only access memory that is
+ /// either inaccessible from the IR, pointed to by its arguments, or errno
+ /// memory.
+ bool onlyAccessesInaccessibleMemOrArgMemOrErrnoMem() const;
+ void setOnlyAccessesInaccessibleMemOrArgMemOrErrnoMem();
+
/// Determine if the function cannot return.
bool doesNotReturn() const {
return hasFnAttribute(Attribute::NoReturn);
diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h
index 47ddc7555594c5..5ef72e09292f3a 100644
--- a/llvm/include/llvm/IR/InstrTypes.h
+++ b/llvm/include/llvm/IR/InstrTypes.h
@@ -1909,11 +1909,26 @@ class CallBase : public Instruction {
bool onlyAccessesInaccessibleMemory() const;
void setOnlyAccessesInaccessibleMemory();
+ /// Determine if the function may only access errno memory.
+ bool onlyAccessesErrnoMemory() const;
+ void setOnlyAccessesErrnoMemory();
+
/// Determine if the function may only access memory that is
/// either inaccessible from the IR or pointed to by its arguments.
bool onlyAccessesInaccessibleMemOrArgMem() const;
void setOnlyAccessesInaccessibleMemOrArgMem();
+ /// Determine if the function may only access memory that is
+ /// either inaccessible from the IR or errno memory.
+ bool onlyAccessesInaccessibleMemOrErrnoMem() const;
+ void setOnlyAccessesInaccessibleMemOrErrnoMem();
+
+ /// Determine if the function may only access memory that is
+ /// either inaccessible from the IR, pointed to by its arguments, or errno
+ /// memory.
+ bool onlyAccessesInaccessibleMemOrArgMemOrErrnoMem() const;
+ void setOnlyAccessesInaccessibleMemOrArgMemOrErrnoMem();
+
/// Determine if the call cannot return.
bool doesNotReturn() const { return hasFnAttr(Attribute::NoReturn); }
void setDoesNotReturn() { addFnAttr(Attribute::NoReturn); }
diff --git a/llvm/include/llvm/Support/ModRef.h b/llvm/include/llvm/Support/ModRef.h
index 9ecdab71ec8ca6..aa4ce8295c77a7 100644
--- a/llvm/include/llvm/Support/ModRef.h
+++ b/llvm/include/llvm/Support/ModRef.h
@@ -61,8 +61,10 @@ enum class IRMemLocation {
ArgMem = 0,
/// Memory that is inaccessible via LLVM IR.
InaccessibleMem = 1,
+ /// Errno memory.
+ ErrnoMem = 2,
/// Any other memory.
- Other = 2,
+ Other = 3,
/// Helpers to iterate all locations in the MemoryEffectsBase class.
First = ArgMem,
@@ -139,6 +141,11 @@ template <typename LocationEnum> class MemoryEffectsBase {
return MemoryEffectsBase(Location::InaccessibleMem, MR);
}
+ /// Create MemoryEffectsBase that can only access errno memory.
+ static MemoryEffectsBase errnoMemOnly(ModRefInfo MR = ModRefInfo::ModRef) {
+ return MemoryEffectsBase(Location::ErrnoMem, MR);
+ }
+
/// Create MemoryEffectsBase that can only access inaccessible or argument
/// memory.
static MemoryEffectsBase
@@ -149,6 +156,27 @@ template <typename LocationEnum> class MemoryEffectsBase {
return FRMB;
}
+ /// Create MemoryEffectsBase that can only access inaccessible or errno
+ /// memory.
+ static MemoryEffectsBase
+ inaccessibleOrErrnoMemOnly(ModRefInfo MR = ModRefInfo::ModRef) {
+ MemoryEffectsBase FRMB = none();
+ FRMB.setModRef(Location::ErrnoMem, MR);
+ FRMB.setModRef(Location::InaccessibleMem, MR);
+ return FRMB;
+ }
+
+ /// Create MemoryEffectsBase that can only access inaccessible, argument or
+ /// errno memory.
+ static MemoryEffectsBase
+ inaccessibleOrArgOrErrnoMemOnly(ModRefInfo MR = ModRefInfo::ModRef) {
+ MemoryEffectsBase FRMB = none();
+ FRMB.setModRef(Location::ArgMem, MR);
+ FRMB.setModRef(Location::ErrnoMem, MR);
+ FRMB.setModRef(Location::InaccessibleMem, MR);
+ return FRMB;
+ }
+
/// Create MemoryEffectsBase from an encoded integer value (used by memory
/// attribute).
static MemoryEffectsBase createFromIntValue(uint32_t Data) {
@@ -207,11 +235,21 @@ template <typename LocationEnum> class MemoryEffectsBase {
return isModOrRefSet(getModRef(Location::ArgMem));
}
+ /// Whether this function may access errno memory.
+ bool doesAccessErrnoMem() const {
+ return isModOrRefSet(getModRef(Location::ErrnoMem));
+ }
+
/// Whether this function only (at most) accesses inaccessible memory.
bool onlyAccessesInaccessibleMem() const {
return getWithoutLoc(Location::InaccessibleMem).doesNotAccessMemory();
}
+ /// Whether this function only (at most) accesses errno memory.
+ bool onlyAccessesErrnoMem() const {
+ return getWithoutLoc(Location::ErrnoMem).doesNotAccessMemory();
+ }
+
/// Whether this function only (at most) accesses argument and inaccessible
/// memory.
bool onlyAccessesInaccessibleOrArgMem() const {
@@ -220,6 +258,23 @@ template <typename LocationEnum> class MemoryEffectsBase {
.doesNotAccessMemory();
}
+ /// Whether this function only (at most) accesses inaccessible and errno
+ /// memory.
+ bool onlyAccessesInaccessibleOrErrnoMem() const {
+ return getWithoutLoc(Location::InaccessibleMem)
+ .getWithoutLoc(Location::ErrnoMem)
+ .doesNotAccessMemory();
+ }
+
+ /// Whether this function only (at most) accesses inaccessible, argument and
+ /// errno memory.
+ bool onlyAccessesInaccessibleOrArgOrErrnoMem() const {
+ return getWithoutLoc(Location::InaccessibleMem)
+ .getWithoutLoc(Location::ArgMem)
+ .getWithoutLoc(Location::ErrnoMem)
+ .doesNotAccessMemory();
+ }
+
/// Intersect with other MemoryEffectsBase.
MemoryEffectsBase operator&(MemoryEffectsBase Other) const {
return MemoryEffectsBase(Data & Other.Data);
diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp
index 5ea507c009bdc6..7fcdd5d6f0df9c 100644
--- a/llvm/lib/AsmParser/LLLexer.cpp
+++ b/llvm/lib/AsmParser/LLLexer.cpp
@@ -701,9 +701,13 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(readwrite);
KEYWORD(argmem);
KEYWORD(inaccessiblemem);
+ KEYWORD(errnomem);
KEYWORD(argmemonly);
KEYWORD(inaccessiblememonly);
+ KEYWORD(errnomemonly);
KEYWORD(inaccessiblemem_or_argmemonly);
+ KEYWORD(inaccessiblemem_or_errnomemonly);
+ KEYWORD(inaccessiblemem_or_argmem_or_errnomemonly);
KEYWORD(address_is_null);
KEYWORD(address);
KEYWORD(provenance);
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index fa0079bac435c1..55b352a44d8838 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -1670,9 +1670,18 @@ static bool upgradeMemoryAttr(MemoryEffects &ME, lltok::Kind Kind) {
case lltok::kw_inaccessiblememonly:
ME &= MemoryEffects::inaccessibleMemOnly();
return true;
+ case lltok::kw_errnomemonly:
+ ME &= MemoryEffects::errnoMemOnly();
+ return true;
case lltok::kw_inaccessiblemem_or_argmemonly:
ME &= MemoryEffects::inaccessibleOrArgMemOnly();
return true;
+ case lltok::kw_inaccessiblemem_or_errnomemonly:
+ ME &= MemoryEffects::inaccessibleOrErrnoMemOnly();
+ return true;
+ case lltok::kw_inaccessiblemem_or_argmem_or_errnomemonly:
+ ME &= MemoryEffects::inaccessibleOrArgOrErrnoMemOnly();
+ return true;
default:
return false;
}
@@ -2490,6 +2499,8 @@ static std::optional<MemoryEffects::Location> keywordToLoc(lltok::Kind Tok) {
return IRMemLocation::ArgMem;
case lltok::kw_inaccessiblemem:
return IRMemLocation::InaccessibleMem;
+ case lltok::kw_errnomem:
+ return IRMemLocation::ErrnoMem;
default:
return std::nullopt;
}
@@ -2538,7 +2549,7 @@ std::optional<MemoryEffects> LLParser::parseMemoryAttr() {
std::optional<ModRefInfo> MR = keywordToModRef(Lex.getKind());
if (!MR) {
if (!Loc)
- tokError("expected memory location (argmem, inaccessiblemem) "
+ tokError("expected memory location (argmem, inaccessiblemem, errnomem) "
"or access kind (none, read, write, readwrite)");
else
tokError("expected access kind (none, read, write, readwrite)");
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index 551dfd4af88bb2..8165002e9d7204 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -1910,6 +1910,10 @@ static uint64_t getRawAttributeMask(Attribute::AttrKind Val) {
return 1ULL << 62;
case Attribute::NoFree:
return 1ULL << 63;
+ // 7ULL << 36 is ErrnoMemOnly, which is upgraded separately.
+ // 7ULL << 37 is InaccessibleMemOrErrnoMemOnly, which is upgraded separately.
+ // 7ULL << 38 is InaccessibleMemOrArgOrErrnoMemOnly, which is upgraded
+ // separately.
default:
// Other attributes are not supported in the raw format,
// as we ran out of space.
@@ -1982,6 +1986,21 @@ static void decodeLLVMAttributesForBitcode(AttrBuilder &B,
Attrs &= ~(1ULL << 53);
ME &= MemoryEffects::writeOnly();
}
+ if (Attrs & (7ULL << 36)) {
+ // ErrnoMemOnly
+ Attrs &= ~(7ULL << 36);
+ ME &= MemoryEffects::errnoMemOnly();
+ }
+ if (Attrs & (7ULL << 37)) {
+ // InaccessibleMemOrErrnoMemOnly
+ Attrs &= ~(7ULL << 37);
+ ME &= MemoryEffects::inaccessibleOrErrnoMemOnly();
+ }
+ if (Attrs & (7ULL << 38)) {
+ // InaccessibleMemOrArgOrErrnoMemOnly
+ Attrs &= ~(7ULL << 38);
+ ME &= MemoryEffects::inaccessibleOrArgOrErrnoMemOnly();
+ }
if (ME != MemoryEffects::unknown())
B.addMemoryAttr(ME);
}
@@ -2289,9 +2308,18 @@ static bool upgradeOldMemoryAttribute(MemoryEffects &ME, uint64_t EncodedKind) {
case bitc::ATTR_KIND_INACCESSIBLEMEM_ONLY:
ME &= MemoryEffects::inaccessibleMemOnly();
return true;
+ case bitc::ATTR_KIND_ERRNOMEMONLY:
+ ME &= MemoryEffects::errnoMemOnly();
+ return true;
case bitc::ATTR_KIND_INACCESSIBLEMEM_OR_ARGMEMONLY:
ME &= MemoryEffects::inaccessibleOrArgMemOnly();
return true;
+ case bitc::ATTR_KIND_INACCESSIBLEMEM_OR_ERRNOMEMONLY:
+ ME &= MemoryEffects::inaccessibleOrErrnoMemOnly();
+ return true;
+ case bitc::ATTR_KIND_INACCESSIBLEMEM_OR_ARGMEM_OR_ERRNOMEMONLY:
+ ME &= MemoryEffects::inaccessibleOrArgOrErrnoMemOnly();
+ return true;
default:
return false;
}
diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp
index ceb31856283c9a..0ead5a26ce5bd3 100644
--- a/llvm/lib/IR/Attributes.cpp
+++ b/llvm/lib/IR/Attributes.cpp
@@ -643,6 +643,9 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
case IRMemLocation::InaccessibleMem:
OS << "inaccessiblemem: ";
break;
+ case IRMemLocation::ErrnoMem:
+ OS << "errnomem: ";
+ break;
case IRMemLocation::Other:
llvm_unreachable("This is represented as the default access kind");
}
diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp
index 9c5dd5aeb92e97..e4c64bcf9bc5d0 100644
--- a/llvm/lib/IR/Function.cpp
+++ b/llvm/lib/IR/Function.cpp
@@ -922,6 +922,14 @@ void Function::setOnlyAccessesInaccessibleMemory() {
setMemoryEffects(getMemoryEffects() & MemoryEffects::inaccessibleMemOnly());
}
+/// Determine if the function may only access errno memory.
+bool Function::onlyAccessesErrnoMemory() const {
+ return getMemoryEffects().onlyAccessesErrnoMem();
+}
+void Function::setOnlyAccessesErrnoMemory() {
+ setMemoryEffects(getMemoryEffects() & MemoryEffects::errnoMemOnly());
+}
+
/// Determine if the function may only access memory that is
/// either inaccessible from the IR or pointed to by its arguments.
bool Function::onlyAccessesInaccessibleMemOrArgMem() const {
@@ -932,6 +940,27 @@ void Function::setOnlyAccessesInaccessibleMemOrArgMem() {
MemoryEffects::inaccessibleOrArgMemOnly());
}
+/// Determine if the function may only access memory that is
+/// either inaccessible from the IR or errno memory.
+bool Function::onlyAccessesInaccessibleMemOrErrnoMem() const {
+ return getMemoryEffects().onlyAccessesInaccessibleOrErrnoMem();
+}
+void Function::setOnlyAccessesInaccessibleMemOrErrnoMem() {
+ setMemoryEffects(getMemoryEffects() &
+ MemoryEffects::inaccessibleOrErrnoMemOnly());
+}
+
+/// Determine if the function may only access memory that is
+/// either inaccessible from the IR, pointed to by its arguments or errno
+/// memory.
+bool Function::onlyAccessesInaccessibleMemOrArgMemOrErrnoMem() const {
+ return getMemoryEffects().onlyAccessesInaccessibleOrArgOrErrnoMem();
+}
+void Function::setOnlyAccessesInaccessibleMemOrArgMemOrErrnoMem() {
+ setMemoryEffects(getMemoryEffects() &
+ MemoryEffects::inaccessibleOrArgOrErrnoMemOnly());
+}
+
bool Function::isTargetIntrinsic() const {
return Intrinsic::isTargetIntrinsic(IntID);
}
diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp
index b585d8cfbf2e2b..e3e17507e9acfb 100644
--- a/llvm/lib/IR/Instructions.cpp
+++ b/llvm/lib/IR/Instructions.cpp
@@ -665,6 +665,14 @@ void CallBase::setOnlyAccessesInaccessibleMemory() {
setMemoryEffects(getMemoryEffects() & MemoryEffects::inaccessibleMemOnly());
}
+/// Determine if the function may only access errno memory.
+bool CallBase::onlyAccessesErrnoMemory() const {
+ return getMemoryEffects().onlyAccessesErrnoMem();
+}
+void CallBase::setOnlyAccessesErrnoMemory() {
+ setMemoryEffects(getMemoryEffects() & MemoryEffects::errnoMemOnly());
+}
+
/// Determine if the function may only access memory that is
/// either inaccessible from the IR or pointed to by its arguments.
bool CallBase::onlyAccessesInaccessibleMemOrArgMem() const {
@@ -675,6 +683,27 @@ void CallBase::setOnlyAccessesInaccessibleMemOrArgMem() {
MemoryEffects::inaccessibleOrArgMemOnly());
}
+/// Determine if the function may only access memory that is
+/// either inaccessible from the IR or errno memory.
+bool CallBase::onlyAccessesInaccessibleMemOrErrnoMem() const {
+ return getMemoryEffects().onlyAccessesInaccessibleOrErrnoMem();
+}
+void CallBase::setOnlyAccessesInaccessibleMemOrErrnoMem() {
+ setMemoryEffects(getMemoryEffects() &
+ MemoryEffects::inaccessibleOrErrnoMemOnly());
+}
+
+/// Determine if the function may only access memory that is
+/// either inaccessible from the IR, pointed to by its arguments or errno
+/// memory.
+bool CallBase::onlyAccessesInaccessibleMemOrArgMemOrErrnoMem() const {
+ return getMemoryEffects().onlyAccessesInaccessibleOrArgOrErrnoMem();
+}
+void CallBase::setOnlyAccessesInaccessibleMemOrArgMemOrErrnoMem() {
+ setMemoryEffects(getMemoryEffects() &
+ MemoryEffects::inaccessibleOrArgOrErrnoMemOnly());
+}
+
//===----------------------------------------------------------------------===//
// CallInst Implementation
//===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Support/ModRef.cpp b/llvm/lib/Support/ModRef.cpp
index d3b3dd11171f16..2bb9bc945bd2e2 100644
--- a/llvm/lib/Support/ModRef.cpp
+++ b/llvm/lib/Support/ModRef.cpp
@@ -43,6 +43,9 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, MemoryEffects ME) {
case IRMemLocation::InaccessibleMem:
OS << "InaccessibleMem: ";
break;
+ case IRMemLocation::ErrnoMem:
+ OS << "ErrnoMem: ";
+ break;
case IRMemLocation::Other:
OS << "Other: ";
break;
diff --git a/llvm/test/Assembler/memory-attribute-errors.ll b/llvm/test/Assembler/memory-attribute-errors.ll
index 1fba90362e79b9..2eed11d9465d58 100644
--- a/llvm/test/Assembler/memory-attribute-errors.ll
+++ b/llvm/test/Assembler/memory-attribute-errors.ll
@@ -12,16 +12,16 @@
; MISSING-ARGS: error: expected '('
declare void @fn() memory
;--- empty.ll
-; EMPTY: error: expected memory location (argmem, inaccessiblemem) or access kind (none, read, write, readwrite)
+; EMPTY: error: expected memory location (argmem, inaccessiblemem, errnomem) or access kind (none, read, write, readwrite)
declare void @fn() memory()
;--- unterminated.ll
; UNTERMINATED: error: unterminated memory attribute
declare void @fn() memory(read
;--- invalid-kind.ll
-; INVALID-KIND: error: expected memory location (argmem, inaccessiblemem) or access kind (none, read, write, readwrite)
+; INVALID-KIND: error: expected memory location (argmem, inaccessiblemem, errnomem) or access kind (none, read, write, readwrite)
declare void @fn() memory(foo)
;--- other.ll
-; OTHER: error: expected memory location (argmem, inaccessiblemem) or access kind (none, read, write, readwrite)
+; OTHER: error: expected memory location (argmem, inaccessiblemem, errnomem) or access kind (none, read, write, readwrite)
declare void @fn() memory(other: read)
;--- missing-colon.ll
; MISSING-COLON: error: expected ':' after location
diff --git a/llvm/test/Assembler/memory-attribute.ll b/llvm/test/Assembler/memory-attribute.ll
index 2f7d3980eb378b..effd4ce7c45483 100644
--- a/llvm/test/Assembler/memory-attribute.ll
+++ b/llvm/test/Assembler/memory-attribute.ll
@@ -40,6 +40,18 @@ declare void @fn_inaccessiblemem_write() memory(inaccessiblemem: write)
; CHECK: @fn_inaccessiblemem_readwrite()
declare void @fn_inaccessiblemem_readwrite() memory(inaccessiblemem: readwrite)
+; CHECK: Function Attrs: memory(errnomem: read)
+; CHECK: @fn_errnomem_read()
+declare void @fn_errnomem_read() memory(errnomem: read)
+
+; CHECK: Function Attrs: memory(errnomem: write)
+; CHECK: @fn_errnomem_write()
+declare void @fn_errnomem_write() memory(errnomem: write)
+
+; CHECK: Function Attrs: memory(errnomem: readwrite)
+; CHECK: @fn_errnomem_readwrite()
+declare void @fn_errnomem_readwrite() memory(errnomem: readwrite)
+
; CHECK: Function Attrs: memory(read, argmem: readwrite)
; CHECK: @fn_read_argmem_readwrite()
declare void @fn_read_argmem_readwrite() memory(read, argmem: readwrite)
diff --git a/llvm/test/Transforms/FunctionAttrs/argmemonly.ll b/llvm/test/Transforms/FunctionAttrs/argmemonly.ll
index 5bbe6fa7c27c2e..6f3bd8c5ae0de6 100644
--- a/llvm/test/Transforms/FunctionAttrs/argmemonly.ll
+++ b/llvm/test/Transforms/FunctionAttrs/argmemonly.ll
@@ -56,7 +56,7 @@ entry:
}
define i32 @test_read_global() {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, argmem: none, inaccessiblemem: none)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, argmem: none, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define i32 @test_read_global
; FNATTRS-SAME: () #[[ATTR2:[0-9]+]] {
; FNATTRS-NEXT: entry:
@@ -76,7 +76,7 @@ entry:
}
define i32 @test_read_loaded_ptr(ptr %ptr) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define i32 @test_read_loaded_ptr
; FNATTRS-SAME: (ptr nocapture readonly [[PTR:%.*]]) #[[ATTR3:[0-9]+]] {
; FNATTRS-NEXT: entry:
@@ -119,7 +119,7 @@ entry:
}
define void @test_write_global() {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define void @test_write_global
; FNATTRS-SAME: () #[[ATTR5:[0-9]+]] {
; FNATTRS-NEXT: entry:
@@ -243,7 +243,7 @@ declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1)
@arr = global [32 x i8] zeroinitializer
define void @test_memcpy_src_global(ptr %dst) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define void @test_memcpy_src_global
; FNATTRS-SAME: (ptr nocapture writeonly initializes((0, 32)) [[DST:%.*]]) #[[ATTR11:[0-9]+]] {
; FNATTRS-NEXT: entry:
@@ -263,7 +263,7 @@ entry:
}
define void @test_memcpy_dst_global(ptr %src) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define void @test_memcpy_dst_global
; FNATTRS-SAME: (ptr nocapture readonly [[SRC:%.*]]) #[[ATTR11]] {
; FNATTRS-NEXT: entry:
@@ -388,7 +388,7 @@ define void @test_inaccessibleorargmemonly_readwrite(ptr %arg) {
}
define void @test_recursive_argmem_read(ptr %p) {
-; FNATTRS: Function Attrs: nofree nosync nounwind memory(read, inaccessiblemem: none)
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(read, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define void @test_recursive_argmem_read
; FNATTRS-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR16:[0-9]+]] {
; FNATTRS-NEXT: [[PVAL:%.*]] = load ptr, ptr [[P]], align 8
@@ -408,7 +408,7 @@ define void @test_recursive_argmem_read(ptr %p) {
}
define void @test_recursive_argmem_readwrite(ptr %p) {
-; FNATTRS: Function Attrs: nofree nosync nounwind memory(readwrite, inaccessiblemem: none)
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(readwrite, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define void @test_recursive_argmem_readwrite
; FNATTRS-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR17:[0-9]+]] {
; FNATTRS-NEXT: [[PVAL:%.*]] = load ptr, ptr [[P]], align 8
@@ -454,7 +454,7 @@ define void @test_recursive_argmem_read_alloca(ptr %p) {
}
define void @test_scc_argmem_read_1(ptr %p) {
-; FNATTRS: Function Attrs: nofree nosync nounwind memory(read, inaccessiblemem: none)
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(read, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define void @test_scc_argmem_read_1
; FNATTRS-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR16]] {
; FNATTRS-NEXT: [[PVAL:%.*]] = load ptr, ptr [[P]], align 8
@@ -474,7 +474,7 @@ define void @test_scc_argmem_read_1(ptr %p) {
}
define void @test_scc_argmem_read_2(ptr %p) {
-; FNATTRS: Function Attrs: nofree nosync nounwind memory(read, inaccessiblemem: none)
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(read, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define void @test_scc_argmem_read_2
; FNATTRS-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR16]] {
; FNATTRS-NEXT: call void @test_scc_argmem_read_1(ptr [[P]])
@@ -518,7 +518,7 @@ entry:
; FIXME: This could be `memory(argmem: read)`.
define i64 @select_different_obj(i1 %c, ptr %p, ptr %p2) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define i64 @select_different_obj
; FNATTRS-SAME: (i1 [[C:%.*]], ptr nocapture readonly [[P:%.*]], ptr nocapture readonly [[P2:%.*]]) #[[ATTR3]] {
; FNATTRS-NEXT: entry:
@@ -580,7 +580,7 @@ join:
; FIXME: This could be `memory(argmem: read)`.
define i64 @phi_different_obj(i1 %c, ptr %p, ptr %p2) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define i64 @phi_different_obj
; FNATTRS-SAME: (i1 [[C:%.*]], ptr nocapture readonly [[P:%.*]], ptr nocapture readonly [[P2:%.*]]) #[[ATTR3]] {
; FNATTRS-NEXT: entry:
diff --git a/llvm/test/Transforms/FunctionAttrs/nocapture.ll b/llvm/test/Transforms/FunctionAttrs/nocapture.ll
index 7df6132ac6a315..ce2373ce574676 100644
--- a/llvm/test/Transforms/FunctionAttrs/nocapture.ll
+++ b/llvm/test/Transforms/FunctionAttrs/nocapture.ll
@@ -20,7 +20,7 @@ define ptr @c1(ptr %q) {
; It would also be acceptable to mark %q as readnone. Update @c3 too.
define void @c2(ptr %q) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define void @c2
; FNATTRS-SAME: (ptr [[Q:%.*]]) #[[ATTR1:[0-9]+]] {
; FNATTRS-NEXT: store ptr [[Q]], ptr @g, align 8
@@ -37,7 +37,7 @@ define void @c2(ptr %q) {
}
define void @c3(ptr %q) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, inaccessiblemem: none)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define void @c3
; FNATTRS-SAME: (ptr [[Q:%.*]]) #[[ATTR2:[0-9]+]] {
; FNATTRS-NEXT: call void @c2(ptr [[Q]])
@@ -127,7 +127,7 @@ l1:
@lookup_table = global [2 x i1] [ i1 0, i1 1 ]
define i1 @c5(ptr %q, i32 %bitno) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, argmem: none, inaccessiblemem: none)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, argmem: none, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define i1 @c5
; FNATTRS-SAME: (ptr [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR3:[0-9]+]] {
; FNATTRS-NEXT: [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32
@@ -222,7 +222,7 @@ define ptr @lookup_bit(ptr %q, i32 %bitno) readnone nounwind {
}
define i1 @c7(ptr %q, i32 %bitno) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define i1 @c7
; FNATTRS-SAME: (ptr readonly [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR6:[0-9]+]] {
; FNATTRS-NEXT: [[PTR:%.*]] = call ptr @lookup_bit(ptr [[Q]], i32 [[BITNO]])
@@ -243,7 +243,7 @@ define i1 @c7(ptr %q, i32 %bitno) {
define i32 @nc1(ptr %q, ptr %p, i1 %b) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define i32 @nc1
; FNATTRS-SAME: (ptr [[Q:%.*]], ptr nocapture [[P:%.*]], i1 [[B:%.*]]) #[[ATTR7:[0-9]+]] {
; FNATTRS-NEXT: e:
@@ -284,7 +284,7 @@ l:
}
define i32 @nc1_addrspace(ptr %q, ptr addrspace(1) %p, i1 %b) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define i32 @nc1_addrspace
; FNATTRS-SAME: (ptr [[Q:%.*]], ptr addrspace(1) nocapture [[P:%.*]], i1 [[B:%.*]]) #[[ATTR7]] {
; FNATTRS-NEXT: e:
@@ -328,7 +328,7 @@ l:
}
define void @nc2(ptr %p, ptr %q) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define void @nc2
; FNATTRS-SAME: (ptr nocapture [[P:%.*]], ptr [[Q:%.*]]) #[[ATTR7]] {
; FNATTRS-NEXT: [[TMP1:%.*]] = call i32 @nc1(ptr [[Q]], ptr [[P]], i1 false)
@@ -398,7 +398,7 @@ define void @nc5(ptr %f, ptr %p) {
; It would be acceptable to add readnone to %y1_1 and %y1_2.
define void @test1_1(ptr %x1_1, ptr %y1_1, i1 %c) {
-; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none)
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define void @test1_1
; FNATTRS-SAME: (ptr nocapture readnone [[X1_1:%.*]], ptr [[Y1_1:%.*]], i1 [[C:%.*]]) #[[ATTR10:[0-9]+]] {
; FNATTRS-NEXT: [[TMP1:%.*]] = call ptr @test1_2(ptr [[X1_1]], ptr [[Y1_1]], i1 [[C]])
@@ -418,7 +418,7 @@ define void @test1_1(ptr %x1_1, ptr %y1_1, i1 %c) {
}
define ptr @test1_2(ptr %x1_2, ptr %y1_2, i1 %c) {
-; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none)
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define ptr @test1_2
; FNATTRS-SAME: (ptr nocapture readnone [[X1_2:%.*]], ptr returned [[Y1_2:%.*]], i1 [[C:%.*]]) #[[ATTR10]] {
; FNATTRS-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
@@ -450,7 +450,7 @@ f:
}
define void @test2(ptr %x2) {
-; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none)
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define void @test2
; FNATTRS-SAME: (ptr nocapture readnone [[X2:%.*]]) #[[ATTR10]] {
; FNATTRS-NEXT: call void @test2(ptr [[X2]])
@@ -470,7 +470,7 @@ define void @test2(ptr %x2) {
}
define void @test3(ptr %x3, ptr %y3, ptr %z3) {
-; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none)
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define void @test3
; FNATTRS-SAME: (ptr nocapture readnone [[X3:%.*]], ptr nocapture readnone [[Y3:%.*]], ptr nocapture readnone [[Z3:%.*]]) #[[ATTR10]] {
; FNATTRS-NEXT: call void @test3(ptr [[Z3]], ptr [[Y3]], ptr [[X3]])
@@ -490,7 +490,7 @@ define void @test3(ptr %x3, ptr %y3, ptr %z3) {
}
define void @test4_1(ptr %x4_1, i1 %c) {
-; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none)
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define void @test4_1
; FNATTRS-SAME: (ptr [[X4_1:%.*]], i1 [[C:%.*]]) #[[ATTR10]] {
; FNATTRS-NEXT: [[TMP1:%.*]] = call ptr @test4_2(ptr [[X4_1]], ptr [[X4_1]], ptr [[X4_1]], i1 [[C]])
@@ -510,7 +510,7 @@ define void @test4_1(ptr %x4_1, i1 %c) {
}
define ptr @test4_2(ptr %x4_2, ptr %y4_2, ptr %z4_2, i1 %c) {
-; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none)
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define ptr @test4_2
; FNATTRS-SAME: (ptr nocapture readnone [[X4_2:%.*]], ptr readnone returned [[Y4_2:%.*]], ptr nocapture readnone [[Z4_2:%.*]], i1 [[C:%.*]]) #[[ATTR10]] {
; FNATTRS-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
@@ -674,7 +674,7 @@ entry:
@g2 = global ptr null
define void @captureLaunder(ptr %p) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: readwrite)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: readwrite, errnomem: none)
; FNATTRS-LABEL: define void @captureLaunder
; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR14:[0-9]+]] {
; FNATTRS-NEXT: [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr [[P]])
@@ -718,7 +718,7 @@ entry:
@g3 = global ptr null
define void @captureStrip(ptr %p) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define void @captureStrip
; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR1]] {
; FNATTRS-NEXT: [[B:%.*]] = call ptr @llvm.strip.invariant.group.p0(ptr [[P]])
diff --git a/llvm/test/Transforms/FunctionAttrs/read-write-scc.ll b/llvm/test/Transforms/FunctionAttrs/read-write-scc.ll
index be61990fd6278b..6dd48a04f3145f 100644
--- a/llvm/test/Transforms/FunctionAttrs/read-write-scc.ll
+++ b/llvm/test/Transforms/FunctionAttrs/read-write-scc.ll
@@ -4,7 +4,7 @@
@i = global i32 0
define void @foo() {
-; CHECK: Function Attrs: nofree nosync nounwind memory(readwrite, argmem: none, inaccessiblemem: none)
+; CHECK: Function Attrs: nofree nosync nounwind memory(readwrite, argmem: none, inaccessiblemem: none, errnomem: none)
; CHECK-LABEL: define {{[^@]+}}@foo
; CHECK-SAME: () #[[ATTR0:[0-9]+]] {
; CHECK-NEXT: store i32 1, ptr @i, align 4
@@ -17,7 +17,7 @@ define void @foo() {
}
define void @bar() {
-; CHECK: Function Attrs: nofree nosync nounwind memory(readwrite, argmem: none, inaccessiblemem: none)
+; CHECK: Function Attrs: nofree nosync nounwind memory(readwrite, argmem: none, inaccessiblemem: none, errnomem: none)
; CHECK-LABEL: define {{[^@]+}}@bar
; CHECK-SAME: () #[[ATTR0]] {
; CHECK-NEXT: [[I:%.*]] = load i32, ptr @i, align 4
diff --git a/llvm/test/Transforms/FunctionAttrs/readattrs.ll b/llvm/test/Transforms/FunctionAttrs/readattrs.ll
index e60954c9cd29a3..6e597c5dd5ab36 100644
--- a/llvm/test/Transforms/FunctionAttrs/readattrs.ll
+++ b/llvm/test/Transforms/FunctionAttrs/readattrs.ll
@@ -33,7 +33,7 @@ define void @test1_2(ptr %x1_2, ptr %y1_2, ptr %z1_2) {
; TODO: Missing with attributor-light: argmem: none, inaccessiblemem: none
define ptr @test2(ptr %p) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define {{[^@]+}}@test2
; FNATTRS-SAME: (ptr readnone returned [[P:%.*]]) #[[ATTR0:[0-9]+]] {
; FNATTRS-NEXT: store i32 0, ptr @x, align 4
diff --git a/llvm/test/Transforms/FunctionAttrs/writeonly.ll b/llvm/test/Transforms/FunctionAttrs/writeonly.ll
index ba546aff6e6211..1d05711541d46d 100644
--- a/llvm/test/Transforms/FunctionAttrs/writeonly.ll
+++ b/llvm/test/Transforms/FunctionAttrs/writeonly.ll
@@ -44,7 +44,7 @@ nouses-argworn-funro_entry:
@d-ccc = internal global %_type_of_d-ccc <{ ptr null, i8 1, i8 13, i8 0, i8 -127 }>, align 8
define void @nouses-argworn-funwo(ptr writeonly %.aaa) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define {{[^@]+}}@nouses-argworn-funwo
; FNATTRS-SAME: (ptr nocapture readnone [[DOTAAA:%.*]]) #[[ATTR2:[0-9]+]] {
; FNATTRS-NEXT: nouses-argworn-funwo_entry:
@@ -82,7 +82,7 @@ define void @test_store(ptr %p) {
@G = external global ptr
define i8 @test_store_capture(ptr %p) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, argmem: read, inaccessiblemem: none)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, argmem: read, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define {{[^@]+}}@test_store_capture
; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR4:[0-9]+]] {
; FNATTRS-NEXT: store ptr [[P]], ptr @G, align 8
diff --git a/llvm/test/Transforms/InferFunctionAttrs/norecurse_debug.ll b/llvm/test/Transforms/InferFunctionAttrs/norecurse_debug.ll
index c8568272d320fb..36791ae60fed60 100644
--- a/llvm/test/Transforms/InferFunctionAttrs/norecurse_debug.ll
+++ b/llvm/test/Transforms/InferFunctionAttrs/norecurse_debug.ll
@@ -52,5 +52,5 @@ attributes #1 = { nounwind readnone speculatable }
!28 = !DILocation(line: 9, column: 18, scope: !2)
!29 = !DILocation(line: 10, column: 1, scope: !2)
-; CHECK: attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, argmem: write, inaccessiblemem: none) }
+; CHECK: attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, argmem: write, inaccessiblemem: none, errnomem: none) }
; CHECK-NOT: foo.coefficient1
diff --git a/llvm/test/Transforms/LowerTypeTests/cfi-nounwind-direct-call.ll b/llvm/test/Transforms/LowerTypeTests/cfi-nounwind-direct-call.ll
index 2795333effd76b..8e212040d66515 100644
--- a/llvm/test/Transforms/LowerTypeTests/cfi-nounwind-direct-call.ll
+++ b/llvm/test/Transforms/LowerTypeTests/cfi-nounwind-direct-call.ll
@@ -117,7 +117,7 @@ attributes #6 = { noreturn nounwind }
; CHECK-NEXT: ret i32 [[DOT]]
;
;
-; CHECK: Function Attrs: minsize mustprogress nofree norecurse nosync nounwind optsize willreturn memory(write, argmem: none, inaccessiblemem: none)
+; CHECK: Function Attrs: minsize mustprogress nofree norecurse nosync nounwind optsize willreturn memory(write, argmem: none, inaccessiblemem: none, errnomem: none)
; CHECK-LABEL: define dso_local noundef range(i32 0, 2) i32 @_Z10call_catchi
; CHECK-SAME: (i32 noundef [[NUM:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] !type [[META4]] !type [[META5]] !type [[META6]] {
; CHECK-NEXT: entry:
diff --git a/llvm/test/Transforms/SCCP/ipscp-drop-argmemonly.ll b/llvm/test/Transforms/SCCP/ipscp-drop-argmemonly.ll
index 22726e0cac1f12..5e8c7cd21324d8 100644
--- a/llvm/test/Transforms/SCCP/ipscp-drop-argmemonly.ll
+++ b/llvm/test/Transforms/SCCP/ipscp-drop-argmemonly.ll
@@ -11,10 +11,10 @@
; Here the pointer argument %arg will be replaced by a constant. We need to
; drop argmemonly.
;.
-; CHECK: @[[G:[a-zA-Z0-9_$"\\.-]+]] = internal global i32 0
+; CHECK: @g = internal global i32 0
;.
define internal void @ptrarg.1(ptr %arg, i32 %val) argmemonly nounwind {
-; CHECK: Function Attrs: nounwind memory(readwrite, inaccessiblemem: none)
+; CHECK: Function Attrs: nounwind memory(readwrite, inaccessiblemem: none, errnomem: none)
; CHECK-LABEL: @ptrarg.1(
; CHECK-NEXT: store i32 10, ptr @g, align 4
; CHECK-NEXT: ret void
@@ -62,7 +62,7 @@ define void @caller.2(ptr %ptr) {
; Here the pointer argument %arg will be replaced by a constant. We need to
; drop inaccessiblemem_or_argmemonly.
define internal void @ptrarg.3(ptr %arg, i32 %val) inaccessiblemem_or_argmemonly nounwind {
-; CHECK: Function Attrs: nounwind memory(readwrite)
+; CHECK: Function Attrs: nounwind memory(readwrite, errnomem: none)
; CHECK-LABEL: @ptrarg.3(
; CHECK-NEXT: store i32 10, ptr @g, align 4
; CHECK-NEXT: ret void
@@ -110,7 +110,7 @@ define void @caller.4(ptr %ptr) {
; Here the pointer argument %arg will be replaced by a constant. We need to
; drop inaccessiblemem_or_argmemonly.
define internal void @ptrarg.5(ptr %arg, i32 %val) argmemonly inaccessiblemem_or_argmemonly nounwind {
-; CHECK: Function Attrs: nounwind memory(readwrite, inaccessiblemem: none)
+; CHECK: Function Attrs: nounwind memory(readwrite, inaccessiblemem: none, errnomem: none)
; CHECK-LABEL: @ptrarg.5(
; CHECK-NEXT: store i32 10, ptr @g, align 4
; CHECK-NEXT: ret void
@@ -163,9 +163,9 @@ define i32 @caller.6.cs.attributes(i32 %n) {
}
;.
-; CHECK: attributes #[[ATTR0]] = { nounwind memory(readwrite, inaccessiblemem: none) }
+; CHECK: attributes #[[ATTR0]] = { nounwind memory(readwrite, inaccessiblemem: none, errnomem: none) }
; CHECK: attributes #[[ATTR1:[0-9]+]] = { nounwind memory(argmem: readwrite) }
-; CHECK: attributes #[[ATTR2]] = { nounwind memory(readwrite) }
+; CHECK: attributes #[[ATTR2]] = { nounwind memory(readwrite, errnomem: none) }
; CHECK: attributes #[[ATTR3:[0-9]+]] = { nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) }
; CHECK: attributes #[[ATTR4]] = { nounwind }
;.
diff --git a/llvm/unittests/Support/ModRefTest.cpp b/llvm/unittests/Support/ModRefTest.cpp
index 35107e50b32db7..9c13908da44bb0 100644
--- a/llvm/unittests/Support/ModRefTest.cpp
+++ b/llvm/unittests/Support/ModRefTest.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
#include "llvm/Support/ModRef.h"
-#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
#include "gtest/gtest.h"
#include <string>
@@ -21,7 +20,8 @@ TEST(ModRefTest, PrintMemoryEffects) {
std::string S;
raw_string_ostream OS(S);
OS << MemoryEffects::none();
- EXPECT_EQ(S, "ArgMem: NoModRef, InaccessibleMem: NoModRef, Other: NoModRef");
+ EXPECT_EQ(S, "ArgMem: NoModRef, InaccessibleMem: NoModRef, ErrnoMem: "
+ "NoModRef, Other: NoModRef");
}
} // namespace
diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir
index 51086bc655af24..80f0693fad7f5e 100644
--- a/mlir/test/Target/LLVMIR/llvmir.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir.mlir
@@ -2347,7 +2347,7 @@ llvm.func @readonly_function(%arg0: !llvm.ptr {llvm.readonly})
llvm.func @arg_mem_none_func() attributes {
memory_effects = #llvm.memory_effects<other = readwrite, argMem = none, inaccessibleMem = readwrite>}
-// CHECK: attributes #[[ATTR]] = { memory(readwrite, argmem: none) }
+// CHECK: attributes #[[ATTR]] = { memory(readwrite, argmem: none, errnomem: none) }
// -----
@@ -2355,7 +2355,7 @@ llvm.func @arg_mem_none_func() attributes {
llvm.func @readwrite_func() attributes {
memory_effects = #llvm.memory_effects<other = readwrite, argMem = readwrite, inaccessibleMem = readwrite>}
-// CHECK: attributes #[[ATTR]] = { memory(readwrite) }
+// CHECK: attributes #[[ATTR]] = { memory(readwrite, errnomem: none) }
// -----
@@ -2613,11 +2613,11 @@ llvm.func @mem_effects_call() {
// CHECK: #[[ATTRS_0]]
// CHECK-SAME: memory(none)
// CHECK: #[[ATTRS_1]]
-// CHECK-SAME: memory(read, argmem: none, inaccessiblemem: write)
+// CHECK-SAME: memory(read, argmem: none, inaccessiblemem: write, errnomem: none)
// CHECK: #[[ATTRS_2]]
-// CHECK-SAME: memory(read, inaccessiblemem: write)
+// CHECK-SAME: memory(read, inaccessiblemem: write, errnomem: none)
// CHECK: #[[ATTRS_3]]
-// CHECK-SAME: memory(readwrite, argmem: read)
+// CHECK-SAME: memory(readwrite, argmem: read, errnomem: none)
// -----
>From 8a71390472c86bc1ef08f943c241a9a370fd8d23 Mon Sep 17 00:00:00 2001
From: Antonio Frighetto <me at antoniofrighetto.com>
Date: Mon, 27 Jan 2025 15:41:59 +0100
Subject: [PATCH 2/3] [InferAttrs] Mark `errnomem`-setting libcalls as such
Mark C standard library functions that set `errno` as such, as
included in the POSIX specification. Likewise, while deducing
function attributes, determine whether a memory access affects
errno memory.
---
llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 12 ++
llvm/lib/Transforms/Utils/BuildLibCalls.cpp | 98 +++++++--
.../Transforms/InferFunctionAttrs/annotate.ll | 189 +++++++++---------
3 files changed, 190 insertions(+), 109 deletions(-)
diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index 17f946e5acdf84..bd1a89db4dafd4 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -128,6 +128,15 @@ static void addLocAccess(MemoryEffects &ME, const MemoryLocation &Loc,
ME |= MemoryEffects::argMemOnly(MR);
return;
}
+ // TODO: This should be refined to use upcoming Loc.TBAAErrno for errno
+ // memory, rather than manually inspecting the underlying object.
+ if (isa<CallInst>(UO)) {
+ auto *Callee = cast<CallInst>(UO)->getCalledFunction();
+ if (Callee && Callee->getName() == "__errno_location") {
+ ME |= MemoryEffects::errnoMemOnly(MR);
+ return;
+ }
+ }
// If it's not an identified object, it might be an argument.
if (!isIdentifiedObject(UO))
@@ -210,6 +219,9 @@ checkFunctionMemoryAccess(Function &F, bool ThisBody, AAResults &AAR,
if (isa<PseudoProbeInst>(I))
continue;
+ // Merge callee's memory effects into caller's ones, including
+ // inaccessible and errno memory, but excluding argument memory, which is
+ // handled separately.
ME |= CallME.getWithoutLoc(IRMemLocation::ArgMem);
// If the call accesses captured memory (currently part of "other") and
diff --git a/llvm/lib/Transforms/Utils/BuildLibCalls.cpp b/llvm/lib/Transforms/Utils/BuildLibCalls.cpp
index fe1b91267c907a..716fb545928890 100644
--- a/llvm/lib/Transforms/Utils/BuildLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/BuildLibCalls.cpp
@@ -34,13 +34,17 @@ using namespace llvm;
//- Infer Attributes ---------------------------------------------------------//
STATISTIC(NumReadNone, "Number of functions inferred as readnone");
-STATISTIC(NumInaccessibleMemOnly,
- "Number of functions inferred as inaccessiblememonly");
STATISTIC(NumReadOnly, "Number of functions inferred as readonly");
STATISTIC(NumWriteOnly, "Number of functions inferred as writeonly");
STATISTIC(NumArgMemOnly, "Number of functions inferred as argmemonly");
+STATISTIC(NumErrnoMemOnly, "Number of functions inferred as errnomemonly");
STATISTIC(NumInaccessibleMemOrArgMemOnly,
"Number of functions inferred as inaccessiblemem_or_argmemonly");
+STATISTIC(NumInaccessibleMemOrErrnoMemOnly,
+ "Number of functions inferred as inaccessiblemem_or_errnomemonly");
+STATISTIC(NumInaccessibleMemOrArgMemOrErrnoMemOnly,
+ "Number of functions inferred as "
+ "inaccessiblemem_or_argmem_or_errnomemonly");
STATISTIC(NumNoUnwind, "Number of functions inferred as nounwind");
STATISTIC(NumNoCapture, "Number of arguments inferred as nocapture");
STATISTIC(NumWriteOnlyArg, "Number of arguments inferred as writeonly");
@@ -76,14 +80,6 @@ static bool setNoReturn(Function &F) {
return true;
}
-static bool setOnlyAccessesInaccessibleMemory(Function &F) {
- if (F.onlyAccessesInaccessibleMemory())
- return false;
- F.setOnlyAccessesInaccessibleMemory();
- ++NumInaccessibleMemOnly;
- return true;
-}
-
static bool setOnlyReadsMemory(Function &F) {
if (F.onlyReadsMemory())
return false;
@@ -108,6 +104,14 @@ static bool setOnlyAccessesArgMemory(Function &F) {
return true;
}
+static bool setOnlyAccessesErrnoMemory(Function &F) {
+ if (F.onlyAccessesErrnoMemory())
+ return false;
+ F.setOnlyAccessesErrnoMemory();
+ ++NumErrnoMemOnly;
+ return true;
+}
+
static bool setOnlyAccessesInaccessibleMemOrArgMem(Function &F) {
if (F.onlyAccessesInaccessibleMemOrArgMem())
return false;
@@ -116,6 +120,22 @@ static bool setOnlyAccessesInaccessibleMemOrArgMem(Function &F) {
return true;
}
+static bool setOnlyAccessesInaccessibleMemOrErrnoMem(Function &F) {
+ if (F.onlyAccessesInaccessibleMemOrErrnoMem())
+ return false;
+ F.setOnlyAccessesInaccessibleMemOrErrnoMem();
+ ++NumInaccessibleMemOrErrnoMemOnly;
+ return true;
+}
+
+static bool setOnlyAccessesInaccessibleMemOrArgMemOrErrnoMem(Function &F) {
+ if (F.onlyAccessesInaccessibleMemOrArgMemOrErrnoMem())
+ return false;
+ F.setOnlyAccessesInaccessibleMemOrArgMemOrErrnoMem();
+ ++NumInaccessibleMemOrArgMemOrErrnoMemOnly;
+ return true;
+}
+
static bool setDoesNotThrow(Function &F) {
if (F.doesNotThrow())
return false;
@@ -319,6 +339,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setWillReturn(F);
Changed |= setDoesNotCapture(F, 1);
Changed |= setOnlyReadsMemory(F, 0);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_strcat:
case LibFunc_strncat:
@@ -407,7 +428,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
[[fallthrough]];
case LibFunc_strdup:
Changed |= setAllocFamily(F, "malloc");
- Changed |= setOnlyAccessesInaccessibleMemOrArgMem(F);
+ Changed |= setOnlyAccessesInaccessibleMemOrArgMemOrErrnoMem(F);
Changed |= setDoesNotThrow(F);
Changed |= setRetDoesNotAlias(F);
Changed |= setWillReturn(F);
@@ -421,6 +442,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setDoesNotCapture(F, 0);
Changed |= setDoesNotCapture(F, 1);
Changed |= setOnlyReadsMemory(F, 0);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_sscanf:
Changed |= setRetAndArgsNoUndef(F);
@@ -429,6 +451,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setDoesNotCapture(F, 1);
Changed |= setOnlyReadsMemory(F, 0);
Changed |= setOnlyReadsMemory(F, 1);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_sprintf:
Changed |= setRetAndArgsNoUndef(F);
@@ -455,12 +478,14 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setDoesNotCapture(F, 1);
Changed |= setDoesNotCapture(F, 2);
Changed |= setOnlyReadsMemory(F, 1);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_system:
// May throw; "system" is a valid pthread cancellation point.
Changed |= setRetAndArgsNoUndef(F);
Changed |= setDoesNotCapture(F, 0);
Changed |= setOnlyReadsMemory(F, 0);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_aligned_alloc:
Changed |= setAlignedAllocParam(F, 0);
@@ -474,7 +499,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
: "malloc");
Changed |= setAllocKind(F, AllocFnKind::Alloc | AllocFnKind::Uninitialized);
Changed |= setAllocSize(F, 0, std::nullopt);
- Changed |= setOnlyAccessesInaccessibleMemory(F);
+ Changed |= setOnlyAccessesInaccessibleMemOrErrnoMem(F);
Changed |= setRetAndArgsNoUndef(F);
Changed |= setDoesNotThrow(F);
Changed |= setRetDoesNotAlias(F);
@@ -543,7 +568,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
AllocFnKind::Uninitialized);
Changed |= setAllocSize(F, 1, std::nullopt);
Changed |= setAlignedAllocParam(F, 0);
- Changed |= setOnlyAccessesInaccessibleMemory(F);
+ Changed |= setOnlyAccessesInaccessibleMemOrErrnoMem(F);
Changed |= setRetNoUndef(F);
Changed |= setDoesNotThrow(F);
Changed |= setRetDoesNotAlias(F);
@@ -554,6 +579,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setDoesNotThrow(F);
Changed |= setDoesNotCapture(F, 0);
Changed |= setOnlyReadsMemory(F, 0);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_mktime:
Changed |= setRetAndArgsNoUndef(F);
@@ -569,7 +595,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setAllocKind(F, AllocFnKind::Realloc);
Changed |= setAllocatedPointerParam(F, 0);
Changed |= setAllocSize(F, 1, std::nullopt);
- Changed |= setOnlyAccessesInaccessibleMemOrArgMem(F);
+ Changed |= setOnlyAccessesInaccessibleMemOrArgMemOrErrnoMem(F);
Changed |= setRetNoUndef(F);
Changed |= setDoesNotThrow(F);
Changed |= setRetDoesNotAlias(F);
@@ -582,7 +608,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setAllocKind(F, AllocFnKind::Realloc);
Changed |= setAllocatedPointerParam(F, 0);
Changed |= setAllocSize(F, 1, 2);
- Changed |= setOnlyAccessesInaccessibleMemOrArgMem(F);
+ Changed |= setOnlyAccessesInaccessibleMemOrArgMemOrErrnoMem(F);
Changed |= setRetNoUndef(F);
Changed |= setDoesNotThrow(F);
Changed |= setRetDoesNotAlias(F);
@@ -595,6 +621,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
// May throw; "read" is a valid pthread cancellation point.
Changed |= setRetAndArgsNoUndef(F);
Changed |= setDoesNotCapture(F, 1);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_rewind:
Changed |= setRetAndArgsNoUndef(F);
@@ -608,6 +635,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setDoesNotThrow(F);
Changed |= setDoesNotCapture(F, 0);
Changed |= setOnlyReadsMemory(F, 0);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_rename:
Changed |= setRetAndArgsNoUndef(F);
@@ -616,6 +644,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setDoesNotCapture(F, 1);
Changed |= setOnlyReadsMemory(F, 0);
Changed |= setOnlyReadsMemory(F, 1);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_readlink:
Changed |= setRetAndArgsNoUndef(F);
@@ -623,12 +652,14 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setDoesNotCapture(F, 0);
Changed |= setDoesNotCapture(F, 1);
Changed |= setOnlyReadsMemory(F, 0);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_write:
// May throw; "write" is a valid pthread cancellation point.
Changed |= setRetAndArgsNoUndef(F);
Changed |= setDoesNotCapture(F, 1);
Changed |= setOnlyReadsMemory(F, 1);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_bcopy:
Changed |= setDoesNotThrow(F);
@@ -660,7 +691,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
: "malloc");
Changed |= setAllocKind(F, AllocFnKind::Alloc | AllocFnKind::Zeroed);
Changed |= setAllocSize(F, 0, 1);
- Changed |= setOnlyAccessesInaccessibleMemory(F);
+ Changed |= setOnlyAccessesInaccessibleMemOrErrnoMem(F);
Changed |= setRetAndArgsNoUndef(F);
Changed |= setDoesNotThrow(F);
Changed |= setRetDoesNotAlias(F);
@@ -672,6 +703,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setDoesNotThrow(F);
Changed |= setDoesNotCapture(F, 0);
Changed |= setOnlyReadsMemory(F, 0);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_ctermid:
case LibFunc_clearerr:
@@ -679,6 +711,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setRetAndArgsNoUndef(F);
Changed |= setDoesNotThrow(F);
Changed |= setDoesNotCapture(F, 0);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_atoi:
case LibFunc_atol:
@@ -694,6 +727,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setDoesNotThrow(F);
Changed |= setDoesNotCapture(F, 0);
Changed |= setOnlyReadsMemory(F, 0);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_fopen:
Changed |= setRetAndArgsNoUndef(F);
@@ -703,6 +737,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setDoesNotCapture(F, 1);
Changed |= setOnlyReadsMemory(F, 0);
Changed |= setOnlyReadsMemory(F, 1);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_fdopen:
Changed |= setRetAndArgsNoUndef(F);
@@ -710,6 +745,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setRetDoesNotAlias(F);
Changed |= setDoesNotCapture(F, 1);
Changed |= setOnlyReadsMemory(F, 1);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_feof:
Changed |= setRetAndArgsNoUndef(F);
@@ -738,6 +774,8 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
case LibFunc_fflush:
case LibFunc_fclose:
case LibFunc_fsetpos:
+ Changed |= setOnlyAccessesErrnoMemory(F);
+ [[fallthrough]];
case LibFunc_flockfile:
case LibFunc_funlockfile:
case LibFunc_ftrylockfile:
@@ -757,6 +795,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setRetAndArgsNoUndef(F);
Changed |= setDoesNotThrow(F);
Changed |= setDoesNotCapture(F, 1);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_frexp:
case LibFunc_frexpf:
@@ -771,6 +810,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setRetAndArgsNoUndef(F);
Changed |= setDoesNotThrow(F);
Changed |= setDoesNotCapture(F, 1);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_fgets:
case LibFunc_fgets_unlocked:
@@ -802,6 +842,8 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setOnlyReadsMemory(F, 0);
break;
case LibFunc_fscanf:
+ Changed |= setOnlyAccessesErrnoMemory(F);
+ [[fallthrough]];
case LibFunc_fprintf:
Changed |= setRetAndArgsNoUndef(F);
Changed |= setDoesNotThrow(F);
@@ -814,6 +856,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setDoesNotThrow(F);
Changed |= setDoesNotCapture(F, 0);
Changed |= setDoesNotCapture(F, 1);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_getc:
Changed |= setRetAndArgsNoUndef(F);
@@ -846,12 +889,14 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setRetAndArgsNoUndef(F);
Changed |= setDoesNotThrow(F);
Changed |= setDoesNotCapture(F, 1);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_getpwnam:
Changed |= setRetAndArgsNoUndef(F);
Changed |= setDoesNotThrow(F);
Changed |= setDoesNotCapture(F, 0);
Changed |= setOnlyReadsMemory(F, 0);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_ungetc:
Changed |= setRetAndArgsNoUndef(F);
@@ -868,12 +913,14 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setDoesNotThrow(F);
Changed |= setDoesNotCapture(F, 0);
Changed |= setOnlyReadsMemory(F, 0);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_unsetenv:
Changed |= setRetAndArgsNoUndef(F);
Changed |= setDoesNotThrow(F);
Changed |= setDoesNotCapture(F, 0);
Changed |= setOnlyReadsMemory(F, 0);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_utime:
case LibFunc_utimes:
@@ -883,6 +930,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setDoesNotCapture(F, 1);
Changed |= setOnlyReadsMemory(F, 0);
Changed |= setOnlyReadsMemory(F, 1);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_putc:
case LibFunc_putc_unlocked:
@@ -902,12 +950,14 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
// May throw; "pread" is a valid pthread cancellation point.
Changed |= setRetAndArgsNoUndef(F);
Changed |= setDoesNotCapture(F, 1);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_pwrite:
// May throw; "pwrite" is a valid pthread cancellation point.
Changed |= setRetAndArgsNoUndef(F);
Changed |= setDoesNotCapture(F, 1);
Changed |= setOnlyReadsMemory(F, 1);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_putchar:
case LibFunc_putchar_unlocked:
@@ -922,17 +972,20 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setDoesNotCapture(F, 1);
Changed |= setOnlyReadsMemory(F, 0);
Changed |= setOnlyReadsMemory(F, 1);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_pclose:
Changed |= setRetAndArgsNoUndef(F);
Changed |= setDoesNotThrow(F);
Changed |= setDoesNotCapture(F, 0);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_vscanf:
Changed |= setRetAndArgsNoUndef(F);
Changed |= setDoesNotThrow(F);
Changed |= setDoesNotCapture(F, 0);
Changed |= setOnlyReadsMemory(F, 0);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_vsscanf:
Changed |= setRetAndArgsNoUndef(F);
@@ -941,6 +994,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setDoesNotCapture(F, 1);
Changed |= setOnlyReadsMemory(F, 0);
Changed |= setOnlyReadsMemory(F, 1);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_vfscanf:
Changed |= setRetAndArgsNoUndef(F);
@@ -948,6 +1002,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setDoesNotCapture(F, 0);
Changed |= setDoesNotCapture(F, 1);
Changed |= setOnlyReadsMemory(F, 1);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_vprintf:
Changed |= setRetAndArgsNoUndef(F);
@@ -975,6 +1030,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setRetAndArgsNoUndef(F);
Changed |= setDoesNotCapture(F, 0);
Changed |= setOnlyReadsMemory(F, 0);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_opendir:
Changed |= setRetAndArgsNoUndef(F);
@@ -982,11 +1038,13 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setRetDoesNotAlias(F);
Changed |= setDoesNotCapture(F, 0);
Changed |= setOnlyReadsMemory(F, 0);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_tmpfile:
Changed |= setRetAndArgsNoUndef(F);
Changed |= setDoesNotThrow(F);
Changed |= setRetDoesNotAlias(F);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_times:
Changed |= setRetAndArgsNoUndef(F);
@@ -1006,12 +1064,14 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setDoesNotCapture(F, 0);
Changed |= setDoesNotCapture(F, 1);
Changed |= setOnlyReadsMemory(F, 0);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_lchown:
Changed |= setRetAndArgsNoUndef(F);
Changed |= setDoesNotThrow(F);
Changed |= setDoesNotCapture(F, 0);
Changed |= setOnlyReadsMemory(F, 0);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_qsort:
// May throw; places call through function pointer.
@@ -1058,6 +1118,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setDoesNotCapture(F, 0);
Changed |= setDoesNotCapture(F, 1);
Changed |= setOnlyReadsMemory(F, 0);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_dunder_isoc99_sscanf:
Changed |= setRetAndArgsNoUndef(F);
@@ -1075,12 +1136,14 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setDoesNotCapture(F, 1);
Changed |= setOnlyReadsMemory(F, 0);
Changed |= setOnlyReadsMemory(F, 1);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_fseeko64:
case LibFunc_ftello64:
Changed |= setRetAndArgsNoUndef(F);
Changed |= setDoesNotThrow(F);
Changed |= setDoesNotCapture(F, 0);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_tmpfile64:
Changed |= setRetAndArgsNoUndef(F);
@@ -1092,12 +1155,14 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setRetAndArgsNoUndef(F);
Changed |= setDoesNotThrow(F);
Changed |= setDoesNotCapture(F, 1);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_open64:
// May throw; "open" is a valid pthread cancellation point.
Changed |= setRetAndArgsNoUndef(F);
Changed |= setDoesNotCapture(F, 0);
Changed |= setOnlyReadsMemory(F, 0);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_gettimeofday:
// Currently some platforms have the restrict keyword on the arguments to
@@ -1107,6 +1172,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
Changed |= setDoesNotThrow(F);
Changed |= setDoesNotCapture(F, 0);
Changed |= setDoesNotCapture(F, 1);
+ Changed |= setOnlyAccessesErrnoMemory(F);
break;
case LibFunc_memset_pattern4:
case LibFunc_memset_pattern8:
diff --git a/llvm/test/Transforms/InferFunctionAttrs/annotate.ll b/llvm/test/Transforms/InferFunctionAttrs/annotate.ll
index e63487de61ba24..3715c8eacf337e 100644
--- a/llvm/test/Transforms/InferFunctionAttrs/annotate.ll
+++ b/llvm/test/Transforms/InferFunctionAttrs/annotate.ll
@@ -198,7 +198,7 @@ declare float @__sinpif(float)
; CHECK: declare i32 @abs(i32) [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY:#[0-9]+]]
declare i32 @abs(i32)
-; CHECK: declare noundef i32 @access(ptr nocapture noundef readonly, i32 noundef) [[NOFREE_NOUNWIND:#[0-9]+]]
+; CHECK: declare noundef i32 @access(ptr nocapture noundef readonly, i32 noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @access(ptr, i32)
; CHECK: declare double @acos(double) [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
@@ -219,7 +219,7 @@ declare x86_fp80 @acoshl(x86_fp80)
; CHECK: declare x86_fp80 @acosl(x86_fp80) [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
declare x86_fp80 @acosl(x86_fp80)
-; CHECK: declare noalias noundef ptr @aligned_alloc(i64 allocalign noundef, i64 noundef) [[INACCESSIBLEMEMONLY_NOFREE_NOUNWIND_WILLRETURN_ALLOCKIND_ALLOCUNINIT_ALLOCSIZE1_FAMILY_MALLOC:#[0-9]+]]
+; CHECK: declare noalias noundef ptr @aligned_alloc(i64 allocalign noundef, i64 noundef) [[INACCESSIBLEMEMORERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN_ALLOCKIND_ALLOCUNINIT_ALLOCSIZE1_FAMILY_MALLOC:#[0-9]+]]
declare ptr @aligned_alloc(i64, i64)
; CHECK: declare double @asin(double) [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
@@ -289,10 +289,10 @@ declare void @bcopy(ptr, ptr, i64)
; CHECK: declare void @bzero(ptr nocapture writeonly, i64) [[ARGMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
declare void @bzero(ptr, i64)
-; CHECK: declare noalias noundef ptr @calloc(i64 noundef, i64 noundef) [[INACCESSIBLEMEMONLY_NOFREE_NOUNWIND_WILLRETURN_ALLOCKIND_ALLOCZEROED_ALLOCSIZE01_FAMILY_MALLOC:#[0-9]+]]
+; CHECK: declare noalias noundef ptr @calloc(i64 noundef, i64 noundef) [[INACCESSIBLEMEMORERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN_ALLOCKIND_ALLOCZEROED_ALLOCSIZE01_FAMILY_MALLOC:#[0-9]+]]
declare ptr @calloc(i64, i64)
-; CHECK-AIX: declare noalias noundef ptr @vec_calloc(i64 noundef, i64 noundef) [[INACCESSIBLEMEMONLY_NOFREE_NOUNWIND_WILLRETURN_ALLOCSIZE01_FAMILY_VEC_MALLOC:#[0-9]+]]
+; CHECK-AIX: declare noalias noundef ptr @vec_calloc(i64 noundef, i64 noundef) [[INACCESSIBLEMEMORERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN_ALLOCSIZE01_FAMILY_VEC_MALLOC:#[0-9]+]]
declare ptr @vec_calloc(i64, i64)
; CHECK: declare double @cbrt(double) [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
@@ -318,16 +318,16 @@ declare x86_fp80 @ceill(x86_fp80)
; the function is still recognized.
; FIXME: this should be tightened up to verify that only the type with
; the right size for the target matches.
-; CHECK: declare noundef i32 @chmod(ptr nocapture noundef readonly, i16 noundef zeroext) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @chmod(ptr nocapture noundef readonly, i16 noundef zeroext) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @chmod(ptr, i16 zeroext)
-; CHECK: declare noundef i32 @chown(ptr nocapture noundef readonly, i32 noundef, i32 noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @chown(ptr nocapture noundef readonly, i32 noundef, i32 noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @chown(ptr, i32, i32)
-; CHECK: declare void @clearerr(ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare void @clearerr(ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare void @clearerr(ptr)
-; CHECK: declare noundef i32 @closedir(ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @closedir(ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @closedir(ptr)
; CHECK: declare double @copysign(double, double) [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
@@ -357,7 +357,7 @@ declare x86_fp80 @coshl(x86_fp80)
; CHECK: declare x86_fp80 @cosl(x86_fp80) [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
declare x86_fp80 @cosl(x86_fp80)
-; CHECK: declare noundef ptr @ctermid(ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef ptr @ctermid(ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare ptr @ctermid(ptr)
; CHECK: declare double @exp(double) [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
@@ -396,19 +396,19 @@ declare float @fabsf(float)
; CHECK: declare x86_fp80 @fabsl(x86_fp80) [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
declare x86_fp80 @fabsl(x86_fp80)
-; CHECK: declare noundef i32 @fclose(ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @fclose(ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @fclose(ptr)
-; CHECK: declare noalias noundef ptr @fdopen(i32 noundef, ptr nocapture noundef readonly) [[NOFREE_NOUNWIND]]
+; CHECK: declare noalias noundef ptr @fdopen(i32 noundef, ptr nocapture noundef readonly) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare ptr @fdopen(i32, ptr)
-; CHECK: declare noundef i32 @feof(ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @feof(ptr nocapture noundef) [[NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @feof(ptr)
; CHECK: declare noundef i32 @ferror(ptr nocapture noundef) [[NOFREE_NOUNWIND_READONLY:#[0-9]+]]
declare i32 @ferror(ptr)
-; CHECK: declare noundef i32 @fflush(ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @fflush(ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @fflush(ptr)
; CHECK: declare i32 @ffs(i32) [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
@@ -422,16 +422,16 @@ declare i32 @ffsl(i64)
; CHECK-UNKNOWN: declare i32 @ffsll(i64){{$}}
declare i32 @ffsll(i64)
-; CHECK: declare noundef i32 @fgetc(ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @fgetc(ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @fgetc(ptr)
-; CHECK: declare noundef i32 @fgetpos(ptr nocapture noundef, ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @fgetpos(ptr nocapture noundef, ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @fgetpos(ptr, ptr)
; CHECK: declare noundef ptr @fgets(ptr noundef, i32 noundef, ptr nocapture noundef) [[NOFREE_NOUNWIND]]
declare ptr @fgets(ptr, i32, ptr)
-; CHECK: declare noundef i32 @fileno(ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @fileno(ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @fileno(ptr)
; CHECK: declare void @flockfile(ptr nocapture noundef) [[NOFREE_NOUNWIND]]
@@ -482,13 +482,13 @@ declare float @fmodf(float, float)
; CHECK: declare x86_fp80 @fmodl(x86_fp80, x86_fp80) [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
declare x86_fp80 @fmodl(x86_fp80, x86_fp80)
-; CHECK: declare noalias noundef ptr @fopen(ptr nocapture noundef readonly, ptr nocapture noundef readonly) [[NOFREE_NOUNWIND]]
+; CHECK: declare noalias noundef ptr @fopen(ptr nocapture noundef readonly, ptr nocapture noundef readonly) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare ptr @fopen(ptr, ptr)
; CHECK: declare noundef i32 @fprintf(ptr nocapture noundef, ptr nocapture noundef readonly, ...) [[NOFREE_NOUNWIND]]
declare i32 @fprintf(ptr, ptr, ...)
-; CHECK: declare noundef i32 @fputc(i32 noundef, ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @fputc(i32 noundef, ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @fputc(i32, ptr)
; CHECK: declare noundef i32 @fputs(ptr nocapture noundef readonly, ptr nocapture noundef) [[NOFREE_NOUNWIND]]
@@ -512,40 +512,40 @@ declare float @frexpf(float, ptr)
; CHECK: declare x86_fp80 @frexpl(x86_fp80, ptr nocapture) [[ARGMEMONLY_NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
declare x86_fp80 @frexpl(x86_fp80, ptr)
-; CHECK: declare noundef i32 @fscanf(ptr nocapture noundef, ptr nocapture noundef readonly, ...) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @fscanf(ptr nocapture noundef, ptr nocapture noundef readonly, ...) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @fscanf(ptr, ptr, ...)
-; CHECK: declare noundef i32 @fseek(ptr nocapture noundef, i64 noundef, i32 noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @fseek(ptr nocapture noundef, i64 noundef, i32 noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @fseek(ptr, i64, i32)
-; CHECK: declare noundef i32 @fseeko(ptr nocapture noundef, i64 noundef, i32 noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @fseeko(ptr nocapture noundef, i64 noundef, i32 noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @fseeko(ptr, i64, i32)
-; CHECK-LINUX: declare noundef i32 @fseeko64(ptr nocapture noundef, i64 noundef, i32 noundef) [[NOFREE_NOUNWIND]]
+; CHECK-LINUX: declare noundef i32 @fseeko64(ptr nocapture noundef, i64 noundef, i32 noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @fseeko64(ptr, i64, i32)
-; CHECK: declare noundef i32 @fsetpos(ptr nocapture noundef, ptr noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @fsetpos(ptr nocapture noundef, ptr noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @fsetpos(ptr, ptr)
-; CHECK: declare noundef i32 @fstat(i32 noundef, ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @fstat(i32 noundef, ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @fstat(i32, ptr)
-; CHECK-LINUX: declare noundef i32 @fstat64(i32 noundef, ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK-LINUX: declare noundef i32 @fstat64(i32 noundef, ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @fstat64(i32, ptr)
-; CHECK: declare noundef i32 @fstatvfs(i32 noundef, ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @fstatvfs(i32 noundef, ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @fstatvfs(i32, ptr)
-; CHECK-LINUX: declare noundef i32 @fstatvfs64(i32 noundef, ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK-LINUX: declare noundef i32 @fstatvfs64(i32 noundef, ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @fstatvfs64(i32, ptr)
-; CHECK: declare noundef i64 @ftell(ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i64 @ftell(ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i64 @ftell(ptr)
-; CHECK: declare noundef i64 @ftello(ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i64 @ftello(ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i64 @ftello(ptr)
-; CHECK-LINUX: declare noundef i64 @ftello64(ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK-LINUX: declare noundef i64 @ftello64(ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i64 @ftello64(ptr)
; CHECK: declare noundef i32 @ftrylockfile(ptr nocapture noundef) [[NOFREE_NOUNWIND]]
@@ -574,19 +574,19 @@ declare i32 @getchar_unlocked()
; CHECK: declare noundef ptr @getenv(ptr nocapture noundef) [[NOFREE_NOUNWIND_READONLY]]
declare ptr @getenv(ptr)
-; CHECK: declare noundef i32 @getitimer(i32 noundef, ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @getitimer(i32 noundef, ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @getitimer(i32, ptr)
; CHECK: declare noundef i32 @getlogin_r(ptr nocapture noundef, i64 noundef) [[NOFREE_NOUNWIND]]
declare i32 @getlogin_r(ptr, i64)
-; CHECK: declare noundef ptr @getpwnam(ptr nocapture noundef readonly) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef ptr @getpwnam(ptr nocapture noundef readonly) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare ptr @getpwnam(ptr)
; CHECK: declare noundef ptr @gets(ptr noundef) [[NOFREE_NOUNWIND]]
declare ptr @gets(ptr)
-; CHECK: declare noundef i32 @gettimeofday(ptr nocapture noundef, ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @gettimeofday(ptr nocapture noundef, ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @gettimeofday(ptr, ptr)
; CHECK: declare double @hypot(double, double) [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
@@ -607,7 +607,7 @@ declare i32 @isdigit(i32)
; CHECK: declare i64 @labs(i64) [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
declare i64 @labs(i64)
-; CHECK: declare noundef i32 @lchown(ptr nocapture noundef readonly, i32 noundef, i32 noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @lchown(ptr nocapture noundef readonly, i32 noundef, i32 noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @lchown(ptr, i32, i32)
; CHECK: declare double @ldexp(double, i32) [[NOFREE_WILLRETURN:#[0-9]+]]
@@ -685,16 +685,16 @@ declare float @tgammaf(float)
; CHECK: declare x86_fp80 @tgammal(x86_fp80) [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
declare x86_fp80 @tgammal(x86_fp80)
-; CHECK: declare noundef i32 @lstat(ptr nocapture noundef readonly, ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @lstat(ptr nocapture noundef readonly, ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @lstat(ptr, ptr)
-; CHECK-LINUX: declare noundef i32 @lstat64(ptr nocapture noundef readonly, ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK-LINUX: declare noundef i32 @lstat64(ptr nocapture noundef readonly, ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @lstat64(ptr, ptr)
-; CHECK: declare noalias noundef ptr @malloc(i64 noundef) [[INACCESSIBLEMEMONLY_NOFREE_NOUNWIND_WILLRETURN_ALLOCKIND_ALLOCUNINIT_ALLOCSIZE0_FAMILY_MALLOC:#[0-9]+]]
+; CHECK: declare noalias noundef ptr @malloc(i64 noundef) [[INACCESSIBLEMEMORERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN_ALLOCKIND_ALLOCUNINIT_ALLOCSIZE0_FAMILY_MALLOC:#[0-9]+]]
declare ptr @malloc(i64)
-; CHECK-AIX: declare noalias noundef ptr @vec_malloc(i64 noundef) [[INACCESSIBLEMEMONLY_NOFREE_NOUNWIND_WILLRETURN_ALLOCSIZE0_FAMILY_VEC_MALLOC:#[0-9]+]]
+; CHECK-AIX: declare noalias noundef ptr @vec_malloc(i64 noundef) [[INACCESSIBLEMEMORERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN_ALLOCSIZE0_FAMILY_VEC_MALLOC:#[0-9]+]]
declare ptr @vec_malloc(i64)
; CHECK-LINUX: declare noalias noundef ptr @memalign(i64 allocalign, i64) [[INACCESSIBLEMEMONLY_NOFREE_NOUNWIND_WILLRETURN:#[0-9]+]]
@@ -729,7 +729,7 @@ declare ptr @memset(ptr, i32, i64)
; CHECK: declare ptr @__memset_chk(ptr writeonly, i32, i64, i64) [[ARGMEMONLY_NOFREE_NOUNWIND]]
declare ptr @__memset_chk(ptr, i32, i64, i64)
-; CHECK: declare noundef i32 @mkdir(ptr nocapture noundef readonly, i16 noundef zeroext) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @mkdir(ptr nocapture noundef readonly, i16 noundef zeroext) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @mkdir(ptr, i16 zeroext)
; CHECK: declare noundef i64 @mktime(ptr nocapture noundef) [[NOFREE_NOUNWIND_WILLRETURN:#[0-9]+]]
@@ -762,26 +762,26 @@ declare float @nearbyintf(float)
; CHECK: declare x86_fp80 @nearbyintl(x86_fp80) [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
declare x86_fp80 @nearbyintl(x86_fp80)
-; CHECK-LINUX: declare noundef i32 @open(ptr nocapture noundef readonly, i32 noundef, ...) [[NOFREE]]
-; CHECK-OPEN: declare noundef i32 @open(ptr nocapture noundef readonly, i32 noundef, ...) [[NOFREE:#[0-9]+]]
+; CHECK-LINUX: declare noundef i32 @open(ptr nocapture noundef readonly, i32 noundef, ...) [[ERRNOMEMONLY_NOFREE:#[0-9]+]]
+; CHECK-OPEN: declare noundef i32 @open(ptr nocapture noundef readonly, i32 noundef, ...) [[ERRNOMEMONLY_NOFREE:#[0-9]+]]
declare i32 @open(ptr, i32, ...)
-; CHECK-LINUX: declare noundef i32 @open64(ptr nocapture noundef readonly, i32 noundef, ...) [[NOFREE]]
+; CHECK-LINUX: declare noundef i32 @open64(ptr nocapture noundef readonly, i32 noundef, ...) [[ERRNOMEMONLY_NOFREE:#[0-9]+]]
declare i32 @open64(ptr, i32, ...)
-; CHECK: declare noalias noundef ptr @opendir(ptr nocapture noundef readonly) [[NOFREE_NOUNWIND]]
+; CHECK: declare noalias noundef ptr @opendir(ptr nocapture noundef readonly) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare ptr @opendir(ptr)
-; CHECK: declare noundef i32 @pclose(ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @pclose(ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @pclose(ptr)
; CHECK: declare void @perror(ptr nocapture noundef readonly) [[NOFREE_NOUNWIND]]
declare void @perror(ptr)
-; CHECK: declare noalias noundef ptr @popen(ptr nocapture noundef readonly, ptr nocapture noundef readonly) [[NOFREE_NOUNWIND]]
+; CHECK: declare noalias noundef ptr @popen(ptr nocapture noundef readonly, ptr nocapture noundef readonly) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare ptr @popen(ptr, ptr)
-; CHECK: declare i32 @posix_memalign(ptr, i64, i64) [[NOFREE]]
+; CHECK: declare i32 @posix_memalign(ptr, i64, i64) [[NOFREE:#[0-9]+]]
declare i32 @posix_memalign(ptr, i64, i64)
; CHECK: declare double @pow(double, double) [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
@@ -793,7 +793,7 @@ declare float @powf(float, float)
; CHECK: declare x86_fp80 @powl(x86_fp80, x86_fp80) [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
declare x86_fp80 @powl(x86_fp80, x86_fp80)
-; CHECK: declare noundef i64 @pread(i32 noundef, ptr nocapture noundef, i64 noundef, i64 noundef) [[NOFREE]]
+; CHECK: declare noundef i64 @pread(i32 noundef, ptr nocapture noundef, i64 noundef, i64 noundef) [[ERRNOMEMONLY_NOFREE:#[0-9]+]]
declare i64 @pread(i32, ptr, i64, i64)
; CHECK: declare noundef i32 @printf(ptr nocapture noundef readonly, ...) [[NOFREE_NOUNWIND]]
@@ -812,31 +812,31 @@ declare i32 @putchar_unlocked(i32)
; CHECK: declare noundef i32 @puts(ptr nocapture noundef readonly) [[NOFREE_NOUNWIND]]
declare i32 @puts(ptr)
-; CHECK: declare noundef i64 @pwrite(i32 noundef, ptr nocapture noundef readonly, i64 noundef, i64 noundef) [[NOFREE]]
+; CHECK: declare noundef i64 @pwrite(i32 noundef, ptr nocapture noundef readonly, i64 noundef, i64 noundef) [[ERRNOMEMONLY_NOFREE:#[0-9]+]]
declare i64 @pwrite(i32, ptr, i64, i64)
; CHECK: declare void @qsort(ptr noundef, i64 noundef, i64 noundef, ptr nocapture noundef) [[NOFREE]]
declare void @qsort(ptr, i64, i64, ptr)
-; CHECK: declare noundef i64 @read(i32 noundef, ptr nocapture noundef, i64 noundef) [[NOFREE]]
+; CHECK: declare noundef i64 @read(i32 noundef, ptr nocapture noundef, i64 noundef) [[ERRNOMEMONLY_NOFREE:#[0-9]+]]
declare i64 @read(i32, ptr, i64)
-; CHECK: declare noundef i64 @readlink(ptr nocapture noundef readonly, ptr nocapture noundef, i64 noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i64 @readlink(ptr nocapture noundef readonly, ptr nocapture noundef, i64 noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i64 @readlink(ptr, ptr, i64)
-; CHECK: declare noalias noundef ptr @realloc(ptr allocptr nocapture, i64 noundef) [[INACCESSIBLEMEMORARGMEMONLY_NOUNWIND_WILLRETURN_ALLOCKIND_REALLOC_ALLOCSIZE1_FAMILY_MALLOC:#[0-9]+]]
+; CHECK: declare noalias noundef ptr @realloc(ptr allocptr nocapture, i64 noundef) [[INACCESSIBLEMEMORARGMEMORERRNOMEMONLY_NOUNWIND_WILLRETURN_ALLOCKIND_REALLOC_ALLOCSIZE1_FAMILY_MALLOC:#[0-9]+]]
declare ptr @realloc(ptr, i64)
-; CHECK: declare noalias noundef ptr @reallocarray(ptr allocptr nocapture, i64 noundef, i64 noundef) [[INACCESSIBLEMEMORARGMEMONLY_NOUNWIND_WILLRETURN_ALLOCKIND_REALLOC_ALLOCSIZE12_FAMILY_MALLOC:#[0-9]+]]
+; CHECK: declare noalias noundef ptr @reallocarray(ptr allocptr nocapture, i64 noundef, i64 noundef) [[INACCESSIBLEMEMORARGMEMORERRNOMEMONLY_NOUNWIND_WILLRETURN_ALLOCKIND_REALLOC_ALLOCSIZE12_FAMILY_MALLOC:#[0-9]+]]
declare ptr @reallocarray(ptr, i64, i64)
-; CHECK: declare noalias noundef ptr @reallocf(ptr allocptr nocapture, i64 noundef) [[INACCESSIBLEMEMORARGMEMONLY_NOUNWIND_WILLRETURN_ALLOCKIND_REALLOC_ALLOCSIZE1_FAMILY_MALLOC]]
+; CHECK: declare noalias noundef ptr @reallocf(ptr allocptr nocapture, i64 noundef) [[INACCESSIBLEMEMORARGMEMORERRNOMEMONLY_NOUNWIND_WILLRETURN_ALLOCKIND_REALLOC_ALLOCSIZE1_FAMILY_MALLOC:#[0-9]+]]
declare ptr @reallocf(ptr, i64)
-; CHECK-AIX: declare noalias noundef ptr @vec_realloc(ptr allocptr nocapture, i64 noundef) [[INACCESSIBLEMEMORARGMEMONLY_NOUNWIND_WILLRETURN_ALLOCSIZE_FAMILY_VEC_MALLOC:#[0-9]+]]
+; CHECK-AIX: declare noalias noundef ptr @vec_realloc(ptr allocptr nocapture, i64 noundef) [[INACCESSIBLEMEMORARGMEMORERRNOMEMONLY_NOUNWIND_WILLRETURN_ALLOCSIZE_FAMILY_VEC_MALLOC:#[0-9]+]]
declare ptr @vec_realloc(ptr, i64)
-; CHECK: declare noundef ptr @realpath(ptr nocapture noundef readonly, ptr noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef ptr @realpath(ptr nocapture noundef readonly, ptr noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare ptr @realpath(ptr, ptr)
; CHECK: declare double @remainder(double, double) [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
@@ -848,7 +848,7 @@ declare float @remainderf(float, float)
; CHECK: declare x86_fp80 @remainderl(x86_fp80, x86_fp80) [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
declare x86_fp80 @remainderl(x86_fp80, x86_fp80)
-; CHECK: declare noundef i32 @remove(ptr nocapture noundef readonly) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @remove(ptr nocapture noundef readonly) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @remove(ptr)
; CHECK: declare double @remquo(double, double, ptr nocapture) [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
@@ -870,7 +870,7 @@ declare float @fdimf(float, float)
; CHECK: declare x86_fp80 @fdiml(x86_fp80, x86_fp80) [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
declare x86_fp80 @fdiml(x86_fp80, x86_fp80)
-; CHECK: declare noundef i32 @rename(ptr nocapture noundef readonly, ptr nocapture noundef readonly) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @rename(ptr nocapture noundef readonly, ptr nocapture noundef readonly) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @rename(ptr, ptr)
; CHECK: declare void @rewind(ptr nocapture noundef) [[NOFREE_NOUNWIND]]
@@ -885,7 +885,7 @@ declare float @rintf(float)
; CHECK: declare x86_fp80 @rintl(x86_fp80) [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
declare x86_fp80 @rintl(x86_fp80)
-; CHECK: declare noundef i32 @rmdir(ptr nocapture noundef readonly) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @rmdir(ptr nocapture noundef readonly) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @rmdir(ptr)
; CHECK: declare double @round(double) [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
@@ -921,7 +921,7 @@ declare i32 @scanf(ptr, ...)
; CHECK: declare void @setbuf(ptr nocapture noundef, ptr noundef) [[NOFREE_NOUNWIND]]
declare void @setbuf(ptr, ptr)
-; CHECK: declare noundef i32 @setitimer(i32 noundef, ptr nocapture noundef readonly, ptr nocapture noundef) [[NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare noundef i32 @setitimer(i32 noundef, ptr nocapture noundef readonly, ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN:#[0-9]+]]
declare i32 @setitimer(i32, ptr, ptr)
; CHECK: declare noundef i32 @setvbuf(ptr nocapture noundef, ptr noundef, i32 noundef, i64 noundef) [[NOFREE_NOUNWIND]]
@@ -960,19 +960,19 @@ declare float @sqrtf(float)
; CHECK: declare x86_fp80 @sqrtl(x86_fp80) [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
declare x86_fp80 @sqrtl(x86_fp80)
-; CHECK: declare noundef i32 @sscanf(ptr nocapture noundef readonly, ptr nocapture noundef readonly, ...) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @sscanf(ptr nocapture noundef readonly, ptr nocapture noundef readonly, ...) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @sscanf(ptr, ptr, ...)
-; CHECK: declare noundef i32 @stat(ptr nocapture noundef readonly, ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @stat(ptr nocapture noundef readonly, ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @stat(ptr, ptr)
-; CHECK-LINUX: declare noundef i32 @stat64(ptr nocapture noundef readonly, ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK-LINUX: declare noundef i32 @stat64(ptr nocapture noundef readonly, ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @stat64(ptr, ptr)
-; CHECK: declare noundef i32 @statvfs(ptr nocapture noundef readonly, ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @statvfs(ptr nocapture noundef readonly, ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @statvfs(ptr, ptr)
-; CHECK-LINUX: declare noundef i32 @statvfs64(ptr nocapture noundef readonly, ptr nocapture noundef) [[NOFREE_NOUNWIND]]
+; CHECK-LINUX: declare noundef i32 @statvfs64(ptr nocapture noundef readonly, ptr nocapture noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @statvfs64(ptr, ptr)
; CHECK: declare ptr @stpcpy(ptr noalias writeonly, ptr noalias nocapture readonly) [[ARGMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
@@ -1002,7 +1002,7 @@ declare ptr @strcpy(ptr, ptr)
; CHECK: declare i64 @strcspn(ptr nocapture, ptr nocapture) [[ARGMEMONLY_NOFREE_NOUNWIND_READONLY]]
declare i64 @strcspn(ptr, ptr)
-; CHECK: declare noalias ptr @strdup(ptr nocapture readonly) [[INACCESSIBLEMEMORARGONLY_NOFREE_NOUNWIND_WILLRETURN_FAMILY_MALLOC:#[0-9]+]]
+; CHECK: declare noalias ptr @strdup(ptr nocapture readonly) [[INACCESSIBLEMEMORARGMEMORERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN_FAMILY_MALLOC:#[0-9]+]]
declare ptr @strdup(ptr)
; CHECK: declare i64 @strlen(ptr nocapture) [[ARGMEMONLY_NOFREE_NOUNWIND_READONLY]]
@@ -1020,7 +1020,7 @@ declare i32 @strncmp(ptr, ptr, i64)
; CHECK: declare ptr @strncpy(ptr noalias returned writeonly, ptr noalias nocapture readonly, i64) [[ARGMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
declare ptr @strncpy(ptr, ptr, i64)
-; CHECK: declare noalias ptr @strndup(ptr nocapture readonly, i64 noundef) [[INACCESSIBLEMEMORARGONLY_NOFREE_NOUNWIND_WILLRETURN_FAMILY_MALLOC]]
+; CHECK: declare noalias ptr @strndup(ptr nocapture readonly, i64 noundef) [[INACCESSIBLEMEMORARGMEMORERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN_FAMILY_MALLOC:#[0-9]+]]
declare ptr @strndup(ptr, i64)
; CHECK: declare i64 @strnlen(ptr nocapture, i64) [[ARGMEMONLY_NOFREE_NOUNWIND_READONLY_WILLRETURN]]
@@ -1038,10 +1038,10 @@ declare i64 @strspn(ptr, ptr)
; CHECK: declare ptr @strstr(ptr, ptr nocapture) [[ARGMEMONLY_NOFREE_NOUNWIND_READONLY_WILLRETURN]]
declare ptr @strstr(ptr, ptr)
-; CHECK: declare double @strtod(ptr readonly, ptr nocapture) [[NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare double @strtod(ptr readonly, ptr nocapture) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN:#[0-9]+]]
declare double @strtod(ptr, ptr)
-; CHECK: declare float @strtof(ptr readonly, ptr nocapture) [[NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare float @strtof(ptr readonly, ptr nocapture) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN:#[0-9]+]]
declare float @strtof(ptr, ptr)
; CHECK: declare ptr @strtok(ptr, ptr nocapture readonly) [[NOFREE_NOUNWIND_WILLRETURN]]
@@ -1050,25 +1050,25 @@ declare ptr @strtok(ptr, ptr)
; CHECK: declare ptr @strtok_r(ptr, ptr nocapture readonly, ptr) [[NOFREE_NOUNWIND_WILLRETURN]]
declare ptr @strtok_r(ptr, ptr, ptr)
-; CHECK: declare i64 @strtol(ptr readonly, ptr nocapture, i32) [[NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare i64 @strtol(ptr readonly, ptr nocapture, i32) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN:#[0-9]+]]
declare i64 @strtol(ptr, ptr, i32)
-; CHECK: declare x86_fp80 @strtold(ptr readonly, ptr nocapture) [[NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare x86_fp80 @strtold(ptr readonly, ptr nocapture) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN:#[0-9]+]]
declare x86_fp80 @strtold(ptr, ptr)
-; CHECK: declare i64 @strtoll(ptr readonly, ptr nocapture, i32) [[NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare i64 @strtoll(ptr readonly, ptr nocapture, i32) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN:#[0-9]+]]
declare i64 @strtoll(ptr, ptr, i32)
-; CHECK: declare i64 @strtoul(ptr readonly, ptr nocapture, i32) [[NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare i64 @strtoul(ptr readonly, ptr nocapture, i32) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN:#[0-9]+]]
declare i64 @strtoul(ptr, ptr, i32)
-; CHECK: declare i64 @strtoull(ptr readonly, ptr nocapture, i32) [[NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare i64 @strtoull(ptr readonly, ptr nocapture, i32) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN:#[0-9]+]]
declare i64 @strtoull(ptr, ptr, i32)
; CHECK: declare i64 @strxfrm(ptr nocapture, ptr nocapture readonly, i64) [[NOFREE_NOUNWIND_WILLRETURN]]
declare i64 @strxfrm(ptr, ptr, i64)
-; CHECK: declare noundef i32 @system(ptr nocapture noundef readonly) [[NOFREE]]
+; CHECK: declare noundef i32 @system(ptr nocapture noundef readonly) [[ERRNOMEMONLY_NOFREE:#[0-9]+]]
declare i32 @system(ptr)
; CHECK: declare double @tan(double) [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
@@ -1092,7 +1092,7 @@ declare x86_fp80 @tanl(x86_fp80)
; CHECK: declare noundef i64 @times(ptr nocapture noundef) [[NOFREE_NOUNWIND]]
declare i64 @times(ptr)
-; CHECK: declare noalias noundef ptr @tmpfile() [[NOFREE_NOUNWIND]]
+; CHECK: declare noalias noundef ptr @tmpfile() [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare ptr @tmpfile()
; CHECK-LINUX: declare noalias noundef ptr @tmpfile64() [[NOFREE_NOUNWIND]]
@@ -1116,31 +1116,31 @@ declare i32 @uname(ptr)
; CHECK: declare noundef i32 @ungetc(i32 noundef, ptr nocapture noundef) [[NOFREE_NOUNWIND]]
declare i32 @ungetc(i32, ptr)
-; CHECK: declare noundef i32 @unlink(ptr nocapture noundef readonly) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @unlink(ptr nocapture noundef readonly) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @unlink(ptr)
-; CHECK: declare noundef i32 @unsetenv(ptr nocapture noundef readonly) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @unsetenv(ptr nocapture noundef readonly) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @unsetenv(ptr)
-; CHECK: declare noundef i32 @utime(ptr nocapture noundef readonly, ptr nocapture noundef readonly) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @utime(ptr nocapture noundef readonly, ptr nocapture noundef readonly) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @utime(ptr, ptr)
-; CHECK: declare noundef i32 @utimes(ptr nocapture noundef readonly, ptr nocapture noundef readonly) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @utimes(ptr nocapture noundef readonly, ptr nocapture noundef readonly) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @utimes(ptr, ptr)
-; CHECK: declare noalias noundef ptr @valloc(i64 noundef) [[INACCESSIBLEMEMONLY_NOFREE_NOUNWIND_WILLRETURN_ALLOCKIND_ALLOCUNINIT_ALLOCSIZE0_FAMILY_MALLOC]]
+; CHECK: declare noalias noundef ptr @valloc(i64 noundef) [[INACCESSIBLEMEMORERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN_ALLOCKIND_ALLOCUNINIT_ALLOCSIZE0_FAMILY_MALLOC]]
declare ptr @valloc(i64)
; CHECK: declare noundef i32 @vfprintf(ptr nocapture noundef, ptr nocapture noundef readonly, ptr noundef) [[NOFREE_NOUNWIND]]
declare i32 @vfprintf(ptr, ptr, ptr)
-; CHECK: declare noundef i32 @vfscanf(ptr nocapture noundef, ptr nocapture noundef readonly, ptr noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @vfscanf(ptr nocapture noundef, ptr nocapture noundef readonly, ptr noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @vfscanf(ptr, ptr, ptr)
; CHECK: declare noundef i32 @vprintf(ptr nocapture noundef readonly, ptr noundef) [[NOFREE_NOUNWIND]]
declare i32 @vprintf(ptr, ptr)
-; CHECK: declare noundef i32 @vscanf(ptr nocapture noundef readonly, ptr noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @vscanf(ptr nocapture noundef readonly, ptr noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @vscanf(ptr, ptr)
; CHECK: declare noundef i32 @vsnprintf(ptr nocapture noundef, i64 noundef, ptr nocapture noundef readonly, ptr noundef) [[NOFREE_NOUNWIND]]
@@ -1149,10 +1149,10 @@ declare i32 @vsnprintf(ptr, i64, ptr, ptr)
; CHECK: declare noundef i32 @vsprintf(ptr nocapture noundef, ptr nocapture noundef readonly, ptr noundef) [[NOFREE_NOUNWIND]]
declare i32 @vsprintf(ptr, ptr, ptr)
-; CHECK: declare noundef i32 @vsscanf(ptr nocapture noundef readonly, ptr nocapture noundef readonly, ptr noundef) [[NOFREE_NOUNWIND]]
+; CHECK: declare noundef i32 @vsscanf(ptr nocapture noundef readonly, ptr nocapture noundef readonly, ptr noundef) [[ERRNOMEMONLY_NOFREE_NOUNWIND:#[0-9]+]]
declare i32 @vsscanf(ptr, ptr, ptr)
-; CHECK: declare noundef i64 @write(i32 noundef, ptr nocapture noundef readonly, i64 noundef) [[NOFREE]]
+; CHECK: declare noundef i64 @write(i32 noundef, ptr nocapture noundef readonly, i64 noundef) [[ERRNOMEMONLY_NOFREE:#[0-9]+]]
declare i64 @write(i32, ptr, i64)
; CHECK: declare void @abort() [[NOFREE_COLD:#[0-9]+]]
@@ -1182,30 +1182,33 @@ declare void @memset_pattern8(ptr, ptr, i64)
declare void @memset_pattern16(ptr, ptr, i64)
; CHECK-DAG: attributes [[NOFREE_NOUNWIND_WILLRETURN]] = { mustprogress nofree nounwind willreturn }
+; CHECK-DAG: attributes [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]] = { mustprogress nofree nounwind willreturn memory(errnomem: readwrite) }
; CHECK-DAG: attributes [[NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]] = { mustprogress nofree nounwind willreturn memory(write) }
; CHECK-DAG: attributes [[ARGMEMONLY_NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]] = { mustprogress nofree nounwind willreturn memory(argmem: write) }
+; CHECK-DAG: attributes [[ERRNOMEMONLY_NOFREE_NOUNWIND]] = { nofree nounwind memory(errnomem: readwrite) }
; CHECK-DAG: attributes [[NOFREE_NOUNWIND]] = { nofree nounwind }
-; CHECK-DAG: attributes [[INACCESSIBLEMEMONLY_NOFREE_NOUNWIND_WILLRETURN_ALLOCKIND_ALLOCUNINIT_ALLOCSIZE1_FAMILY_MALLOC]] = { mustprogress nofree nounwind willreturn allockind("alloc,uninitialized,aligned") allocsize(1) memory(inaccessiblemem: readwrite) "alloc-family"="malloc" }
-; CHECK-DAG: attributes [[INACCESSIBLEMEMONLY_NOFREE_NOUNWIND_WILLRETURN_ALLOCKIND_ALLOCZEROED_ALLOCSIZE01_FAMILY_MALLOC]] = { mustprogress nofree nounwind willreturn allockind("alloc,zeroed") allocsize(0,1) memory(inaccessiblemem: readwrite) "alloc-family"="malloc" }
+; CHECK-DAG: attributes [[INACCESSIBLEMEMORERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN_ALLOCKIND_ALLOCUNINIT_ALLOCSIZE1_FAMILY_MALLOC]] = { mustprogress nofree nounwind willreturn allockind("alloc,uninitialized,aligned") allocsize(1) memory(inaccessiblemem: readwrite, errnomem: readwrite) "alloc-family"="malloc" }
+; CHECK-DAG: attributes [[INACCESSIBLEMEMORERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN_ALLOCKIND_ALLOCZEROED_ALLOCSIZE01_FAMILY_MALLOC]] = { mustprogress nofree nounwind willreturn allockind("alloc,zeroed") allocsize(0,1) memory(inaccessiblemem: readwrite, errnomem: readwrite) "alloc-family"="malloc" }
; CHECK-DAG: attributes [[NOFREE_NOUNWIND_READONLY_WILLRETURN]] = { mustprogress nofree nounwind willreturn memory(read) }
; CHECK-DAG: attributes [[ARGMEMONLY_NOFREE_NOUNWIND_WILLRETURN]] = { mustprogress nofree nounwind willreturn memory(argmem: readwrite) }
; CHECK-DAG: attributes [[NOFREE_NOUNWIND_READONLY]] = { nofree nounwind memory(read) }
; CHECK-DAG: attributes [[INACCESSIBLEMEMORARGMEMONLY_NOUNWIND_WILLRETURN_ALLOCKIND_FREE_FAMILY_MALLOC]] = { mustprogress nounwind willreturn allockind("free") memory(argmem: readwrite, inaccessiblemem: readwrite) "alloc-family"="malloc" }
; CHECK-DAG: attributes [[NOFREE_WILLRETURN]] = { mustprogress nofree willreturn }
-; CHECK-DAG: attributes [[INACCESSIBLEMEMONLY_NOFREE_NOUNWIND_WILLRETURN_ALLOCKIND_ALLOCUNINIT_ALLOCSIZE0_FAMILY_MALLOC]] = { mustprogress nofree nounwind willreturn allockind("alloc,uninitialized") allocsize(0) memory(inaccessiblemem: readwrite) "alloc-family"="malloc" }
+; CHECK-DAG: attributes [[INACCESSIBLEMEMORERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN_ALLOCKIND_ALLOCUNINIT_ALLOCSIZE0_FAMILY_MALLOC]] = { mustprogress nofree nounwind willreturn allockind("alloc,uninitialized") allocsize(0) memory(inaccessiblemem: readwrite, errnomem: readwrite) "alloc-family"="malloc" }
; CHECK-DAG: attributes [[ARGMEMONLY_NOFREE_NOUNWIND_READONLY_WILLRETURN]] = { mustprogress nofree nounwind willreturn memory(argmem: read) }
; CHECK-DAG: attributes [[NOFREE]] = { nofree }
+; CHECK-DAG: attributes [[ERRNOMEMONLY_NOFREE]] = { nofree memory(errnomem: readwrite) }
; CHECK-DAG: attributes [[ARGMEMONLY_NOFREE_NOUNWIND]] = { nofree nounwind memory(argmem: readwrite) }
-; CHECK-DAG: attributes [[INACCESSIBLEMEMORARGMEMONLY_NOUNWIND_WILLRETURN_ALLOCKIND_REALLOC_ALLOCSIZE1_FAMILY_MALLOC]] = { mustprogress nounwind willreturn allockind("realloc") allocsize(1) memory(argmem: readwrite, inaccessiblemem: readwrite) "alloc-family"="malloc" }
-; CHECK-DAG: attributes [[INACCESSIBLEMEMORARGMEMONLY_NOUNWIND_WILLRETURN_ALLOCKIND_REALLOC_ALLOCSIZE12_FAMILY_MALLOC]] = { mustprogress nounwind willreturn allockind("realloc") allocsize(1,2) memory(argmem: readwrite, inaccessiblemem: readwrite) "alloc-family"="malloc" }
-; CHECK-DAG: attributes [[INACCESSIBLEMEMORARGONLY_NOFREE_NOUNWIND_WILLRETURN_FAMILY_MALLOC]] = { mustprogress nofree nounwind willreturn memory(argmem: readwrite, inaccessiblemem: readwrite) "alloc-family"="malloc" }
+; CHECK-DAG: attributes [[INACCESSIBLEMEMORARGMEMORERRNOMEMONLY_NOUNWIND_WILLRETURN_ALLOCKIND_REALLOC_ALLOCSIZE1_FAMILY_MALLOC]] = { mustprogress nounwind willreturn allockind("realloc") allocsize(1) memory(argmem: readwrite, inaccessiblemem: readwrite, errnomem: readwrite) "alloc-family"="malloc" }
+; CHECK-DAG: attributes [[INACCESSIBLEMEMORARGMEMORERRNOMEMONLY_NOUNWIND_WILLRETURN_ALLOCKIND_REALLOC_ALLOCSIZE12_FAMILY_MALLOC]] = { mustprogress nounwind willreturn allockind("realloc") allocsize(1,2) memory(argmem: readwrite, inaccessiblemem: readwrite, errnomem: readwrite) "alloc-family"="malloc" }
+; CHECK-DAG: attributes [[INACCESSIBLEMEMORARGMEMORERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN_FAMILY_MALLOC]] = { mustprogress nofree nounwind willreturn memory(argmem: readwrite, inaccessiblemem: readwrite, errnomem: readwrite) "alloc-family"="malloc" }
; CHECK-DAG: attributes [[NOFREE_COLD]] = { cold nofree }
; CHECK-DAG: attributes [[NOFREE_COLD_NORETURN]] = { cold nofree noreturn }
; CHECK-DAG: attributes [[COLD_NORETURN]] = { cold noreturn }
; CHECK-NVPTX-DAG: attributes [[NOFREE_NOUNWIND_READNONE]] = { nofree nosync nounwind memory(none) }
-; CHECK-AIX-DAG: attributes [[INACCESSIBLEMEMONLY_NOFREE_NOUNWIND_WILLRETURN_ALLOCSIZE0_FAMILY_VEC_MALLOC]] = { mustprogress nofree nounwind willreturn allockind("alloc,uninitialized") allocsize(0) memory(inaccessiblemem: readwrite) "alloc-family"="vec_malloc" }
+; CHECK-AIX-DAG: attributes [[INACCESSIBLEMEMORERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN_ALLOCSIZE0_FAMILY_VEC_MALLOC]] = { mustprogress nofree nounwind willreturn allockind("alloc,uninitialized") allocsize(0) memory(inaccessiblemem: readwrite, errnomem: readwrite) "alloc-family"="vec_malloc" }
; CHECK-AIX-DAG: attributes [[INACCESSIBLEMEMORARGMEMONLY_NOUNWIND_WILLRETURN_FAMILY_VEC_MALLOC]] = { mustprogress nounwind willreturn allockind("free") memory(argmem: readwrite, inaccessiblemem: readwrite) "alloc-family"="vec_malloc" }
-; CHECK-AIX-DAG: attributes [[INACCESSIBLEMEMORARGMEMONLY_NOUNWIND_WILLRETURN_ALLOCSIZE_FAMILY_VEC_MALLOC]] = { mustprogress nounwind willreturn allockind("realloc") allocsize(1) memory(argmem: readwrite, inaccessiblemem: readwrite) "alloc-family"="vec_malloc" }
-; CHECK-AIX-DAG: attributes [[INACCESSIBLEMEMONLY_NOFREE_NOUNWIND_WILLRETURN_ALLOCSIZE01_FAMILY_VEC_MALLOC]] = { mustprogress nofree nounwind willreturn allockind("alloc,zeroed") allocsize(0,1) memory(inaccessiblemem: readwrite) "alloc-family"="vec_malloc" }
+; CHECK-AIX-DAG: attributes [[INACCESSIBLEMEMORARGMEMORERRNOMEMONLY_NOUNWIND_WILLRETURN_ALLOCSIZE_FAMILY_VEC_MALLOC]] = { mustprogress nounwind willreturn allockind("realloc") allocsize(1) memory(argmem: readwrite, inaccessiblemem: readwrite, errnomem: readwrite) "alloc-family"="vec_malloc" }
+; CHECK-AIX-DAG: attributes [[INACCESSIBLEMEMORERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN_ALLOCSIZE01_FAMILY_VEC_MALLOC]] = { mustprogress nofree nounwind willreturn allockind("alloc,zeroed") allocsize(0,1) memory(inaccessiblemem: readwrite, errnomem: readwrite) "alloc-family"="vec_malloc" }
>From 7edf8568e35421f4f13f21b422d9926b467f90a2 Mon Sep 17 00:00:00 2001
From: Antonio Frighetto <me at antoniofrighetto.com>
Date: Tue, 28 Jan 2025 15:57:54 +0100
Subject: [PATCH 3/3] [clang][CodeGen][AA] Add `llvm.errno.tbaa` gathering
int-compatible TBAA
---
clang/lib/CodeGen/CGExpr.cpp | 34 +++++++++++++++-----
clang/lib/CodeGen/CGValue.h | 7 ++++
clang/lib/CodeGen/CodeGenFunction.h | 26 ++++++++-------
clang/lib/CodeGen/CodeGenModule.cpp | 7 ++++
clang/lib/CodeGen/CodeGenModule.h | 3 ++
llvm/include/llvm/IR/FixedMetadataKinds.def | 1 +
llvm/include/llvm/IR/Metadata.h | 20 ++++++++----
llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp | 2 ++
llvm/lib/IR/Metadata.cpp | 2 ++
9 files changed, 76 insertions(+), 26 deletions(-)
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 9a9a8c7f6eae09..a9f877f5ef52e8 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -1875,7 +1875,8 @@ llvm::Value *CodeGenFunction::EmitLoadOfScalar(LValue lvalue,
SourceLocation Loc) {
return EmitLoadOfScalar(lvalue.getAddress(), lvalue.isVolatile(),
lvalue.getType(), Loc, lvalue.getBaseInfo(),
- lvalue.getTBAAInfo(), lvalue.isNontemporal());
+ lvalue.getTBAAInfo(), lvalue.isNontemporal(),
+ lvalue.isErrno());
}
static bool hasBooleanRepresentation(QualType Ty) {
@@ -1910,6 +1911,16 @@ static bool getRangeForType(CodeGenFunction &CGF, QualType Ty,
return true;
}
+static bool maybeDereferencingErrno(const Expr *E) {
+ if (auto *CE = dyn_cast<CallExpr>(E))
+ if (auto *ICE = dyn_cast<ImplicitCastExpr>(CE->getCallee());
+ ICE && ICE->getCastKind() == CK_FunctionToPointerDecay)
+ if (auto *DRE = dyn_cast<DeclRefExpr>(ICE->getSubExpr()))
+ if (auto *FD = dyn_cast<FunctionDecl>(DRE->getDecl()))
+ return FD->getName() == "__errno_location";
+ return false;
+}
+
llvm::MDNode *CodeGenFunction::getRangeForLoadFromType(QualType Ty) {
llvm::APInt Min, End;
if (!getRangeForType(*this, Ty, Min, End, CGM.getCodeGenOpts().StrictEnums,
@@ -1972,11 +1983,11 @@ bool CodeGenFunction::EmitScalarRangeCheck(llvm::Value *Value, QualType Ty,
}
llvm::Value *CodeGenFunction::EmitLoadOfScalar(Address Addr, bool Volatile,
- QualType Ty,
- SourceLocation Loc,
+ QualType Ty, SourceLocation Loc,
LValueBaseInfo BaseInfo,
TBAAAccessInfo TBAAInfo,
- bool isNontemporal) {
+ bool isNontemporal,
+ bool IsErrno) {
if (auto *GV = dyn_cast<llvm::GlobalValue>(Addr.getBasePointer()))
if (GV->isThreadLocal())
Addr = Addr.withPointer(Builder.CreateThreadLocalAddress(GV),
@@ -2038,6 +2049,8 @@ llvm::Value *CodeGenFunction::EmitLoadOfScalar(Address Addr, bool Volatile,
}
CGM.DecorateInstructionWithTBAA(Load, TBAAInfo);
+ if (IsErrno)
+ CGM.DecorateInstructionWithErrnoTBAA(Load);
if (EmitScalarRangeCheck(Load, Ty, Loc)) {
// In order to prevent the optimizer from throwing away the check, don't
@@ -2132,14 +2145,14 @@ static void EmitStoreOfMatrixScalar(llvm::Value *value, LValue lvalue,
value->getType()->isVectorTy());
CGF.EmitStoreOfScalar(value, Addr, lvalue.isVolatile(), lvalue.getType(),
lvalue.getBaseInfo(), lvalue.getTBAAInfo(), isInit,
- lvalue.isNontemporal());
+ lvalue.isNontemporal(), lvalue.isErrno());
}
void CodeGenFunction::EmitStoreOfScalar(llvm::Value *Value, Address Addr,
bool Volatile, QualType Ty,
LValueBaseInfo BaseInfo,
- TBAAAccessInfo TBAAInfo,
- bool isInit, bool isNontemporal) {
+ TBAAAccessInfo TBAAInfo, bool isInit,
+ bool isNontemporal, bool IsErrno) {
if (auto *GV = dyn_cast<llvm::GlobalValue>(Addr.getBasePointer()))
if (GV->isThreadLocal())
Addr = Addr.withPointer(Builder.CreateThreadLocalAddress(GV),
@@ -2182,6 +2195,8 @@ void CodeGenFunction::EmitStoreOfScalar(llvm::Value *Value, Address Addr,
}
CGM.DecorateInstructionWithTBAA(Store, TBAAInfo);
+ if (IsErrno)
+ CGM.DecorateInstructionWithErrnoTBAA(Store);
}
void CodeGenFunction::EmitStoreOfScalar(llvm::Value *value, LValue lvalue,
@@ -2193,7 +2208,8 @@ void CodeGenFunction::EmitStoreOfScalar(llvm::Value *value, LValue lvalue,
EmitStoreOfScalar(value, lvalue.getAddress(), lvalue.isVolatile(),
lvalue.getType(), lvalue.getBaseInfo(),
- lvalue.getTBAAInfo(), isInit, lvalue.isNontemporal());
+ lvalue.getTBAAInfo(), isInit, lvalue.isNontemporal(),
+ lvalue.isErrno());
}
// Emit a load of a LValue of matrix type. This may require casting the pointer
@@ -3245,6 +3261,8 @@ LValue CodeGenFunction::EmitUnaryOpLValue(const UnaryOperator *E) {
&TBAAInfo);
LValue LV = MakeAddrLValue(Addr, T, BaseInfo, TBAAInfo);
LV.getQuals().setAddressSpace(ExprTy.getAddressSpace());
+ if (bool IsErrno = maybeDereferencingErrno(E->getSubExpr()))
+ LV.setErrno(IsErrno);
// We should not generate __weak write barrier on indirect reference
// of a pointer to object; as in void foo (__weak id *param); *param = 0;
diff --git a/clang/lib/CodeGen/CGValue.h b/clang/lib/CodeGen/CGValue.h
index c4ec8d207d2e3c..8e4e231fb124aa 100644
--- a/clang/lib/CodeGen/CGValue.h
+++ b/clang/lib/CodeGen/CGValue.h
@@ -234,6 +234,9 @@ class LValue {
// this lvalue.
bool Nontemporal : 1;
+ // Lvalue is a reference to errno.
+ bool Errno : 1;
+
LValueBaseInfo BaseInfo;
TBAAAccessInfo TBAAInfo;
@@ -261,6 +264,7 @@ class LValue {
this->ImpreciseLifetime = false;
this->Nontemporal = false;
this->ThreadLocalRef = false;
+ this->Errno = false;
this->BaseIvarExp = nullptr;
}
@@ -318,6 +322,9 @@ class LValue {
bool isNontemporal() const { return Nontemporal; }
void setNontemporal(bool Value) { Nontemporal = Value; }
+ bool isErrno() const { return Errno; }
+ void setErrno(bool Value) { Errno = Value; }
+
bool isObjCWeak() const {
return Quals.getObjCGCAttr() == Qualifiers::Weak;
}
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index ba8ed040477cc9..03caa6c806ac45 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -4270,15 +4270,17 @@ class CodeGenFunction : public CodeGenTypeCache {
llvm::Value *EmitLoadOfScalar(Address Addr, bool Volatile, QualType Ty,
SourceLocation Loc,
AlignmentSource Source = AlignmentSource::Type,
- bool isNontemporal = false) {
+ bool isNontemporal = false,
+ bool IsErrno = false) {
return EmitLoadOfScalar(Addr, Volatile, Ty, Loc, LValueBaseInfo(Source),
- CGM.getTBAAAccessInfo(Ty), isNontemporal);
+ CGM.getTBAAAccessInfo(Ty), isNontemporal, IsErrno);
}
llvm::Value *EmitLoadOfScalar(Address Addr, bool Volatile, QualType Ty,
SourceLocation Loc, LValueBaseInfo BaseInfo,
TBAAAccessInfo TBAAInfo,
- bool isNontemporal = false);
+ bool isNontemporal = false,
+ bool IsErrno = false);
/// EmitLoadOfScalar - Load a scalar value from an address, taking
/// care to appropriately convert from the memory representation to
@@ -4289,18 +4291,20 @@ class CodeGenFunction : public CodeGenTypeCache {
/// EmitStoreOfScalar - Store a scalar value to an address, taking
/// care to appropriately convert from the memory representation to
/// the LLVM value representation.
- void EmitStoreOfScalar(llvm::Value *Value, Address Addr,
- bool Volatile, QualType Ty,
+ void EmitStoreOfScalar(llvm::Value *Value, Address Addr, bool Volatile,
+ QualType Ty,
AlignmentSource Source = AlignmentSource::Type,
- bool isInit = false, bool isNontemporal = false) {
+ bool isInit = false, bool isNontemporal = false,
+ bool isErrno = false) {
EmitStoreOfScalar(Value, Addr, Volatile, Ty, LValueBaseInfo(Source),
- CGM.getTBAAAccessInfo(Ty), isInit, isNontemporal);
+ CGM.getTBAAAccessInfo(Ty), isInit, isNontemporal,
+ isErrno);
}
- void EmitStoreOfScalar(llvm::Value *Value, Address Addr,
- bool Volatile, QualType Ty,
- LValueBaseInfo BaseInfo, TBAAAccessInfo TBAAInfo,
- bool isInit = false, bool isNontemporal = false);
+ void EmitStoreOfScalar(llvm::Value *Value, Address Addr, bool Volatile,
+ QualType Ty, LValueBaseInfo BaseInfo,
+ TBAAAccessInfo TBAAInfo, bool isInit = false,
+ bool isNontemporal = false, bool isErrno = false);
/// EmitStoreOfScalar - Store a scalar value to an address, taking
/// care to appropriately convert from the memory representation to
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index eb8d3ceeeba4c0..fd8f1863483fbd 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -1571,6 +1571,13 @@ void CodeGenModule::DecorateInstructionWithTBAA(llvm::Instruction *Inst,
Inst->setMetadata(llvm::LLVMContext::MD_tbaa, Tag);
}
+void CodeGenModule::DecorateInstructionWithErrnoTBAA(llvm::Instruction *Inst) {
+ if (TBAA) {
+ llvm::MDNode *IntegerNode = TBAA->getTypeInfo(Context.IntTy);
+ Inst->setMetadata(llvm::LLVMContext::MD_errno_tbaa, IntegerNode);
+ }
+}
+
void CodeGenModule::DecorateInstructionWithInvariantGroup(
llvm::Instruction *I, const CXXRecordDecl *RD) {
I->setMetadata(llvm::LLVMContext::MD_invariant_group,
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index d5ef1a710eb403..a460aa77f22399 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -903,6 +903,9 @@ class CodeGenModule : public CodeGenTypeCache {
void DecorateInstructionWithInvariantGroup(llvm::Instruction *I,
const CXXRecordDecl *RD);
+ /// Adorn the instruction with a !llvm.errno.tbaa tag.
+ void DecorateInstructionWithErrnoTBAA(llvm::Instruction *Inst);
+
/// Emit the given number of characters as a value of type size_t.
llvm::ConstantInt *getSize(CharUnits numChars);
diff --git a/llvm/include/llvm/IR/FixedMetadataKinds.def b/llvm/include/llvm/IR/FixedMetadataKinds.def
index df572e8791e13b..e4b6f3bc9abe11 100644
--- a/llvm/include/llvm/IR/FixedMetadataKinds.def
+++ b/llvm/include/llvm/IR/FixedMetadataKinds.def
@@ -53,3 +53,4 @@ LLVM_FIXED_MD_KIND(MD_DIAssignID, "DIAssignID", 38)
LLVM_FIXED_MD_KIND(MD_coro_outside_frame, "coro.outside.frame", 39)
LLVM_FIXED_MD_KIND(MD_mmra, "mmra", 40)
LLVM_FIXED_MD_KIND(MD_noalias_addrspace, "noalias.addrspace", 41)
+LLVM_FIXED_MD_KIND(MD_errno_tbaa, "llvm.errno.tbaa", 42)
diff --git a/llvm/include/llvm/IR/Metadata.h b/llvm/include/llvm/IR/Metadata.h
index ec7d030a20de8c..39196b28876e64 100644
--- a/llvm/include/llvm/IR/Metadata.h
+++ b/llvm/include/llvm/IR/Metadata.h
@@ -763,18 +763,18 @@ class MDString : public Metadata {
/// memory access used by the alias-analysis infrastructure.
struct AAMDNodes {
explicit AAMDNodes() = default;
- explicit AAMDNodes(MDNode *T, MDNode *TS, MDNode *S, MDNode *N)
- : TBAA(T), TBAAStruct(TS), Scope(S), NoAlias(N) {}
+ explicit AAMDNodes(MDNode *T, MDNode *TS, MDNode *S, MDNode *N, MDNode *ET)
+ : TBAA(T), TBAAStruct(TS), Scope(S), NoAlias(N), ErrnoTBAA(ET) {}
bool operator==(const AAMDNodes &A) const {
return TBAA == A.TBAA && TBAAStruct == A.TBAAStruct && Scope == A.Scope &&
- NoAlias == A.NoAlias;
+ NoAlias == A.NoAlias && ErrnoTBAA == A.ErrnoTBAA;
}
bool operator!=(const AAMDNodes &A) const { return !(*this == A); }
explicit operator bool() const {
- return TBAA || TBAAStruct || Scope || NoAlias;
+ return TBAA || TBAAStruct || Scope || NoAlias || ErrnoTBAA;
}
/// The tag for type-based alias analysis.
@@ -788,6 +788,8 @@ struct AAMDNodes {
/// The tag specifying the noalias scope.
MDNode *NoAlias = nullptr;
+
+ MDNode *ErrnoTBAA = nullptr;
// Shift tbaa Metadata node to start off bytes later
static MDNode *shiftTBAA(MDNode *M, size_t off);
@@ -810,6 +812,7 @@ struct AAMDNodes {
Result.TBAAStruct = Other.TBAAStruct == TBAAStruct ? TBAAStruct : nullptr;
Result.Scope = Other.Scope == Scope ? Scope : nullptr;
Result.NoAlias = Other.NoAlias == NoAlias ? NoAlias : nullptr;
+ Result.ErrnoTBAA = Other.ErrnoTBAA == ErrnoTBAA ? ErrnoTBAA : nullptr;
return Result;
}
@@ -822,6 +825,7 @@ struct AAMDNodes {
TBAAStruct ? shiftTBAAStruct(TBAAStruct, Offset) : nullptr;
Result.Scope = Scope;
Result.NoAlias = NoAlias;
+ Result.ErrnoTBAA = nullptr;
return Result;
}
@@ -837,6 +841,7 @@ struct AAMDNodes {
Result.TBAAStruct = TBAAStruct;
Result.Scope = Scope;
Result.NoAlias = NoAlias;
+ Result.ErrnoTBAA = nullptr;
return Result;
}
@@ -865,19 +870,20 @@ template<>
struct DenseMapInfo<AAMDNodes> {
static inline AAMDNodes getEmptyKey() {
return AAMDNodes(DenseMapInfo<MDNode *>::getEmptyKey(),
- nullptr, nullptr, nullptr);
+ nullptr, nullptr, nullptr, nullptr);
}
static inline AAMDNodes getTombstoneKey() {
return AAMDNodes(DenseMapInfo<MDNode *>::getTombstoneKey(),
- nullptr, nullptr, nullptr);
+ nullptr, nullptr, nullptr, nullptr);
}
static unsigned getHashValue(const AAMDNodes &Val) {
return DenseMapInfo<MDNode *>::getHashValue(Val.TBAA) ^
DenseMapInfo<MDNode *>::getHashValue(Val.TBAAStruct) ^
DenseMapInfo<MDNode *>::getHashValue(Val.Scope) ^
- DenseMapInfo<MDNode *>::getHashValue(Val.NoAlias);
+ DenseMapInfo<MDNode *>::getHashValue(Val.NoAlias) ^
+ DenseMapInfo<MDNode *>::getHashValue(Val.ErrnoTBAA);
}
static bool isEqual(const AAMDNodes &LHS, const AAMDNodes &RHS) {
diff --git a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
index 3f44f746eb173a..a3b7dc2a79f7cd 100644
--- a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
@@ -525,6 +525,7 @@ AAMDNodes AAMDNodes::merge(const AAMDNodes &Other) const {
Result.TBAAStruct = nullptr;
Result.Scope = MDNode::getMostGenericAliasScope(Scope, Other.Scope);
Result.NoAlias = MDNode::intersect(NoAlias, Other.NoAlias);
+ Result.ErrnoTBAA = nullptr;
return Result;
}
@@ -533,6 +534,7 @@ AAMDNodes AAMDNodes::concat(const AAMDNodes &Other) const {
Result.TBAA = Result.TBAAStruct = nullptr;
Result.Scope = MDNode::getMostGenericAliasScope(Scope, Other.Scope);
Result.NoAlias = MDNode::intersect(NoAlias, Other.NoAlias);
+ Result.ErrnoTBAA = nullptr;
return Result;
}
diff --git a/llvm/lib/IR/Metadata.cpp b/llvm/lib/IR/Metadata.cpp
index e3a46840eea0e7..6ff7704ecef2b3 100644
--- a/llvm/lib/IR/Metadata.cpp
+++ b/llvm/lib/IR/Metadata.cpp
@@ -1757,6 +1757,7 @@ AAMDNodes Instruction::getAAMetadata() const {
Result.TBAAStruct = Info.lookup(LLVMContext::MD_tbaa_struct);
Result.Scope = Info.lookup(LLVMContext::MD_alias_scope);
Result.NoAlias = Info.lookup(LLVMContext::MD_noalias);
+ Result.ErrnoTBAA = Info.lookup(LLVMContext::MD_errno_tbaa);
}
return Result;
}
@@ -1766,6 +1767,7 @@ void Instruction::setAAMetadata(const AAMDNodes &N) {
setMetadata(LLVMContext::MD_tbaa_struct, N.TBAAStruct);
setMetadata(LLVMContext::MD_alias_scope, N.Scope);
setMetadata(LLVMContext::MD_noalias, N.NoAlias);
+ setMetadata(LLVMContext::MD_errno_tbaa, N.ErrnoTBAA);
}
void Instruction::setNoSanitizeMetadata() {
More information about the llvm-commits
mailing list