Index: mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td =================================================================== --- mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td +++ mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td @@ -66,6 +66,12 @@ /// 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. + static StringRef getReadnoneAttrName() { + return "llvm.readnone"; + } + private: /// A cache storing compatible LLVM types that have been verified. This /// can save us lots of verification time if there are many occurrences Index: mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp =================================================================== --- mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp +++ mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp @@ -2861,6 +2861,17 @@ << "' to be a `loopopts` attribute"; } + if (attr.getName() == LLVMDialect::getReadnoneAttrName()) { + const auto attrName = LLVMDialect::getReadnoneAttrName(); + if (!isa(op)) + return op->emitOpError() + << "'" << attrName + << "' is permitted only on FunctionOpInterface operations"; + if (!attr.getValue().isa()) + return op->emitOpError() + << "expected '" << attrName << "' to be a unit attribute"; + } + if (attr.getName() == LLVMDialect::getStructAttrsAttrName()) { return op->emitOpError() << "'" << LLVM::LLVMDialect::getStructAttrsAttrName() Index: mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp =================================================================== --- mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp +++ mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp @@ -168,6 +168,10 @@ /// Imports `f` into the current module. LogicalResult processFunction(llvm::Function *f); + /// Converts function attributes of LLVM Function \p f + /// into LLVM dialect attributes of LLVMFuncOp \p funcOp. + void processFunctionAttributes(llvm::Function *f, LLVMFuncOp funcOp); + /// Imports GV as a GlobalOp, creating it if it doesn't exist. GlobalOp processGlobal(llvm::GlobalVariable *gv); @@ -1165,6 +1169,15 @@ return FlatSymbolRefAttr(); } +void Importer::processFunctionAttributes(llvm::Function *func, + LLVMFuncOp funcOp) { + auto addNamedUnitAttr = [&](StringRef name) { + return funcOp->setAttr(name, UnitAttr::get(context)); + }; + if (func->hasFnAttribute(llvm::Attribute::ReadNone)) + addNamedUnitAttr(LLVMDialect::getReadnoneAttrName()); +} + LogicalResult Importer::processFunction(llvm::Function *f) { blocks.clear(); instMap.clear(); @@ -1199,6 +1212,9 @@ if (f->hasGC()) fop.setGarbageCollectorAttr(b.getStringAttr(f->getGC())); + // Handle Function attributes. + processFunctionAttributes(f, fop); + if (f->isDeclaration()) return success(); Index: mlir/lib/Target/LLVMIR/ModuleTranslation.cpp =================================================================== --- mlir/lib/Target/LLVMIR/ModuleTranslation.cpp +++ mlir/lib/Target/LLVMIR/ModuleTranslation.cpp @@ -927,6 +927,10 @@ mapFunction(function.getName(), llvmFunc); addRuntimePreemptionSpecifier(function.getDsoLocal(), llvmFunc); + if (function->getAttrOfType(LLVMDialect::getReadnoneAttrName())) { + llvmFunc->addFnAttr(llvm::Attribute::ReadNone); + } + // Forward the pass-through attributes to LLVM. if (failed(forwardPassthroughAttributes( function.getLoc(), function.getPassthrough(), llvmFunc))) Index: mlir/test/Dialect/LLVMIR/func.mlir =================================================================== --- mlir/test/Dialect/LLVMIR/func.mlir +++ mlir/test/Dialect/LLVMIR/func.mlir @@ -277,3 +277,21 @@ "llvm.func"() ({ }) {sym_name = "generic_unknown_calling_convention", CConv = #llvm.cconv, function_type = !llvm.func} : () -> () } + +// ----- + +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} : () -> () +} + +// ----- + +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} : () -> () +} Index: mlir/test/Target/LLVMIR/Import/function_attributes.ll =================================================================== --- /dev/null +++ mlir/test/Target/LLVMIR/Import/function_attributes.ll @@ -0,0 +1,17 @@ +; RUN: mlir-translate -import-llvm -split-input-file %s | FileCheck %s + +// ----- + +; CHECK: llvm.func @readnone_attr() attributes {llvm.readnone} +declare void @readnone_attr() #0 +attributes #0 = { readnone } + +// ----- + +; CHECK: llvm.func @readnone_attr() attributes {llvm.readnone} { +; CHECK: llvm.return +; CHECK: } +define void @readnone_attr() readnone { +entry: + ret void +} Index: mlir/test/Target/LLVMIR/llvmir.mlir =================================================================== --- mlir/test/Target/LLVMIR/llvmir.mlir +++ mlir/test/Target/LLVMIR/llvmir.mlir @@ -1937,3 +1937,11 @@ // CHECK: ret void llvm.return } + +// ----- + +// Function attributes: readnone + +// CHECK: declare void @readnone_function() #[[ATTR:[0-9]+]] +// CHECK: attributes #[[ATTR]] = { readnone } +llvm.func @readnone_function() attributes {llvm.readnone}