Skip to content

Commit ff585fe

Browse files
[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.
1 parent eef0205 commit ff585fe

File tree

17 files changed

+103
-23
lines changed

17 files changed

+103
-23
lines changed

llvm/docs/LangRef.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2046,8 +2046,8 @@ For example:
20462046
This attribute specifies the possible memory effects of the call-site or
20472047
function. It allows specifying the possible access kinds (``none``,
20482048
``read``, ``write``, or ``readwrite``) for the possible memory location
2049-
kinds (``argmem``, ``inaccessiblemem``, as well as a default). It is best
2050-
understood by example:
2049+
kinds (``argmem``, ``inaccessiblemem``, ``errnomem``, as well as a default).
2050+
It is best understood by example:
20512051

20522052
- ``memory(none)``: Does not access any memory.
20532053
- ``memory(read)``: May read (but not write) any memory.
@@ -2056,6 +2056,8 @@ For example:
20562056
- ``memory(argmem: read)``: May only read argument memory.
20572057
- ``memory(argmem: read, inaccessiblemem: write)``: May only read argument
20582058
memory and only write inaccessible memory.
2059+
- ``memory(argmem: read, errnomem: write)``: May only read argument memory
2060+
and only write errno.
20592061
- ``memory(read, argmem: readwrite)``: May read any memory (default mode)
20602062
and additionally write argument memory.
20612063
- ``memory(readwrite, argmem: none)``: May access any memory apart from
@@ -2085,6 +2087,7 @@ For example:
20852087
allocator function may return newly accessible memory while only
20862088
accessing inaccessible memory itself). Inaccessible memory is often used
20872089
to model control dependencies of intrinsics.
2090+
- ``errnomem``: This refers to accesses to the ``errno`` variable.
20882091
- The default access kind (specified without a location prefix) applies to
20892092
all locations that haven't been specified explicitly, including those that
20902093
don't currently have a dedicated location kind (e.g. accesses to globals

llvm/include/llvm/AsmParser/LLToken.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ enum Kind {
201201
kw_readwrite,
202202
kw_argmem,
203203
kw_inaccessiblemem,
204+
kw_errnomem,
204205

205206
// Legacy attributes:
206207
kw_argmemonly,

llvm/include/llvm/Support/ModRef.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,10 @@ enum class IRMemLocation {
6161
ArgMem = 0,
6262
/// Memory that is inaccessible via LLVM IR.
6363
InaccessibleMem = 1,
64+
/// Errno memory.
65+
ErrnoMem = 2,
6466
/// Any other memory.
65-
Other = 2,
67+
Other = 3,
6668

6769
/// Helpers to iterate all locations in the MemoryEffectsBase class.
6870
First = ArgMem,
@@ -139,6 +141,16 @@ template <typename LocationEnum> class MemoryEffectsBase {
139141
return MemoryEffectsBase(Location::InaccessibleMem, MR);
140142
}
141143

144+
/// Create MemoryEffectsBase that can only access errno memory.
145+
static MemoryEffectsBase errnoMemOnly(ModRefInfo MR = ModRefInfo::ModRef) {
146+
return MemoryEffectsBase(Location::ErrnoMem, MR);
147+
}
148+
149+
/// Create MemoryEffectsBase that can only access other memory.
150+
static MemoryEffectsBase otherMemOnly(ModRefInfo MR = ModRefInfo::ModRef) {
151+
return MemoryEffectsBase(Location::Other, MR);
152+
}
153+
142154
/// Create MemoryEffectsBase that can only access inaccessible or argument
143155
/// memory.
144156
static MemoryEffectsBase
@@ -212,6 +224,11 @@ template <typename LocationEnum> class MemoryEffectsBase {
212224
return getWithoutLoc(Location::InaccessibleMem).doesNotAccessMemory();
213225
}
214226

227+
/// Whether this function only (at most) accesses errno memory.
228+
bool onlyAccessesErrnoMem() const {
229+
return getWithoutLoc(Location::ErrnoMem).doesNotAccessMemory();
230+
}
231+
215232
/// Whether this function only (at most) accesses argument and inaccessible
216233
/// memory.
217234
bool onlyAccessesInaccessibleOrArgMem() const {

llvm/lib/AsmParser/LLLexer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,7 @@ lltok::Kind LLLexer::LexIdentifier() {
701701
KEYWORD(readwrite);
702702
KEYWORD(argmem);
703703
KEYWORD(inaccessiblemem);
704+
KEYWORD(errnomem);
704705
KEYWORD(argmemonly);
705706
KEYWORD(inaccessiblememonly);
706707
KEYWORD(inaccessiblemem_or_argmemonly);

llvm/lib/AsmParser/LLParser.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2497,6 +2497,8 @@ static std::optional<MemoryEffects::Location> keywordToLoc(lltok::Kind Tok) {
24972497
return IRMemLocation::ArgMem;
24982498
case lltok::kw_inaccessiblemem:
24992499
return IRMemLocation::InaccessibleMem;
2500+
case lltok::kw_errnomem:
2501+
return IRMemLocation::ErrnoMem;
25002502
default:
25012503
return std::nullopt;
25022504
}
@@ -2545,7 +2547,7 @@ std::optional<MemoryEffects> LLParser::parseMemoryAttr() {
25452547
std::optional<ModRefInfo> MR = keywordToModRef(Lex.getKind());
25462548
if (!MR) {
25472549
if (!Loc)
2548-
tokError("expected memory location (argmem, inaccessiblemem) "
2550+
tokError("expected memory location (argmem, inaccessiblemem, errnomem) "
25492551
"or access kind (none, read, write, readwrite)");
25502552
else
25512553
tokError("expected access kind (none, read, write, readwrite)");

llvm/lib/Bitcode/Reader/BitcodeReader.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1937,8 +1937,7 @@ static void addRawAttributeValue(AttrBuilder &B, uint64_t Val) {
19371937
}
19381938

19391939
/// This fills an AttrBuilder object with the LLVM attributes that have
1940-
/// been decoded from the given integer. This function must stay in sync with
1941-
/// 'encodeLLVMAttributesForBitcode'.
1940+
/// been decoded from the given integer.
19421941
static void decodeLLVMAttributesForBitcode(AttrBuilder &B,
19431942
uint64_t EncodedAttrs,
19441943
uint64_t AttrIdx) {
@@ -2398,9 +2397,28 @@ Error BitcodeReader::parseAttributeGroupBlock() {
23982397
B.addUWTableAttr(UWTableKind(Record[++i]));
23992398
else if (Kind == Attribute::AllocKind)
24002399
B.addAllocKindAttr(static_cast<AllocFnKind>(Record[++i]));
2401-
else if (Kind == Attribute::Memory)
2402-
B.addMemoryAttr(MemoryEffects::createFromIntValue(Record[++i]));
2403-
else if (Kind == Attribute::Captures)
2400+
else if (Kind == Attribute::Memory) {
2401+
uint64_t EncodedME = Record[++i];
2402+
const uint8_t Version = (EncodedME >> 56);
2403+
if (Version == 0) {
2404+
// Errno memory location was previously encompassed into default
2405+
// memory. Ensure this is taken into account while reconstructing
2406+
// the memory attribute prior to its introduction.
2407+
ModRefInfo ArgMem = ModRefInfo((EncodedME >> 0) & 3);
2408+
ModRefInfo InaccessibleMem = ModRefInfo((EncodedME >> 2) & 3);
2409+
ModRefInfo OtherMem = ModRefInfo((EncodedME >> 4) & 3);
2410+
auto ME = MemoryEffects::inaccessibleMemOnly(InaccessibleMem) |
2411+
MemoryEffects::argMemOnly(ArgMem) |
2412+
MemoryEffects::errnoMemOnly(OtherMem) |
2413+
MemoryEffects::otherMemOnly(OtherMem);
2414+
B.addMemoryAttr(ME);
2415+
} else {
2416+
// Construct the memory attribute directly from the encoded base
2417+
// on newer versions.
2418+
B.addMemoryAttr(MemoryEffects::createFromIntValue(
2419+
EncodedME & 0x00FFFFFFFFFFFFFFULL));
2420+
}
2421+
} else if (Kind == Attribute::Captures)
24042422
B.addCapturesAttr(CaptureInfo::createFromIntValue(Record[++i]));
24052423
else if (Kind == Attribute::NoFPClass)
24062424
B.addNoFPClassAttr(

llvm/lib/Bitcode/Writer/BitcodeWriter.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -977,8 +977,15 @@ void ModuleBitcodeWriter::writeAttributeGroupTable() {
977977
Record.push_back(getAttrKindEncoding(Attr.getKindAsEnum()));
978978
} else if (Attr.isIntAttribute()) {
979979
Record.push_back(1);
980-
Record.push_back(getAttrKindEncoding(Attr.getKindAsEnum()));
981-
Record.push_back(Attr.getValueAsInt());
980+
Attribute::AttrKind Kind = Attr.getKindAsEnum();
981+
Record.push_back(getAttrKindEncoding(Kind));
982+
if (Kind == Attribute::Memory) {
983+
// Version field for upgrading old memory effects.
984+
const uint64_t Version = 1;
985+
Record.push_back((Version << 56) | Attr.getValueAsInt());
986+
} else {
987+
Record.push_back(Attr.getValueAsInt());
988+
}
982989
} else if (Attr.isStringAttribute()) {
983990
StringRef Kind = Attr.getKindAsString();
984991
StringRef Val = Attr.getValueAsString();

llvm/lib/IR/Attributes.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,9 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
647647
case IRMemLocation::InaccessibleMem:
648648
OS << "inaccessiblemem: ";
649649
break;
650+
case IRMemLocation::ErrnoMem:
651+
OS << "errnomem: ";
652+
break;
650653
case IRMemLocation::Other:
651654
llvm_unreachable("This is represented as the default access kind");
652655
}

llvm/lib/Support/ModRef.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, MemoryEffects ME) {
4343
case IRMemLocation::InaccessibleMem:
4444
OS << "InaccessibleMem: ";
4545
break;
46+
case IRMemLocation::ErrnoMem:
47+
OS << "ErrnoMem: ";
48+
break;
4649
case IRMemLocation::Other:
4750
OS << "Other: ";
4851
break;

llvm/lib/Transforms/IPO/FunctionAttrs.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ static void addLocAccess(MemoryEffects &ME, const MemoryLocation &Loc,
141141
// If it's not an identified object, it might be an argument.
142142
if (!isIdentifiedObject(UO))
143143
ME |= MemoryEffects::argMemOnly(MR);
144+
ME |= MemoryEffects(IRMemLocation::ErrnoMem, MR);
144145
ME |= MemoryEffects(IRMemLocation::Other, MR);
145146
}
146147

@@ -219,6 +220,9 @@ checkFunctionMemoryAccess(Function &F, bool ThisBody, AAResults &AAR,
219220
if (isa<PseudoProbeInst>(I))
220221
continue;
221222

223+
// Merge callee's memory effects into caller's ones, including
224+
// inaccessible and errno memory, but excluding argument memory, which is
225+
// handled separately.
222226
ME |= CallME.getWithoutLoc(IRMemLocation::ArgMem);
223227

224228
// If the call accesses captured memory (currently part of "other") and

llvm/lib/Transforms/IPO/SCCP.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,10 @@ static bool runIPSCCP(
191191
if (ME == MemoryEffects::unknown())
192192
return AL;
193193

194-
ME |= MemoryEffects(IRMemLocation::Other,
195-
ME.getModRef(IRMemLocation::ArgMem));
194+
ModRefInfo ArgMemMR = ME.getModRef(IRMemLocation::ArgMem);
195+
ME |= MemoryEffects(IRMemLocation::ErrnoMem, ArgMemMR);
196+
ME |= MemoryEffects(IRMemLocation::Other, ArgMemMR);
197+
196198
return AL.addFnAttribute(
197199
F.getContext(),
198200
Attribute::getWithMemoryEffects(F.getContext(), ME));

llvm/test/Assembler/memory-attribute-errors.ll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@
1212
; MISSING-ARGS: error: expected '('
1313
declare void @fn() memory
1414
;--- empty.ll
15-
; EMPTY: error: expected memory location (argmem, inaccessiblemem) or access kind (none, read, write, readwrite)
15+
; EMPTY: error: expected memory location (argmem, inaccessiblemem, errnomem) or access kind (none, read, write, readwrite)
1616
declare void @fn() memory()
1717
;--- unterminated.ll
1818
; UNTERMINATED: error: unterminated memory attribute
1919
declare void @fn() memory(read
2020
;--- invalid-kind.ll
21-
; INVALID-KIND: error: expected memory location (argmem, inaccessiblemem) or access kind (none, read, write, readwrite)
21+
; INVALID-KIND: error: expected memory location (argmem, inaccessiblemem, errnomem) or access kind (none, read, write, readwrite)
2222
declare void @fn() memory(foo)
2323
;--- other.ll
24-
; OTHER: error: expected memory location (argmem, inaccessiblemem) or access kind (none, read, write, readwrite)
24+
; OTHER: error: expected memory location (argmem, inaccessiblemem, errnomem) or access kind (none, read, write, readwrite)
2525
declare void @fn() memory(other: read)
2626
;--- missing-colon.ll
2727
; MISSING-COLON: error: expected ':' after location

llvm/test/Assembler/memory-attribute.ll

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,18 @@ declare void @fn_inaccessiblemem_write() memory(inaccessiblemem: write)
4040
; CHECK: @fn_inaccessiblemem_readwrite()
4141
declare void @fn_inaccessiblemem_readwrite() memory(inaccessiblemem: readwrite)
4242

43+
; CHECK: Function Attrs: memory(errnomem: read)
44+
; CHECK: @fn_errnomem_read()
45+
declare void @fn_errnomem_read() memory(errnomem: read)
46+
47+
; CHECK: Function Attrs: memory(errnomem: write)
48+
; CHECK: @fn_errnomem_write()
49+
declare void @fn_errnomem_write() memory(errnomem: write)
50+
51+
; CHECK: Function Attrs: memory(errnomem: readwrite)
52+
; CHECK: @fn_errnomem_readwrite()
53+
declare void @fn_errnomem_readwrite() memory(errnomem: readwrite)
54+
4355
; CHECK: Function Attrs: memory(read, argmem: readwrite)
4456
; CHECK: @fn_read_argmem_readwrite()
4557
declare void @fn_read_argmem_readwrite() memory(read, argmem: readwrite)
Binary file not shown.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
; RUN: llvm-dis < %S/Inputs/memory-attribute-upgrade.bc | FileCheck %s
2+
3+
; CHECK: ; Function Attrs: memory(write, argmem: read)
4+
; CHECK-NEXT: define void @test_any_write_argmem_read(ptr %p)
5+
6+
; CHECK: ; Function Attrs: memory(read, argmem: readwrite, inaccessiblemem: none)
7+
; CHECK-NEXT: define void @test_any_read_argmem_readwrite(ptr %p)

llvm/unittests/Support/ModRefTest.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "llvm/Support/ModRef.h"
10-
#include "llvm/ADT/SmallString.h"
1110
#include "llvm/Support/raw_ostream.h"
1211
#include "gtest/gtest.h"
1312
#include <string>
@@ -21,7 +20,8 @@ TEST(ModRefTest, PrintMemoryEffects) {
2120
std::string S;
2221
raw_string_ostream OS(S);
2322
OS << MemoryEffects::none();
24-
EXPECT_EQ(S, "ArgMem: NoModRef, InaccessibleMem: NoModRef, Other: NoModRef");
23+
EXPECT_EQ(S, "ArgMem: NoModRef, InaccessibleMem: NoModRef, ErrnoMem: "
24+
"NoModRef, Other: NoModRef");
2525
}
2626

2727
} // namespace

mlir/test/Target/LLVMIR/llvmir.mlir

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2347,15 +2347,15 @@ llvm.func @readonly_function(%arg0: !llvm.ptr {llvm.readonly})
23472347
llvm.func @arg_mem_none_func() attributes {
23482348
memory_effects = #llvm.memory_effects<other = readwrite, argMem = none, inaccessibleMem = readwrite>}
23492349

2350-
// CHECK: attributes #[[ATTR]] = { memory(readwrite, argmem: none) }
2350+
// CHECK: attributes #[[ATTR]] = { memory(readwrite, argmem: none, errnomem: none) }
23512351

23522352
// -----
23532353

23542354
// CHECK: declare void @readwrite_func() #[[ATTR:[0-9]+]]
23552355
llvm.func @readwrite_func() attributes {
23562356
memory_effects = #llvm.memory_effects<other = readwrite, argMem = readwrite, inaccessibleMem = readwrite>}
23572357

2358-
// CHECK: attributes #[[ATTR]] = { memory(readwrite) }
2358+
// CHECK: attributes #[[ATTR]] = { memory(readwrite, errnomem: none) }
23592359

23602360
// -----
23612361

@@ -2613,11 +2613,11 @@ llvm.func @mem_effects_call() {
26132613
// CHECK: #[[ATTRS_0]]
26142614
// CHECK-SAME: memory(none)
26152615
// CHECK: #[[ATTRS_1]]
2616-
// CHECK-SAME: memory(read, argmem: none, inaccessiblemem: write)
2616+
// CHECK-SAME: memory(read, argmem: none, inaccessiblemem: write, errnomem: none)
26172617
// CHECK: #[[ATTRS_2]]
2618-
// CHECK-SAME: memory(read, inaccessiblemem: write)
2618+
// CHECK-SAME: memory(read, inaccessiblemem: write, errnomem: none)
26192619
// CHECK: #[[ATTRS_3]]
2620-
// CHECK-SAME: memory(readwrite, argmem: read)
2620+
// CHECK-SAME: memory(readwrite, argmem: read, errnomem: none)
26212621

26222622
// -----
26232623

0 commit comments

Comments
 (0)