let genVerifyDecl = 1;
}
+//===----------------------------------------------------------------------===//
+// MemoryEffectsAttr
+//===----------------------------------------------------------------------===//
+
+def LLVM_MemoryEffectsAttr : LLVM_Attr<"MemoryEffects", "memory_effects", [
+ SubElementAttrInterface
+ ]> {
+ let parameters = (ins
+ "ModRefInfo":$other,
+ "ModRefInfo":$argMem,
+ "ModRefInfo":$inaccessibleMem
+ );
+ let extraClassDeclaration = [{
+ bool isReadWrite();
+ }];
+ let builders = [
+ TypeBuilder<(ins "ArrayRef<ModRefInfo>":$memInfoArgs)>
+ ];
+ let assemblyFormat = "`<` struct(params) `>`";
+}
+
#endif // LLVMIR_ATTRDEFS
let cppNamespace = "::mlir::LLVM";
}
+//===----------------------------------------------------------------------===//
+// ModRefInfo
+//===----------------------------------------------------------------------===//
+
+def ModRefInfoNoModRef : LLVM_EnumAttrCase<"NoModRef", "none", "NoModRef", 0>;
+def ModRefInfoRef : LLVM_EnumAttrCase<"Ref", "read", "Ref", 1>;
+def ModRefInfoMod : LLVM_EnumAttrCase<"Mod", "write", "Mod", 2>;
+def ModRefInfoModRef : LLVM_EnumAttrCase<"ModRef", "readwrite", "ModRef", 3>;
+
+def ModRefInfoEnum : LLVM_EnumAttr<
+ "ModRefInfo",
+ "::llvm::ModRefInfo",
+ "LLVM ModRefInfo",
+ [ModRefInfoNoModRef, ModRefInfoRef, ModRefInfoMod, ModRefInfoModRef]> {
+ let cppNamespace = "::mlir::LLVM";
+}
+
#endif // LLVMIR_ENUMS_TD
/// Returns `true` if the given type is compatible with the LLVM dialect.
static bool isCompatibleType(Type);
- /// Name of the attribute mapped to LLVM's 'readnone' function attribute.
- /// It is allowed on any FunctionOpInterface operations.
+ /// TODO Remove this once there is an alternative way of modeling memory
+ /// effects on FunctionOpInterface.
+ /// Name of the attribute that will cause the creation of a readnone memory
+ /// effect when lowering to the LLVMDialect.
static StringRef getReadnoneAttrName() {
return "llvm.readnone";
}
OptionalAttr<ArrayAttr>:$passthrough,
OptionalAttr<DictArrayAttr>:$arg_attrs,
OptionalAttr<DictArrayAttr>:$res_attrs,
- OptionalAttr<I64Attr>:$function_entry_count
+ OptionalAttr<I64Attr>:$function_entry_count,
+ OptionalAttr<LLVM_MemoryEffectsAttr>:$memory
);
let regions = (region AnyRegion:$body);
attr.getName() == func.getFunctionTypeAttrName() ||
attr.getName() == linkageAttrName ||
attr.getName() == varargsAttrName ||
+ attr.getName() == LLVM::LLVMDialect::getReadnoneAttrName() ||
(filterArgAndResAttrs &&
(attr.getName() == func.getArgAttrsAttrName() ||
attr.getName() == func.getResAttrsAttrName())))
}
linkage = attr.getLinkage();
}
+
+ // Create a memory effect attribute corresponding to readnone.
+ StringRef readnoneAttrName = LLVM::LLVMDialect::getReadnoneAttrName();
+ LLVM::MemoryEffectsAttr memoryAttr = {};
+ if (funcOp->hasAttr(readnoneAttrName)) {
+ auto attr = funcOp->getAttrOfType<UnitAttr>(readnoneAttrName);
+ if (!attr) {
+ funcOp->emitError() << "Contains " << readnoneAttrName
+ << " attribute not of type UnitAttr";
+ return nullptr;
+ }
+ memoryAttr = LLVM::MemoryEffectsAttr::get(rewriter.getContext(),
+ {LLVM::ModRefInfo::NoModRef,
+ LLVM::ModRefInfo::NoModRef,
+ LLVM::ModRefInfo::NoModRef});
+ }
auto newFuncOp = rewriter.create<LLVM::LLVMFuncOp>(
funcOp.getLoc(), funcOp.getName(), llvmType, linkage,
/*dsoLocal*/ false, /*cconv*/ LLVM::CConv::C, attributes);
+ // If the memory attribute was created, add it to the function.
+ if (memoryAttr)
+ newFuncOp.setMemoryAttr(memoryAttr);
rewriter.inlineRegionBefore(funcOp.getBody(), newFuncOp.getBody(),
newFuncOp.end());
if (failed(rewriter.convertRegionTypes(&newFuncOp.getBody(), *typeConverter,
llvm::sort(options, llvm::less_first());
return get(parser.getContext(), options);
}
+
+//===----------------------------------------------------------------------===//
+// MemoryEffectsAttr
+//===----------------------------------------------------------------------===//
+
+MemoryEffectsAttr MemoryEffectsAttr::get(MLIRContext *context,
+ ArrayRef<ModRefInfo> memInfoArgs) {
+ if (memInfoArgs.empty())
+ return MemoryEffectsAttr::get(context, ModRefInfo::ModRef,
+ ModRefInfo::ModRef, ModRefInfo::ModRef);
+ if (memInfoArgs.size() == 3)
+ return MemoryEffectsAttr::get(context, memInfoArgs[0], memInfoArgs[1],
+ memInfoArgs[2]);
+ return {};
+}
+
+bool MemoryEffectsAttr::isReadWrite() {
+ if (this->getArgMem() != ModRefInfo::ModRef)
+ return false;
+ if (this->getInaccessibleMem() != ModRefInfo::ModRef)
+ return false;
+ if (this->getOther() != ModRefInfo::ModRef)
+ return false;
+ return true;
+}
<< "' to be a `loopopts` attribute";
}
- if (attr.getName() == LLVMDialect::getReadnoneAttrName()) {
- const auto attrName = LLVMDialect::getReadnoneAttrName();
- if (!isa<FunctionOpInterface>(op))
- return op->emitOpError()
- << "'" << attrName
- << "' is permitted only on FunctionOpInterface operations";
- if (!attr.getValue().isa<UnitAttr>())
- return op->emitOpError()
- << "expected '" << attrName << "' to be a unit attribute";
- }
-
if (attr.getName() == LLVMDialect::getStructAttrsAttrName()) {
return op->emitOpError()
<< "'" << LLVM::LLVMDialect::getStructAttrsAttrName()
#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/Support/ModRef.h"
using namespace mlir;
using namespace mlir::LLVM;
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Operator.h"
+#include "llvm/Support/ModRef.h"
using namespace mlir;
using namespace mlir::LLVM;
return FlatSymbolRefAttr();
}
+static void processMemoryEffects(llvm::Function *func, LLVMFuncOp funcOp) {
+ llvm::MemoryEffects memEffects = func->getMemoryEffects();
+
+ auto othermem = convertModRefInfoFromLLVM(
+ memEffects.getModRef(llvm::MemoryEffects::Location::Other));
+ auto argMem = convertModRefInfoFromLLVM(
+ memEffects.getModRef(llvm::MemoryEffects::Location::ArgMem));
+ auto inaccessibleMem = convertModRefInfoFromLLVM(
+ memEffects.getModRef(llvm::MemoryEffects::Location::InaccessibleMem));
+ auto memAttr = MemoryEffectsAttr::get(funcOp.getContext(), othermem, argMem,
+ inaccessibleMem);
+ // Only set the attr when it does not match the default value.
+ if (memAttr.isReadWrite())
+ return;
+ funcOp.setMemoryAttr(memAttr);
+}
+
void ModuleImport::processFunctionAttributes(llvm::Function *func,
LLVMFuncOp funcOp) {
- auto addNamedUnitAttr = [&](StringRef name) {
- return funcOp->setAttr(name, UnitAttr::get(context));
- };
- if (func->doesNotAccessMemory())
- addNamedUnitAttr(LLVMDialect::getReadnoneAttrName());
+ processMemoryEffects(func, funcOp);
}
LogicalResult ModuleImport::processFunction(llvm::Function *func) {
return success();
}
+/// Converts the function attributes from LLVMFuncOp and attaches them to the
+/// llvm::Function.
+static void convertFunctionAttributes(LLVMFuncOp func,
+ llvm::Function *llvmFunc) {
+ if (!func.getMemory())
+ return;
+
+ MemoryEffectsAttr memEffects = func.getMemoryAttr();
+
+ // Add memory effects incrementally.
+ llvm::MemoryEffects newMemEffects =
+ llvm::MemoryEffects(llvm::MemoryEffects::Location::ArgMem,
+ convertModRefInfoToLLVM(memEffects.getArgMem()));
+ newMemEffects |= llvm::MemoryEffects(
+ llvm::MemoryEffects::Location::InaccessibleMem,
+ convertModRefInfoToLLVM(memEffects.getInaccessibleMem()));
+ newMemEffects |=
+ llvm::MemoryEffects(llvm::MemoryEffects::Location::Other,
+ convertModRefInfoToLLVM(memEffects.getOther()));
+ llvmFunc->setMemoryEffects(newMemEffects);
+}
+
LogicalResult ModuleTranslation::convertFunctionSignatures() {
// Declare all functions first because there may be function calls that form a
// call graph with cycles, or global initializers that reference functions.
addRuntimePreemptionSpecifier(function.getDsoLocal(), llvmFunc);
// Convert function attributes.
- if (function->getAttrOfType<UnitAttr>(LLVMDialect::getReadnoneAttrName()))
- llvmFunc->setDoesNotAccessMemory();
+ convertFunctionAttributes(function, llvmFunc);
// Convert function_entry_count attribute to metadata.
if (std::optional<uint64_t> entryCount = function.getFunctionEntryCount())
func.func private @llvmlinkage(i32) attributes { "llvm.linkage" = #llvm.linkage<extern_weak> }
// CHECK-LABEL: llvm.func @llvmreadnone(i32)
-// CHECK-SAME: llvm.readnone
+// CHECK-SAME: memory = #llvm.memory_effects<other = none, argMem = none, inaccessibleMem = none>
func.func private @llvmreadnone(i32) attributes { llvm.readnone }
// CHECK-LABEL: llvm.func @body(i32)
llvm.func @variadic_def(...) {
llvm.return
}
+
+ // CHECK-LABEL: llvm.func @memory_attr
+ // CHECK-SAME: attributes {memory = #llvm.memory_effects<other = none, argMem = read, inaccessibleMem = readwrite>} {
+ llvm.func @memory_attr() attributes {memory = #llvm.memory_effects<other = none, argMem = read, inaccessibleMem = readwrite>} {
+ llvm.return
+ }
}
// -----
// expected-error @below {{failed to parse CConvAttr parameter 'CallingConv' which is to be a `CConv`}}
}) {sym_name = "generic_unknown_calling_convention", CConv = #llvm.cconv<cc_12>, function_type = !llvm.func<i64 (i64, i64)>} : () -> ()
}
-
-// -----
-
-module {
- // expected-error@+3 {{'llvm.readnone' is permitted only on FunctionOpInterface operations}}
- "llvm.func"() ({
- ^bb0:
- llvm.return {llvm.readnone}
- }) {sym_name = "readnone_return", function_type = !llvm.func<void ()>} : () -> ()
-}
-
-// -----
-
-module {
- // expected-error@+1 {{op expected 'llvm.readnone' to be a unit attribute}}
- "llvm.func"() ({
- }) {sym_name = "readnone_func", llvm.readnone = true, function_type = !llvm.func<void ()>} : () -> ()
-}
; // -----
; CHECK-LABEL: @func_readnone
-; CHECK-SAME: attributes {llvm.readnone}
+; CHECK-SAME: attributes {memory = #llvm.memory_effects<other = none, argMem = none, inaccessibleMem = none>}
; CHECK: llvm.return
define void @func_readnone() readnone {
ret void
}
; CHECK-LABEL: @func_readnone_indirect
-; CHECK-SAME: attributes {llvm.readnone}
+; CHECK-SAME: attributes {memory = #llvm.memory_effects<other = none, argMem = none, inaccessibleMem = none>}
declare void @func_readnone_indirect() #0
attributes #0 = { readnone }
}
!1 = !{!"function_entry_count", i64 4242}
+
+; // -----
+
+; CHECK-LABEL: @func_memory
+; CHECK-SAME: attributes {memory = #llvm.memory_effects<other = readwrite, argMem = none, inaccessibleMem = readwrite>}
+; CHECK: llvm.return
+define void @func_memory() memory(readwrite, argmem: none) {
+ ret void
+}
// -----
-// Function attributes: readnone
+// CHECK: declare void @readonly_function([[PTR:.+]] readonly)
+llvm.func @readonly_function(%arg0: !llvm.ptr<f32> {llvm.readonly})
+
+// -----
+
+// CHECK: declare void @arg_mem_none_func() #[[ATTR:[0-9]+]]
+llvm.func @arg_mem_none_func() attributes {
+ memory = #llvm.memory_effects<other = readwrite, argMem = none, inaccessibleMem = readwrite>}
-// CHECK: declare void @readnone_function() #[[ATTR:[0-9]+]]
-// CHECK: attributes #[[ATTR]] = { memory(none) }
-llvm.func @readnone_function() attributes {llvm.readnone}
+// CHECK: attributes #[[ATTR]] = { memory(readwrite, argmem: none) }
// -----
-// CHECK: declare void @readonly_function([[PTR:.+]] readonly)
-llvm.func @readonly_function(%arg0: !llvm.ptr<f32> {llvm.readonly})
+
+// CHECK: declare void @readwrite_func() #[[ATTR:[0-9]+]]
+llvm.func @readwrite_func() attributes {
+ memory = #llvm.memory_effects<other = readwrite, argMem = readwrite, inaccessibleMem = readwrite>}
+
+// CHECK: attributes #[[ATTR]] = { memory(readwrite) }