Index: llvm/include/llvm/AsmParser/LLParser.h =================================================================== --- llvm/include/llvm/AsmParser/LLParser.h +++ llvm/include/llvm/AsmParser/LLParser.h @@ -34,6 +34,7 @@ struct MaybeAlign; template class Optional; class Function; + class FunctionModRefBehavior; class Value; class BasicBlock; class Instruction; @@ -284,6 +285,7 @@ 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); Index: llvm/include/llvm/AsmParser/LLToken.h =================================================================== --- llvm/include/llvm/AsmParser/LLToken.h +++ llvm/include/llvm/AsmParser/LLToken.h @@ -183,6 +183,14 @@ kw_##DISPLAY_NAME, #include "llvm/IR/Attributes.inc" + // Memory attribute: + kw_read, + kw_write, + kw_readwrite, + kw_argmem, + kw_inaccessiblemem, + kw_other, + kw_type, kw_opaque, Index: llvm/include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -690,6 +690,7 @@ ATTR_KIND_PRESPLIT_COROUTINE = 83, ATTR_KIND_FNRETTHUNK_EXTERN = 84, ATTR_KIND_SKIP_PROFILE = 85, + ATTR_KIND_MEMORY = 86, }; enum ComdatSelectionKindCodes { Index: llvm/include/llvm/IR/Attributes.h =================================================================== --- llvm/include/llvm/IR/Attributes.h +++ llvm/include/llvm/IR/Attributes.h @@ -41,6 +41,7 @@ class AttributeSetNode; class FoldingSetNodeID; class Function; +class FunctionModRefBehavior; class LLVMContext; class Type; @@ -243,6 +244,9 @@ // Returns the allocator function kind. AllocFnKind getAllocKind() const; + /// Returns memory attribute as FunctionModRefBehavior. + FunctionModRefBehavior getModRefBehavior() 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 @@ // This turns the allocator kind into the form used internally in Attribute. AttrBuilder &addAllocKindAttr(AllocFnKind Kind); + /// Add memory effect attribute. + AttrBuilder &addMemoryAttr(FunctionModRefBehavior FMRB); + ArrayRef attrs() const { return Attrs; } bool operator==(const AttrBuilder &B) const; Index: llvm/include/llvm/IR/Attributes.td =================================================================== --- llvm/include/llvm/IR/Attributes.td +++ llvm/include/llvm/IR/Attributes.td @@ -126,6 +126,9 @@ /// 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]>; Index: llvm/include/llvm/IR/ModRef.h =================================================================== --- llvm/include/llvm/IR/ModRef.h +++ llvm/include/llvm/IR/ModRef.h @@ -80,11 +80,6 @@ return (uint32_t)Loc * BitsPerLoc; } - static auto locations() { - return enum_seq_inclusive(Location::ArgMem, Location::Other, - force_iteration_on_noniterable_enum); - } - FunctionModRefBehavior(uint32_t Data) : Data(Data) {} void setModRef(Location Loc, ModRefInfo MR) { @@ -95,6 +90,11 @@ friend raw_ostream &operator<<(raw_ostream &OS, FunctionModRefBehavior RMRB); public: + static auto locations() { + return enum_seq_inclusive(Location::ArgMem, Location::Other, + force_iteration_on_noniterable_enum); + } + /// Create FunctionModRefBehavior that can access only the given location /// with the given ModRefInfo. FunctionModRefBehavior(Location Loc, ModRefInfo MR) { setModRef(Loc, MR); } @@ -145,6 +145,18 @@ return FRMB; } + /// Create FunctionModRefBehavior from an encoded integer value (used by + /// memory attribute). + static FunctionModRefBehavior createFromIntValue(uint32_t Data) { + return FunctionModRefBehavior(Data); + } + + /// Convert FunctionModRefBehavior 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); Index: llvm/lib/AsmParser/LLLexer.cpp =================================================================== --- llvm/lib/AsmParser/LLLexer.cpp +++ llvm/lib/AsmParser/LLLexer.cpp @@ -644,6 +644,13 @@ KEYWORD(DISPLAY_NAME); #include "llvm/IR/Attributes.inc" + KEYWORD(read); + KEYWORD(write); + KEYWORD(readwrite); + KEYWORD(argmem); + KEYWORD(inaccessiblemem); + KEYWORD(other); + KEYWORD(type); KEYWORD(opaque); Index: llvm/lib/AsmParser/LLParser.cpp =================================================================== --- llvm/lib/AsmParser/LLParser.cpp +++ 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" @@ -1466,6 +1468,13 @@ B.addAllocKindAttr(Kind); return false; } + case Attribute::Memory: { + Optional FMRB = parseMemoryAttr(); + if (!FMRB) + return true; + B.addMemoryAttr(*FMRB); + return false; + } default: B.addAttribute(Attr); Lex.Lex(); @@ -2187,6 +2196,95 @@ return false; } +static Optional +keywordToLoc(lltok::Kind Tok) { + switch (Tok) { + case lltok::kw_argmem: + return FunctionModRefBehavior::ArgMem; + case lltok::kw_inaccessiblemem: + return FunctionModRefBehavior::InaccessibleMem; + case lltok::kw_other: + return FunctionModRefBehavior::Other; + 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() { + FunctionModRefBehavior FMRB = FunctionModRefBehavior::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; + } + + // Allow empty memory() attribute if there are no memory effects (readnone). + if (EatIfPresent(lltok::rparen)) + return FMRB; + + 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, other) " + "or access kind (none, read, write, readwrite)"); + else + tokError("expected access kind (none, read, write, readwrite)"); + return None; + } + + Lex.Lex(); + if (Loc) { + SeenLoc = true; + FMRB = FMRB.getWithModRef(*Loc, *MR); + } else { + if (SeenLoc) { + tokError("default access kind must be specified first"); + return None; + } + FMRB = FunctionModRefBehavior(*MR); + } + + if (EatIfPresent(lltok::rparen)) + return FMRB; + } while (EatIfPresent(lltok::comma)); + + tokError("unterminated memory attribute"); + return None; +} + /// parseOptionalCommaAlign /// ::= /// ::= ',' align 4 Index: llvm/lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ 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" @@ -1880,6 +1881,8 @@ 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: @@ -2124,6 +2127,9 @@ B.addUWTableAttr(UWTableKind(Record[++i])); else if (Kind == Attribute::AllocKind) B.addAllocKindAttr(static_cast(Record[++i])); + else if (Kind == Attribute::Memory) + B.addMemoryAttr( + FunctionModRefBehavior::createFromIntValue(Record[++i])); } else if (Record[i] == 3 || Record[i] == 4) { // String attribute bool HasValue = (Record[i++] == 4); SmallString<64> KindStr; Index: llvm/lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -656,6 +656,8 @@ 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: Index: llvm/lib/IR/Attributes.cpp =================================================================== --- llvm/lib/IR/Attributes.cpp +++ 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 @@ return AllocFnKind(pImpl->getValueAsInt()); } +FunctionModRefBehavior Attribute::getModRefBehavior() const { + assert(hasAttribute(Attribute::Memory) && + "Can only call getModRefBehavior() on memory attribute"); + return FunctionModRefBehavior::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,50 @@ .str(); } + if (hasAttribute(Attribute::Memory)) { + std::string Result; + raw_string_ostream OS(Result); + bool First = true; + OS << "memory("; + + FunctionModRefBehavior FMRB = getModRefBehavior(); + + // 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". + // Also print memory(none), which is more obvious than just memory(). + ModRefInfo OtherMR = FMRB.getModRef(FunctionModRefBehavior::Other); + if (OtherMR != ModRefInfo::NoModRef || FMRB.getModRef() == OtherMR) { + First = false; + OS << getModRefStr(OtherMR); + } + + for (auto Loc : FunctionModRefBehavior::locations()) { + ModRefInfo MR = FMRB.getModRef(Loc); + if (MR == OtherMR) + continue; + + if (!First) + OS << ", "; + First = false; + + switch (Loc) { + case FunctionModRefBehavior::ArgMem: + OS << "argmem: "; + break; + case FunctionModRefBehavior::InaccessibleMem: + OS << "inaccessiblemem: "; + break; + case FunctionModRefBehavior::Other: + OS << "other: "; + break; + } + OS << getModRefStr(MR); + } + OS << ")"; + OS.flush(); + return Result; + } + // Convert target-dependent attributes to strings of the form: // // "kind" @@ -1723,6 +1788,10 @@ return addRawIntAttr(Attribute::UWTable, uint64_t(Kind)); } +AttrBuilder &AttrBuilder::addMemoryAttr(FunctionModRefBehavior FMRB) { + return addRawIntAttr(Attribute::Memory, FMRB.toIntValue()); +} + AttrBuilder &AttrBuilder::addAllocKindAttr(AllocFnKind Kind) { return addRawIntAttr(Attribute::AllocKind, static_cast(Kind)); } Index: llvm/lib/Transforms/Utils/CodeExtractor.cpp =================================================================== --- llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -922,6 +922,7 @@ 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: Index: llvm/test/Assembler/memory-attribute-errors.ll =================================================================== --- /dev/null +++ llvm/test/Assembler/memory-attribute-errors.ll @@ -0,0 +1,26 @@ +; 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/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/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 +;--- unterminated.ll +; UNTERMINATED: error: unterminated memory attribute +declare void @fn() memory(read +;--- invalid-kind.ll +; INVALID-KIND: error: expected memory location (argmem, inaccessiblemem, other) or access kind (none, read, write, readwrite) +declare void @fn() memory(foo) +;--- 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) Index: llvm/test/Assembler/memory-attribute.ll =================================================================== --- /dev/null +++ llvm/test/Assembler/memory-attribute.ll @@ -0,0 +1,79 @@ +; RUN: llvm-as < %s | llvm-dis | FileCheck %s + +; CHECK: Function Attrs: memory(none) +; CHECK: @fn_readnone1() +declare void @fn_readnone1() memory() + +; 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: none, inaccessiblemem: none) +; CHECK: @fn_other_read() +declare void @fn_other_read() memory(other: read) + +; CHECK: Function Attrs: memory(write, argmem: none, inaccessiblemem: none) +; CHECK: @fn_other_write() +declare void @fn_other_write() memory(other: write) + +; CHECK: Function Attrs: memory(readwrite, argmem: none, inaccessiblemem: none) +; CHECK: @fn_other_readwrite() +declare void @fn_other_readwrite() memory(other: 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)