[clang] [clang][CodeGen] Trigger AST lookup for libfunction builtins (PR #209997)
Takuto Ikuta via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 16 00:53:42 PDT 2026
https://github.com/atetubou updated https://github.com/llvm/llvm-project/pull/209997
>From b0c8b27062988285159c7cc6a0a55ff0bf161bab Mon Sep 17 00:00:00 2001
From: Takuto Ikuta <tikuta at google.com>
Date: Thu, 16 Jul 2026 16:37:25 +0900
Subject: [PATCH] [clang][CodeGen] Trigger AST lookup for libfunction builtins
Unlike normal function calls where Sema performs name lookup during parsing
and triggers lazy deserialization of AST declarations from Clang Modules
(PCMs), built-in library function calls (e.g., __builtin_hypotf) bypass Sema
name lookup for the target C function name (hypotf). Consequently, Clang
Modules does not lazily deserialize module-defined declarations (such as
MSVC UCRT's inline hypotf wrapper in corecrt_math.h) from PCM files. CodeGen
then falls back to emitting direct external references to non-existent
symbols like hypotf, causing unresolved symbol linker errors.
This change triggers an explicit AST name lookup on TranslationUnitDecl for
the target library function name in CodeGenModule::getBuiltinLibFunction.
When a corresponding C-linkage FunctionDecl (isExternC) is deserialized from
modules, CodeGen uses its address (GetAddrOfFunction) to properly emit its
inline definition and dependent DLL import symbols (e.g. _hypotf).
Added regression test in clang/test/Modules/builtin-libfunction-lookup.cpp.
---
clang/lib/CodeGen/CGBuiltin.cpp | 19 +++++
.../Modules/builtin-libfunction-lookup.cpp | 79 +++++++++++++++++++
2 files changed, 98 insertions(+)
create mode 100644 clang/test/Modules/builtin-libfunction-lookup.cpp
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 37846bbb0b5ec..c0022ae14bc88 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -247,6 +247,25 @@ llvm::Constant *CodeGenModule::getBuiltinLibFunction(const FunctionDecl *FD,
Name = Context.BuiltinInfo.getName(BuiltinID).substr(10);
}
+ // Look up the corresponding standard library C function in the AST. Unlike
+ // normal function calls where Sema performs name lookup and lazily
+ // deserializes declarations from Clang Modules (PCMs), built-in library
+ // function calls (e.g. __builtin_hypotf) bypass Sema name lookup for the C
+ // function name ("hypotf"). Explicitly looking up the name in TranslationUnitDecl
+ // triggers ASTReader to lazily deserialize any module-defined declaration (such
+ // as MSVC UCRT's inline hypotf wrapper). If found, use GetAddrOfFunction to
+ // properly emit its definition and dependencies.
+ DeclarationName DecName = &Context.Idents.get(Name);
+ DeclContext::lookup_result Decls =
+ Context.getTranslationUnitDecl()->lookup(DecName);
+ for (NamedDecl *ND : Decls) {
+ if (auto *TargetFD = dyn_cast<FunctionDecl>(ND)) {
+ if (TargetFD->isExternC()) {
+ return GetAddrOfFunction(TargetFD);
+ }
+ }
+ }
+
llvm::FunctionType *Ty =
cast<llvm::FunctionType>(getTypes().ConvertType(FD->getType()));
diff --git a/clang/test/Modules/builtin-libfunction-lookup.cpp b/clang/test/Modules/builtin-libfunction-lookup.cpp
new file mode 100644
index 0000000000000..b764d7274a8d7
--- /dev/null
+++ b/clang/test/Modules/builtin-libfunction-lookup.cpp
@@ -0,0 +1,79 @@
+// Test that calling built-in library functions like __builtin_hypotf under Clang Modules
+// triggers AST name lookup for the target C function (hypotf). This ensures that
+// module-defined inline wrappers (e.g. MSVC UCRT's hypotf wrapper calling _hypotf)
+// are lazily deserialized from PCMs rather than emitting external non-existent function calls.
+//
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: cd %t
+//
+// RUN: %clang_cc1 -xc++ -emit-module -fmodules -fmodule-name=ucrt -fmodule-map-file=ucrt.modulemap -triple x86_64-pc-windows-msvc -fms-extensions -I. ucrt.modulemap -o ucrt.pcm
+// RUN: %clang_cc1 -xc++ -emit-module -fmodules -fmodule-name=std -fmodule-map-file=std.modulemap -fmodule-file=ucrt=ucrt.pcm -triple x86_64-pc-windows-msvc -fms-extensions -I. std.modulemap -o std.pcm
+// RUN: %clang_cc1 -xc++ -emit-llvm -fmodules -fmodule-map-file=std.modulemap -fmodule-map-file=ucrt.modulemap -fmodule-file=std=std.pcm -fmodule-file=ucrt=ucrt.pcm -triple x86_64-pc-windows-msvc -fms-extensions -I. main.cc -o - | FileCheck %s
+
+//--- corecrt_math.h
+#ifndef MOCK_CORECRT_MATH_H
+#define MOCK_CORECRT_MATH_H
+extern "C" {
+__declspec(dllimport) float __cdecl _hypotf(float x, float y);
+inline float __cdecl hypotf(float x, float y) {
+ return _hypotf(x, y);
+}
+}
+#endif
+
+//--- math.h
+#ifndef MOCK_MATH_H
+#define MOCK_MATH_H
+#include "corecrt_math.h"
+#endif
+
+//--- __math/hypot.h
+#ifndef MOCK_MATH_HYPOT_H
+#define MOCK_MATH_HYPOT_H
+inline float hypot(float x, float y) {
+ return __builtin_hypotf(x, y);
+}
+#endif
+
+//--- cmath
+#ifndef MOCK_CMATH
+#define MOCK_CMATH
+#include "math.h"
+#include "__math/hypot.h"
+#endif
+
+//--- std.modulemap
+module std {
+ module cmath {
+ header "cmath"
+ export *
+ }
+ module math_hypot {
+ header "__math/hypot.h"
+ export *
+ }
+}
+
+//--- ucrt.modulemap
+module ucrt {
+ module math {
+ header "math.h"
+ export *
+ }
+ module corecrt_math {
+ header "corecrt_math.h"
+ export *
+ }
+}
+
+//--- main.cc
+#include "cmath"
+
+float test_call(float x, float y) {
+ return hypot(x, y);
+}
+
+// CHECK: define linkonce_odr dso_local float @hypotf(float noundef %{{.*}}, float noundef %{{.*}})
+// CHECK: call float @_hypotf(float noundef %{{.*}}, float noundef %{{.*}})
+// CHECK: declare dllimport float @_hypotf(float noundef, float noundef)
More information about the cfe-commits
mailing list