[clang] [mlir] [CIR] Implement lowering for 'no-builtins' attributes (PR #178899)

Erich Keane via cfe-commits cfe-commits at lists.llvm.org
Fri Jan 30 06:59:23 PST 2026


https://github.com/erichkeane updated https://github.com/llvm/llvm-project/pull/178899

>From e1427c2ec1484b600cd231658d5bc7a71136a444 Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Wed, 28 Jan 2026 07:21:56 -0800
Subject: [PATCH 1/2] [CIR] Implement lowering for 'no-builtins' attributes

This patch adds the 'no-builtins' and 'no-builtin-XXX' attributes from
LLVM-IR to both LLVMIR-MLIR and Clang lowering.  However, I've done a
slightly different implementation of them.

LLVM-IR represents them as 'no-builtins' and 'no-builtin-NAME', where
the latter can be multiple names.  This is problematic for the MLIR for
a variety of reasons, most particularly is our preference for explicit
attribute (of which the latter is an infinite list).  Additionally of
course, our inability to have dashes in attribute names is troublesome.

Therefore, I've lowered them instead as `nobuiltins` for both, which is
an array attribute.

IF the array attribute is empty, it is intended to mean 'all functions'
(ie, the same as `no-builtins`), else it is a list of StringAttrs that
contain the variants of `NAME`.

I considered using nobuiltins=['*'] for the 'all functions', but that
seemed like a differentiation without purpose.
---
 .../clang/CIR/Dialect/IR/CIRDialect.td        |   1 +
 clang/lib/CIR/CodeGen/CIRGenCall.cpp          |  52 ++++-
 clang/test/CIR/CodeGen/no-builtin-attr.cpp    | 204 ++++++++++++++++++
 .../CodeGenBuiltins/X86/avx512-reduceIntrin.c |   8 +-
 .../X86/avx512-reduceMinMaxIntrin.c           |   8 +-
 .../CodeGenBuiltins/X86/avx512fp16-builtins.c |   8 +-
 .../X86/avx512vlbf16-builtins.c               |  12 +-
 .../X86/avx512vlfp16-builtins.c               |  16 +-
 mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td   |   2 +
 mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp    |   8 +-
 .../LLVMIR/LLVMToLLVMIRTranslation.cpp        |  14 ++
 mlir/lib/Target/LLVMIR/ModuleImport.cpp       |  66 +++++-
 mlir/lib/Target/LLVMIR/ModuleTranslation.cpp  |  13 ++
 mlir/test/Dialect/LLVMIR/func.mlir            |  12 ++
 mlir/test/Dialect/LLVMIR/roundtrip.mlir       |   6 +
 .../LLVMIR/Import/function-attributes.ll      |  12 ++
 .../test/Target/LLVMIR/Import/instructions.ll |  24 +++
 mlir/test/Target/LLVMIR/llvmir.mlir           |  23 ++
 18 files changed, 450 insertions(+), 39 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/no-builtin-attr.cpp

diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
index 079b4cd87d019..bbd9831e73a50 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
@@ -60,6 +60,7 @@ def CIR_Dialect : Dialect {
     // of a [a-zA-Z0-9_] character regex(numbers only if not first), so there is
     // no way to get an underscore into this, even with escaping.
     static llvm::StringRef getModularFormatAttrName() { return "modular_format"; }
+    static llvm::StringRef getNoBuiltinsAttrName() { return "nobuiltins"; }
 
     void registerAttributes();
     void registerTypes();
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 809d775e77d55..46118300d5a9e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -111,6 +111,45 @@ static void addAttributesFromFunctionProtoType(CIRGenBuilderTy &builder,
               mlir::UnitAttr::get(builder.getContext()));
 }
 
+static void addNoBuiltinAttributes(mlir::MLIRContext &ctx,
+                                   mlir::NamedAttrList &attrs,
+                                   const LangOptions &langOpts,
+                                   const NoBuiltinAttr *nba = nullptr) {
+  // First, handle the language options passed through -fno-builtin.
+  // or, if there is a wildcard in the builtin names specified through the
+  // attribute, disable them all.
+  if (langOpts.NoBuiltin ||
+      (nba && llvm::is_contained(nba->builtinNames(), "*"))) {
+    // -fno-builtin disables them all.
+    // Empty attribute means 'all'.
+    attrs.set(cir::CIRDialect::getNoBuiltinsAttrName(),
+              mlir::ArrayAttr::get(&ctx, {}));
+    return;
+  }
+
+  llvm::SmallVector<mlir::Attribute> nbFuncs;
+  auto addNoBuiltinAttr = [&ctx, &nbFuncs](StringRef builtinName) {
+    auto attrMatches = [=](mlir::Attribute a) {
+      return mlir::cast<mlir::StringAttr>(a).getValue() == builtinName;
+    };
+    if (nbFuncs.end() == llvm::find_if(nbFuncs, attrMatches))
+      nbFuncs.push_back(mlir::StringAttr::get(&ctx, builtinName));
+  };
+
+  // Then, add attributes for builtins specified through -fno-builtin-<name>.
+  llvm::for_each(langOpts.NoBuiltinFuncs, addNoBuiltinAttr);
+
+  if (nba) {
+    // Now, let's check the __attribute__((no_builtin("...")) attribute added to
+    // the source.
+    llvm::for_each(nba->builtinNames(), addNoBuiltinAttr);
+  }
+
+  if (!nbFuncs.empty())
+    attrs.set(cir::CIRDialect::getNoBuiltinsAttrName(),
+              mlir::ArrayAttr::get(&ctx, nbFuncs));
+}
+
 /// Construct the CIR attribute list of a function or call.
 void CIRGenModule::constructAttributeList(llvm::StringRef name,
                                           const CIRGenFunctionInfo &info,
@@ -137,6 +176,13 @@ void CIRGenModule::constructAttributeList(llvm::StringRef name,
 
   const Decl *targetDecl = calleeInfo.getCalleeDecl().getDecl();
 
+  // TODO(cir): OMP Assume Attributes should be here.
+
+  const NoBuiltinAttr *nba = nullptr;
+
+  // TODO(cir): Some work for arg memory effects can be done here, as it is in
+  // classic codegen.
+
   if (targetDecl) {
     if (targetDecl->hasAttr<NoThrowAttr>())
       addUnitAttr(cir::CIRDialect::getNoThrowAttrName());
@@ -173,7 +219,7 @@ void CIRGenModule::constructAttributeList(llvm::StringRef name,
       if (!(attrOnCallSite && isVirtualCall)) {
         if (func->isNoReturn())
           addUnitAttr(cir::CIRDialect::getNoReturnAttrName());
-        // TODO(cir): Set NoBuiltinAttr here.
+        nba = func->getAttr<NoBuiltinAttr>();
       }
     }
 
@@ -230,7 +276,9 @@ void CIRGenModule::constructAttributeList(llvm::StringRef name,
                 builder.getStringAttr(llvm::join(args, ",")));
     }
 
-    // TODO(cir): We should set nobuiltin and default function attrs here.
+    addNoBuiltinAttributes(getMLIRContext(), attrs, getLangOpts(), nba);
+
+    // TODO(cir): We should set default function attrs here.
 
     // TODO(cir): There is another region of `if (targetDecl)` that handles
     // removing some attributes that are necessary modifications of the
diff --git a/clang/test/CIR/CodeGen/no-builtin-attr.cpp b/clang/test/CIR/CodeGen/no-builtin-attr.cpp
new file mode 100644
index 0000000000000..6abdb8835d5dd
--- /dev/null
+++ b/clang/test/CIR/CodeGen/no-builtin-attr.cpp
@@ -0,0 +1,204 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefixes=CIR,CIR-DEF
+//
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefixes=LLVM,LLVM-DEF
+//
+// 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,LLVM-DEF
+//
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -fno-builtin-memcmp %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefixes=CIR,CIR-SPC
+//
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm -fno-builtin-memcmp %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefixes=LLVM,LLVM-SPC
+//
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fno-builtin-memcmp %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefixes=LLVM,LLVM-SPC
+//
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -fno-builtin-memcmp -fno-builtin-memset %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefixes=CIR,CIR-BTH
+//
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm -fno-builtin-memcmp -fno-builtin-memset %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefixes=LLVM,LLVM-BTH
+//
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fno-builtin-memcmp -fno-builtin-memset %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefixes=LLVM,LLVM-BTH
+//
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -fno-builtin %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefixes=CIR,CIR-ALL
+//
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm -fno-builtin %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefixes=LLVM,LLVM-ALL
+//
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fno-builtin %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefixes=LLVM,LLVM-ALL
+
+extern "C" {
+// CIR: cir.func{{.*}}@normal() attributes {
+// CIR-DEF-NOT: nobuiltins
+// CIR-SPC-SAME: nobuiltins = ["memcmp"]
+// CIR-BTH-SAME: nobuiltins = ["memcmp", "memset"]
+// CIR-ALL-SAME: nobuiltins = []
+// LLVM: define{{.*}}normal() #[[NORM_ATTR:.*]] {
+__attribute__((cold)) // to force attributes on the call to be around.
+void normal(){}
+
+// CIR: cir.func{{.*}}@no_builtins() attributes {
+// CIR-DEF-SAME: nobuiltins = []
+// CIR-SPC-SAME: nobuiltins = []
+// CIR-BTH-SAME: nobuiltins = []
+// CIR-ALL-SAME: nobuiltins = []
+// LLVM: define{{.*}}no_builtins() #[[NB_ATTR:.*]] {
+__attribute__((no_builtin))
+__attribute__((hot)) // force unique attributes
+void no_builtins() {}
+
+// CIR: cir.func{{.*}}@no_memcpy() attributes {
+// CIR-DEF-SAME: nobuiltins = ["memcpy"]
+// CIR-SPC-SAME: nobuiltins = ["memcmp", "memcpy"]
+// CIR-BTH-SAME: nobuiltins = ["memcmp", "memset", "memcpy"]
+// CIR-ALL-SAME: nobuiltins = []
+// LLVM: define{{.*}}no_memcpy() #[[NO_MCPY_ATTR:.*]] {
+__attribute__((no_builtin("memcpy")))
+__attribute__((leaf)) // force unique attributes
+void no_memcpy() {}
+
+// CIR: cir.func{{.*}}@no_memcmp() attributes {
+// CIR-DEF-SAME: nobuiltins = ["memcmp"]
+// CIR-SPC-SAME: nobuiltins = ["memcmp"]
+// CIR-BTH-SAME: nobuiltins = ["memcmp", "memset"]
+// CIR-ALL-SAME: nobuiltins = []
+// LLVM: define{{.*}}no_memcmp() #[[NO_MCMP_ATTR:.*]] {
+__attribute__((no_builtin("memcmp")))
+__attribute__((noduplicate)) // force unique attributes
+void no_memcmp() {}
+
+// CIR: cir.func{{.*}}@no_both() attributes {
+// CIR-DEF-SAME: nobuiltins = ["memcmp", "memcpy"]
+// CIR-SPC-SAME: nobuiltins = ["memcmp", "memcpy"]
+// CIR-BTH-SAME: nobuiltins = ["memcmp", "memset", "memcpy"]
+// CIR-ALL-SAME: nobuiltins = []
+// LLVM: define{{.*}}no_both() #[[NO_BOTH_ATTR:.*]] {
+__attribute__((no_builtin("memcpy")))
+__attribute__((no_builtin("memcmp")))
+__attribute__((convergent)) // force unique attributes
+void no_both(){}
+}
+
+void caller() {
+  // CIR: cir.call @normal() {
+  // CIR-DEF-NOT: nobuiltins
+  // CIR-SPC-SAME: nobuiltins = ["memcmp"]
+  // CIR-BTH-SAME: nobuiltins = ["memcmp", "memset"]
+  // CIR-ALL-SAME: nobuiltins = []
+  // LLVM: call void @normal() #[[NORM_CALL_ATTR:.*]]
+  normal();
+  // CIR: cir.call @no_builtins() {
+  // CIR-DEF-SAME: nobuiltins = []
+  // CIR-SPC-SAME: nobuiltins = []
+  // CIR-BTH-SAME: nobuiltins = []
+  // CIR-ALL-SAME: nobuiltins = []
+  // LLVM: call void @no_builtins() #[[NB_CALL_ATTR:.*]]
+  no_builtins();
+  // CIR: cir.call @no_memcpy() {
+  // CIR-DEF-SAME: nobuiltins = ["memcpy"]
+  // CIR-SPC-SAME: nobuiltins = ["memcmp", "memcpy"]
+  // CIR-BTH-SAME: nobuiltins = ["memcmp", "memset", "memcpy"]
+  // CIR-ALL-SAME: nobuiltins = []
+  // LLVM: call void @no_memcpy() #[[NO_MCPY_CALL_ATTR:.*]]
+  no_memcpy();
+  // CIR: cir.call @no_memcmp() {
+  // CIR-DEF-SAME: nobuiltins = ["memcmp"]
+  // CIR-SPC-SAME: nobuiltins = ["memcmp"]
+  // CIR-BTH-SAME: nobuiltins = ["memcmp", "memset"]
+  // CIR-ALL-SAME: nobuiltins = []
+  // LLVM: call void @no_memcmp() #[[NO_MCMP_CALL_ATTR:.*]]
+  no_memcmp();
+  // CIR: cir.call @no_both() {
+  // CIR-DEF-SAME: nobuiltins = ["memcmp", "memcpy"]
+  // CIR-SPC-SAME: nobuiltins = ["memcmp", "memcpy"]
+  // CIR-BTH-SAME: nobuiltins = ["memcmp", "memset", "memcpy"]
+  // CIR-ALL-SAME: nobuiltins = []
+  // LLVM: call void @no_both() #[[NO_BOTH_CALL_ATTR:.*]]
+  no_both();
+}
+
+// LLVM: attributes #[[NORM_ATTR]] = {
+// LLVM-DEF-NOT: no-builtin
+// LLVM-SPC-SAME: "no-builtin-memcmp"
+// LLVM-BTH-SAME: "no-builtin-memcmp"
+// LLVM-BTH-SAME: "no-builtin-memset"
+// LLVM-ALL-SAME:"no-builtins"
+//
+// LLVM: attributes #[[NB_ATTR]] = {
+// LLVM-DEF-SAME:"no-builtins"
+// LLVM-SPC-SAME:"no-builtins"
+// LLVM-BTH-SAME:"no-builtins"
+// LLVM-ALL-SAME:"no-builtins"
+// 
+// LLVM: attributes #[[NO_MCPY_ATTR]] = {
+// LLVM-DEF-SAME: "no-builtin-memcpy"
+// LLVM-SPC-SAME: "no-builtin-memcmp"
+// LLVM-SPC-SAME: "no-builtin-memcpy"
+// LLVM-BTH-SAME: "no-builtin-memcmp"
+// LLVM-BTH-SAME: "no-builtin-memcpy"
+// LLVM-BTH-SAME: "no-builtin-memset"
+// LLVM-ALL-SAME:"no-builtins"
+//
+// LLVM: attributes #[[NO_MCMP_ATTR]] = {
+// LLVM-DEF-SAME: "no-builtin-memcmp"
+// LLVM-SPC-SAME: "no-builtin-memcmp"
+// LLVM-BTH-SAME: "no-builtin-memcmp"
+// LLVM-BTH-SAME: "no-builtin-memset"
+// LLVM-ALL-SAME:"no-builtins"
+//
+// LLVM: attributes #[[NO_BOTH_ATTR]] = {
+// LLVM-DEF-SAME: "no-builtin-memcmp"
+// LLVM-DEF-SAME: "no-builtin-memcpy"
+// LLVM-SPC-SAME: "no-builtin-memcmp"
+// LLVM-SPC-SAME: "no-builtin-memcpy"
+// LLVM-BTH-SAME: "no-builtin-memcmp"
+// LLVM-BTH-SAME: "no-builtin-memcpy"
+// LLVM-BTH-SAME: "no-builtin-memset"
+// LLVM-ALL-SAME:"no-builtins"
+//
+//
+// LLVM: attributes #[[NORM_CALL_ATTR]] = {
+// LLVM-DEF-NOT: no-builtin
+// LLVM-SPC-SAME: "no-builtin-memcmp"
+// LLVM-BTH-SAME: "no-builtin-memcmp"
+// LLVM-BTH-SAME: "no-builtin-memset"
+// LLVM-ALL-SAME:"no-builtins"
+//
+// LLVM: attributes #[[NB_CALL_ATTR]] = {
+// LLVM-DEF-SAME:"no-builtins"
+// LLVM-SPC-SAME:"no-builtins"
+// LLVM-BTH-SAME:"no-builtins"
+// LLVM-ALL-SAME:"no-builtins"
+//
+// LLVM: attributes #[[NO_MCPY_CALL_ATTR]] = {
+// LLVM-DEF-SAME: "no-builtin-memcpy"
+// LLVM-SPC-SAME: "no-builtin-memcmp"
+// LLVM-SPC-SAME: "no-builtin-memcpy"
+// LLVM-BTH-SAME: "no-builtin-memcmp"
+// LLVM-BTH-SAME: "no-builtin-memcpy"
+// LLVM-BTH-SAME: "no-builtin-memset"
+// LLVM-ALL-SAME:"no-builtins"
+//
+// LLVM: attributes #[[NO_MCMP_CALL_ATTR]] = {
+// LLVM-DEF-SAME: "no-builtin-memcmp"
+// LLVM-SPC-SAME: "no-builtin-memcmp"
+// LLVM-BTH-SAME: "no-builtin-memcmp"
+// LLVM-BTH-SAME: "no-builtin-memset"
+// LLVM-ALL-SAME:"no-builtins"
+//
+// LLVM: attributes #[[NO_BOTH_CALL_ATTR]] = {
+// LLVM-DEF-SAME: "no-builtin-memcmp"
+// LLVM-DEF-SAME: "no-builtin-memcpy"
+// LLVM-SPC-SAME: "no-builtin-memcmp"
+// LLVM-SPC-SAME: "no-builtin-memcpy"
+// LLVM-BTH-SAME: "no-builtin-memcmp"
+// LLVM-BTH-SAME: "no-builtin-memcpy"
+// LLVM-BTH-SAME: "no-builtin-memset"
+// LLVM-ALL-SAME:"no-builtins"
diff --git a/clang/test/CIR/CodeGenBuiltins/X86/avx512-reduceIntrin.c b/clang/test/CIR/CodeGenBuiltins/X86/avx512-reduceIntrin.c
index bc4249ffd25fc..e10c7cbd104c4 100644
--- a/clang/test/CIR/CodeGenBuiltins/X86/avx512-reduceIntrin.c
+++ b/clang/test/CIR/CodeGenBuiltins/X86/avx512-reduceIntrin.c
@@ -10,7 +10,7 @@ double test_mm512_reduce_add_pd(__m512d __W, double ExtraAddOp){
   // CIR: cir.call_llvm_intrinsic "vector.reduce.fadd" %[[R:.*]], %[[V:.*]] : (!cir.double, !cir.vector<8 x !cir.double>) -> !cir.double
 
   // CIR-LABEL: test_mm512_reduce_add_pd
-  // CIR: cir.call @_mm512_reduce_add_pd(%[[VEC:.*]]) : (!cir.vector<8 x !cir.double>) -> !cir.double
+  // CIR: cir.call @_mm512_reduce_add_pd(%[[VEC:.*]]) {nobuiltins = []} : (!cir.vector<8 x !cir.double>) -> !cir.double
 
   // LLVM-LABEL: test_mm512_reduce_add_pd
   // LLVM: call double @llvm.vector.reduce.fadd.v8f64(double -0.000000e+00, <8 x double> %{{.*}})
@@ -27,7 +27,7 @@ double test_mm512_reduce_mul_pd(__m512d __W, double ExtraMulOp){
   // CIR: cir.call_llvm_intrinsic "vector.reduce.fmul" %[[R:.*]], %[[V:.*]] : (!cir.double, !cir.vector<8 x !cir.double>) -> !cir.double
 
   // CIR-LABEL: test_mm512_reduce_mul_pd
-  // CIR: cir.call @_mm512_reduce_mul_pd(%[[VEC:.*]]) : (!cir.vector<8 x !cir.double>) -> !cir.double
+  // CIR: cir.call @_mm512_reduce_mul_pd(%[[VEC:.*]]) {nobuiltins = []} : (!cir.vector<8 x !cir.double>) -> !cir.double
 
   // LLVM-LABEL: test_mm512_reduce_mul_pd
   // LLVM: call double @llvm.vector.reduce.fmul.v8f64(double 1.000000e+00, <8 x double> %{{.*}})
@@ -45,7 +45,7 @@ float test_mm512_reduce_add_ps(__m512 __W){
   // CIR: cir.call_llvm_intrinsic "vector.reduce.fadd" %[[R:.*]], %[[V:.*]] : (!cir.float, !cir.vector<16 x !cir.float>) -> !cir.float
 
   // CIR-LABEL: test_mm512_reduce_add_ps
-  // CIR: cir.call @_mm512_reduce_add_ps(%[[VEC:.*]]) : (!cir.vector<16 x !cir.float>) -> !cir.float
+  // CIR: cir.call @_mm512_reduce_add_ps(%[[VEC:.*]]) {nobuiltins = []} : (!cir.vector<16 x !cir.float>) -> !cir.float
 
   // LLVM-LABEL: test_mm512_reduce_add_ps
   // LLVM: call float @llvm.vector.reduce.fadd.v16f32(float -0.000000e+00, <16 x float> %{{.*}})
@@ -60,7 +60,7 @@ float test_mm512_reduce_mul_ps(__m512 __W){
   // CIR: cir.call_llvm_intrinsic "vector.reduce.fmul" %[[R:.*]], %[[V:.*]] : (!cir.float, !cir.vector<16 x !cir.float>) -> !cir.float
 
   // CIR-LABEL: test_mm512_reduce_mul_ps
-  // CIR: cir.call @_mm512_reduce_mul_ps(%[[VEC:.*]]) : (!cir.vector<16 x !cir.float>) -> !cir.float
+  // CIR: cir.call @_mm512_reduce_mul_ps(%[[VEC:.*]]) {nobuiltins = []} : (!cir.vector<16 x !cir.float>) -> !cir.float
 
   // LLVM-LABEL: test_mm512_reduce_mul_ps
   // LLVM: call float @llvm.vector.reduce.fmul.v16f32(float 1.000000e+00, <16 x float> %{{.*}})
diff --git a/clang/test/CIR/CodeGenBuiltins/X86/avx512-reduceMinMaxIntrin.c b/clang/test/CIR/CodeGenBuiltins/X86/avx512-reduceMinMaxIntrin.c
index 104e76fa6ad03..334b41f6db526 100644
--- a/clang/test/CIR/CodeGenBuiltins/X86/avx512-reduceMinMaxIntrin.c
+++ b/clang/test/CIR/CodeGenBuiltins/X86/avx512-reduceMinMaxIntrin.c
@@ -9,7 +9,7 @@ double test_mm512_reduce_max_pd(__m512d __W, double ExtraAddOp){
   // CIR: cir.call_llvm_intrinsic "vector.reduce.fmax" %[[V:.*]] : (!cir.vector<8 x !cir.double>) -> !cir.double
 
   // CIR-LABEL: test_mm512_reduce_max_pd
-  // CIR: cir.call @_mm512_reduce_max_pd(%[[VEC:.*]]) : (!cir.vector<8 x !cir.double>) -> !cir.double
+  // CIR: cir.call @_mm512_reduce_max_pd(%[[VEC:.*]]) {nobuiltins = []} : (!cir.vector<8 x !cir.double>) -> !cir.double
 
   // LLVM-LABEL: test_mm512_reduce_max_pd
   // LLVM: call double @llvm.vector.reduce.fmax.v8f64(<8 x double> %{{.*}})
@@ -26,7 +26,7 @@ double test_mm512_reduce_min_pd(__m512d __W, double ExtraMulOp){
   // CIR: cir.call_llvm_intrinsic "vector.reduce.fmin" %[[V:.*]] : (!cir.vector<8 x !cir.double>) -> !cir.double
 
   // CIR-LABEL: test_mm512_reduce_min_pd
-  // CIR: cir.call @_mm512_reduce_min_pd(%[[VEC:.*]]) : (!cir.vector<8 x !cir.double>) -> !cir.double
+  // CIR: cir.call @_mm512_reduce_min_pd(%[[VEC:.*]]) {nobuiltins = []} : (!cir.vector<8 x !cir.double>) -> !cir.double
 
   // LLVM-LABEL: test_mm512_reduce_min_pd
   // LLVM: call double @llvm.vector.reduce.fmin.v8f64(<8 x double> %{{.*}})
@@ -43,7 +43,7 @@ float test_mm512_reduce_max_ps(__m512 __W){
   // CIR: cir.call_llvm_intrinsic "vector.reduce.fmax" %[[V:.*]] : (!cir.vector<16 x !cir.float>) -> !cir.float
 
   // CIR-LABEL: test_mm512_reduce_max_ps
-  // CIR: cir.call @_mm512_reduce_max_ps(%[[VEC:.*]]) : (!cir.vector<16 x !cir.float>) -> !cir.float
+  // CIR: cir.call @_mm512_reduce_max_ps(%[[VEC:.*]]) {nobuiltins = []} : (!cir.vector<16 x !cir.float>) -> !cir.float
 
   // LLVM-LABEL: test_mm512_reduce_max_ps
   // LLVM: call float @llvm.vector.reduce.fmax.v16f32(<16 x float> %{{.*}})
@@ -58,7 +58,7 @@ float test_mm512_reduce_min_ps(__m512 __W){
   // CIR: cir.call_llvm_intrinsic "vector.reduce.fmin" %[[V:.*]] : (!cir.vector<16 x !cir.float>) -> !cir.float
 
   // CIR-LABEL: test_mm512_reduce_min_ps
-  // CIR: cir.call @_mm512_reduce_min_ps(%[[VEC:.*]]) : (!cir.vector<16 x !cir.float>) -> !cir.float
+  // CIR: cir.call @_mm512_reduce_min_ps(%[[VEC:.*]]) {nobuiltins = []} : (!cir.vector<16 x !cir.float>) -> !cir.float
 
   // LLVM-LABEL: test_mm512_reduce_min_ps
   // LLVM: call float @llvm.vector.reduce.fmin.v16f32(<16 x float> %{{.*}})
diff --git a/clang/test/CIR/CodeGenBuiltins/X86/avx512fp16-builtins.c b/clang/test/CIR/CodeGenBuiltins/X86/avx512fp16-builtins.c
index 74e40c0d5a76e..92bb9cf0dc02a 100644
--- a/clang/test/CIR/CodeGenBuiltins/X86/avx512fp16-builtins.c
+++ b/clang/test/CIR/CodeGenBuiltins/X86/avx512fp16-builtins.c
@@ -70,7 +70,7 @@ _Float16 test_mm512_reduce_add_ph(__m512h __W) {
   // CIR: cir.call_llvm_intrinsic "vector.reduce.fadd" %[[R:.*]], %[[V:.*]] : (!cir.f16, !cir.vector<32 x !cir.f16>) -> !cir.f16
 
   // CIR-LABEL: test_mm512_reduce_add_ph
-  // CIR: cir.call @_mm512_reduce_add_ph(%[[VEC:.*]]) : (!cir.vector<32 x !cir.f16>) -> !cir.f16
+  // CIR: cir.call @_mm512_reduce_add_ph(%[[VEC:.*]]) {nobuiltins = []} : (!cir.vector<32 x !cir.f16>) -> !cir.f16
 
   // LLVM-LABEL: test_mm512_reduce_add_ph
   // LLVM: call half @llvm.vector.reduce.fadd.v32f16(half 0xH8000, <32 x half> %{{.*}})
@@ -85,7 +85,7 @@ _Float16 test_mm512_reduce_mul_ph(__m512h __W) {
   // CIR: cir.call_llvm_intrinsic "vector.reduce.fmul" %[[R:.*]], %[[V:.*]] : (!cir.f16, !cir.vector<32 x !cir.f16>) -> !cir.f16
 
   // CIR-LABEL: test_mm512_reduce_mul_ph
-  // CIR: cir.call @_mm512_reduce_mul_ph(%[[VEC:.*]]) : (!cir.vector<32 x !cir.f16>) -> !cir.f16
+  // CIR: cir.call @_mm512_reduce_mul_ph(%[[VEC:.*]]) {nobuiltins = []} : (!cir.vector<32 x !cir.f16>) -> !cir.f16
 
   // LLVM-LABEL: test_mm512_reduce_mul_ph
   // LLVM: call half @llvm.vector.reduce.fmul.v32f16(half 0xH3C00, <32 x half> %{{.*}})
@@ -100,7 +100,7 @@ _Float16 test_mm512_reduce_max_ph(__m512h __W) {
   // CIR: cir.call_llvm_intrinsic "vector.reduce.fmax" %[[V:.*]] (!cir.vector<32 x !cir.f16>) -> !cir.f16 
 
   // CIR-LABEL: test_mm512_reduce_max_ph
-  // CIR: cir.call @_mm512_reduce_max_ph(%[[VEC:.*]]) : (!cir.vector<32 x !cir.f16>) -> !cir.f16
+  // CIR: cir.call @_mm512_reduce_max_ph(%[[VEC:.*]]) {nobuiltins = []} : (!cir.vector<32 x !cir.f16>) -> !cir.f16
 
   // LLVM-LABEL: test_mm512_reduce_max_ph
   // LLVM: call half @llvm.vector.reduce.fmax.v32f16(<32 x half> %{{.*}})
@@ -115,7 +115,7 @@ _Float16 test_mm512_reduce_min_ph(__m512h __W) {
   // CIR: cir.call_llvm_intrinsic "vector.reduce.fmin" %[[V:.*]] (!cir.vector<32 x !cir.f16>) -> !cir.f16 
 
   // CIR-LABEL: test_mm512_reduce_min_ph
-  // CIR: cir.call @_mm512_reduce_min_ph(%[[VEC:.*]]) : (!cir.vector<32 x !cir.f16>) -> !cir.f16
+  // CIR: cir.call @_mm512_reduce_min_ph(%[[VEC:.*]]) {nobuiltins = []} : (!cir.vector<32 x !cir.f16>) -> !cir.f16
 
   // LLVM-LABEL: test_mm512_reduce_min_ph
   // LLVM: call half @llvm.vector.reduce.fmin.v32f16(<32 x half> %{{.*}})
diff --git a/clang/test/CIR/CodeGenBuiltins/X86/avx512vlbf16-builtins.c b/clang/test/CIR/CodeGenBuiltins/X86/avx512vlbf16-builtins.c
index d1e9a030e637c..c6356263b96c1 100644
--- a/clang/test/CIR/CodeGenBuiltins/X86/avx512vlbf16-builtins.c
+++ b/clang/test/CIR/CodeGenBuiltins/X86/avx512vlbf16-builtins.c
@@ -9,7 +9,7 @@
 
 __m256bh test_mm512_mask_cvtneps_pbh(__m256bh src, __mmask16 k, __m512 a) {
   // CIR-LABEL: test_mm512_mask_cvtneps_pbh
-  // CIR: cir.call @_mm512_mask_cvtneps_pbh({{.+}}, {{.+}}, {{.+}}) : (!cir.vector<16 x !cir.bf16>, !u16i, !cir.vector<16 x !cir.float>) -> !cir.vector<16 x !cir.bf16>
+  // CIR: cir.call @_mm512_mask_cvtneps_pbh({{.+}}, {{.+}}, {{.+}}) {nobuiltins = []} : (!cir.vector<16 x !cir.bf16>, !u16i, !cir.vector<16 x !cir.float>) -> !cir.vector<16 x !cir.bf16>
 
   // LLVM-LABEL: @test_mm512_mask_cvtneps_pbh
   // LLVM: call <16 x bfloat> @llvm.x86.avx512bf16.cvtneps2bf16.512
@@ -21,7 +21,7 @@ __m256bh test_mm512_mask_cvtneps_pbh(__m256bh src, __mmask16 k, __m512 a) {
 
 __m256bh test_mm512_maskz_cvtneps_pbh(__mmask16 k, __m512 a) {
   // CIR-LABEL: test_mm512_maskz_cvtneps_pbh
-  // CIR: cir.call @_mm512_maskz_cvtneps_pbh({{.+}}, {{.+}}) : (!u16i, !cir.vector<16 x !cir.float>) -> !cir.vector<16 x !cir.bf16>
+  // CIR: cir.call @_mm512_maskz_cvtneps_pbh({{.+}}, {{.+}}) {nobuiltins = []} : (!u16i, !cir.vector<16 x !cir.float>) -> !cir.vector<16 x !cir.bf16>
 
   // LLVM-LABEL: @test_mm512_maskz_cvtneps_pbh
   // LLVM: call <16 x bfloat> @llvm.x86.avx512bf16.cvtneps2bf16.512(<16 x float> {{.+}})
@@ -34,7 +34,7 @@ __m256bh test_mm512_maskz_cvtneps_pbh(__mmask16 k, __m512 a) {
 
 __m128bh test_mm256_mask_cvtneps_pbh(__m128bh src, __mmask8 k, __m256 a) {
   // CIR-LABEL: test_mm256_mask_cvtneps_pbh
-  // CIR: cir.call @_mm256_mask_cvtneps_pbh({{.+}}, {{.+}}, {{.+}}) : (!cir.vector<8 x !cir.bf16>, !u8i, !cir.vector<8 x !cir.float>) -> !cir.vector<8 x !cir.bf16>
+  // CIR: cir.call @_mm256_mask_cvtneps_pbh({{.+}}, {{.+}}, {{.+}}) {nobuiltins = []} : (!cir.vector<8 x !cir.bf16>, !u8i, !cir.vector<8 x !cir.float>) -> !cir.vector<8 x !cir.bf16>
   
   // LLVM-LABEL: @test_mm256_mask_cvtneps_pbh
   // LLVM: call <8 x bfloat> @llvm.x86.avx512bf16.cvtneps2bf16.256(<8 x float> {{.+}})
@@ -46,7 +46,7 @@ __m128bh test_mm256_mask_cvtneps_pbh(__m128bh src, __mmask8 k, __m256 a) {
 
 __m128bh test_mm256_maskz_cvtneps_pbh(__mmask8 k, __m256 a) {
   // CIR-LABEL: test_mm256_maskz_cvtneps_pbh
-  // CIR: cir.call @_mm256_maskz_cvtneps_pbh({{.+}}, {{.+}}) : (!u8i, !cir.vector<8 x !cir.float>) -> !cir.vector<8 x !cir.bf16>
+  // CIR: cir.call @_mm256_maskz_cvtneps_pbh({{.+}}, {{.+}}) {nobuiltins = []} : (!u8i, !cir.vector<8 x !cir.float>) -> !cir.vector<8 x !cir.bf16>
 
   // LLVM-LABEL: @test_mm256_maskz_cvtneps_pbh
   // LLVM: call <8 x bfloat> @llvm.x86.avx512bf16.cvtneps2bf16.256(<8 x float> {{.+}})
@@ -58,7 +58,7 @@ __m128bh test_mm256_maskz_cvtneps_pbh(__mmask8 k, __m256 a) {
 
 __m128bh test_mm_mask_cvtneps_pbh(__m128bh src, __mmask8 k, __m128 a) {
   // CIR-LABEL: test_mm_mask_cvtneps_pbh
-  // CIR: cir.call @_mm_mask_cvtneps_pbh({{.+}}, {{.+}}, {{.+}}) : (!cir.vector<8 x !cir.bf16>, !u8i, !cir.vector<4 x !cir.float>) -> !cir.vector<8 x !cir.bf16>{{.+}}
+  // CIR: cir.call @_mm_mask_cvtneps_pbh({{.+}}, {{.+}}, {{.+}}) {nobuiltins = []} : (!cir.vector<8 x !cir.bf16>, !u8i, !cir.vector<4 x !cir.float>) -> !cir.vector<8 x !cir.bf16>{{.+}}
 
   // LLVM-LABEL: @test_mm_mask_cvtneps_pbh
   // LLVM: call <8 x bfloat> @llvm.x86.avx512bf16.mask.cvtneps2bf16.128(<4 x float> {{.+}}, <8 x bfloat> {{.+}}, <4 x i1> {{.+}})
@@ -70,7 +70,7 @@ __m128bh test_mm_mask_cvtneps_pbh(__m128bh src, __mmask8 k, __m128 a) {
 
 __m128bh test_mm_maskz_cvtneps_pbh(__mmask8 k, __m128 a) {
   // CIR-LABEL: test_mm_maskz_cvtneps_pbh
-  // CIR: cir.call @_mm_maskz_cvtneps_pbh({{.+}}, {{.+}}) : (!u8i, !cir.vector<4 x !cir.float>) -> !cir.vector<8 x !cir.bf16>
+  // CIR: cir.call @_mm_maskz_cvtneps_pbh({{.+}}, {{.+}}) {nobuiltins = []} : (!u8i, !cir.vector<4 x !cir.float>) -> !cir.vector<8 x !cir.bf16>
   
   // LLVM-LABEL: @test_mm_maskz_cvtneps_pbh
   // LLVM: call <8 x bfloat> @llvm.x86.avx512bf16.mask.cvtneps2bf16.128(<4 x float> {{.+}}, <8 x bfloat> {{.+}}, <4 x i1> {{.+}})
diff --git a/clang/test/CIR/CodeGenBuiltins/X86/avx512vlfp16-builtins.c b/clang/test/CIR/CodeGenBuiltins/X86/avx512vlfp16-builtins.c
index 994fdfec23c2c..995521f686d9b 100644
--- a/clang/test/CIR/CodeGenBuiltins/X86/avx512vlfp16-builtins.c
+++ b/clang/test/CIR/CodeGenBuiltins/X86/avx512vlfp16-builtins.c
@@ -12,7 +12,7 @@ _Float16 test_mm256_reduce_add_ph(__m256h __W) {
   // CIR: cir.call_llvm_intrinsic "vector.reduce.fadd" %[[R:.*]], %[[V:.*]] : (!cir.f16, !cir.vector<16 x !cir.f16>) -> !cir.f16
 
   // CIR-LABEL: test_mm256_reduce_add_ph
-  // CIR: cir.call @_mm256_reduce_add_ph(%[[VEC:.*]]) : (!cir.vector<16 x !cir.f16>) -> !cir.f16
+  // CIR: cir.call @_mm256_reduce_add_ph(%[[VEC:.*]]) {nobuiltins = []} : (!cir.vector<16 x !cir.f16>) -> !cir.f16
 
   // LLVM-LABEL: test_mm256_reduce_add_ph
   // LLVM: call half @llvm.vector.reduce.fadd.v16f16(half 0xH8000, <16 x half> %{{.*}})
@@ -27,7 +27,7 @@ _Float16 test_mm256_reduce_mul_ph(__m256h __W) {
   // CIR: cir.call_llvm_intrinsic "vector.reduce.fmul" %[[R:.*]], %[[V:.*]] : (!cir.f16, !cir.vector<16 x !cir.f16>) -> !cir.f16
 
   // CIR-LABEL: test_mm256_reduce_mul_ph
-  // CIR: cir.call @_mm256_reduce_mul_ph(%[[VEC:.*]]) : (!cir.vector<16 x !cir.f16>) -> !cir.f16
+  // CIR: cir.call @_mm256_reduce_mul_ph(%[[VEC:.*]]) {nobuiltins = []} : (!cir.vector<16 x !cir.f16>) -> !cir.f16
 
   // LLVM-LABEL: test_mm256_reduce_mul_ph
   // LLVM: call half @llvm.vector.reduce.fmul.v16f16(half 0xH3C00, <16 x half> %{{.*}})
@@ -42,7 +42,7 @@ _Float16 test_mm256_reduce_max_ph(__m256h __W) {
   // CIR: cir.call_llvm_intrinsic "vector.reduce.fmax" %[[V:.*]] (!cir.vector<16 x !cir.f16>) -> !cir.f16 
 
   // CIR-LABEL: test_mm256_reduce_max_ph
-  // CIR: cir.call @_mm256_reduce_max_ph(%[[VEC:.*]]) : (!cir.vector<16 x !cir.f16>) -> !cir.f16
+  // CIR: cir.call @_mm256_reduce_max_ph(%[[VEC:.*]]) {nobuiltins = []} : (!cir.vector<16 x !cir.f16>) -> !cir.f16
 
   // LLVM-LABEL: test_mm256_reduce_max_ph
   // LLVM: call half @llvm.vector.reduce.fmax.v16f16(<16 x half> %{{.*}})
@@ -57,7 +57,7 @@ _Float16 test_mm256_reduce_min_ph(__m256h __W) {
   // CIR: cir.call_llvm_intrinsic "vector.reduce.fmin" %[[V:.*]] : (!cir.vector<16 x !cir.f16>) -> !cir.f16
 
   // CIR-LABEL: test_mm256_reduce_min_ph
-  // CIR: cir.call @_mm256_reduce_min_ph(%[[VEC:.*]]) : (!cir.vector<16 x !cir.f16>) -> !cir.f16
+  // CIR: cir.call @_mm256_reduce_min_ph(%[[VEC:.*]]) {nobuiltins = []} : (!cir.vector<16 x !cir.f16>) -> !cir.f16
 
   // LLVM-LABEL: test_mm256_reduce_min_ph
   // LLVM: call half @llvm.vector.reduce.fmin.v16f16(<16 x half> %{{.*}})
@@ -72,7 +72,7 @@ _Float16 test_mm_reduce_add_ph(__m128h __W) {
   // CIR: cir.call_llvm_intrinsic "vector.reduce.fadd" %[[R:.*]], %[[V:.*]] : (!cir.f16, !cir.vector<8 x !cir.f16>) -> !cir.f16
 
   // CIR-LABEL: test_mm_reduce_add_ph
-  // CIR: cir.call @_mm_reduce_add_ph(%[[VEC:.*]]) : (!cir.vector<8 x !cir.f16>) -> !cir.f16
+  // CIR: cir.call @_mm_reduce_add_ph(%[[VEC:.*]]) {nobuiltins = []} : (!cir.vector<8 x !cir.f16>) -> !cir.f16
 
   // LLVM-LABEL: test_mm_reduce_add_ph
   // LLVM: call half @llvm.vector.reduce.fadd.v8f16(half 0xH8000, <8 x half> %{{.*}})
@@ -87,7 +87,7 @@ _Float16 test_mm_reduce_mul_ph(__m128h __W) {
   // CIR: cir.call_llvm_intrinsic "vector.reduce.fmul" %[[R:.*]], %[[V:.*]] : (!cir.f16, !cir.vector<8 x !cir.f16>) -> !cir.f16
 
   // CIR-LABEL: test_mm_reduce_mul_ph
-  // CIR: cir.call @_mm_reduce_mul_ph(%[[VEC:.*]]) : (!cir.vector<8 x !cir.f16>) -> !cir.f16
+  // CIR: cir.call @_mm_reduce_mul_ph(%[[VEC:.*]]) {nobuiltins = []} : (!cir.vector<8 x !cir.f16>) -> !cir.f16
 
   // LLVM-LABEL: test_mm_reduce_mul_ph
   // LLVM: call half @llvm.vector.reduce.fmul.v8f16(half 0xH3C00, <8 x half> %{{.*}})
@@ -102,7 +102,7 @@ _Float16 test_mm_reduce_max_ph(__m128h __W) {
   // CIR: cir.call_llvm_intrinsic "vector.reduce.fmax" %[[V:.*]] (!cir.vector<8 x !cir.f16>) -> !cir.f16 
 
   // CIR-LABEL: test_mm_reduce_max_ph
-  // CIR: cir.call @_mm_reduce_max_ph(%[[VEC:.*]]) : (!cir.vector<8 x !cir.f16>) -> !cir.f16
+  // CIR: cir.call @_mm_reduce_max_ph(%[[VEC:.*]]) {nobuiltins = []} : (!cir.vector<8 x !cir.f16>) -> !cir.f16
 
   // LLVM-LABEL: test_mm_reduce_max_ph
   // LLVM: call half @llvm.vector.reduce.fmax.v8f16(<8 x half> %{{.*}})
@@ -117,7 +117,7 @@ _Float16 test_mm_reduce_min_ph(__m128h __W) {
   // CIR: cir.call_llvm_intrinsic "vector.reduce.fmin" %[[V:.*]] : (!cir.vector<8 x !cir.f16>) -> !cir.f16
 
   // CIR-LABEL: test_mm_reduce_min_ph
-  // CIR: cir.call @_mm_reduce_min_ph(%[[VEC:.*]]) : (!cir.vector<8 x !cir.f16>) -> !cir.f16
+  // CIR: cir.call @_mm_reduce_min_ph(%[[VEC:.*]]) {nobuiltins = []} : (!cir.vector<8 x !cir.f16>) -> !cir.f16
 
   // LLVM-LABEL: test_mm_reduce_min_ph
   // LLVM: call half @llvm.vector.reduce.fmin.v8f16(<8 x half> %{{.*}})
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index 3bf4875678a9d..e2358dcf1ed4c 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -799,6 +799,7 @@ def LLVM_CallOp
       UnitAttr:$cold, UnitAttr:$noduplicate,
       UnitAttr:$no_caller_saved_registers, UnitAttr:$nocallback,
       OptionalAttr<StrAttr>:$modular_format,
+      OptionalAttr<ArrayAttr>:$nobuiltins,
       VariadicOfVariadic<LLVM_Type, "op_bundle_sizes">:$op_bundle_operands,
       DenseI32ArrayAttr:$op_bundle_sizes,
       OptionalAttr<ArrayAttr>:$op_bundle_tags,
@@ -2005,6 +2006,7 @@ def LLVM_LLVMFuncOp : LLVM_Op<"func", [
     OptionalAttr<UnitAttr>:$no_caller_saved_registers,
     OptionalAttr<UnitAttr>:$nocallback,
     OptionalAttr<StrAttr>:$modular_format,
+    OptionalAttr<ArrayAttr>:$nobuiltins,
     OptionalAttr<LLVM_VecTypeHintAttr>:$vec_type_hint,
     OptionalAttr<DenseI32ArrayAttr>:$work_group_size_hint,
     OptionalAttr<DenseI32ArrayAttr>:$reqd_work_group_size,
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index fa7e9e53cfec7..34d0de078e553 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -998,7 +998,7 @@ void CallOp::build(OpBuilder &builder, OperationState &state, TypeRange results,
         /*noreturn=*/nullptr, /*returns_twice=*/nullptr, /*hot=*/nullptr,
         /*cold=*/nullptr, /*noduplicate=*/nullptr,
         /*no_caller_saved_registers=*/nullptr, /*nocallback=*/nullptr,
-        /*modular_format=*/nullptr,
+        /*modular_format=*/nullptr, /*nobuiltins=*/nullptr,
         /*op_bundle_operands=*/{}, /*op_bundle_tags=*/{},
         /*arg_attrs=*/nullptr, /*res_attrs=*/nullptr,
         /*access_groups=*/nullptr, /*alias_scopes=*/nullptr,
@@ -1033,7 +1033,7 @@ void CallOp::build(OpBuilder &builder, OperationState &state,
         /*returns_twice=*/nullptr, /*hot=*/nullptr,
         /*cold=*/nullptr, /*noduplicate=*/nullptr,
         /*no_caller_saved_registers=*/nullptr, /*nocallback=*/nullptr,
-        /*modular_format=*/nullptr,
+        /*modular_format=*/nullptr, /*nobuiltins=*/nullptr,
         /*op_bundle_operands=*/{}, /*op_bundle_tags=*/{},
         /*arg_attrs=*/nullptr, /*res_attrs=*/nullptr,
         /*access_groups=*/nullptr,
@@ -1054,7 +1054,7 @@ void CallOp::build(OpBuilder &builder, OperationState &state,
         /*returns_twice=*/nullptr, /*hot=*/nullptr,
         /*cold=*/nullptr, /*noduplicate=*/nullptr,
         /*no_caller_saved_registers=*/nullptr, /*nocallback=*/nullptr,
-        /*modular_format=*/nullptr,
+        /*modular_format=*/nullptr, /*nobuiltins=*/nullptr,
         /*op_bundle_operands=*/{}, /*op_bundle_tags=*/{},
         /*arg_attrs=*/nullptr, /*res_attrs=*/nullptr,
         /*access_groups=*/nullptr, /*alias_scopes=*/nullptr,
@@ -1075,7 +1075,7 @@ void CallOp::build(OpBuilder &builder, OperationState &state, LLVMFuncOp func,
         /*returns_twice=*/nullptr, /*hot=*/nullptr,
         /*cold=*/nullptr, /*noduplicate=*/nullptr,
         /*no_caller_saved_registers=*/nullptr, /*nocallback=*/nullptr,
-        /*modular_format=*/nullptr,
+        /*modular_format=*/nullptr, /*nobuiltins=*/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 e32719f10d7c1..8cb4528a2ab44 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
@@ -447,6 +447,20 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
                                            "modular-format",
                                            modFormat.getValue()));
 
+    if (ArrayAttr noBuiltins = callOp.getNobuiltinsAttr()) {
+      if (noBuiltins.empty())
+        call->addFnAttr(llvm::Attribute::get(moduleTranslation.getLLVMContext(),
+                                             "no-builtins"));
+
+      for (Attribute a : noBuiltins) {
+        if (auto str = dyn_cast<StringAttr>(a)) {
+          std::string attrName = ("no-builtin-" + str.getValue()).str();
+          call->addFnAttr(llvm::Attribute::get(
+              moduleTranslation.getLLVMContext(), attrName));
+        }
+      }
+    }
+
     if (failed(moduleTranslation.convertArgAndResultAttrs(callOp, call)))
       return failure();
 
diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index deaeb98d9abdc..d666434f61f41 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -1419,10 +1419,10 @@ LogicalResult ModuleImport::convertIFunc(llvm::GlobalIFunc *ifunc) {
 /// Converts LLVM string, integer, and enum attributes into MLIR attributes,
 /// skipping those in `attributesToSkip` and emitting a warning at `loc` for
 /// any other unsupported attributes.
-static ArrayAttr
-convertLLVMAttributesToMLIR(Location loc, MLIRContext *context,
-                            llvm::AttributeSet attributes,
-                            ArrayRef<StringLiteral> attributesToSkip = {}) {
+static ArrayAttr convertLLVMAttributesToMLIR(
+    Location loc, MLIRContext *context, llvm::AttributeSet attributes,
+    ArrayRef<StringLiteral> attributesToSkip = {},
+    ArrayRef<StringLiteral> attributePrefixesToSkip = {}) {
   SmallVector<Attribute> mlirAttributes;
   for (llvm::Attribute attr : attributes) {
     StringRef attrName;
@@ -1433,6 +1433,13 @@ convertLLVMAttributesToMLIR(Location loc, MLIRContext *context,
     if (llvm::is_contained(attributesToSkip, attrName))
       continue;
 
+    auto attrNameStartsWith = [attrName](StringLiteral sl) {
+      return attrName.starts_with(sl);
+    };
+    if (attributePrefixesToSkip.end() !=
+        llvm::find_if(attributePrefixesToSkip, attrNameStartsWith))
+      continue;
+
     auto keyAttr = StringAttr::get(context, attrName);
     if (attr.isStringAttribute()) {
       StringRef val = attr.getValueAsString();
@@ -2669,6 +2676,7 @@ static constexpr std::array kExplicitLLVMFuncOpAttributes{
     StringLiteral("no-infs-fp-math"),
     StringLiteral("no-nans-fp-math"),
     StringLiteral("no-signed-zeros-fp-math"),
+    StringLiteral("no-builtins"),
     StringLiteral("nocallback"),
     StringLiteral("noduplicate"),
     StringLiteral("noinline"),
@@ -2683,15 +2691,55 @@ static constexpr std::array kExplicitLLVMFuncOpAttributes{
     StringLiteral("willreturn"),
 };
 
+// List of LLVM IR attributes that are handled by prefix to map onto an MLIR
+// LLVMFuncOp.
+static constexpr std::array kExplicitLLVMFuncOpAttributePrefixes{
+    StringLiteral("no-builtin-"),
+};
+
+template <typename OpTy>
+static void convertNoBuiltinAttrs(MLIRContext *ctx,
+                                  const llvm::AttributeSet &attrs,
+                                  OpTy target) {
+  // 'no-builtins' is the complete collection, and overrides all the rest.
+  if (attrs.hasAttribute("no-builtins")) {
+    target.setNobuiltinsAttr(mlir::ArrayAttr::get(ctx, {}));
+    return;
+  }
+
+  llvm::SmallVector<mlir::Attribute> nbAttrs;
+  for (llvm::Attribute attr : attrs) {
+    // Attributes that are part of llvm directly (that is, have an AttributeKind
+    // in the enum) shouldn't be checked.
+    if (attr.hasKindAsEnum())
+      continue;
+
+    StringRef val = attr.getKindAsString();
+
+    if (val.starts_with("no-builtin-")) {
+      StringRef str = val.drop_front(sizeof("no-builtin-") - 1);
+
+      if (nbAttrs.end() == llvm::find_if(nbAttrs, [str](Attribute a) {
+            return mlir::cast<StringAttr>(a).getValue() == str;
+          }))
+        nbAttrs.push_back(mlir::StringAttr::get(
+            ctx, val.drop_front(sizeof("no-builtin-") - 1)));
+    }
+  }
+
+  if (!nbAttrs.empty())
+    target.setNobuiltinsAttr(mlir::ArrayAttr::get(ctx, nbAttrs));
+}
+
 /// Converts LLVM attributes from `func` into MLIR attributes and adds them
 /// to `funcOp` as passthrough attributes, skipping those listed in
 /// `kExplicitLLVMFuncAttributes`.
 static void processPassthroughAttrs(llvm::Function *func, LLVMFuncOp funcOp) {
   llvm::AttributeSet funcAttrs = func->getAttributes().getAttributes(
       llvm::AttributeList::AttrIndex::FunctionIndex);
-  ArrayAttr passthroughAttr =
-      convertLLVMAttributesToMLIR(funcOp.getLoc(), funcOp.getContext(),
-                                  funcAttrs, kExplicitLLVMFuncOpAttributes);
+  ArrayAttr passthroughAttr = convertLLVMAttributesToMLIR(
+      funcOp.getLoc(), funcOp.getContext(), funcAttrs,
+      kExplicitLLVMFuncOpAttributes, kExplicitLLVMFuncOpAttributePrefixes);
   if (!passthroughAttr.empty())
     funcOp.setPassthroughAttr(passthroughAttr);
 }
@@ -2751,6 +2799,8 @@ void ModuleImport::processFunctionAttributes(llvm::Function *func,
   else if (func->hasFnAttribute("aarch64_preserves_za"))
     funcOp.setArmPreservesZa(true);
 
+  convertNoBuiltinAttrs(context, func->getAttributes().getFnAttrs(), funcOp);
+
   llvm::Attribute attr = func->getFnAttribute(llvm::Attribute::VScaleRange);
   if (attr.isValid()) {
     MLIRContext *context = funcOp.getContext();
@@ -2991,6 +3041,8 @@ LogicalResult ModuleImport::convertCallAttributes(llvm::CallInst *inst,
   if (!memAttr.isReadWrite())
     op.setMemoryEffectsAttr(memAttr);
 
+  convertNoBuiltinAttrs(op.getContext(), callAttrs.getFnAttrs(), op);
+
   return convertCallBaseAttributes(inst, op);
 }
 
diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
index 437701a48e460..4b6b1b70a7f9e 100644
--- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
@@ -1700,6 +1700,19 @@ static void convertFunctionAttributes(LLVMFuncOp func,
   if (UWTableKindAttr uwTableKindAttr = func.getUwtableKindAttr())
     llvmFunc->setUWTableKind(
         convertUWTableKindToLLVM(uwTableKindAttr.getUwtableKind()));
+
+  if (ArrayAttr noBuiltins = func.getNobuiltinsAttr()) {
+    if (noBuiltins.empty())
+      llvmFunc->addFnAttr("no-builtins");
+
+    for (Attribute a : noBuiltins) {
+      if (auto str = dyn_cast<StringAttr>(a)) {
+        std::string attrName = ("no-builtin-" + str.getValue()).str();
+        llvmFunc->addFnAttr(attrName);
+      }
+    }
+  }
+
   convertFunctionMemoryAttributes(func, llvmFunc);
 }
 
diff --git a/mlir/test/Dialect/LLVMIR/func.mlir b/mlir/test/Dialect/LLVMIR/func.mlir
index e57a54cb43267..2f1bd0eb96910 100644
--- a/mlir/test/Dialect/LLVMIR/func.mlir
+++ b/mlir/test/Dialect/LLVMIR/func.mlir
@@ -372,6 +372,18 @@ module {
     llvm.return
   }
 
+  llvm.func @no_builtins_all() attributes { nobuiltins = [] } {
+    // CHECK: @no_builtins_all
+    // CHECK-SAME: attributes {nobuiltins = []}
+    llvm.return
+  }
+
+  llvm.func @no_builtins_2() attributes { nobuiltins = ["foo", "bar"] } {
+    // CHECK: @no_builtins_2
+    // CHECK-SAME: attributes {nobuiltins = ["foo", "bar"]}
+    llvm.return
+  }
+
 }
 
 // -----
diff --git a/mlir/test/Dialect/LLVMIR/roundtrip.mlir b/mlir/test/Dialect/LLVMIR/roundtrip.mlir
index c97574f41e8a4..2d1a383274e97 100644
--- a/mlir/test/Dialect/LLVMIR/roundtrip.mlir
+++ b/mlir/test/Dialect/LLVMIR/roundtrip.mlir
@@ -149,6 +149,12 @@ func.func @ops(%arg0: i32, %arg1: f32,
 // 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>} : () -> ()
 
+// CHECK: llvm.call @baz() {nobuiltins = []} : () -> ()
+  llvm.call @baz() {nobuiltins = []} : () -> ()
+
+// CHECK: llvm.call @baz() {nobuiltins = ["asdf", "defg"]} : () -> ()
+  llvm.call @baz() {nobuiltins = ["asdf", "defg"]} : () -> ()
+
 // Terminator operations and their successors.
 //
 // CHECK: llvm.br ^[[BB1:.*]]
diff --git a/mlir/test/Target/LLVMIR/Import/function-attributes.ll b/mlir/test/Target/LLVMIR/Import/function-attributes.ll
index 153912fbae34a..f4a060280a95c 100644
--- a/mlir/test/Target/LLVMIR/Import/function-attributes.ll
+++ b/mlir/test/Target/LLVMIR/Import/function-attributes.ll
@@ -459,5 +459,17 @@ declare void @modular_format_attribute(i32) "modular-format" = "Ident,1,1,Foo,Ba
 
 // -----
 
+; CHECK-LABEL: @no_builtins_all
+; CHECK-SAME: attributes {nobuiltins = []}
+declare void @no_builtins_all() "no-builtins"
+
+// -----
+
+; CHECK-LABEL: @no_builtins_2
+; CHECK-SAME: attributes {nobuiltins = ["asdf", "defg"]}
+declare void @no_builtins_2() "no-builtin-asdf" "no-builtin-defg"
+
+// -----
+
 ; 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 9b3ad17c31a28..cf3962a92c46c 100644
--- a/mlir/test/Target/LLVMIR/Import/instructions.ll
+++ b/mlir/test/Target/LLVMIR/Import/instructions.ll
@@ -798,6 +798,30 @@ define void @call_modular_format() {
 ; CHECK: llvm.func @f()
 declare void @f()
 
+; CHECK-LABEL: @call_nobuiltins_all
+define void @call_nobuiltins_all() {
+; CHECK: llvm.call @f() {nobuiltins = []}
+  call void @f() "no-builtins"
+  ret void
+}
+
+; // -----
+
+; CHECK: llvm.func @f()
+declare void @f()
+
+; CHECK-LABEL: @call_nobuiltins_2
+define void @call_nobuiltins_2() {
+; CHECK: llvm.call @f() {nobuiltins = ["asdf", "ghij"]}
+  call void @f() "no-builtin-asdf" "no-builtin-ghij"
+  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 978199fbfb1a1..da5328af12773 100644
--- a/mlir/test/Target/LLVMIR/llvmir.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir.mlir
@@ -2719,6 +2719,29 @@ llvm.func @modular_format(%arg : i32) attributes { modular_format = "ident,1,1,f
 
 // -----
 
+// CHECK-LABEL: @no_builtins_all
+// CHECK-SAME: #[[ATTRS:[0-9]+]]
+llvm.func @no_builtins_all(%arg : i32) attributes { nobuiltins = [] } {
+  llvm.return
+}
+
+// CHECK: #[[ATTRS]]
+// CHECK-SAME: no-builtins
+
+// -----
+
+// CHECK-LABEL: @no_builtins_2
+// CHECK-SAME: #[[ATTRS:[0-9]+]]
+llvm.func @no_builtins_2(%arg : i32) attributes { nobuiltins = ["asdf", "defg"] } {
+  llvm.return
+}
+
+// CHECK: #[[ATTRS]]
+// CHECK-SAME: no-builtin-asdf
+// CHECK-SAME: no-builtin-defg
+
+// -----
+
 llvm.func @f()
 
 // CHECK-LABEL: @convergent_call

>From b3db197bbb4364f8a44d8274dd6b645d12fe8886 Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Fri, 30 Jan 2026 06:58:43 -0800
Subject: [PATCH 2/2] Fix scope of targetDecl to better match classic codegen

---
 clang/lib/CIR/CodeGen/CIRGenCall.cpp | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 46118300d5a9e..d5195d936f969 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -275,15 +275,15 @@ void CIRGenModule::constructAttributeList(llvm::StringRef name,
       attrs.set(cir::CIRDialect::getModularFormatAttrName(),
                 builder.getStringAttr(llvm::join(args, ",")));
     }
+  }
 
-    addNoBuiltinAttributes(getMLIRContext(), attrs, getLangOpts(), nba);
+  addNoBuiltinAttributes(getMLIRContext(), attrs, getLangOpts(), nba);
 
-    // TODO(cir): We should set default function attrs here.
+  // TODO(cir): We should set default function attrs here.
 
-    // TODO(cir): There is another region of `if (targetDecl)` that handles
-    // removing some attributes that are necessary modifications of the
-    // default-function attrs.  We should do that here.
-  }
+  // TODO(cir): There is another region of `if (targetDecl)` that handles
+  // removing some attributes that are necessary modifications of the
+  // default-function attrs.  We should do that here.
   assert(!cir::MissingFeatures::opCallAttrs());
 }
 



More information about the cfe-commits mailing list