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

0 commit comments

Comments
 (0)