[llvm] da8497e - [IR][Verifier] Verification for `target-features` attribute (#173119)

via llvm-commits llvm-commits at lists.llvm.org
Mon Dec 22 02:14:00 PST 2025


Author: Stefan Weigl-Bosker
Date: 2025-12-22T11:13:56+01:00
New Revision: da8497ed08f5b5f0f938b488d8540333133d685b

URL: https://github.com/llvm/llvm-project/commit/da8497ed08f5b5f0f938b488d8540333133d685b
DIFF: https://github.com/llvm/llvm-project/commit/da8497ed08f5b5f0f938b488d8540333133d685b.diff

LOG: [IR][Verifier] Verification for `target-features` attribute (#173119)

Fixes https://github.com/llvm/llvm-project/issues/172647

Currently, MC assumes that all `target-feature` flag attributes are well
formed and will crash otherwise. This change handles those cases more
gracefully.

Added: 
    llvm/test/Verifier/invalid-target-feature.ll

Modified: 
    llvm/lib/IR/Verifier.cpp
    llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
    llvm/test/CodeGen/AMDGPU/bug-deadlanes.ll
    llvm/test/CodeGen/AMDGPU/mixed-wave32-wave64.ll
    llvm/test/CodeGen/AMDGPU/pal-metadata-3.0-dvgpr.ll
    llvm/test/CodeGen/AMDGPU/pal-metadata-3.0.ll
    llvm/test/CodeGen/AMDGPU/pal-metadata-3.6-dvgpr.ll
    llvm/test/CodeGen/AMDGPU/pal-metadata-3.6.ll
    llvm/test/CodeGen/AMDGPU/ps-shader-arg-count.ll
    llvm/test/CodeGen/AMDGPU/sgpr-spill-overlap-wwm-reserve.mir
    llvm/test/CodeGen/AMDGPU/si-opt-vgpr-liverange-bug-deadlanes.mir
    llvm/test/CodeGen/Hexagon/bit-rie.ll
    llvm/test/CodeGen/Hexagon/hasfp-crash2.ll
    llvm/test/CodeGen/WebAssembly/multivalue-dont-move-def-past-use.mir
    llvm/test/tools/llvm-reduce/reduce-target-features-attr.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index dd42623a1de75..78e96e51eabcb 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -2613,6 +2613,22 @@ void Verifier::verifyFunctionAttrs(FunctionType *FT, AttributeList Attrs,
     Check(FirstArgIdx > 0 && FirstArgIdx <= UpperBound,
           "modular-format attribute first arg index is out of bounds", V);
   }
+
+  if (auto A = Attrs.getFnAttr("target-features"); A.isValid()) {
+    StringRef S = A.getValueAsString();
+    if (!S.empty()) {
+      for (auto FeatureFlag : split(S, ',')) {
+        if (FeatureFlag.empty())
+          CheckFailed(
+              "target-features attribute should not contain an empty string");
+        else
+          Check(FeatureFlag[0] == '+' || FeatureFlag[0] == '-',
+                "target feature '" + FeatureFlag +
+                    "' must start with a '+' or '-'",
+                V);
+      }
+    }
+  }
 }
 void Verifier::verifyUnknownProfileMetadata(MDNode *MD) {
   Check(MD->getNumOperands() == 2,

diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
index 621640c12f695..402929caa760c 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -334,6 +334,8 @@ class CoalesceFeaturesAndStripAtomics final : public ModulePass {
       else
         Ret += (StringRef("-") + KV.Key + ",").str();
     }
+    // remove trailing ','
+    Ret.pop_back();
     return Ret;
   }
 

diff  --git a/llvm/test/CodeGen/AMDGPU/bug-deadlanes.ll b/llvm/test/CodeGen/AMDGPU/bug-deadlanes.ll
index 8d9c1b69592dc..10ed7bd870491 100644
--- a/llvm/test/CodeGen/AMDGPU/bug-deadlanes.ll
+++ b/llvm/test/CodeGen/AMDGPU/bug-deadlanes.ll
@@ -87,4 +87,4 @@ declare float @llvm.amdgcn.struct.buffer.load.format.f32(<4 x i32>, i32, i32, i3
 declare i32 @llvm.amdgcn.s.buffer.load.i32(<4 x i32>, i32, i32 immarg)
 declare <3 x i32> @llvm.amdgcn.s.buffer.load.v3i32(<4 x i32>, i32, i32 immarg)
 
-attributes #0 = { "target-features"=",+wavefrontsize64,+cumode" }
+attributes #0 = { "target-features"="+wavefrontsize64,+cumode" }

diff  --git a/llvm/test/CodeGen/AMDGPU/mixed-wave32-wave64.ll b/llvm/test/CodeGen/AMDGPU/mixed-wave32-wave64.ll
index aba14c3b97509..e82c03c21b6ee 100644
--- a/llvm/test/CodeGen/AMDGPU/mixed-wave32-wave64.ll
+++ b/llvm/test/CodeGen/AMDGPU/mixed-wave32-wave64.ll
@@ -34,8 +34,8 @@ define amdgpu_gs void @_amdgpu_gs_main() #4 {
 declare float @llvm.amdgcn.interp.p2(float, float, i32, i32, i32) #2
 declare float @llvm.amdgcn.image.sample.2d.f32.f32(i32, float, float, <8 x i32>, <4 x i32>, i1, i32, i32) #3
 
-attributes #0 = { "amdgpu-max-work-group-size"="128" "target-features"=",+wavefrontsize32" }
-attributes #1 = { "target-features"=",+wavefrontsize64" }
+attributes #0 = { "amdgpu-max-work-group-size"="128" "target-features"="+wavefrontsize32" }
+attributes #1 = { "target-features"="+wavefrontsize64" }
 attributes #2 = { nounwind readnone speculatable }
 attributes #3 = { nounwind readonly }
-attributes #4 = { "target-features"=",+wavefrontsize32" }
+attributes #4 = { "target-features"="+wavefrontsize32" }

diff  --git a/llvm/test/CodeGen/AMDGPU/pal-metadata-3.0-dvgpr.ll b/llvm/test/CodeGen/AMDGPU/pal-metadata-3.0-dvgpr.ll
index c1846c0f2c23b..cb2c8ddbaa6f2 100644
--- a/llvm/test/CodeGen/AMDGPU/pal-metadata-3.0-dvgpr.ll
+++ b/llvm/test/CodeGen/AMDGPU/pal-metadata-3.0-dvgpr.ll
@@ -201,7 +201,7 @@ declare i64 @llvm.amdgcn.s.getpc() #2
 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(write)
 declare void @llvm.amdgcn.raw.buffer.store.i32(i32, <4 x i32>, i32, i32, i32 immarg) #3
 
-attributes #0 = { nounwind memory(readwrite) "amdgpu-flat-work-group-size"="1024,1024" "amdgpu-memory-bound"="false" "amdgpu-unroll-threshold"="700" "amdgpu-wave-limiter"="false" "amdgpu-work-group-info-arg-no"="4" "denormal-fp-math-f32"="preserve-sign" "target-features"=",+wavefrontsize64,+cumode" "amdgpu-dynamic-vgpr-block-size"="16" }
+attributes #0 = { nounwind memory(readwrite) "amdgpu-flat-work-group-size"="1024,1024" "amdgpu-memory-bound"="false" "amdgpu-unroll-threshold"="700" "amdgpu-wave-limiter"="false" "amdgpu-work-group-info-arg-no"="4" "denormal-fp-math-f32"="preserve-sign" "target-features"="+wavefrontsize64,+cumode" "amdgpu-dynamic-vgpr-block-size"="16" }
 
 attributes #1 = { nounwind memory(readwrite) "InitialPSInputAddr"="36983" }
 

diff  --git a/llvm/test/CodeGen/AMDGPU/pal-metadata-3.0.ll b/llvm/test/CodeGen/AMDGPU/pal-metadata-3.0.ll
index 53254992c1122..05f3e9e14c77a 100644
--- a/llvm/test/CodeGen/AMDGPU/pal-metadata-3.0.ll
+++ b/llvm/test/CodeGen/AMDGPU/pal-metadata-3.0.ll
@@ -199,7 +199,7 @@ declare ptr addrspace(7) @lgc.buffer.desc.to.ptr(<4 x i32>) #1
 declare i64 @llvm.amdgcn.s.getpc()
 declare void @llvm.amdgcn.raw.buffer.store.i32(i32, <4 x i32>, i32, i32, i32 immarg) #3
 
-attributes #0 = { nounwind memory(readwrite) "target-features"=",+wavefrontsize64,+cumode" }
+attributes #0 = { nounwind memory(readwrite) "target-features"="+wavefrontsize64,+cumode" }
 
 attributes #1 = { nounwind memory(readwrite) "InitialPSInputAddr"="36983" }
 

diff  --git a/llvm/test/CodeGen/AMDGPU/pal-metadata-3.6-dvgpr.ll b/llvm/test/CodeGen/AMDGPU/pal-metadata-3.6-dvgpr.ll
index e598b0c5bef4d..9006aaa84cb1f 100644
--- a/llvm/test/CodeGen/AMDGPU/pal-metadata-3.6-dvgpr.ll
+++ b/llvm/test/CodeGen/AMDGPU/pal-metadata-3.6-dvgpr.ll
@@ -196,7 +196,7 @@ define dllexport amdgpu_hs void @hs_shader() {
 
 !amdgpu.pal.metadata.msgpack = !{!0}
 
-attributes #0 = { nounwind memory(readwrite) "target-features"=",+wavefrontsize64,+cumode" "amdgpu-dynamic-vgpr-block-size"="16" }
+attributes #0 = { nounwind memory(readwrite) "target-features"="+wavefrontsize64,+cumode" "amdgpu-dynamic-vgpr-block-size"="16" }
 
 attributes #1 = { nounwind memory(readwrite) "InitialPSInputAddr"="36983" "amdgpu-dynamic-vgpr-block-size"="16" }
 

diff  --git a/llvm/test/CodeGen/AMDGPU/pal-metadata-3.6.ll b/llvm/test/CodeGen/AMDGPU/pal-metadata-3.6.ll
index d2f26e87243d2..8fc533028357d 100644
--- a/llvm/test/CodeGen/AMDGPU/pal-metadata-3.6.ll
+++ b/llvm/test/CodeGen/AMDGPU/pal-metadata-3.6.ll
@@ -195,7 +195,7 @@ define dllexport amdgpu_hs void @hs_shader() {
 
 !amdgpu.pal.metadata.msgpack = !{!0}
 
-attributes #0 = { nounwind memory(readwrite) "target-features"=",+wavefrontsize64,+cumode" }
+attributes #0 = { nounwind memory(readwrite) "target-features"="+wavefrontsize64,+cumode" }
 
 attributes #1 = { nounwind memory(readwrite) "InitialPSInputAddr"="36983" }
 

diff  --git a/llvm/test/CodeGen/AMDGPU/ps-shader-arg-count.ll b/llvm/test/CodeGen/AMDGPU/ps-shader-arg-count.ll
index 99e5d0017f30b..a466df21a5e71 100644
--- a/llvm/test/CodeGen/AMDGPU/ps-shader-arg-count.ll
+++ b/llvm/test/CodeGen/AMDGPU/ps-shader-arg-count.ll
@@ -359,7 +359,7 @@ define dllexport amdgpu_ps { <4 x float>, <4 x float>, <4 x float>, <4 x float>
   ret { < 4 x float>, <4 x float>, <4 x float>, <4 x float> } %ret.res
 }
 
-attributes #0 = { nounwind "target-features"=",+wavefrontsize64,+cumode"  }
-attributes #1 = { nounwind "InitialPSInputAddr"="2" "target-features"=",+wavefrontsize64,+cumode" }
-attributes #2 = { nounwind "InitialPSInputAddr"="0xffff" "target-features"=",+wavefrontsize64,+cumode" }
-attributes #3 = { nounwind "InitialPSInputAddr"="0" "target-features"=",+wavefrontsize64,+cumode" }
+attributes #0 = { nounwind "target-features"="+wavefrontsize64,+cumode"  }
+attributes #1 = { nounwind "InitialPSInputAddr"="2" "target-features"="+wavefrontsize64,+cumode" }
+attributes #2 = { nounwind "InitialPSInputAddr"="0xffff" "target-features"="+wavefrontsize64,+cumode" }
+attributes #3 = { nounwind "InitialPSInputAddr"="0" "target-features"="+wavefrontsize64,+cumode" }

diff  --git a/llvm/test/CodeGen/AMDGPU/sgpr-spill-overlap-wwm-reserve.mir b/llvm/test/CodeGen/AMDGPU/sgpr-spill-overlap-wwm-reserve.mir
index 925984b15367d..b4f23ec00b8e2 100644
--- a/llvm/test/CodeGen/AMDGPU/sgpr-spill-overlap-wwm-reserve.mir
+++ b/llvm/test/CodeGen/AMDGPU/sgpr-spill-overlap-wwm-reserve.mir
@@ -6,7 +6,7 @@
     ret [13 x i32] poison
   }
 
-  attributes #0 = { alwaysinline nounwind memory(readwrite) "amdgpu-flat-work-group-size"="32,32" "amdgpu-memory-bound"="false" "amdgpu-unroll-threshold"="700" "amdgpu-wave-limiter"="false" "denormal-fp-math-f32"="preserve-sign" "target-cpu"="gfx1030" "target-features"=",+wavefrontsize32,+cumode,+enable-flat-scratch" "uniform-work-group-size"="false" }
+  attributes #0 = { alwaysinline nounwind memory(readwrite) "amdgpu-flat-work-group-size"="32,32" "amdgpu-memory-bound"="false" "amdgpu-unroll-threshold"="700" "amdgpu-wave-limiter"="false" "denormal-fp-math-f32"="preserve-sign" "target-cpu"="gfx1030" "target-features"="+wavefrontsize32,+cumode,+enable-flat-scratch" "uniform-work-group-size"="false" }
 ...
 ---
 

diff  --git a/llvm/test/CodeGen/AMDGPU/si-opt-vgpr-liverange-bug-deadlanes.mir b/llvm/test/CodeGen/AMDGPU/si-opt-vgpr-liverange-bug-deadlanes.mir
index 93796b3049b5b..683a1c062ba65 100644
--- a/llvm/test/CodeGen/AMDGPU/si-opt-vgpr-liverange-bug-deadlanes.mir
+++ b/llvm/test/CodeGen/AMDGPU/si-opt-vgpr-liverange-bug-deadlanes.mir
@@ -11,7 +11,7 @@
     unreachable
   }
 
-  attributes #0 = { "target-cpu"="gfx1100" "target-features"=",+wavefrontsize64,+cumode" "uniform-work-group-size"="false" }
+  attributes #0 = { "target-cpu"="gfx1100" "target-features"="+wavefrontsize64,+cumode" "uniform-work-group-size"="false" }
 ...
 ---
 name:            _amdgpu_ps_main

diff  --git a/llvm/test/CodeGen/Hexagon/bit-rie.ll b/llvm/test/CodeGen/Hexagon/bit-rie.ll
index 6bb8e912d2043..3b470787772f8 100644
--- a/llvm/test/CodeGen/Hexagon/bit-rie.ll
+++ b/llvm/test/CodeGen/Hexagon/bit-rie.ll
@@ -190,7 +190,7 @@ declare i64 @llvm.hexagon.M2.mpyd.ll.s1(i32, i32) #2
 declare void @llvm.lifetime.end.p0(i64, ptr nocapture) #1
 declare void @llvm.lifetime.start.p0(i64, ptr nocapture) #1
 
-attributes #0 = { norecurse nounwind "target-cpu"="hexagonv60" "target-features"="+hvx,,+hvx-length64b" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #0 = { norecurse nounwind "target-cpu"="hexagonv60" "target-features"="+hvx,+hvx-length64b" "unsafe-fp-math"="false" "use-soft-float"="false" }
 attributes #1 = { argmemonly nounwind }
 attributes #2 = { nounwind readnone }
 

diff  --git a/llvm/test/CodeGen/Hexagon/hasfp-crash2.ll b/llvm/test/CodeGen/Hexagon/hasfp-crash2.ll
index e2c6efc159d93..309e8b8bf616b 100644
--- a/llvm/test/CodeGen/Hexagon/hasfp-crash2.ll
+++ b/llvm/test/CodeGen/Hexagon/hasfp-crash2.ll
@@ -19,7 +19,7 @@ entry:
 ; Function Attrs: nounwind readnone speculatable
 declare void @llvm.dbg.value(metadata, i64, metadata, metadata) #1
 
-attributes #0 = { nounwind "disable-tail-calls"="true" "frame-pointer"="all" "stack-protector-buffer-size"="8" "target-cpu"="hexagonv5" "target-features"=",-hvx,-long-calls" }
+attributes #0 = { nounwind "disable-tail-calls"="true" "frame-pointer"="all" "stack-protector-buffer-size"="8" "target-cpu"="hexagonv5" "target-features"="-hvx,-long-calls" }
 attributes #1 = { nounwind readnone speculatable }
 
 !llvm.dbg.cu = !{!0}

diff  --git a/llvm/test/CodeGen/WebAssembly/multivalue-dont-move-def-past-use.mir b/llvm/test/CodeGen/WebAssembly/multivalue-dont-move-def-past-use.mir
index 14a21a06c8fcd..7e928a559613b 100644
--- a/llvm/test/CodeGen/WebAssembly/multivalue-dont-move-def-past-use.mir
+++ b/llvm/test/CodeGen/WebAssembly/multivalue-dont-move-def-past-use.mir
@@ -19,7 +19,7 @@
     ret i32 %b
   }
 
-  attributes #0 = { "target-features"="+multivalue,+mutable-globals,+sign-ext," }
+  attributes #0 = { "target-features"="+multivalue,+mutable-globals,+sign-ext" }
 
   !llvm.module.flags = !{!0, !1, !2}
 

diff  --git a/llvm/test/Verifier/invalid-target-feature.ll b/llvm/test/Verifier/invalid-target-feature.ll
new file mode 100644
index 0000000000000..60f5319c7e284
--- /dev/null
+++ b/llvm/test/Verifier/invalid-target-feature.ll
@@ -0,0 +1,13 @@
+; RUN: not llvm-as -disable-output %s 2>&1 | FileCheck %s
+
+; CHECK: target feature 'foobar' must start with a '+' or '-'
+define void @f1() "target-features"="foobar" {
+entry:
+  ret void
+}
+
+; CHECK: target-features attribute should not contain an empty string
+define void @f2() "target-features"="+a,-b,,+c" {
+entry:
+  ret void
+}

diff  --git a/llvm/test/tools/llvm-reduce/reduce-target-features-attr.ll b/llvm/test/tools/llvm-reduce/reduce-target-features-attr.ll
index b497758437358..1e36de061466f 100644
--- a/llvm/test/tools/llvm-reduce/reduce-target-features-attr.ll
+++ b/llvm/test/tools/llvm-reduce/reduce-target-features-attr.ll
@@ -26,11 +26,6 @@ define void @keep_all_of_two() #4 {
   ret void
 }
 
-; CHECK: @drop_empty_element() [[$DROP_EMPTY_ELEMENT:#[0-9]+]]
-define void @drop_empty_element() #5 {
-  ret void
-}
-
 ; CHECK: @keep_second_from_three() [[$KEEP_SECOND_FROM_THREE:#[0-9]+]]
 define void @keep_second_from_three() #6 {
   ret void
@@ -52,7 +47,6 @@ attributes #1 = { "target-features"="+foo" "unique-attr-1" }
 attributes #2 = { "target-features"="+first,+second" "unique-attr-2" }
 attributes #3 = { "target-features"="+first,+second" "unique-attr-3" }
 attributes #4 = { "target-features"="+first,+second" "unique-attr-4" }
-attributes #5 = { "target-features"="+dead,,+beef" "unique-attr-5" }
 attributes #6 = { "target-features"="+a,+b,+c" "unique-attr-6" }
 attributes #7 = { "target-features" }
 
@@ -60,7 +54,6 @@ attributes #7 = { "target-features" }
 ; INTERESTING-DAG: [[$KEEP_FIRST_FROM_TWO]] = { "target-features"="{{.*}}+first
 ; INTERESTING-DAG: [[$KEEP_SECOND_FROM_TWO]] = { "target-features"="{{.*}}+second
 ; INTERESTING-DAG: [[$KEEP_ALL_OF_TWO]] = { "target-features"="{{.*}}+first,+second
-; INTERESTING-DAG: [[$DROP_EMPTY_ELEMENT]] = { "target-features"="{{.*}}+dead{{.*}}+beef
 ; INTERESTING-DAG: [[$KEEP_SECOND_FROM_THREE]] = { "target-features"="{{.*}}+b
 
 
@@ -68,5 +61,4 @@ attributes #7 = { "target-features" }
 ; RESULT-DAG: [[$KEEP_FIRST_FROM_TWO]] = { "target-features"="+first" "unique-attr-2" }
 ; RESULT-DAG: [[$KEEP_SECOND_FROM_TWO]] = { "target-features"="+second" "unique-attr-3" }
 ; RESULT-DAG: [[$KEEP_ALL_OF_TWO]] = { "target-features"="+first,+second" "unique-attr-4" }
-; RESULT-DAG: [[$DROP_EMPTY_ELEMENT]] = { "target-features"="+dead,+beef" "unique-attr-5" }
 ; RESULT-DAG: [[$KEEP_SECOND_FROM_THREE]] = { "target-features"="+b" "unique-attr-6" }


        


More information about the llvm-commits mailing list