[Mlir-commits] [mlir] c183af3 - [CIR] Implement 'noreturn' attribute for functions/calls. (#177978)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Tue Jan 27 11:00:44 PST 2026
Author: Erich Keane
Date: 2026-01-27T11:00:39-08:00
New Revision: c183af34af4d94ea58a17ec189471cb0df24f326
URL: https://github.com/llvm/llvm-project/commit/c183af34af4d94ea58a17ec189471cb0df24f326
DIFF: https://github.com/llvm/llvm-project/commit/c183af34af4d94ea58a17ec189471cb0df24f326.diff
LOG: [CIR] Implement 'noreturn' attribute for functions/calls. (#177978)
This mirrors what LLVM does, and requires propagating into the LLVM
dialect: When the user specifies 'noreturn' we propagate this down
throughout the stack.
Note the similar 'willreturn' is too strong of a guarantee (in that they
are not opposites of each other, as there is a 'unknown' implied by all
others), so we cannot use that on non-noreturn functions.
Added:
clang/test/CIR/CodeGen/noreturn.cpp
Modified:
clang/include/clang/CIR/Dialect/IR/CIRDialect.td
clang/lib/CIR/CodeGen/CIRGenCall.cpp
clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
clang/lib/CIR/CodeGen/CIRGenTypes.cpp
clang/lib/CIR/CodeGen/CIRGenTypes.h
clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
clang/test/CIR/CodeGenBuiltins/builtin_call.cpp
mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
mlir/lib/Target/LLVMIR/ModuleImport.cpp
mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
mlir/test/Dialect/LLVMIR/func.mlir
mlir/test/Dialect/LLVMIR/roundtrip.mlir
mlir/test/Target/LLVMIR/Import/function-attributes.ll
mlir/test/Target/LLVMIR/Import/instructions.ll
mlir/test/Target/LLVMIR/llvmir.mlir
Removed:
################################################################################
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
index 7c38492544b39..47ca625c34816 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
@@ -39,6 +39,7 @@ def CIR_Dialect : Dialect {
static llvm::StringRef getOptInfoAttrName() { return "cir.opt_info"; }
static llvm::StringRef getCalleeAttrName() { return "callee"; }
static llvm::StringRef getNoThrowAttrName() { return "nothrow"; }
+ static llvm::StringRef getNoReturnAttrName() { return "noreturn"; }
static llvm::StringRef getSideEffectAttrName() { return "side_effect"; }
static llvm::StringRef getModuleLevelAsmAttrName() { return "cir.module_asm"; }
static llvm::StringRef getGlobalCtorsAttrName() { return "cir.global_ctors"; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 5fd11c6d97c07..a7680d3b9e823 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -21,7 +21,7 @@ using namespace clang;
using namespace clang::CIRGen;
CIRGenFunctionInfo *
-CIRGenFunctionInfo::create(CanQualType resultType,
+CIRGenFunctionInfo::create(FunctionType::ExtInfo info, CanQualType resultType,
llvm::ArrayRef<CanQualType> argTypes,
RequiredArgs required) {
// The first slot allocated for arg type slot is for the return value.
@@ -32,6 +32,8 @@ CIRGenFunctionInfo::create(CanQualType resultType,
CIRGenFunctionInfo *fi = new (buffer) CIRGenFunctionInfo();
+ fi->noReturn = info.getNoReturn();
+
fi->required = required;
fi->numArgs = argTypes.size();
@@ -120,6 +122,11 @@ void CIRGenModule::constructAttributeList(llvm::StringRef name,
assert(!cir::MissingFeatures::opCallCallConv());
sideEffect = cir::SideEffect::All;
+ if (info.isNoReturn())
+ attrs.set(cir::CIRDialect::getNoReturnAttrName(),
+ mlir::UnitAttr::get(&getMLIRContext()));
+ // TODO(cir): Check/add cmse_nonsecure_call attribute here.
+
addAttributesFromFunctionProtoType(getBuilder(), attrs,
calleeInfo.getCalleeFunctionProtoType());
@@ -129,11 +136,30 @@ void CIRGenModule::constructAttributeList(llvm::StringRef name,
if (targetDecl->hasAttr<NoThrowAttr>())
attrs.set(cir::CIRDialect::getNoThrowAttrName(),
mlir::UnitAttr::get(&getMLIRContext()));
+ // TODO(cir): This is actually only possible if targetDecl isn't a
+ // declarator, which ObjCMethodDecl seems to be the only way to get this to
+ // happen. We're including it here for completeness, but we should add a
+ // test for this when we start generating ObjectiveC.
+ if (targetDecl->hasAttr<NoReturnAttr>())
+ attrs.set(cir::CIRDialect::getNoReturnAttrName(),
+ mlir::UnitAttr::get(&getMLIRContext()));
if (const FunctionDecl *func = dyn_cast<FunctionDecl>(targetDecl)) {
addAttributesFromFunctionProtoType(
getBuilder(), attrs, func->getType()->getAs<FunctionProtoType>());
assert(!cir::MissingFeatures::opCallAttrs());
+
+ const CXXMethodDecl *md = dyn_cast<CXXMethodDecl>(func);
+ bool isVirtualCall = md && md->isVirtual();
+
+ // Don't use [[noreturn]], _Noreturn or [[no_builtin]] for a call to a
+ // virtual function. These attributes are not inherited by overloads.
+ if (!(attrOnCallSite && isVirtualCall)) {
+ if (func->isNoReturn())
+ attrs.set(cir::CIRDialect::getNoReturnAttrName(),
+ mlir::UnitAttr::get(&getMLIRContext()));
+ // TODO(cir): Set NoBuiltinAttr here.
+ }
}
assert(!cir::MissingFeatures::opCallAttrs());
@@ -222,7 +248,8 @@ CIRGenTypes::arrangeCXXStructorDeclaration(GlobalDecl gd) {
assert(!cir::MissingFeatures::opCallCIRGenFuncInfoExtParamInfo());
assert(!cir::MissingFeatures::opCallFnInfoOpts());
- return arrangeCIRFunctionInfo(resultType, argTypes, required);
+ return arrangeCIRFunctionInfo(resultType, argTypes, fpt->getExtInfo(),
+ required);
}
/// Derives the 'this' type for CIRGen purposes, i.e. ignoring method CVR
@@ -259,7 +286,8 @@ arrangeCIRFunctionInfo(CIRGenTypes &cgt, SmallVectorImpl<CanQualType> &prefix,
assert(!cir::MissingFeatures::opCallExtParameterInfo());
appendParameterTypes(cgt, prefix, fpt);
CanQualType resultType = fpt->getReturnType().getUnqualifiedType();
- return cgt.arrangeCIRFunctionInfo(resultType, prefix, required);
+ return cgt.arrangeCIRFunctionInfo(resultType, prefix, fpt->getExtInfo(),
+ required);
}
void CIRGenFunction::emitDelegateCallArg(CallArgList &args,
@@ -325,7 +353,8 @@ arrangeFreeFunctionLikeCall(CIRGenTypes &cgt, CIRGenModule &cgm,
CanQualType retType = fnType->getReturnType()->getCanonicalTypeUnqualified();
assert(!cir::MissingFeatures::opCallFnInfoOpts());
- return cgt.arrangeCIRFunctionInfo(retType, argTypes, required);
+ return cgt.arrangeCIRFunctionInfo(retType, argTypes, fnType->getExtInfo(),
+ required);
}
/// Arrange a call to a C++ method, passing the given arguments.
@@ -364,7 +393,8 @@ const CIRGenFunctionInfo &CIRGenTypes::arrangeCXXConstructorCall(
assert(!cir::MissingFeatures::opCallFnInfoOpts());
assert(!cir::MissingFeatures::opCallCIRGenFuncInfoExtParamInfo());
- return arrangeCIRFunctionInfo(resultType, argTypes, required);
+ return arrangeCIRFunctionInfo(resultType, argTypes, fpt->getExtInfo(),
+ required);
}
/// Arrange a call to a C++ method, passing the given arguments.
@@ -386,7 +416,7 @@ const CIRGenFunctionInfo &CIRGenTypes::arrangeCXXMethodCall(
assert(!cir::MissingFeatures::opCallFnInfoOpts());
return arrangeCIRFunctionInfo(
proto->getReturnType()->getCanonicalTypeUnqualified(), argTypes,
- required);
+ proto->getExtInfo(), required);
}
const CIRGenFunctionInfo &
@@ -457,7 +487,7 @@ CIRGenTypes::arrangeFunctionDeclaration(const FunctionDecl *fd) {
assert(!cir::MissingFeatures::opCallCIRGenFuncInfoExtParamInfo());
assert(!cir::MissingFeatures::opCallFnInfoOpts());
return arrangeCIRFunctionInfo(noProto->getReturnType(), {},
- RequiredArgs::All);
+ noProto->getExtInfo(), RequiredArgs::All);
}
return arrangeFreeFunctionType(funcTy.castAs<FunctionProtoType>());
@@ -535,7 +565,8 @@ const CIRGenFunctionInfo &
CIRGenTypes::arrangeFreeFunctionType(CanQual<FunctionNoProtoType> fnpt) {
CanQualType resultType = fnpt->getReturnType().getUnqualifiedType();
assert(!cir::MissingFeatures::opCallFnInfoOpts());
- return arrangeCIRFunctionInfo(resultType, {}, RequiredArgs(0));
+ return arrangeCIRFunctionInfo(resultType, {}, fnpt->getExtInfo(),
+ RequiredArgs(0));
}
RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo,
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h b/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
index 4f5754cb43986..fb7da9e414139 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
@@ -72,6 +72,10 @@ class RequiredArgs {
class CIRGenFunctionInfo final
: public llvm::FoldingSetNode,
private llvm::TrailingObjects<CIRGenFunctionInfo, CanQualType> {
+ // Whether this function has noreturn.
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned noReturn : 1;
+
RequiredArgs required;
unsigned numArgs;
@@ -81,8 +85,19 @@ class CIRGenFunctionInfo final
CIRGenFunctionInfo() : required(RequiredArgs::All) {}
+ FunctionType::ExtInfo getExtInfo() const {
+ // TODO(cir): as we add this information to this type, we need to add calls
+ // here instead of explicit false/0.
+ return FunctionType::ExtInfo(
+ isNoReturn(), /*getHasRegParm=*/false, /*getRegParm=*/false,
+ /*getASTCallingConvention=*/CallingConv(0), /*isReturnsRetained=*/false,
+ /*isNoCallerSavedRegs=*/false, /*isNoCfCheck=*/false,
+ /*isCmseNSCall=*/false);
+ }
+
public:
- static CIRGenFunctionInfo *create(CanQualType resultType,
+ static CIRGenFunctionInfo *create(FunctionType::ExtInfo info,
+ CanQualType resultType,
llvm::ArrayRef<CanQualType> argTypes,
RequiredArgs required);
@@ -97,9 +112,10 @@ class CIRGenFunctionInfo final
// This function has to be CamelCase because llvm::FoldingSet requires so.
// NOLINTNEXTLINE(readability-identifier-naming)
- static void Profile(llvm::FoldingSetNodeID &id, RequiredArgs required,
- CanQualType resultType,
+ static void Profile(llvm::FoldingSetNodeID &id, FunctionType::ExtInfo info,
+ RequiredArgs required, CanQualType resultType,
llvm::ArrayRef<CanQualType> argTypes) {
+ id.AddBoolean(info.getNoReturn());
id.AddBoolean(required.getOpaqueData());
resultType.Profile(id);
for (const CanQualType &arg : argTypes)
@@ -111,7 +127,7 @@ class CIRGenFunctionInfo final
// If the Profile functions get out of sync, we can end up with incorrect
// function signatures, so we call the static Profile function here rather
// than duplicating the logic.
- Profile(id, required, getReturnType(), arguments());
+ Profile(id, getExtInfo(), required, getReturnType(), arguments());
}
llvm::ArrayRef<CanQualType> arguments() const {
@@ -144,6 +160,8 @@ class CIRGenFunctionInfo final
return isVariadic() ? getRequiredArgs().getNumRequiredArgs()
: argTypeSize();
}
+
+ bool isNoReturn() const { return noReturn; }
};
} // namespace clang::CIRGen
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
index 610ee2ceb1da4..851119ee52096 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
@@ -662,15 +662,14 @@ bool CIRGenTypes::isZeroInitializable(const RecordDecl *rd) {
return getCIRGenRecordLayout(rd).isZeroInitializable();
}
-const CIRGenFunctionInfo &
-CIRGenTypes::arrangeCIRFunctionInfo(CanQualType returnType,
- llvm::ArrayRef<CanQualType> argTypes,
- RequiredArgs required) {
+const CIRGenFunctionInfo &CIRGenTypes::arrangeCIRFunctionInfo(
+ CanQualType returnType, llvm::ArrayRef<CanQualType> argTypes,
+ FunctionType::ExtInfo info, RequiredArgs required) {
assert(llvm::all_of(argTypes,
[](CanQualType t) { return t.isCanonicalAsParam(); }));
// Lookup or create unique function info.
llvm::FoldingSetNodeID id;
- CIRGenFunctionInfo::Profile(id, required, returnType, argTypes);
+ CIRGenFunctionInfo::Profile(id, info, required, returnType, argTypes);
void *insertPos = nullptr;
CIRGenFunctionInfo *fi = functionInfos.FindNodeOrInsertPos(id, insertPos);
@@ -687,7 +686,7 @@ CIRGenTypes::arrangeCIRFunctionInfo(CanQualType returnType,
assert(!cir::MissingFeatures::opCallCallConv());
// Construction the function info. We co-allocate the ArgInfos.
- fi = CIRGenFunctionInfo::create(returnType, argTypes, required);
+ fi = CIRGenFunctionInfo::create(info, returnType, argTypes, required);
functionInfos.InsertNode(fi, insertPos);
return *fi;
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.h b/clang/lib/CIR/CodeGen/CIRGenTypes.h
index e79cdfc9f8224..e36e81b07684b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.h
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.h
@@ -199,7 +199,7 @@ class CIRGenTypes {
const CIRGenFunctionInfo &
arrangeCIRFunctionInfo(CanQualType returnType,
llvm::ArrayRef<CanQualType> argTypes,
- RequiredArgs required);
+ FunctionType::ExtInfo info, RequiredArgs required);
const CIRGenFunctionInfo &
arrangeFreeFunctionType(CanQual<FunctionProtoType> fpt);
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 67a57d7b9d538..1894e92a485cd 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -310,7 +310,8 @@ mlir::Value lowerCirAttrAsValue(mlir::Operation *parentOp,
void convertSideEffectForCall(mlir::Operation *callOp, bool isNothrow,
cir::SideEffect sideEffect,
mlir::LLVM::MemoryEffectsAttr &memoryEffect,
- bool &noUnwind, bool &willReturn) {
+ bool &noUnwind, bool &willReturn,
+ bool &noReturn) {
using mlir::LLVM::ModRefInfo;
switch (sideEffect) {
@@ -344,6 +345,8 @@ void convertSideEffectForCall(mlir::Operation *callOp, bool isNothrow,
willReturn = true;
break;
}
+
+ noReturn = callOp->hasAttr(CIRDialect::getNoReturnAttrName());
}
static mlir::LLVM::CallIntrinsicOp
@@ -1620,8 +1623,9 @@ rewriteCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands,
mlir::LLVM::MemoryEffectsAttr memoryEffects;
bool noUnwind = false;
bool willReturn = false;
+ bool noReturn = false;
convertSideEffectForCall(op, call.getNothrow(), call.getSideEffect(),
- memoryEffects, noUnwind, willReturn);
+ memoryEffects, noUnwind, willReturn, noReturn);
mlir::LLVM::LLVMFunctionType llvmFnTy;
@@ -1684,6 +1688,7 @@ rewriteCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands,
newOp.setMemoryEffectsAttr(memoryEffects);
newOp.setNoUnwind(noUnwind);
newOp.setWillReturn(willReturn);
+ newOp.setNoreturn(noReturn);
return mlir::success();
}
@@ -2036,6 +2041,7 @@ void CIRToLLVMFuncOpLowering::lowerFuncAttributes(
attr.getName() == func.getDsoLocalAttrName() ||
attr.getName() == func.getInlineKindAttrName() ||
attr.getName() == func.getSideEffectAttrName() ||
+ attr.getName() == CIRDialect::getNoReturnAttrName() ||
(filterArgAndResAttrs &&
(attr.getName() == func.getArgAttrsAttrName() ||
attr.getName() == func.getResAttrsAttrName())))
@@ -2151,6 +2157,9 @@ mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite(
}
}
+ if (op->hasAttr(CIRDialect::getNoReturnAttrName()))
+ fn.setNoreturn(true);
+
if (std::optional<cir::InlineKind> inlineKind = op.getInlineKind()) {
fn.setNoInline(*inlineKind == cir::InlineKind::NoInline);
fn.setInlineHint(*inlineKind == cir::InlineKind::InlineHint);
diff --git a/clang/test/CIR/CodeGen/noreturn.cpp b/clang/test/CIR/CodeGen/noreturn.cpp
new file mode 100644
index 0000000000000..840f883062e7c
--- /dev/null
+++ b/clang/test/CIR/CodeGen/noreturn.cpp
@@ -0,0 +1,41 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefixes=LLVM,OGCG
+
+extern "C" {
+// CIR: cir.func {{.*}} @bar() -> !s32i attributes {noreturn} {
+// LLVM: Function Attrs:{{.*}} noreturn
+// LLVM-NEXT: define {{.*}} i32 @bar() #[[BAR_FOO_ATTR:.*]] {
+__attribute((noreturn))
+int bar() { }
+
+// Note: Classic codegen puts this here, so we need this to make sure the
+// FunctionAttrs from `trap` doesn't interfere with 'foo'. However, CIR->LLVM
+// lowering puts the trap decl at the end, so it isn't here to worry about.
+// OGCG: declare void @llvm.trap
+
+// CIR: cir.func {{.*}} @foo() -> !s32i attributes {noreturn} {
+// LLVM: Function Attrs:{{.*}} noreturn
+// LLVM-NEXT: define {{.*}} i32 @foo() #[[BAR_FOO_ATTR]] {
+[[noreturn]]
+int foo() { }
+
+void caller() {
+ // CIR: cir.call @bar() {noreturn} : () -> !s32i
+ // LLVM: call i32 @bar() #[[CALL_ATTR:.*]]
+ bar();
+}
+
+void caller2() {
+ // CIR: cir.call @foo() {noreturn} : () -> !s32i
+ // LLVM: call i32 @foo() #[[CALL_ATTR]]
+ foo();
+}
+
+// LLVM: attributes #[[BAR_FOO_ATTR]] = {{.*}}noreturn
+// LLVM: attributes #[[CALL_ATTR]] = {{.*}}noreturn
+
+}
diff --git a/clang/test/CIR/CodeGenBuiltins/builtin_call.cpp b/clang/test/CIR/CodeGenBuiltins/builtin_call.cpp
index a08a784951247..8ca7762257cf3 100644
--- a/clang/test/CIR/CodeGenBuiltins/builtin_call.cpp
+++ b/clang/test/CIR/CodeGenBuiltins/builtin_call.cpp
@@ -85,7 +85,7 @@ void library_builtins() {
// CIR: cir.func{{.*}} @_Z16library_builtinsv()
// CIR: %[[NULL:.+]] = cir.const #cir.ptr<null> : !cir.ptr<!s8i>
// CIR: cir.call @printf(%[[NULL]]) nothrow : (!cir.ptr<!s8i>) -> !s32i
-// CIR: cir.call @abort() nothrow : () -> ()
+// CIR: cir.call @abort() nothrow {noreturn} : () -> ()
// LLVM: define{{.*}} void @_Z16library_builtinsv()
// LLVM: call i32 (ptr, ...) @printf(ptr null)
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index 6789ca22c3d5f..390d5a1ea7dfe 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -795,6 +795,7 @@ def LLVM_CallOp
DefaultValuedAttr<TailCallKind, "TailCallKind::None">:$TailCallKind,
OptionalAttr<LLVM_MemoryEffectsAttr>:$memory_effects,
UnitAttr:$convergent, UnitAttr:$no_unwind, UnitAttr:$will_return,
+ UnitAttr:$noreturn,
VariadicOfVariadic<LLVM_Type, "op_bundle_sizes">:$op_bundle_operands,
DenseI32ArrayAttr:$op_bundle_sizes,
OptionalAttr<ArrayAttr>:$op_bundle_tags,
@@ -1992,6 +1993,7 @@ def LLVM_LLVMFuncOp : LLVM_Op<"func", [
OptionalAttr<UnitAttr>:$inline_hint,
OptionalAttr<UnitAttr>:$no_unwind,
OptionalAttr<UnitAttr>:$will_return,
+ OptionalAttr<UnitAttr>:$noreturn,
OptionalAttr<UnitAttr>:$optimize_none,
OptionalAttr<LLVM_VecTypeHintAttr>:$vec_type_hint,
OptionalAttr<DenseI32ArrayAttr>:$work_group_size_hint,
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index 91fbc53c5eb32..d1c45bcb1656b 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -995,6 +995,7 @@ void CallOp::build(OpBuilder &builder, OperationState &state, TypeRange results,
/*CConv=*/nullptr, /*TailCallKind=*/nullptr,
/*memory_effects=*/nullptr,
/*convergent=*/nullptr, /*no_unwind=*/nullptr, /*will_return=*/nullptr,
+ /*noreturn=*/nullptr,
/*op_bundle_operands=*/{}, /*op_bundle_tags=*/{},
/*arg_attrs=*/nullptr, /*res_attrs=*/nullptr,
/*access_groups=*/nullptr, /*alias_scopes=*/nullptr,
@@ -1025,6 +1026,7 @@ void CallOp::build(OpBuilder &builder, OperationState &state,
/*TailCallKind=*/nullptr, /*memory_effects=*/nullptr,
/*convergent=*/nullptr,
/*no_unwind=*/nullptr, /*will_return=*/nullptr,
+ /*noreturn=*/nullptr,
/*op_bundle_operands=*/{}, /*op_bundle_tags=*/{},
/*arg_attrs=*/nullptr, /*res_attrs=*/nullptr,
/*access_groups=*/nullptr,
@@ -1041,6 +1043,7 @@ void CallOp::build(OpBuilder &builder, OperationState &state,
/*fastmathFlags=*/nullptr,
/*CConv=*/nullptr, /*TailCallKind=*/nullptr, /*memory_effects=*/nullptr,
/*convergent=*/nullptr, /*no_unwind=*/nullptr, /*will_return=*/nullptr,
+ /*noreturn=*/nullptr,
/*op_bundle_operands=*/{}, /*op_bundle_tags=*/{},
/*arg_attrs=*/nullptr, /*res_attrs=*/nullptr,
/*access_groups=*/nullptr, /*alias_scopes=*/nullptr,
@@ -1057,6 +1060,7 @@ void CallOp::build(OpBuilder &builder, OperationState &state, LLVMFuncOp func,
/*fastmathFlags=*/nullptr,
/*CConv=*/nullptr, /*TailCallKind=*/nullptr, /*memory_effects=*/nullptr,
/*convergent=*/nullptr, /*no_unwind=*/nullptr, /*will_return=*/nullptr,
+ /*noreturn=*/nullptr,
/*op_bundle_operands=*/{}, /*op_bundle_tags=*/{},
/*access_groups=*/nullptr, /*alias_scopes=*/nullptr,
/*arg_attrs=*/nullptr, /*res_attrs=*/nullptr,
diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
index b6ea4ba6e4921..b1d7f3ab7b777 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
@@ -421,6 +421,8 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
call->addFnAttr(llvm::Attribute::NoUnwind);
if (callOp.getWillReturnAttr())
call->addFnAttr(llvm::Attribute::WillReturn);
+ if (callOp.getNoreturnAttr())
+ call->addFnAttr(llvm::Attribute::NoReturn);
if (callOp.getNoInlineAttr())
call->addFnAttr(llvm::Attribute::NoInline);
if (callOp.getAlwaysInlineAttr())
diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index cec968c02078d..555e72bd77358 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -2666,6 +2666,7 @@ static constexpr std::array kExplicitLLVMFuncOpAttributes{
StringLiteral("no-nans-fp-math"),
StringLiteral("no-signed-zeros-fp-math"),
StringLiteral("noinline"),
+ StringLiteral("noreturn"),
StringLiteral("nounwind"),
StringLiteral("optnone"),
StringLiteral("target-features"),
@@ -2707,6 +2708,8 @@ void ModuleImport::processFunctionAttributes(llvm::Function *func,
funcOp.setNoUnwind(true);
if (func->hasFnAttribute(llvm::Attribute::WillReturn))
funcOp.setWillReturn(true);
+ if (func->hasFnAttribute(llvm::Attribute::NoReturn))
+ funcOp.setNoreturn(true);
if (func->hasFnAttribute("aarch64_pstate_sm_enabled"))
funcOp.setArmStreaming(true);
@@ -2927,6 +2930,7 @@ LogicalResult ModuleImport::convertCallAttributes(llvm::CallInst *inst,
op.setConvergent(callAttrs.getFnAttr(llvm::Attribute::Convergent).isValid());
op.setNoUnwind(callAttrs.getFnAttr(llvm::Attribute::NoUnwind).isValid());
op.setWillReturn(callAttrs.getFnAttr(llvm::Attribute::WillReturn).isValid());
+ op.setNoreturn(callAttrs.getFnAttr(llvm::Attribute::NoReturn).isValid());
op.setNoInline(callAttrs.getFnAttr(llvm::Attribute::NoInline).isValid());
op.setAlwaysInline(
callAttrs.getFnAttr(llvm::Attribute::AlwaysInline).isValid());
diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
index fad9bd6b78018..46a0b97f1edbd 100644
--- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
@@ -1676,6 +1676,8 @@ static void convertFunctionAttributes(LLVMFuncOp func,
llvmFunc->addFnAttr(llvm::Attribute::NoUnwind);
if (func.getWillReturnAttr())
llvmFunc->addFnAttr(llvm::Attribute::WillReturn);
+ if (func.getNoreturnAttr())
+ llvmFunc->addFnAttr(llvm::Attribute::NoReturn);
if (TargetFeaturesAttr targetFeatAttr = func.getTargetFeaturesAttr())
llvmFunc->addFnAttr("target-features", targetFeatAttr.getFeaturesString());
if (FramePointerKindAttr fpAttr = func.getFramePointerAttr())
diff --git a/mlir/test/Dialect/LLVMIR/func.mlir b/mlir/test/Dialect/LLVMIR/func.mlir
index 094313ca048c7..936260f862a98 100644
--- a/mlir/test/Dialect/LLVMIR/func.mlir
+++ b/mlir/test/Dialect/LLVMIR/func.mlir
@@ -324,6 +324,11 @@ module {
llvm.return
}
+ llvm.func @noreturn_function() attributes {noreturn} {
+ // CHECK: @noreturn_function
+ // CHECK-SAME: attributes {noreturn}
+ llvm.return
+ }
}
diff --git a/mlir/test/Dialect/LLVMIR/roundtrip.mlir b/mlir/test/Dialect/LLVMIR/roundtrip.mlir
index afbf47ef3041d..fc2456b350a56 100644
--- a/mlir/test/Dialect/LLVMIR/roundtrip.mlir
+++ b/mlir/test/Dialect/LLVMIR/roundtrip.mlir
@@ -122,6 +122,9 @@ func.func @ops(%arg0: i32, %arg1: f32,
// CHECK: llvm.call @baz() {will_return} : () -> ()
llvm.call @baz() {will_return} : () -> ()
+// CHECK: llvm.call @baz() {noreturn} : () -> ()
+ llvm.call @baz() {noreturn} : () -> ()
+
// CHECK: llvm.call @baz() {memory = #llvm.memory_effects<other = none, argMem = read, inaccessibleMem = write, errnoMem = none, targetMem0 = none, targetMem1 = none>} : () -> ()
llvm.call @baz() {memory = #llvm.memory_effects<other = none, argMem = read, inaccessibleMem = write, errnoMem = none, targetMem0 = none, targetMem1 = none>} : () -> ()
diff --git a/mlir/test/Target/LLVMIR/Import/function-attributes.ll b/mlir/test/Target/LLVMIR/Import/function-attributes.ll
index 023b0120f9bbe..69e18e1c5bbff 100644
--- a/mlir/test/Target/LLVMIR/Import/function-attributes.ll
+++ b/mlir/test/Target/LLVMIR/Import/function-attributes.ll
@@ -411,5 +411,11 @@ declare void @willreturn_attribute() willreturn
// -----
+; CHECK-LABEL: @noreturn_attribute
+; CHECK-SAME: attributes {noreturn}
+declare void @noreturn_attribute() noreturn
+
+// -----
+
; expected-warning @unknown {{'preallocated' attribute is invalid on current operation, skipping it}}
declare void @test() preallocated(i32)
diff --git a/mlir/test/Target/LLVMIR/Import/instructions.ll b/mlir/test/Target/LLVMIR/Import/instructions.ll
index 7f9c511ec75b0..766f19ce386f8 100644
--- a/mlir/test/Target/LLVMIR/Import/instructions.ll
+++ b/mlir/test/Target/LLVMIR/Import/instructions.ll
@@ -701,6 +701,18 @@ define void @call_will_return() {
; CHECK: llvm.func @f()
declare void @f()
+; CHECK-LABEL: @call_noreturn
+define void @call_noreturn() {
+; CHECK: llvm.call @f() {noreturn}
+ call void @f() noreturn
+ ret void
+}
+
+; // -----
+
+; CHECK: llvm.func @f()
+declare void @f()
+
; CHECK-LABEL: @call_memory_effects
define void @call_memory_effects() {
; CHECK: llvm.call @f() {memory_effects = #llvm.memory_effects<other = none, argMem = none, inaccessibleMem = none, errnoMem = none, targetMem0 = none, targetMem1 = none>}
diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir
index 819a514bc8b7e..12d73b429b3ce 100644
--- a/mlir/test/Target/LLVMIR/llvmir.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir.mlir
@@ -2631,6 +2631,17 @@ llvm.func @willreturn() attributes { will_return } {
// -----
+// CHECK-LABEL: @noreturn
+// CHECK-SAME: #[[ATTRS:[0-9]+]]
+llvm.func @noreturn() attributes { noreturn } {
+ llvm.return
+}
+
+// CHECK: #[[ATTRS]]
+// CHECK-SAME: noreturn
+
+// -----
+
llvm.func @f()
// CHECK-LABEL: @convergent_call
@@ -2675,6 +2686,20 @@ llvm.func @willreturn_call() {
llvm.func @f()
+// CHECK-LABEL: @noreturn_call
+// CHECK: call void @f() #[[ATTRS:[0-9]+]]
+llvm.func @noreturn_call() {
+ llvm.call @f() {noreturn} : () -> ()
+ llvm.return
+}
+
+// CHECK: #[[ATTRS]]
+// CHECK-SAME: noreturn
+
+// -----
+
+llvm.func @f()
+
// CHECK-LABEL: @no_inline_call
// CHECK: call void @f() #[[ATTRS:[0-9]+]]
llvm.func @no_inline_call() {
More information about the Mlir-commits
mailing list