diff --git a/llvm/include/llvm/AsmParser/LLParser.h b/llvm/include/llvm/AsmParser/LLParser.h index e088002dbb5c1c..87575430715597 100644 --- a/llvm/include/llvm/AsmParser/LLParser.h +++ b/llvm/include/llvm/AsmParser/LLParser.h @@ -42,6 +42,7 @@ namespace llvm { class Comdat; class MDString; class MDNode; + class MemoryEffects; struct SlotMapping; /// ValID - Represents a reference of a definition of some sort with no type. @@ -284,6 +285,7 @@ namespace llvm { bool parseOptionalDerefAttrBytes(lltok::Kind AttrKind, uint64_t &Bytes); bool parseOptionalUWTableKind(UWTableKind &Kind); bool parseAllocKind(AllocFnKind &Kind); + Optional parseMemoryAttr(); bool parseScopeAndOrdering(bool IsAtomic, SyncScope::ID &SSID, AtomicOrdering &Ordering); bool parseScope(SyncScope::ID &SSID); diff --git a/llvm/include/llvm/AsmParser/LLToken.h b/llvm/include/llvm/AsmParser/LLToken.h index a070cd89e3873a..87df7549682655 100644 --- a/llvm/include/llvm/AsmParser/LLToken.h +++ b/llvm/include/llvm/AsmParser/LLToken.h @@ -183,6 +183,13 @@ enum Kind { kw_##DISPLAY_NAME, #include "llvm/IR/Attributes.inc" + // Memory attribute: + kw_read, + kw_write, + kw_readwrite, + kw_argmem, + kw_inaccessiblemem, + kw_type, kw_opaque, diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h index d26507a7204919..ee5669c6c6aa86 100644 --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -690,6 +690,7 @@ enum AttributeKindCodes { ATTR_KIND_PRESPLIT_COROUTINE = 83, ATTR_KIND_FNRETTHUNK_EXTERN = 84, ATTR_KIND_SKIP_PROFILE = 85, + ATTR_KIND_MEMORY = 86, }; enum ComdatSelectionKindCodes { diff --git a/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h index 5b8b843c6ab57f..71cd7fafa87551 100644 --- a/llvm/include/llvm/IR/Attributes.h +++ b/llvm/include/llvm/IR/Attributes.h @@ -42,6 +42,7 @@ class AttributeSetNode; class FoldingSetNodeID; class Function; class LLVMContext; +class MemoryEffects; class Type; enum class AllocFnKind : uint64_t { @@ -243,6 +244,9 @@ class Attribute { // Returns the allocator function kind. AllocFnKind getAllocKind() const; + /// Returns memory effects. + MemoryEffects getMemoryEffects() const; + /// The Attribute is converted to a string of equivalent mnemonic. This /// is, presumably, for writing out the mnemonics for the assembly writer. std::string getAsString(bool InAttrGrp = false) const; @@ -1220,6 +1224,9 @@ class AttrBuilder { // This turns the allocator kind into the form used internally in Attribute. AttrBuilder &addAllocKindAttr(AllocFnKind Kind); + /// Add memory effect attribute. + AttrBuilder &addMemoryAttr(MemoryEffects ME); + ArrayRef attrs() const { return Attrs; } bool operator==(const AttrBuilder &B) const; diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td index 642a39076c9b60..595c9197de1dde 100644 --- a/llvm/include/llvm/IR/Attributes.td +++ b/llvm/include/llvm/IR/Attributes.td @@ -126,6 +126,9 @@ def InReg : EnumAttr<"inreg", [ParamAttr, RetAttr]>; /// Build jump-instruction tables and replace refs. def JumpTable : EnumAttr<"jumptable", [FnAttr]>; +/// Memory effects of the function. +def Memory : IntAttr<"memory", [FnAttr]>; + /// Function must be optimized for size first. def MinSize : EnumAttr<"minsize", [FnAttr]>; diff --git a/llvm/include/llvm/IR/ModRef.h b/llvm/include/llvm/IR/ModRef.h index b24359391fb1b1..becfd27712490d 100644 --- a/llvm/include/llvm/IR/ModRef.h +++ b/llvm/include/llvm/IR/ModRef.h @@ -82,11 +82,6 @@ class MemoryEffects { return (uint32_t)Loc * BitsPerLoc; } - static auto locations() { - return enum_seq_inclusive(Location::ArgMem, Location::Other, - force_iteration_on_noniterable_enum); - } - MemoryEffects(uint32_t Data) : Data(Data) {} void setModRef(Location Loc, ModRefInfo MR) { @@ -97,6 +92,12 @@ class MemoryEffects { friend raw_ostream &operator<<(raw_ostream &OS, MemoryEffects RMRB); public: + /// Returns iterator over all supported location kinds. + static auto locations() { + return enum_seq_inclusive(Location::ArgMem, Location::Other, + force_iteration_on_noniterable_enum); + } + /// Create MemoryEffects that can access only the given location with the /// given ModRefInfo. MemoryEffects(Location Loc, ModRefInfo MR) { setModRef(Loc, MR); } @@ -147,6 +148,18 @@ class MemoryEffects { return FRMB; } + /// Create MemoryEffects from an encoded integer value (used by memory + /// attribute). + static MemoryEffects createFromIntValue(uint32_t Data) { + return MemoryEffects(Data); + } + + /// Convert MemoryEffects into an encoded integer value (used by memory + /// attribute). + uint32_t toIntValue() const { + return Data; + } + /// Get ModRefInfo for the given Location. ModRefInfo getModRef(Location Loc) const { return ModRefInfo((Data >> getLocationPos(Loc)) & LocMask); diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp index c020fe779827ee..496f1ed435663c 100644 --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -644,6 +644,12 @@ lltok::Kind LLLexer::LexIdentifier() { KEYWORD(DISPLAY_NAME); #include "llvm/IR/Attributes.inc" + KEYWORD(read); + KEYWORD(write); + KEYWORD(readwrite); + KEYWORD(argmem); + KEYWORD(inaccessiblemem); + KEYWORD(type); KEYWORD(opaque); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index 922d081a2e318b..0fda0559b5b417 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -14,6 +14,7 @@ #include "llvm/ADT/APSInt.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/None.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/AsmParser/LLToken.h" @@ -36,6 +37,7 @@ #include "llvm/IR/Intrinsics.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Metadata.h" +#include "llvm/IR/ModRef.h" #include "llvm/IR/Module.h" #include "llvm/IR/Operator.h" #include "llvm/IR/Value.h" @@ -1456,6 +1458,13 @@ bool LLParser::parseEnumAttribute(Attribute::AttrKind Attr, AttrBuilder &B, B.addAllocKindAttr(Kind); return false; } + case Attribute::Memory: { + Optional ME = parseMemoryAttr(); + if (!ME) + return true; + B.addMemoryAttr(*ME); + return false; + } default: B.addAttribute(Attr); Lex.Lex(); @@ -2177,6 +2186,87 @@ bool LLParser::parseAllocKind(AllocFnKind &Kind) { return false; } +static Optional keywordToLoc(lltok::Kind Tok) { + switch (Tok) { + case lltok::kw_argmem: + return MemoryEffects::ArgMem; + case lltok::kw_inaccessiblemem: + return MemoryEffects::InaccessibleMem; + default: + return None; + } +} + +static Optional keywordToModRef(lltok::Kind Tok) { + switch (Tok) { + case lltok::kw_none: + return ModRefInfo::NoModRef; + case lltok::kw_read: + return ModRefInfo::Ref; + case lltok::kw_write: + return ModRefInfo::Mod; + case lltok::kw_readwrite: + return ModRefInfo::ModRef; + default: + return None; + } +} + +Optional LLParser::parseMemoryAttr() { + MemoryEffects ME = MemoryEffects::none(); + + // We use syntax like memory(argmem: read), so the colon should not be + // interpreted as a label terminator. + Lex.setIgnoreColonInIdentifiers(true); + auto _ = make_scope_exit([&] { Lex.setIgnoreColonInIdentifiers(false); }); + + Lex.Lex(); + if (!EatIfPresent(lltok::lparen)) { + tokError("expected '('"); + return None; + } + + bool SeenLoc = false; + do { + Optional Loc = keywordToLoc(Lex.getKind()); + if (Loc) { + Lex.Lex(); + if (!EatIfPresent(lltok::colon)) { + tokError("expected ':' after location"); + return None; + } + } + + Optional MR = keywordToModRef(Lex.getKind()); + if (!MR) { + if (!Loc) + tokError("expected memory location (argmem, inaccessiblemem) " + "or access kind (none, read, write, readwrite)"); + else + tokError("expected access kind (none, read, write, readwrite)"); + return None; + } + + Lex.Lex(); + if (Loc) { + SeenLoc = true; + ME = ME.getWithModRef(*Loc, *MR); + } else { + if (SeenLoc) { + tokError("default access kind must be specified first"); + return None; + } + ME = MemoryEffects(*MR); + } + + if (EatIfPresent(lltok::rparen)) + return ME; + } while (EatIfPresent(lltok::comma)); + + tokError("unterminated memory attribute"); + return None; +} + /// parseOptionalCommaAlign /// ::= /// ::= ',' align 4 diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index 217c7db607a1ec..338674c0863562 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -55,6 +55,7 @@ #include "llvm/IR/IntrinsicsARM.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Metadata.h" +#include "llvm/IR/ModRef.h" #include "llvm/IR/Module.h" #include "llvm/IR/ModuleSummaryIndex.h" #include "llvm/IR/Operator.h" @@ -1878,6 +1879,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) { return Attribute::InReg; case bitc::ATTR_KIND_JUMP_TABLE: return Attribute::JumpTable; + case bitc::ATTR_KIND_MEMORY: + return Attribute::Memory; case bitc::ATTR_KIND_MIN_SIZE: return Attribute::MinSize; case bitc::ATTR_KIND_NAKED: @@ -2122,6 +2125,8 @@ Error BitcodeReader::parseAttributeGroupBlock() { B.addUWTableAttr(UWTableKind(Record[++i])); else if (Kind == Attribute::AllocKind) B.addAllocKindAttr(static_cast(Record[++i])); + else if (Kind == Attribute::Memory) + B.addMemoryAttr(MemoryEffects::createFromIntValue(Record[++i])); } else if (Record[i] == 3 || Record[i] == 4) { // String attribute bool HasValue = (Record[i++] == 4); SmallString<64> KindStr; diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp index d33ef5a13ecf70..1ac4413f158eba 100644 --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -656,6 +656,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) { return bitc::ATTR_KIND_ALLOCATED_POINTER; case Attribute::AllocKind: return bitc::ATTR_KIND_ALLOC_KIND; + case Attribute::Memory: + return bitc::ATTR_KIND_MEMORY; case Attribute::Naked: return bitc::ATTR_KIND_NAKED; case Attribute::Nest: diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp index 3ae15b6b0daadd..333caf77f9918e 100644 --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -26,6 +26,7 @@ #include "llvm/Config/llvm-config.h" #include "llvm/IR/Function.h" #include "llvm/IR/LLVMContext.h" +#include "llvm/IR/ModRef.h" #include "llvm/IR/Type.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" @@ -383,6 +384,26 @@ AllocFnKind Attribute::getAllocKind() const { return AllocFnKind(pImpl->getValueAsInt()); } +MemoryEffects Attribute::getMemoryEffects() const { + assert(hasAttribute(Attribute::Memory) && + "Can only call getMemoryEffects() on memory attribute"); + return MemoryEffects::createFromIntValue(pImpl->getValueAsInt()); +} + +static const char *getModRefStr(ModRefInfo MR) { + switch (MR) { + case ModRefInfo::NoModRef: + return "none"; + case ModRefInfo::Ref: + return "read"; + case ModRefInfo::Mod: + return "write"; + case ModRefInfo::ModRef: + return "readwrite"; + } + llvm_unreachable("Invalid ModRefInfo"); +} + std::string Attribute::getAsString(bool InAttrGrp) const { if (!pImpl) return {}; @@ -474,6 +495,48 @@ std::string Attribute::getAsString(bool InAttrGrp) const { .str(); } + if (hasAttribute(Attribute::Memory)) { + std::string Result; + raw_string_ostream OS(Result); + bool First = true; + OS << "memory("; + + MemoryEffects ME = getMemoryEffects(); + + // Print access kind for "other" as the default access kind. This way it + // will apply to any new location kinds that get split out of "other". + ModRefInfo OtherMR = ME.getModRef(MemoryEffects::Other); + if (OtherMR != ModRefInfo::NoModRef || ME.getModRef() == OtherMR) { + First = false; + OS << getModRefStr(OtherMR); + } + + for (auto Loc : MemoryEffects::locations()) { + ModRefInfo MR = ME.getModRef(Loc); + if (MR == OtherMR) + continue; + + if (!First) + OS << ", "; + First = false; + + switch (Loc) { + case MemoryEffects::ArgMem: + OS << "argmem: "; + break; + case MemoryEffects::InaccessibleMem: + OS << "inaccessiblemem: "; + break; + case MemoryEffects::Other: + llvm_unreachable("This is represented as the default access kind"); + } + OS << getModRefStr(MR); + } + OS << ")"; + OS.flush(); + return Result; + } + // Convert target-dependent attributes to strings of the form: // // "kind" @@ -1723,6 +1786,10 @@ AttrBuilder &AttrBuilder::addUWTableAttr(UWTableKind Kind) { return addRawIntAttr(Attribute::UWTable, uint64_t(Kind)); } +AttrBuilder &AttrBuilder::addMemoryAttr(MemoryEffects ME) { + return addRawIntAttr(Attribute::Memory, ME.toIntValue()); +} + AttrBuilder &AttrBuilder::addAllocKindAttr(AllocFnKind Kind) { return addRawIntAttr(Attribute::AllocKind, static_cast(Kind)); } diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp index db885203df11e9..5c8160e6c3939a 100644 --- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -922,6 +922,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs, case Attribute::WriteOnly: case Attribute::AllocKind: case Attribute::PresplitCoroutine: + case Attribute::Memory: continue; // Those attributes should be safe to propagate to the extracted function. case Attribute::AlwaysInline: diff --git a/llvm/test/Assembler/memory-attribute-errors.ll b/llvm/test/Assembler/memory-attribute-errors.ll new file mode 100644 index 00000000000000..1fba90362e79b9 --- /dev/null +++ b/llvm/test/Assembler/memory-attribute-errors.ll @@ -0,0 +1,34 @@ +; RUN: split-file %s %t +; RUN: not llvm-as < %t/missing-args.ll 2>&1 | FileCheck %s --check-prefix=MISSING-ARGS +; RUN: not llvm-as < %t/empty.ll 2>&1 | FileCheck %s --check-prefix=EMPTY +; RUN: not llvm-as < %t/unterminated.ll 2>&1 | FileCheck %s --check-prefix=UNTERMINATED +; RUN: not llvm-as < %t/invalid-kind.ll 2>&1 | FileCheck %s --check-prefix=INVALID-KIND +; RUN: not llvm-as < %t/other.ll 2>&1 | FileCheck %s --check-prefix=OTHER +; RUN: not llvm-as < %t/missing-colon.ll 2>&1 | FileCheck %s --check-prefix=MISSING-COLON +; RUN: not llvm-as < %t/invalid-access-kind.ll 2>&1 | FileCheck %s --check-prefix=INVALID-ACCESS-KIND +; RUN: not llvm-as < %t/default-after-loc.ll 2>&1 | FileCheck %s --check-prefix=DEFAULT-AFTER-LOC + +;--- missing-args.ll +; MISSING-ARGS: error: expected '(' +declare void @fn() memory +;--- empty.ll +; EMPTY: error: expected memory location (argmem, inaccessiblemem) 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) +declare void @fn() memory(foo) +;--- other.ll +; OTHER: error: expected memory location (argmem, inaccessiblemem) or access kind (none, read, write, readwrite) +declare void @fn() memory(other: read) +;--- missing-colon.ll +; MISSING-COLON: error: expected ':' after location +declare void @fn() memory(argmem) +;--- invalid-access-kind.ll +; INVALID-ACCESS-KIND: error: expected access kind (none, read, write, readwrite) +declare void @fn() memory(argmem: foo) +;--- default-after-loc.ll +; DEFAULT-AFTER-LOC: error: default access kind must be specified first +declare void @fn() memory(argmem: read, write) diff --git a/llvm/test/Assembler/memory-attribute.ll b/llvm/test/Assembler/memory-attribute.ll new file mode 100644 index 00000000000000..2f7d3980eb378b --- /dev/null +++ b/llvm/test/Assembler/memory-attribute.ll @@ -0,0 +1,68 @@ +; RUN: llvm-as < %s | llvm-dis | FileCheck %s + +; CHECK: Function Attrs: memory(none) +; CHECK: @fn_readnone2() +declare void @fn_readnone2() memory(none) + +; CHECK: Function Attrs: memory(read) +; CHECK: @fn_readonly() +declare void @fn_readonly() memory(read) + +; CHECK: Function Attrs: memory(write) +; CHECK: @fn_writeonly() +declare void @fn_writeonly() memory(write) + +; CHECK: Function Attrs: memory(readwrite) +; CHECK: @fn_readwrite() +declare void @fn_readwrite() memory(readwrite) + +; CHECK: Function Attrs: memory(argmem: read) +; CHECK: @fn_argmem_read() +declare void @fn_argmem_read() memory(argmem: read) + +; CHECK: Function Attrs: memory(argmem: write) +; CHECK: @fn_argmem_write() +declare void @fn_argmem_write() memory(argmem: write) + +; CHECK: Function Attrs: memory(argmem: readwrite) +; CHECK: @fn_argmem_readwrite() +declare void @fn_argmem_readwrite() memory(argmem: readwrite) + +; CHECK: Function Attrs: memory(inaccessiblemem: read) +; CHECK: @fn_inaccessiblemem_read() +declare void @fn_inaccessiblemem_read() memory(inaccessiblemem: read) + +; CHECK: Function Attrs: memory(inaccessiblemem: write) +; CHECK: @fn_inaccessiblemem_write() +declare void @fn_inaccessiblemem_write() memory(inaccessiblemem: write) + +; CHECK: Function Attrs: memory(inaccessiblemem: readwrite) +; CHECK: @fn_inaccessiblemem_readwrite() +declare void @fn_inaccessiblemem_readwrite() memory(inaccessiblemem: readwrite) + +; CHECK: Function Attrs: memory(read, argmem: readwrite) +; CHECK: @fn_read_argmem_readwrite() +declare void @fn_read_argmem_readwrite() memory(read, argmem: readwrite) + +; CHECK: Function Attrs: memory(read, argmem: write) +; CHECK: @fn_read_argmem_write() +declare void @fn_read_argmem_write() memory(read, argmem: write) + +; CHECK: Function Attrs: memory(read, argmem: none) +; CHECK: @fn_read_argmem_none() +declare void @fn_read_argmem_none() memory(read, argmem: none) + +; CHECK: Function Attrs: memory(argmem: read, inaccessiblemem: read) +; CHECK: @fn_argmem_inaccessiblemem_read() +declare void @fn_argmem_inaccessiblemem_read() + memory(argmem: read, inaccessiblemem: read) + +; CHECK: Function Attrs: memory(argmem: read, inaccessiblemem: write) +; CHECK: @fn_argmem_read_inaccessiblemem_write() +declare void @fn_argmem_read_inaccessiblemem_write() + memory(argmem: read, inaccessiblemem: write) + +; CHECK: Function Attrs: memory(argmem: read, inaccessiblemem: write) +; CHECK: @fn_argmem_read_inaccessiblemem_write_reordered() +declare void @fn_argmem_read_inaccessiblemem_write_reordered() + memory(inaccessiblemem: write, argmem: read)