Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IR][ModRef] Introduce errno memory location #120783

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2046,8 +2046,8 @@ For example:
This attribute specifies the possible memory effects of the call-site or
function. It allows specifying the possible access kinds (``none``,
``read``, ``write``, or ``readwrite``) for the possible memory location
kinds (``argmem``, ``inaccessiblemem``, as well as a default). It is best
understood by example:
kinds (``argmem``, ``inaccessiblemem``, ``errnomem``, as well as a default).
It is best understood by example:

- ``memory(none)``: Does not access any memory.
- ``memory(read)``: May read (but not write) any memory.
Expand All @@ -2056,6 +2056,8 @@ For example:
- ``memory(argmem: read)``: May only read argument memory.
- ``memory(argmem: read, inaccessiblemem: write)``: May only read argument
memory and only write inaccessible memory.
- ``memory(argmem: read, errnomem: write)``: May only read argument memory
and only write errno.
- ``memory(read, argmem: readwrite)``: May read any memory (default mode)
and additionally write argument memory.
- ``memory(readwrite, argmem: none)``: May access any memory apart from
Expand Down Expand Up @@ -2085,6 +2087,7 @@ For example:
allocator function may return newly accessible memory while only
accessing inaccessible memory itself). Inaccessible memory is often used
to model control dependencies of intrinsics.
- ``errnomem``: This refers to accesses to the ``errno`` variable.
- The default access kind (specified without a location prefix) applies to
all locations that haven't been specified explicitly, including those that
don't currently have a dedicated location kind (e.g. accesses to globals
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/AsmParser/LLToken.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ enum Kind {
kw_readwrite,
kw_argmem,
kw_inaccessiblemem,
kw_errnomem,

// Legacy attributes:
kw_argmemonly,
Expand Down
19 changes: 18 additions & 1 deletion llvm/include/llvm/Support/ModRef.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -139,6 +141,16 @@ 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 other memory.
static MemoryEffectsBase otherMemOnly(ModRefInfo MR = ModRefInfo::ModRef) {
return MemoryEffectsBase(Location::Other, MR);
}

/// Create MemoryEffectsBase that can only access inaccessible or argument
/// memory.
static MemoryEffectsBase
Expand Down Expand Up @@ -212,6 +224,11 @@ template <typename LocationEnum> class MemoryEffectsBase {
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 {
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/AsmParser/LLLexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(readwrite);
KEYWORD(argmem);
KEYWORD(inaccessiblemem);
KEYWORD(errnomem);
KEYWORD(argmemonly);
KEYWORD(inaccessiblememonly);
KEYWORD(inaccessiblemem_or_argmemonly);
Expand Down
4 changes: 3 additions & 1 deletion llvm/lib/AsmParser/LLParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2497,6 +2497,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;
}
Expand Down Expand Up @@ -2545,7 +2547,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)");
Expand Down
28 changes: 23 additions & 5 deletions llvm/lib/Bitcode/Reader/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1937,8 +1937,7 @@ static void addRawAttributeValue(AttrBuilder &B, uint64_t Val) {
}

/// This fills an AttrBuilder object with the LLVM attributes that have
/// been decoded from the given integer. This function must stay in sync with
/// 'encodeLLVMAttributesForBitcode'.
/// been decoded from the given integer.
static void decodeLLVMAttributesForBitcode(AttrBuilder &B,
uint64_t EncodedAttrs,
uint64_t AttrIdx) {
Expand Down Expand Up @@ -2398,9 +2397,28 @@ Error BitcodeReader::parseAttributeGroupBlock() {
B.addUWTableAttr(UWTableKind(Record[++i]));
else if (Kind == Attribute::AllocKind)
B.addAllocKindAttr(static_cast<AllocFnKind>(Record[++i]));
else if (Kind == Attribute::Memory)
B.addMemoryAttr(MemoryEffects::createFromIntValue(Record[++i]));
else if (Kind == Attribute::Captures)
else if (Kind == Attribute::Memory) {
uint64_t EncodedME = Record[++i];
const uint8_t Version = (EncodedME >> 56);
if (Version == 0) {
// Errno memory location was previously encompassed into default
// memory. Ensure this is taken into account while reconstructing
// the memory attribute prior to its introduction.
ModRefInfo ArgMem = ModRefInfo((EncodedME >> 0) & 3);
ModRefInfo InaccessibleMem = ModRefInfo((EncodedME >> 2) & 3);
ModRefInfo OtherMem = ModRefInfo((EncodedME >> 4) & 3);
auto ME = MemoryEffects::inaccessibleMemOnly(InaccessibleMem) |
MemoryEffects::argMemOnly(ArgMem) |
MemoryEffects::errnoMemOnly(OtherMem) |
MemoryEffects::otherMemOnly(OtherMem);
B.addMemoryAttr(ME);
} else {
// Construct the memory attribute directly from the encoded base
// on newer versions.
B.addMemoryAttr(MemoryEffects::createFromIntValue(
EncodedME & 0x00FFFFFFFFFFFFFFULL));
}
} else if (Kind == Attribute::Captures)
B.addCapturesAttr(CaptureInfo::createFromIntValue(Record[++i]));
else if (Kind == Attribute::NoFPClass)
B.addNoFPClassAttr(
Expand Down
11 changes: 9 additions & 2 deletions llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -977,8 +977,15 @@ void ModuleBitcodeWriter::writeAttributeGroupTable() {
Record.push_back(getAttrKindEncoding(Attr.getKindAsEnum()));
} else if (Attr.isIntAttribute()) {
Record.push_back(1);
Record.push_back(getAttrKindEncoding(Attr.getKindAsEnum()));
Record.push_back(Attr.getValueAsInt());
Attribute::AttrKind Kind = Attr.getKindAsEnum();
Record.push_back(getAttrKindEncoding(Kind));
if (Kind == Attribute::Memory) {
// Version field for upgrading old memory effects.
const uint64_t Version = 1;
Record.push_back((Version << 56) | Attr.getValueAsInt());
} else {
Record.push_back(Attr.getValueAsInt());
}
} else if (Attr.isStringAttribute()) {
StringRef Kind = Attr.getKindAsString();
StringRef Val = Attr.getValueAsString();
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/IR/Attributes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,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");
}
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Support/ModRef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Transforms/IPO/FunctionAttrs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ static void addLocAccess(MemoryEffects &ME, const MemoryLocation &Loc,
// If it's not an identified object, it might be an argument.
if (!isIdentifiedObject(UO))
ME |= MemoryEffects::argMemOnly(MR);
ME |= MemoryEffects(IRMemLocation::ErrnoMem, MR);
ME |= MemoryEffects(IRMemLocation::Other, MR);
}

Expand Down Expand Up @@ -219,6 +220,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
Expand Down
6 changes: 4 additions & 2 deletions llvm/lib/Transforms/IPO/SCCP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,10 @@ static bool runIPSCCP(
if (ME == MemoryEffects::unknown())
return AL;

ME |= MemoryEffects(IRMemLocation::Other,
ME.getModRef(IRMemLocation::ArgMem));
ModRefInfo ArgMemMR = ME.getModRef(IRMemLocation::ArgMem);
ME |= MemoryEffects(IRMemLocation::ErrnoMem, ArgMemMR);
ME |= MemoryEffects(IRMemLocation::Other, ArgMemMR);

return AL.addFnAttribute(
F.getContext(),
Attribute::getWithMemoryEffects(F.getContext(), ME));
Expand Down
6 changes: 3 additions & 3 deletions llvm/test/Assembler/memory-attribute-errors.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions llvm/test/Assembler/memory-attribute.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Binary file not shown.
7 changes: 7 additions & 0 deletions llvm/test/Bitcode/memory-attribute-upgrade.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
; RUN: llvm-dis < %S/Inputs/memory-attribute-upgrade.bc | FileCheck %s

; CHECK: ; Function Attrs: memory(write, argmem: read)
; CHECK-NEXT: define void @test_any_write_argmem_read(ptr %p)

; CHECK: ; Function Attrs: memory(read, argmem: readwrite, inaccessiblemem: none)
; CHECK-NEXT: define void @test_any_read_argmem_readwrite(ptr %p)
4 changes: 2 additions & 2 deletions llvm/unittests/Support/ModRefTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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
10 changes: 5 additions & 5 deletions mlir/test/Target/LLVMIR/llvmir.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -2347,15 +2347,15 @@ 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) }

// -----

// CHECK: declare void @readwrite_func() #[[ATTR:[0-9]+]]
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) }

// -----

Expand Down Expand Up @@ -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)

// -----

Expand Down