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()


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to