[llvm] e9754f0 - [IR] Add support for memory attribute

Nikita Popov via llvm-commits llvm-commits at lists.llvm.org
Fri Oct 21 03:11:36 PDT 2022


Author: Nikita Popov
Date: 2022-10-21T12:11:25+02:00
New Revision: e9754f0211076bab34e5a070cb8eb392a21c0540

URL: https://github.com/llvm/llvm-project/commit/e9754f0211076bab34e5a070cb8eb392a21c0540
DIFF: https://github.com/llvm/llvm-project/commit/e9754f0211076bab34e5a070cb8eb392a21c0540.diff

LOG: [IR] Add support for memory attribute

This implements IR and bitcode support for the memory attribute,
as specified in https://reviews.llvm.org/D135597.

The new attribute is not used for anything yet (and as such, the
old memory attributes are unaffected).

Differential Revision: https://reviews.llvm.org/D135592

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

Modified: 
    llvm/include/llvm/AsmParser/LLParser.h
    llvm/include/llvm/AsmParser/LLToken.h
    llvm/include/llvm/Bitcode/LLVMBitCodes.h
    llvm/include/llvm/IR/Attributes.h
    llvm/include/llvm/IR/Attributes.td
    llvm/include/llvm/IR/ModRef.h
    llvm/lib/AsmParser/LLLexer.cpp
    llvm/lib/AsmParser/LLParser.cpp
    llvm/lib/Bitcode/Reader/BitcodeReader.cpp
    llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
    llvm/lib/IR/Attributes.cpp
    llvm/lib/Transforms/Utils/CodeExtractor.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/AsmParser/LLParser.h b/llvm/include/llvm/AsmParser/LLParser.h
index e088002dbb5c1..8757543071559 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<MemoryEffects> 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 a070cd89e3873..87df754968265 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 d26507a720491..ee5669c6c6aa8 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 5b8b843c6ab57..71cd7fafa8755 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<Attribute> 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 642a39076c9b6..595c9197de1dd 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 b24359391fb1b..becfd27712490 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 c020fe779827e..496f1ed435663 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 922d081a2e318..0fda0559b5b41 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<MemoryEffects> 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<MemoryEffects::Location> 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<ModRefInfo> 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<MemoryEffects> 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<MemoryEffects::Location> Loc = keywordToLoc(Lex.getKind());
+    if (Loc) {
+      Lex.Lex();
+      if (!EatIfPresent(lltok::colon)) {
+        tokError("expected ':' after location");
+        return None;
+      }
+    }
+
+    Optional<ModRefInfo> 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 217c7db607a1e..338674c086356 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<AllocFnKind>(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 d33ef5a13ecf7..1ac4413f158eb 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 3ae15b6b0daad..333caf77f9918 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<uint64_t>(Kind));
 }

diff  --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
index db885203df11e..5c8160e6c3939 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 0000000000000..1fba90362e79b
--- /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 0000000000000..2f7d3980eb378
--- /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)


        


More information about the llvm-commits mailing list