[clang] [Clang] Warn on unused inline asm operands (PR #220577)
isanbard wrote:
> Does this sort of warning make sense for inline assembly? There are valid use
> cases for having an asm operand that is never used, e.g. [Google Benchmark’s
> `DoNotOptimize`](https://github.com/google/benchmark/blob/04b5f41ec7e3b68b28a2379bc19804a48953117d/include/benchmark/utils.h#L53-L60):
>
> ```c++
> template
> inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp& value) {
> #if defined(__clang__)
> asm volatile("" : "+r,m"(value) : : "memory");
> #else
> asm volatile("" : "+m,r"(value) : : "memory");
> #endif
> }
> ```
I think this is only something someone wouldn't enable for just anything. Your
example specifically wouldn't want this flag.
https://github.com/llvm/llvm-project/pull/220577
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang] Warn on unused inline asm operands (PR #220577)
Sirraide wrote:
Does this sort of warning make sense for inline assembly? There are valid use
cases for having an asm operand that is never used, e.g. [Google Benchmark’s
`DoNotOptimize`](https://github.com/google/benchmark/blob/04b5f41ec7e3b68b28a2379bc19804a48953117d/include/benchmark/utils.h#L53-L60):
```c++
template
inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp& value) {
#if defined(__clang__)
asm volatile("" : "+r,m"(value) : : "memory");
#else
asm volatile("" : "+m,r"(value) : : "memory");
#endif
}
```
https://github.com/llvm/llvm-project/pull/220577
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang] Warn on unused inline asm operands (PR #220577)
AdUhTkJm wrote: CI fails on ARM. It looks like you are passing 32-bit integers to 64-bit registers on AArch64, which causes extra warnings. https://github.com/llvm/llvm-project/pull/220577 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang] Warn on unused inline asm operands (PR #220577)
@@ -10181,6 +10181,9 @@ let CategoryName = "Inline Assembly Issue" in {
"value size does not match register size specified by the constraint "
"and modifier">,
InGroup;
+ def warn_unused_asm_operand : Warning<
+"unused asm %select{output|input}0 operand">,
bwendling wrote:
Good call, thanks -- switched to enum_select in 81327b682ba2.
https://github.com/llvm/llvm-project/pull/220577
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang] Warn on unused inline asm operands (PR #220577)
https://github.com/isanbard updated
https://github.com/llvm/llvm-project/pull/220577
>From 31911e5cc93941023c833111a5779beac2285fec Mon Sep 17 00:00:00 2001
From: Bill Wendling
Date: Wed, 2 Sep 2026 05:04:25 -0700
Subject: [PATCH 1/2] [Clang] Warn on unused inline asm operands
Adds -Wunused-asm-operand (under the -Wasm umbrella), off by default,
which flags an output or input operand that the asm template string
never references via %N or %[name]. Tied inputs (matched constraints
like "0") are excluded: by convention their storage is referenced via
the output's own number, not their own, so flagging them would be a
near-universal false positive on ordinary tied-operand usage.
The check reuses the existing per-piece walk in ActOnGCCAsmStmt that
already validates every operand reference in the template string for
constraint-modifier correctness, rather than adding a second pass over
the asm string: a SmallBitVector records which operand indices get
referenced as a side effect of that walk, and a single linear scan
over it (bounded by operand count, not template length) after the
loop reports anything left unset. No new parsing, no quadratic
behavior.
That bitset also replaces the existing isOperandMentioned() helper,
which rescanned the whole piece list from scratch for every query and
was called up to three times per tied operand during the tied-operand
type-mismatch checks a bit further down in the same function -- now
those are direct O(1) bitset lookups instead. isOperandMentioned()
itself is now dead and removed; it was only ever called from this one
function.
Fixes https://github.com/llvm/llvm-project/issues/5545.
Assisted-by: Claude Sonnet 5
---
clang/include/clang/Basic/DiagnosticGroups.td | 4 +-
.../clang/Basic/DiagnosticSemaKinds.td| 3 +
clang/lib/Sema/SemaStmtAsm.cpp| 52 +
clang/test/Sema/warn-unused-asm-operand.c | 57 +++
4 files changed, 92 insertions(+), 24 deletions(-)
create mode 100644 clang/test/Sema/warn-unused-asm-operand.c
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td
b/clang/include/clang/Basic/DiagnosticGroups.td
index 3e91d74cd66a9..a4e1975469566 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1755,8 +1755,10 @@ def ObjCStrictPotentiallyDirectSelector :
// Inline ASM warnings.
def ASMOperandWidths : DiagGroup<"asm-operand-widths">;
+def ASMUnusedOperand : DiagGroup<"unused-asm-operand">;
def ASM : DiagGroup<"asm", [
-ASMOperandWidths
+ASMOperandWidths,
+ASMUnusedOperand
]>;
// Linker warnings.
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index b5100b67dccd7..ab449531d450b 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10181,6 +10181,9 @@ let CategoryName = "Inline Assembly Issue" in {
"value size does not match register size specified by the constraint "
"and modifier">,
InGroup;
+ def warn_unused_asm_operand : Warning<
+"unused asm %select{output|input}0 operand">,
+InGroup, DefaultIgnore;
def note_asm_missing_constraint_modifier : Note<
"use constraint modifier \"%0\"">;
diff --git a/clang/lib/Sema/SemaStmtAsm.cpp b/clang/lib/Sema/SemaStmtAsm.cpp
index 6bf12d9cd98da..0606aa9bb020f 100644
--- a/clang/lib/Sema/SemaStmtAsm.cpp
+++ b/clang/lib/Sema/SemaStmtAsm.cpp
@@ -21,6 +21,7 @@
#include "clang/Sema/Scope.h"
#include "clang/Sema/ScopeInfo.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/MC/MCParser/MCAsmParser.h"
@@ -103,24 +104,6 @@ static bool CheckAsmLValue(Expr *E, Sema &S) {
return true;
}
-/// isOperandMentioned - Return true if the specified operand # is mentioned
-/// anywhere in the decomposed asm string.
-static bool
-isOperandMentioned(unsigned OpNo,
- ArrayRef AsmStrPieces) {
- for (unsigned p = 0, e = AsmStrPieces.size(); p != e; ++p) {
-const GCCAsmStmt::AsmStringPiece &Piece = AsmStrPieces[p];
-if (!Piece.isOperand())
- continue;
-
-// If this is a reference to the input and if the input was the smaller
-// one, then we have to reject this asm.
-if (Piece.getOperandNo() == OpNo)
- return true;
- }
- return false;
-}
-
static bool CheckNakedParmReference(Expr *E, Sema &S) {
FunctionDecl *Func = dyn_cast(S.CurContext);
if (!Func)
@@ -560,7 +543,11 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc,
bool IsSimple,
return NS;
}
- // Validate constraints and modifiers.
+ // Validate constraints and modifiers, and (cheaply, piggybacking on this
+ // same walk over Pieces rather than doing a second pass over the asm
+ // string) track which operands the template string actually references,
+ // so unreferenced ones can be
[clang] [Clang] Warn on unused inline asm operands (PR #220577)
llvmorg-github-actions[bot] wrote:
@llvm/pr-subscribers-clang
Author: Bill Wendling (isanbard)
Changes
Adds -Wunused-asm-operand (under the -Wasm umbrella), off by default, which
flags an output or input operand that the asm template string never references
via %N or %[name]. Tied inputs (matched constraints like "0") are excluded: by
convention their storage is referenced via the output's own number, not their
own, so flagging them would be a near-universal false positive on ordinary
tied-operand usage.
The check reuses the existing per-piece walk in ActOnGCCAsmStmt that already
validates every operand reference in the template string for
constraint-modifier correctness, rather than adding a second pass over the asm
string: a SmallBitVector records which operand indices get referenced as a side
effect of that walk, and a single linear scan over it (bounded by operand
count, not template length) after the loop reports anything left unset. No new
parsing, no quadratic behavior.
That bitset also replaces the existing isOperandMentioned() helper, which
rescanned the whole piece list from scratch for every query and was called up
to three times per tied operand during the tied-operand type-mismatch checks a
bit further down in the same function -- now those are direct O(1) bitset
lookups instead. isOperandMentioned() itself is now dead and removed; it was
only ever called from this one function.
Fixes https://github.com/llvm/llvm-project/issues/5545.
Assisted-by: Claude Sonnet 5
---
Full diff: https://github.com/llvm/llvm-project/pull/220577.diff
4 Files Affected:
- (modified) clang/include/clang/Basic/DiagnosticGroups.td (+3-1)
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+3)
- (modified) clang/lib/Sema/SemaStmtAsm.cpp (+29-23)
- (added) clang/test/Sema/warn-unused-asm-operand.c (+57)
``diff
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td
b/clang/include/clang/Basic/DiagnosticGroups.td
index 3e91d74cd66a9..a4e1975469566 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1755,8 +1755,10 @@ def ObjCStrictPotentiallyDirectSelector :
// Inline ASM warnings.
def ASMOperandWidths : DiagGroup<"asm-operand-widths">;
+def ASMUnusedOperand : DiagGroup<"unused-asm-operand">;
def ASM : DiagGroup<"asm", [
-ASMOperandWidths
+ASMOperandWidths,
+ASMUnusedOperand
]>;
// Linker warnings.
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index b5100b67dccd7..ab449531d450b 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10181,6 +10181,9 @@ let CategoryName = "Inline Assembly Issue" in {
"value size does not match register size specified by the constraint "
"and modifier">,
InGroup;
+ def warn_unused_asm_operand : Warning<
+"unused asm %select{output|input}0 operand">,
+InGroup, DefaultIgnore;
def note_asm_missing_constraint_modifier : Note<
"use constraint modifier \"%0\"">;
diff --git a/clang/lib/Sema/SemaStmtAsm.cpp b/clang/lib/Sema/SemaStmtAsm.cpp
index 6bf12d9cd98da..0606aa9bb020f 100644
--- a/clang/lib/Sema/SemaStmtAsm.cpp
+++ b/clang/lib/Sema/SemaStmtAsm.cpp
@@ -21,6 +21,7 @@
#include "clang/Sema/Scope.h"
#include "clang/Sema/ScopeInfo.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/MC/MCParser/MCAsmParser.h"
@@ -103,24 +104,6 @@ static bool CheckAsmLValue(Expr *E, Sema &S) {
return true;
}
-/// isOperandMentioned - Return true if the specified operand # is mentioned
-/// anywhere in the decomposed asm string.
-static bool
-isOperandMentioned(unsigned OpNo,
- ArrayRef AsmStrPieces) {
- for (unsigned p = 0, e = AsmStrPieces.size(); p != e; ++p) {
-const GCCAsmStmt::AsmStringPiece &Piece = AsmStrPieces[p];
-if (!Piece.isOperand())
- continue;
-
-// If this is a reference to the input and if the input was the smaller
-// one, then we have to reject this asm.
-if (Piece.getOperandNo() == OpNo)
- return true;
- }
- return false;
-}
-
static bool CheckNakedParmReference(Expr *E, Sema &S) {
FunctionDecl *Func = dyn_cast(S.CurContext);
if (!Func)
@@ -560,7 +543,11 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc,
bool IsSimple,
return NS;
}
- // Validate constraints and modifiers.
+ // Validate constraints and modifiers, and (cheaply, piggybacking on this
+ // same walk over Pieces rather than doing a second pass over the asm
+ // string) track which operands the template string actually references,
+ // so unreferenced ones can be flagged below.
+ llvm::SmallBitVector UsedOperands(NumOutputs + NumInputs);
for (unsigned i = 0, e = Pieces.size(); i != e; ++i) {
GCCAsmStmt::AsmStringPiece &Piece =
[clang] [Clang] Warn on unused inline asm operands (PR #220577)
isanbard wrote: > Your PR description and reply look like they were generated. Could you please > summarize the changes in your own words? Sure. Done. :-) https://github.com/llvm/llvm-project/pull/220577 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang] Warn on unused inline asm operands (PR #220577)
https://github.com/isanbard edited https://github.com/llvm/llvm-project/pull/220577 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang] Warn on unused inline asm operands (PR #220577)
el-ev wrote: Your PR description and reply look like they were generated. Could you please summarize the changes in your own words? https://github.com/llvm/llvm-project/pull/220577 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang] Warn on unused inline asm operands (PR #220577)
https://github.com/isanbard created
https://github.com/llvm/llvm-project/pull/220577
Adds -Wunused-asm-operand (under the -Wasm umbrella), off by default, which
flags an output or input operand that the asm template string never references
via %N or %[name]. Tied inputs (matched constraints like "0") are excluded: by
convention their storage is referenced via the output's own number, not their
own, so flagging them would be a near-universal false positive on ordinary
tied-operand usage.
The check reuses the existing per-piece walk in ActOnGCCAsmStmt that already
validates every operand reference in the template string for
constraint-modifier correctness, rather than adding a second pass over the asm
string: a SmallBitVector records which operand indices get referenced as a side
effect of that walk, and a single linear scan over it (bounded by operand
count, not template length) after the loop reports anything left unset. No new
parsing, no quadratic behavior.
That bitset also replaces the existing isOperandMentioned() helper, which
rescanned the whole piece list from scratch for every query and was called up
to three times per tied operand during the tied-operand type-mismatch checks a
bit further down in the same function -- now those are direct O(1) bitset
lookups instead. isOperandMentioned() itself is now dead and removed; it was
only ever called from this one function.
Fixes https://github.com/llvm/llvm-project/issues/5545.
Assisted-by: Claude Sonnet 5
>From 31911e5cc93941023c833111a5779beac2285fec Mon Sep 17 00:00:00 2001
From: Bill Wendling
Date: Wed, 2 Sep 2026 05:04:25 -0700
Subject: [PATCH] [Clang] Warn on unused inline asm operands
Adds -Wunused-asm-operand (under the -Wasm umbrella), off by default,
which flags an output or input operand that the asm template string
never references via %N or %[name]. Tied inputs (matched constraints
like "0") are excluded: by convention their storage is referenced via
the output's own number, not their own, so flagging them would be a
near-universal false positive on ordinary tied-operand usage.
The check reuses the existing per-piece walk in ActOnGCCAsmStmt that
already validates every operand reference in the template string for
constraint-modifier correctness, rather than adding a second pass over
the asm string: a SmallBitVector records which operand indices get
referenced as a side effect of that walk, and a single linear scan
over it (bounded by operand count, not template length) after the
loop reports anything left unset. No new parsing, no quadratic
behavior.
That bitset also replaces the existing isOperandMentioned() helper,
which rescanned the whole piece list from scratch for every query and
was called up to three times per tied operand during the tied-operand
type-mismatch checks a bit further down in the same function -- now
those are direct O(1) bitset lookups instead. isOperandMentioned()
itself is now dead and removed; it was only ever called from this one
function.
Fixes https://github.com/llvm/llvm-project/issues/5545.
Assisted-by: Claude Sonnet 5
---
clang/include/clang/Basic/DiagnosticGroups.td | 4 +-
.../clang/Basic/DiagnosticSemaKinds.td| 3 +
clang/lib/Sema/SemaStmtAsm.cpp| 52 +
clang/test/Sema/warn-unused-asm-operand.c | 57 +++
4 files changed, 92 insertions(+), 24 deletions(-)
create mode 100644 clang/test/Sema/warn-unused-asm-operand.c
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td
b/clang/include/clang/Basic/DiagnosticGroups.td
index 3e91d74cd66a9..a4e1975469566 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1755,8 +1755,10 @@ def ObjCStrictPotentiallyDirectSelector :
// Inline ASM warnings.
def ASMOperandWidths : DiagGroup<"asm-operand-widths">;
+def ASMUnusedOperand : DiagGroup<"unused-asm-operand">;
def ASM : DiagGroup<"asm", [
-ASMOperandWidths
+ASMOperandWidths,
+ASMUnusedOperand
]>;
// Linker warnings.
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index b5100b67dccd7..ab449531d450b 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10181,6 +10181,9 @@ let CategoryName = "Inline Assembly Issue" in {
"value size does not match register size specified by the constraint "
"and modifier">,
InGroup;
+ def warn_unused_asm_operand : Warning<
+"unused asm %select{output|input}0 operand">,
+InGroup, DefaultIgnore;
def note_asm_missing_constraint_modifier : Note<
"use constraint modifier \"%0\"">;
diff --git a/clang/lib/Sema/SemaStmtAsm.cpp b/clang/lib/Sema/SemaStmtAsm.cpp
index 6bf12d9cd98da..0606aa9bb020f 100644
--- a/clang/lib/Sema/SemaStmtAsm.cpp
+++ b/clang/lib/Sema/SemaStmtAsm.cpp
@@ -21,6 +21,7 @@
#include "clang/Sema/Scope.h"
#include "clang/Sema/ScopeInfo.h"
