67 changes: 67 additions & 0 deletions llvm/lib/IR/Attributes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 {};

Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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));
}
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Transforms/Utils/CodeExtractor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
34 changes: 34 additions & 0 deletions llvm/test/Assembler/memory-attribute-errors.ll
Original file line number Diff line number Diff line change
@@ -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)
68 changes: 68 additions & 0 deletions llvm/test/Assembler/memory-attribute.ll
Original file line number Diff line number Diff line change
@@ -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)