[clang] 661cffb - [CIR] Record target-cpu and target-features on function declarations (#214986)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Aug 11 16:49:36 PDT 2026
Author: Adam Smith
Date: 2026-08-11T18:49:30-05:00
New Revision: 661cffb60d3ea84997716167822548b01ba9a626
URL: https://github.com/llvm/llvm-project/commit/661cffb60d3ea84997716167822548b01ba9a626
DIFF: https://github.com/llvm/llvm-project/commit/661cffb60d3ea84997716167822548b01ba9a626.diff
LOG: [CIR] Record target-cpu and target-features on function declarations (#214986)
A function declaration carried no CPU or feature attributes. CIRGen set
them from `setNonAliasAttributes`, which runs only for a definition,
where classic CodeGen sets them from `ConstructAttributeList` for a
declaration too. We now set them properly in `constructAttributeList`
alongside the other non-call-site attributes.
Recording them on a declaration exposes a second bug. When a function is
declared first and defined later with a `target` attribute,
`setNonAliasAttributes` wrote the definition's values over the
declaration's rather than replacing them, so a `tune-cpu` that the
`target` attribute suppresses survived. It now clears the three
attributes before writing, which is safe because
`getCPUAndFeaturesAttributes` resolves the most recent declaration, so
its result supersedes anything an earlier one wrote.
Assisted-by: Cursor / claude-opus-5
Added:
Modified:
clang/include/clang/CIR/Dialect/IR/CIRDialect.td
clang/lib/CIR/CodeGen/CIRGenCall.cpp
clang/lib/CIR/CodeGen/CIRGenModule.cpp
clang/test/CIR/CodeGen/alloc-size.c
clang/test/CIR/CodeGen/asm-label-redirect.c
clang/test/CIR/CodeGen/attr-target-x86.c
clang/test/CIR/CodeGen/global-init.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
index de90fed45f170..8829d37a52c97 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
@@ -77,6 +77,9 @@ def CIR_Dialect : Dialect {
static llvm::StringRef getResAttrsAttrName() { return "res_attrs"; }
static llvm::StringRef getArgAttrsAttrName() { return "arg_attrs"; }
static llvm::StringRef getRecordLayoutsAttrName() { return "cir.record_layouts"; }
+ static llvm::StringRef getTargetCPUAttrName() { return "cir.target-cpu"; }
+ static llvm::StringRef getTuneCPUAttrName() { return "cir.tune-cpu"; }
+ static llvm::StringRef getTargetFeaturesAttrName() { return "cir.target-features"; }
static llvm::StringRef getCUDABinaryHandleAttrName() { return "cir.cu.binary_handle"; }
static llvm::StringRef getMustTailAttrName() { return "musttail"; }
static llvm::StringRef getCatchCopyThunkAttrName() { return "cir.eh.catch_copy_thunk"; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index d3b3a8a471dbf..3e689e031f7ad 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -500,11 +500,18 @@ void CIRGenModule::constructAttributeList(
"sigsetjmp", "__sigsetjmp", "savectx", "getcontext"};
if (returnsTwiceFn.contains(name))
addUnitAttr(cir::CIRDialect::getReturnsTwiceAttrName());
+
+ llvm::StringMap<std::string> cpuAndFeatures;
+ if (getCPUAndFeaturesAttributes(calleeInfo.getCalleeDecl(),
+ cpuAndFeatures)) {
+ for (const auto &[key, val] : cpuAndFeatures)
+ attrs.set(key, builder.getStringAttr(val));
+ }
}
// TODO(cir): A bunch of non-call-site function IR attributes from
// declaration-specific information, including tail calls,
- // cmse_nonsecure_entry, CPU-features/overrides, and hotpatch support.
+ // cmse_nonsecure_entry, and hotpatch support.
// TODO(cir): Add loader-replaceable attribute here.
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index ae9cad0b7c30f..3aa381fec5a70 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -885,11 +885,11 @@ bool CIRGenModule::getCPUAndFeaturesAttributes(
}
if (!targetCPU.empty()) {
- attrs["cir.target-cpu"] = targetCPU.str();
+ attrs[cir::CIRDialect::getTargetCPUAttrName()] = targetCPU.str();
addedAttr = true;
}
if (!tuneCPU.empty()) {
- attrs["cir.tune-cpu"] = tuneCPU.str();
+ attrs[cir::CIRDialect::getTuneCPUAttrName()] = tuneCPU.str();
addedAttr = true;
}
if (!features.empty() && setTargetFeatures) {
@@ -899,7 +899,8 @@ bool CIRGenModule::getCPUAndFeaturesAttributes(
return getTarget().isReadOnlyFeature(f.substr(1));
});
llvm::sort(features);
- attrs["cir.target-features"] = llvm::join(features, ",");
+ attrs[cir::CIRDialect::getTargetFeaturesAttrName()] =
+ llvm::join(features, ",");
addedAttr = true;
}
// TODO(cir): add metadata for AArch64 Function Multi Versioning.
@@ -921,9 +922,17 @@ void CIRGenModule::setNonAliasAttributes(GlobalDecl gd, mlir::Operation *op) {
if (auto func = dyn_cast<cir::FuncOp>(op)) {
llvm::StringMap<std::string> attrs;
if (getCPUAndFeaturesAttributes(gd, attrs)) {
- // TODO(cir): Classic codegen removes the existing target-cpu,
- // target-features, tune-cpu and fmv-features attributes here
- // before adding the new ones.
+ // TODO(cir): Classic codegen also removes fmv-features here, which
+ // CIR does not emit yet.
+ //
+ // getCPUAndFeaturesAttributes reads the most recent declaration, so
+ // its result supersedes anything an earlier one wrote. Clear first:
+ // setAttr alone would leave a name this call no longer produces.
+ for (llvm::StringRef name :
+ {cir::CIRDialect::getTargetCPUAttrName(),
+ cir::CIRDialect::getTuneCPUAttrName(),
+ cir::CIRDialect::getTargetFeaturesAttrName()})
+ func->removeAttr(name);
for (const auto &[key, val] : attrs)
func->setAttr(key, builder.getStringAttr(val));
}
diff --git a/clang/test/CIR/CodeGen/alloc-size.c b/clang/test/CIR/CodeGen/alloc-size.c
index e3ff12da1a083..c3c28cafb92db 100644
--- a/clang/test/CIR/CodeGen/alloc-size.c
+++ b/clang/test/CIR/CodeGen/alloc-size.c
@@ -9,9 +9,9 @@
typedef unsigned long size_t;
-// CIR: cir.func{{.*}}@my_malloc(!s32i {llvm.noundef}){{.*}} attributes {allocsize = array<i32: 0>}
+// CIR: cir.func{{.*}}@my_malloc(!s32i {llvm.noundef}){{.*}} attributes {allocsize = array<i32: 0>{{[,}]}}
extern void *my_malloc(int) __attribute__((alloc_size(1)));
-// CIR: cir.func{{.*}}@my_calloc(!s32i {llvm.noundef}, !s32i {llvm.noundef}){{.*}} attributes {allocsize = array<i32: 0, 1>}
+// CIR: cir.func{{.*}}@my_calloc(!s32i {llvm.noundef}, !s32i {llvm.noundef}){{.*}} attributes {allocsize = array<i32: 0, 1>{{[,}]}}
extern void *my_calloc(int, int) __attribute__((alloc_size(1, 2)));
// CIR-LABEL: @call_direct
diff --git a/clang/test/CIR/CodeGen/asm-label-redirect.c b/clang/test/CIR/CodeGen/asm-label-redirect.c
index 3e40976df04a9..0dc01c912da2d 100644
--- a/clang/test/CIR/CodeGen/asm-label-redirect.c
+++ b/clang/test/CIR/CodeGen/asm-label-redirect.c
@@ -26,8 +26,8 @@ int test(const char *p) {
// it sees first - here, the my_stat declaration.
//
// CIR-LABEL: cir.func private @real_impl(
-// CIR-SAME: !cir.ptr<!s8i> {{.*}},
-// CIR-SAME: !cir.ptr<!rec_my_stat> {{.*}}) -> !s32i
+// CIR-SAME: !cir.ptr<!s8i> {{[^,]*}},
+// CIR-SAME: !cir.ptr<!rec_my_stat> {{[^,]*}}) -> !s32i
// CIR-LABEL: cir.func {{.*}} @test(
//
diff --git a/clang/test/CIR/CodeGen/attr-target-x86.c b/clang/test/CIR/CodeGen/attr-target-x86.c
index c99484c42a1db..d46749c58ac34 100644
--- a/clang/test/CIR/CodeGen/attr-target-x86.c
+++ b/clang/test/CIR/CodeGen/attr-target-x86.c
@@ -26,6 +26,8 @@
// LLVM: define {{.*}}@f_avx10_1{{.*}} [[f_avx10_1:#[0-9]+]]
// LLVM: define {{.*}}@f_prefer_256_bit({{.*}} [[f_prefer_256_bit:#[0-9]+]]
// LLVM: define {{.*}}@f_no_prefer_256_bit({{.*}} [[f_no_prefer_256_bit:#[0-9]+]]
+// LLVM: declare {{.*}}@f_decl_only() [[f_decl_only:#[0-9]+]]
+// LLVM: declare {{.*}}@f_decl_default() [[f_decl_default:#[0-9]+]]
// CIR: cir.func{{.*}} @f_default()
// CIR-SAME: "cir.target-cpu" = "i686"
@@ -124,6 +126,11 @@ void usage(void){
// f_use_before_def: same attributes as f_lakemont_mmx (checked above) - the
// definition's attribute should be propagated to the earlier declaration.
+// The dictionary closes after the features, so the tune-cpu the declaration
+// recorded before the definition was seen does not survive.
+// CIR: cir.func{{.*}} @f_use_before_def()
+// CIR-SAME: "cir.target-cpu" = "lakemont"
+// CIR-SAME: "cir.target-features" = "+cx8,+mmx", nothrow}
__attribute__((target("arch=lakemont,mmx")))
void f_use_before_def(void) {}
@@ -184,3 +191,25 @@ void f_prefer_256_bit(void) {}
// LLVM: [[f_no_prefer_256_bit]] = {{.*}}"target-features"="{{.*}}-prefer-256-bit
__attribute__((target("no-prefer-256-bit")))
void f_no_prefer_256_bit(void) {}
+
+// A declaration gets the attributes too. target(arch=) suppresses tune-cpu,
+// so the dictionary closes after the features.
+// CIR: cir.func private @f_decl_only()
+// CIR-SAME: "cir.target-cpu" = "lakemont"
+// CIR-SAME: "cir.target-features" = "+cx8,+mmx"}
+
+// LLVM: [[f_decl_only]] = {{.*}}"target-cpu"="lakemont" "target-features"="+cx8,+mmx" }
+__attribute__((target("arch=lakemont,mmx")))
+void f_decl_only(void);
+void use_decl_only(void) { f_decl_only(); }
+
+// A declaration with no target attribute takes all three from the command
+// line, tune-cpu included.
+// CIR: cir.func private @f_decl_default()
+// CIR-SAME: "cir.target-cpu" = "i686"
+// CIR-SAME: "cir.target-features" = "+cmov,+cx8,+x87"
+// CIR-SAME: "cir.tune-cpu" = "i686"
+
+// LLVM: [[f_decl_default]] = {{.*}}"target-cpu"="i686" "target-features"="+cmov,+cx8,+x87" "tune-cpu"="i686"
+void f_decl_default(void);
+void use_decl_default(void) { f_decl_default(); }
diff --git a/clang/test/CIR/CodeGen/global-init.cpp b/clang/test/CIR/CodeGen/global-init.cpp
index 7abb70e42d608..c5bdded22af98 100644
--- a/clang/test/CIR/CodeGen/global-init.cpp
+++ b/clang/test/CIR/CodeGen/global-init.cpp
@@ -230,7 +230,7 @@ ArrayDtor arrDtor[16];
// LLVM: %[[CUR:.*]] = load ptr, ptr %[[CUR_ADDR]]
// LLVM: %[[PREV:.*]] = getelementptr %struct.ArrayDtor, ptr %[[CUR]], i64 -1
// LLVM: store ptr %[[PREV]], ptr %[[CUR_ADDR]]
-// LLVM: call void @_ZN9ArrayDtorD1Ev(ptr noundef nonnull align 1 dereferenceable(1) %[[PREV]]) #0
+// LLVM: call void @_ZN9ArrayDtorD1Ev(ptr noundef nonnull align 1 dereferenceable(1) %[[PREV]]) [[NOUNWIND:#[0-9]+]]
// LLVM: br label %[[LOOP_COND]]
// LLVM: [[LOOP_END]]:
// LLVM: ret void
@@ -279,6 +279,8 @@ ArrayDtor arrDtor[16];
// LLVM: call void @__cxx_global_var_init.4()
// LLVM: call void @__cxx_global_var_init.5()
+// LLVM: attributes [[NOUNWIND]] = { nounwind }
+
// OGCG: define internal void @_GLOBAL__sub_I_[[FILENAME]]() {{.*}} section ".text.startup" {
// OGCG: call void @__cxx_global_var_init()
// OGCG: call void @__cxx_global_var_init.1()
More information about the cfe-commits
mailing list