[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