diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp --- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp +++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp @@ -2152,14 +2152,6 @@ << stringifyLinkage(LLVM::Linkage::Common) << "' linkage"; - // Check to see if this function has a void return with a result attribute to - // it. It isn't clear what semantics we would assign to that. - if (getFunctionType().getReturnType().isa() && - !getResultAttrs(0).empty()) { - return emitOpError() - << "cannot attach result attributes to functions with a void return"; - } - if (isExternal()) { if (getLinkage() != LLVM::Linkage::External && getLinkage() != LLVM::Linkage::ExternWeak) @@ -2777,12 +2769,75 @@ unsigned regionIdx, unsigned resIdx, NamedAttribute resAttr) { - if (resAttr.getName() == LLVMDialect::getStructAttrsAttrName()) { + StringAttr name = resAttr.getName(); + if (name == LLVMDialect::getStructAttrsAttrName()) { return verifyFuncOpInterfaceStructAttr( op, resAttr.getValue(), [resIdx](FunctionOpInterface funcOp) { return funcOp.getResultTypes()[resIdx]; }); } + if (auto funcOp = dyn_cast(op)) { + mlir::Type resTy = funcOp.getResultTypes()[resIdx]; + + // Check to see if this function has a void return with a result attribute + // to it. It isn't clear what semantics we would assign to that. + if (resTy.isa()) + return op->emitError() << "cannot attach result attributes to functions " + "with a void return"; + + // LLVM attribute may be attached to a result of operation + // that has not been converted to LLVM dialect yet, so the result + // may have a type with unknown representation in LLVM dialect type + // space. In this case we cannot verify whether the attribute may be + // attached to a result of such type. + bool verifyValueType = isCompatibleType(resTy); + Attribute attrValue = resAttr.getValue(); + + // TODO: get rid of code duplication here and in verifyRegionArgAttribute(). + if (name == LLVMDialect::getAlignAttrName()) { + if (!attrValue.isa()) + return op->emitError() << "expected llvm.align result attribute to be " + "an integer attribute"; + if (verifyValueType && !resTy.isa()) + return op->emitError() + << "llvm.align attribute attached to non-pointer result"; + return success(); + } + if (name == LLVMDialect::getNoAliasAttrName()) { + if (!attrValue.isa()) + return op->emitError() << "expected llvm.noalias result attribute to " + "be a unit attribute"; + if (verifyValueType && !resTy.isa()) + return op->emitError() + << "llvm.noalias attribute attached to non-pointer result"; + return success(); + } + if (name == LLVMDialect::getNoUndefAttrName()) { + if (!attrValue.isa()) + return op->emitError() << "expected llvm.noundef result attribute to " + "be a unit attribute"; + return success(); + } + if (name == LLVMDialect::getSExtAttrName()) { + if (!attrValue.isa()) + return op->emitError() << "expected llvm.signext result attribute to " + "be a unit attribute"; + if (verifyValueType && !resTy.isa()) + return op->emitError() + << "llvm.signext attribute attached to non-integer result"; + return success(); + } + if (name == LLVMDialect::getZExtAttrName()) { + if (!attrValue.isa()) + return op->emitError() << "expected llvm.zeroext result attribute to " + "be a unit attribute"; + if (verifyValueType && !resTy.isa()) + return op->emitError() + << "llvm.zeroext attribute attached to non-integer result"; + return success(); + } + } + return success(); } diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp --- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp @@ -885,9 +885,32 @@ mapFunction(function.getName(), llvmFunc); addRuntimePreemptionSpecifier(function.getDsoLocal(), llvmFunc); + // Convert function attributes. if (function->getAttrOfType(LLVMDialect::getReadnoneAttrName())) llvmFunc->setDoesNotAccessMemory(); + // Convert result attributes. + if (ArrayAttr allResultAttrs = function.getAllResultAttrs()) { + llvm::AttrBuilder retAttrs(llvmFunc->getContext()); + DictionaryAttr resultAttrs = allResultAttrs[0].cast(); + for (const NamedAttribute &attr : resultAttrs) { + StringAttr name = attr.getName(); + if (name == LLVMDialect::getAlignAttrName()) { + auto alignAmount = attr.getValue().cast(); + retAttrs.addAlignmentAttr(llvm::Align(alignAmount.getInt())); + } else if (name == LLVMDialect::getNoAliasAttrName()) { + retAttrs.addAttribute(llvm::Attribute::NoAlias); + } else if (name == LLVMDialect::getNoUndefAttrName()) { + retAttrs.addAttribute(llvm::Attribute::NoUndef); + } else if (name == LLVMDialect::getSExtAttrName()) { + retAttrs.addAttribute(llvm::Attribute::SExt); + } else if (name == LLVMDialect::getZExtAttrName()) { + retAttrs.addAttribute(llvm::Attribute::ZExt); + } + } + llvmFunc->addRetAttrs(retAttrs); + } + // Convert argument attributes. unsigned int argIdx = 0; for (auto [mlirArgTy, llvmArg] : diff --git a/mlir/test/Dialect/LLVMIR/func.mlir b/mlir/test/Dialect/LLVMIR/func.mlir --- a/mlir/test/Dialect/LLVMIR/func.mlir +++ b/mlir/test/Dialect/LLVMIR/func.mlir @@ -257,11 +257,56 @@ module { // expected-error@+1 {{cannot attach result attributes to functions with a void return}} - llvm.func @variadic_def() -> (!llvm.void {llvm.noalias}) + llvm.func @variadic_def() -> (!llvm.void {llvm.noundef}) } // ----- +// expected-error @below{{expected llvm.align result attribute to be an integer attribute}} +llvm.func @alignattr_ret() -> (!llvm.ptr {llvm.align = 1.0 : f32}) + +// ----- + +// expected-error @below{{llvm.align attribute attached to non-pointer result}} +llvm.func @alignattr_ret() -> (i32 {llvm.align = 4}) + +// ----- + +// expected-error @below{{expected llvm.noalias result attribute to be a unit attribute}} +llvm.func @noaliasattr_ret() -> (!llvm.ptr {llvm.noalias = 1}) + +// ----- + +// expected-error @below{{llvm.noalias attribute attached to non-pointer result}} +llvm.func @noaliasattr_ret() -> (i32 {llvm.noalias}) + +// ----- + +// expected-error @below{{expected llvm.noundef result attribute to be a unit attribute}} +llvm.func @noundefattr_ret() -> (!llvm.ptr {llvm.noundef = 1}) + +// ----- + +// expected-error @below{{expected llvm.signext result attribute to be a unit attribute}} +llvm.func @signextattr_ret() -> (i32 {llvm.signext = 1}) + +// ----- + +// expected-error @below{{llvm.signext attribute attached to non-integer result}} +llvm.func @signextattr_ret() -> (f32 {llvm.signext}) + +// ----- + +// expected-error @below{{expected llvm.zeroext result attribute to be a unit attribute}} +llvm.func @zeroextattr_ret() -> (i32 {llvm.zeroext = 1}) + +// ----- + +// expected-error @below{{llvm.zeroext attribute attached to non-integer result}} +llvm.func @zeroextattr_ret() -> (f32 {llvm.zeroext}) + +// ----- + module { // expected-error@+1 {{variadic arguments must be in the end of the argument list}} llvm.func @variadic_inside(%arg0: i32, ..., %arg1: i32) diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir --- a/mlir/test/Target/LLVMIR/llvmir.mlir +++ b/mlir/test/Target/LLVMIR/llvmir.mlir @@ -1124,6 +1124,17 @@ // CHECK-LABEL: declare void @zeroextattr_decl(i1 zeroext) llvm.func @zeroextattr_decl(i1 {llvm.zeroext}) +// CHECK-LABEL: declare align 4 ptr @alignattr_ret_decl() +llvm.func @alignattr_ret_decl() -> (!llvm.ptr {llvm.align = 4}) +// CHECK-LABEL: declare noalias ptr @noaliasattr_ret_decl() +llvm.func @noaliasattr_ret_decl() -> (!llvm.ptr {llvm.noalias}) +// CHECK-LABEL: declare noundef ptr @noundefattr_ret_decl() +llvm.func @noundefattr_ret_decl() -> (!llvm.ptr {llvm.noundef}) +// CHECK-LABEL: declare signext i1 @signextattr_ret_decl() +llvm.func @signextattr_ret_decl() -> (i1 {llvm.signext}) +// CHECK-LABEL: declare zeroext i1 @zeroextattr_ret_decl() +llvm.func @zeroextattr_ret_decl() -> (i1 {llvm.zeroext}) + // CHECK-LABEL: @llvm_varargs(...) llvm.func @llvm_varargs(...)