[clang] [CIR] Fix assumption that 'curFn' is always a function in direct-call (PR #197766)

Erich Keane via cfe-commits cfe-commits at lists.llvm.org
Sat May 16 05:57:12 PDT 2026


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

>From a54829c7ff6e1a1a44848ac9144012e87bc3c2ce Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Thu, 14 May 2026 10:17:04 -0700
Subject: [PATCH 1/2] [CIR] Fix assumption that 'curFn' is always a function in
 direct-call

The code to do some checking with a builtin function tried to tell
whether it is being called inside of a function of the same name. This
isn't necessarily true (that it is in a function), since we generate
'global' ops as a curFn too.  This patch just removes the assumption and
changes the condition to only happen when we're in a function.
---
 clang/lib/CIR/CodeGen/CIRGenExpr.cpp    |  5 +--
 clang/test/CIR/CodeGen/builtin-call.cpp | 41 +++++++++++++++++++++++++
 2 files changed, 44 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/builtin-call.cpp

diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index d2f30a9d6562c..c3a44e0ff416d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -2194,8 +2194,9 @@ CIRGenCallee CIRGenFunction::emitDirectCallee(const GlobalDecl &gd) {
 
     // When directing calling an inline builtin, call it through it's mangled
     // name to make it clear it's not the actual builtin.
-    auto fn = cast<cir::FuncOp>(curFn);
-    if (fn.getName() != fdInlineName && onlyHasInlineBuiltinDeclaration(fd)) {
+    if (auto fn = dyn_cast<cir::FuncOp>(curFn);
+        fn && fn.getName() != fdInlineName &&
+        onlyHasInlineBuiltinDeclaration(fd)) {
       cir::FuncOp clone =
           mlir::cast_or_null<cir::FuncOp>(cgm.getGlobalValue(fdInlineName));
 
diff --git a/clang/test/CIR/CodeGen/builtin-call.cpp b/clang/test/CIR/CodeGen/builtin-call.cpp
new file mode 100644
index 0000000000000..f91dd1562831c
--- /dev/null
+++ b/clang/test/CIR/CodeGen/builtin-call.cpp
@@ -0,0 +1,41 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2>&1 | FileCheck %s --check-prefix=CIR-BEFORE-LPP
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - | FileCheck %s --check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - | FileCheck %s --check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefix=LLVM
+
+extern "C" {
+  // A 'builtin' function.
+  double strtod( const char *, char **);
+}
+
+const char * str = "Asdf";
+const double parsed = strtod(str, nullptr);
+
+// CIR-BEFORE-LPP:  cir.global "private" internal dso_local @_ZL6parsed = ctor : !cir.double {
+// CIR-BEFORE-LPP:    %[[GET_GLOB:.*]] = cir.get_global @_ZL6parsed : !cir.ptr<!cir.double>
+// CIR-BEFORE-LPP:    %[[GET_BUILTIN:.*]] = cir.get_global @strtod : !cir.ptr<!cir.func<(!cir.ptr<!s8i>, !cir.ptr<!cir.ptr<!s8i>>) -> !cir.double>>
+// CIR-BEFORE-LPP:    %[[GET_STR:.*]] = cir.get_global @str : !cir.ptr<!cir.ptr<!s8i>>
+// CIR-BEFORE-LPP:    %[[LOAD_STR:.*]] = cir.load align(8) %[[GET_STR]] : !cir.ptr<!cir.ptr<!s8i>>, !cir.ptr<!s8i>
+// CIR-BEFORE-LPP:    %[[NULL:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!cir.ptr<!s8i>>
+// CIR-BEFORE-LPP:    %[[CALL:.*]] = cir.call @strtod(%[[LOAD_STR]], %[[NULL]])
+// CIR-BEFORE-LPP:    cir.store {{.*}}%[[CALL]], %[[GET_GLOB]] : !cir.double, !cir.ptr<!cir.double>
+// CIR-BEFORE-LPP:    %{{.*}} = cir.get_global @_ZL6parsed : !cir.ptr<!cir.double>
+// CIR-BEFORE-LPP:  }
+// CIR-BEFORE-LPP:}
+
+// CIR: cir.global "private" internal dso_local @_ZL6parsed = #cir.fp<0.000000e+00> : !cir.double
+// CIR: cir.func internal private @__cxx_global_var_init{{.*}}() {
+// CIR:    %[[GET_GLOB:.*]] = cir.get_global @_ZL6parsed : !cir.ptr<!cir.double>
+// CIR:    %[[GET_BUILTIN:.*]] = cir.get_global @strtod : !cir.ptr<!cir.func<(!cir.ptr<!s8i>, !cir.ptr<!cir.ptr<!s8i>>) -> !cir.double>>
+// CIR:    %[[GET_STR:.*]] = cir.get_global @str : !cir.ptr<!cir.ptr<!s8i>>
+// CIR:    %[[LOAD_STR:.*]] = cir.load align(8) %[[GET_STR]] : !cir.ptr<!cir.ptr<!s8i>>, !cir.ptr<!s8i>
+// CIR:    %[[NULL:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!cir.ptr<!s8i>>
+// CIR:    %[[CALL:.*]] = cir.call @strtod(%[[LOAD_STR]], %[[NULL]])
+// CIR:    cir.store {{.*}}%[[CALL]], %[[GET_GLOB]] : !cir.double, !cir.ptr<!cir.double>
+// CIR:    %{{.*}} = cir.get_global @_ZL6parsed : !cir.ptr<!cir.double>
+
+// LLVM:  @_ZL6parsed = internal global double 0.000000e+00, align 8 
+// LLVM:  define internal void @__cxx_global_var_init() 
+// LLVM:    %[[LOAD_STR:.*]] = load ptr, ptr @str, align 8
+// LLVM:    %[[CALL:.*]] = call double @strtod(ptr noundef %[[LOAD_STR]], ptr noundef null)
+// LLVM:    store double %[[CALL]], ptr @_ZL6parsed, align 8

>From a3754b441fe2ae7decc4145afe9430ab42d823d9 Mon Sep 17 00:00:00 2001
From: Erich Keane <erich.keane at verizon.net>
Date: Sat, 16 May 2026 05:55:20 -0700
Subject: [PATCH 2/2] Fixup suggestions from andy, add test for global call to
 make sure it hits clone logic

---
 clang/lib/CIR/CodeGen/CIRGenExpr.cpp    |  4 +--
 clang/test/CIR/CodeGen/builtin-call.cpp | 42 +++++++++++++++++++++++--
 2 files changed, 41 insertions(+), 5 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index c3a44e0ff416d..43f68e0bc6172 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -2192,10 +2192,10 @@ CIRGenCallee CIRGenFunction::emitDirectCallee(const GlobalDecl &gd) {
     bool hasAttributeNoBuiltin = false;
     assert(!cir::MissingFeatures::attributeNoBuiltin());
 
-    // When directing calling an inline builtin, call it through it's mangled
+    // When directly calling an inline builtin, call it through it's mangled
     // name to make it clear it's not the actual builtin.
     if (auto fn = dyn_cast<cir::FuncOp>(curFn);
-        fn && fn.getName() != fdInlineName &&
+        (!fn || fn.getName() != fdInlineName) &&
         onlyHasInlineBuiltinDeclaration(fd)) {
       cir::FuncOp clone =
           mlir::cast_or_null<cir::FuncOp>(cgm.getGlobalValue(fdInlineName));
diff --git a/clang/test/CIR/CodeGen/builtin-call.cpp b/clang/test/CIR/CodeGen/builtin-call.cpp
index f91dd1562831c..2b486d10dc65a 100644
--- a/clang/test/CIR/CodeGen/builtin-call.cpp
+++ b/clang/test/CIR/CodeGen/builtin-call.cpp
@@ -1,7 +1,7 @@
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2>&1 | FileCheck %s --check-prefix=CIR-BEFORE-LPP
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - | FileCheck %s --check-prefix=CIR
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - | FileCheck %s --check-prefix=LLVM
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s --check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s --check-prefix=LLVM
 
 extern "C" {
   // A 'builtin' function.
@@ -11,6 +11,9 @@ extern "C" {
 const char * str = "Asdf";
 const double parsed = strtod(str, nullptr);
 
+// LLVM:  @_ZL6parsed = internal global double 0.000000e+00, align 8 
+// LLVM:  @v = global ptr null, align 8
+
 // CIR-BEFORE-LPP:  cir.global "private" internal dso_local @_ZL6parsed = ctor : !cir.double {
 // CIR-BEFORE-LPP:    %[[GET_GLOB:.*]] = cir.get_global @_ZL6parsed : !cir.ptr<!cir.double>
 // CIR-BEFORE-LPP:    %[[GET_BUILTIN:.*]] = cir.get_global @strtod : !cir.ptr<!cir.func<(!cir.ptr<!s8i>, !cir.ptr<!cir.ptr<!s8i>>) -> !cir.double>>
@@ -34,8 +37,41 @@ const double parsed = strtod(str, nullptr);
 // CIR:    cir.store {{.*}}%[[CALL]], %[[GET_GLOB]] : !cir.double, !cir.ptr<!cir.double>
 // CIR:    %{{.*}} = cir.get_global @_ZL6parsed : !cir.ptr<!cir.double>
 
-// LLVM:  @_ZL6parsed = internal global double 0.000000e+00, align 8 
 // LLVM:  define internal void @__cxx_global_var_init() 
 // LLVM:    %[[LOAD_STR:.*]] = load ptr, ptr @str, align 8
 // LLVM:    %[[CALL:.*]] = call double @strtod(ptr noundef %[[LOAD_STR]], ptr noundef null)
 // LLVM:    store double %[[CALL]], ptr @_ZL6parsed, align 8
+
+extern "C" {
+  using size_t = unsigned long;
+extern inline __attribute__((always_inline)) __attribute__((gnu_inline))
+void *memcpy(void *a, const void *b, size_t c) {
+  return __builtin_memcpy(a, b, c);
+}
+}
+
+const void* v = memcpy(nullptr, nullptr, 1);
+// CIR-BEFORE-LPP:  cir.global external @v = ctor : !cir.ptr<!void> {
+// CIR-BEFORE-LPP:    %[[GET_V:.*]] = cir.get_global @v : !cir.ptr<!cir.ptr<!void>>
+// CIR-BEFORE-LPP:    %[[NULL1:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>
+// CIR-BEFORE-LPP:    %[[NULL2:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>
+// CIR-BEFORE-LPP:    %[[ONE:.*]] = cir.const #cir.int<1> : !u64i
+// CIR-BEFORE-LPP:    %[[MEMCPY:.*]] = cir.call @memcpy.inline(%[[NULL1]], %[[NULL2]], %[[ONE]]) nothrow
+// CIR-BEFORE-LPP:    cir.store align(8) %[[MEMCPY]], %[[GET_V]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+// CIR-BEFORE-LPP:  }
+
+// CIR:  cir.global external @v = #cir.ptr<null> : !cir.ptr<!void> {alignment = 8 : i64, ast = #cir.var.decl.ast}
+// CIR:  cir.func internal private @__cxx_global_var_init.1() {
+// CIR:    %[[GET_V:.*]] = cir.get_global @v : !cir.ptr<!cir.ptr<!void>>
+// CIR:    %[[NULL1:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>
+// CIR:    %[[NULL2:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>
+// CIR:    %[[ONE:.*]] = cir.const #cir.int<1> : !u64i
+// CIR:    %[[MEMCPY:.*]] = cir.call @memcpy.inline(%[[NULL1]], %[[NULL2]], %[[ONE]]) nothrow
+// CIR:    cir.store align(8) %[[MEMCPY]], %[[GET_V]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+// CIR:    cir.return
+// CIR:  }
+
+// LLVM: define internal void @__cxx_global_var_init.1()
+// LLVM: call ptr @memcpy.inline({{.*}}
+// LLVM: store{{.*}}@v
+



More information about the cfe-commits mailing list