[clang] [clang-c] introduce queries on GCC-style inline assembly statements (PR #143424)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 9 14:38:18 PDT 2025
https://github.com/dingxiangfei2009 updated https://github.com/llvm/llvm-project/pull/143424
>From 4f9e599067c954a3ed3028efdc2abd72aa2b775f Mon Sep 17 00:00:00 2001
From: Xiangfei Ding <dingxiangfei2009 at protonmail.ch>
Date: Mon, 9 Jun 2025 13:57:18 +0000
Subject: [PATCH] [clang-c] introduce queries on GCC-style inline assembly
statements
We strive for exposing such information using existing stable ABIs.
In doing so, queries are limited to what the original source holds
or the LLVM IR `asm` block would expose in connection with attributes
that the queries are concerned.
Signed-off-by: Xiangfei Ding <dingxiangfei2009 at protonmail.ch>
---
clang/include/clang-c/Index.h | 93 +++++++++++++++++-
clang/test/Index/inline-assembly.c | 48 +++++++++
clang/tools/c-index-test/c-index-test.c | 52 ++++++++++
clang/tools/libclang/CIndex.cpp | 125 ++++++++++++++++++++++--
clang/tools/libclang/libclang.map | 10 ++
5 files changed, 320 insertions(+), 8 deletions(-)
create mode 100644 clang/test/Index/inline-assembly.c
diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h
index e4cb4327fbaac..340d050654a69 100644
--- a/clang/include/clang-c/Index.h
+++ b/clang/include/clang-c/Index.h
@@ -36,7 +36,7 @@
#define CINDEX_VERSION_MAJOR 0
#define CINDEX_VERSION_MINOR 64
-#define CINDEX_VERSION_ENCODE(major, minor) (((major)*10000) + ((minor)*1))
+#define CINDEX_VERSION_ENCODE(major, minor) (((major) * 10000) + ((minor) * 1))
#define CINDEX_VERSION \
CINDEX_VERSION_ENCODE(CINDEX_VERSION_MAJOR, CINDEX_VERSION_MINOR)
@@ -4495,6 +4495,97 @@ CINDEX_LINKAGE CXStringSet *clang_Cursor_getCXXManglings(CXCursor);
*/
CINDEX_LINKAGE CXStringSet *clang_Cursor_getObjCManglings(CXCursor);
+/**
+ * @}
+ */
+
+/**
+ * \defgroup CINDEX_MODULE Inline Assembly introspection
+ *
+ * The functions in this group provide access to information about GCC-style
+ * inline assembly statements.
+ *
+ * @{
+ */
+
+/**
+ * Given a CXCursor_GCCAsmStmt cursor, return the assembly template string.
+ * As per LLVM IR Assembly Template language, template placeholders for
+ * inputs and outputs are either of the form $N where N is a decimal number
+ * as an index into the input-output specification,
+ * or ${N:M} where N is a decimal number also as an index into the
+ * input-output specification and M is the template argument modifier.
+ * The index N in both cases points into the the total inputs and outputs,
+ * or more specifically, into the list of outputs followed by the inputs,
+ * starting from index 0 as the first available template argument.
+ */
+
+CINDEX_LINKAGE CXString clang_Cursor_getGCCAssemblyTemplate(CXCursor);
+
+/**
+ * Given a CXCursor_GCCAsmStmt cursor, check if the assembly block has goto
+ * labels.
+ */
+
+CINDEX_LINKAGE unsigned clang_Cursor_isGCCAssemblyHasGoto(CXCursor);
+
+/**
+ * Given a CXCursor_GCCAsmStmt cursor, count the number of outputs
+ */
+
+CINDEX_LINKAGE unsigned clang_Cursor_getGCCAssemblyNumOutputs(CXCursor);
+
+/**
+ * Given a CXCursor_GCCAsmStmt cursor, count the number of inputs
+ */
+
+CINDEX_LINKAGE unsigned clang_Cursor_getGCCAssemblyNumInputs(CXCursor);
+
+/**
+ * Given a CXCursor_GCCAsmStmt cursor, get the constraint and expression cursor
+ * to the Index-th input.
+ */
+
+CINDEX_LINKAGE unsigned clang_Cursor_getGCCAssemblyInput(CXCursor Cursor,
+ unsigned Index,
+ CXString *Constraint,
+ CXCursor *Expr);
+
+/**
+ * Given a CXCursor_GCCAsmStmt cursor, get the constraint and expression cursor
+ * to the Index-th output.
+ */
+
+CINDEX_LINKAGE unsigned clang_Cursor_getGCCAssemblyOutput(CXCursor Cursor,
+ unsigned Index,
+ CXString *Constraint,
+ CXCursor *Expr);
+
+/**
+ * Given a CXCursor_GCCAsmStmt cursor, count the clobbers in it.
+ */
+
+unsigned clang_Cursor_getGCCAssemblyNumClobbers(CXCursor Cursor);
+
+/**
+ * Given a CXCursor_GCCAsmStmt cursor, get the Index-th clobber of it.
+ */
+
+CXString clang_Cursor_getGCCAssemblyClobber(CXCursor Cursor, unsigned Index);
+
+/**
+ * Given a CXCursor_GCCAsmStmt cursor, check if the inline assembly is simple.
+ */
+
+unsigned clang_Cursor_isGCCAssemblySimple(CXCursor Cursor);
+
+/**
+ * Given a CXCursor_GCCAsmStmt cursor, check if the inline assembly is
+ * `volatile`.
+ */
+
+unsigned clang_Cursor_isGCCAssemblyVolatile(CXCursor Cursor);
+
/**
* @}
*/
diff --git a/clang/test/Index/inline-assembly.c b/clang/test/Index/inline-assembly.c
new file mode 100644
index 0000000000000..2223dd2985f51
--- /dev/null
+++ b/clang/test/Index/inline-assembly.c
@@ -0,0 +1,48 @@
+static void inline_assembly_template_regardless_of_target_machine() {
+ int tmp;
+ asm volatile (
+ "nop\n"
+ "a_value %w[v]\n"
+ "o_value %w[o]"
+ : [v] "=&r" (tmp)
+ : [o] "r" (tmp)
+ : "cc", "memory"
+ );
+}
+
+// RUN: c-index-test -test-inline-assembly %s 2>&1 | FileCheck %s
+// CHECK: ===ASM TEMPLATE===
+// CHECK: nop
+// CHECK: a_value ${0:w}
+// CHECK: o_value ${1:w}
+// CHECK: ===ASM TEMPLATE END===
+// CHECK: volatile: true
+// CHECK: simple: false
+// CHECK: Output #0 Constraint (=&r): DeclRefExpr=tmp:2:9
+// CHECK: Input #0 Constraint (r): UnexposedExpr=tmp:2:9
+// CHECK: Clobber #0: cc
+// CHECK: Clobber #1: memory
+// CHECK: ===ASM END===
+
+static void inline_assembly_valid_x86_example() {
+ int tmp;
+ asm (
+ "nop\n"
+ "mov %w[o], %w[v]"
+ : [v] "=&r" (tmp)
+ : [o] "r" (tmp)
+ : "cc", "memory"
+ );
+}
+
+// CHECK: ===ASM TEMPLATE===
+// CHECK: nop
+// CHECK: mov ${1:w}, ${0:w}
+// CHECK: ===ASM TEMPLATE END===
+// CHECK: volatile: false
+// CHECK: simple: false
+// CHECK: Output #0 Constraint (=&r): DeclRefExpr=tmp:28:9
+// CHECK: Input #0 Constraint (r): UnexposedExpr=tmp:28:9
+// CHECK: Clobber #0: cc
+// CHECK: Clobber #1: memory
+// CHECK: ===ASM END===
diff --git a/clang/tools/c-index-test/c-index-test.c b/clang/tools/c-index-test/c-index-test.c
index 4a887cd0c1e2e..0917439898f98 100644
--- a/clang/tools/c-index-test/c-index-test.c
+++ b/clang/tools/c-index-test/c-index-test.c
@@ -1988,6 +1988,53 @@ static enum CXChildVisitResult PrintDeclAttributes(CXCursor cursor, CXCursor p,
return CXChildVisit_Continue;
}
+/******************************************************************************/
+/* Inline assembly cursor testing */
+/******************************************************************************/
+
+static enum CXChildVisitResult
+PrintGCCInlineAssembly(CXCursor cursor, CXCursor p, CXClientData d) {
+ CXString Constraint, Template, Clobber;
+ CXCursor Expr;
+ unsigned hasGoto, i, e;
+ if (clang_getCursorKind(cursor) != CXCursor_AsmStmt) {
+ return CXChildVisit_Recurse;
+ }
+ hasGoto = clang_Cursor_isGCCAssemblyHasGoto(cursor);
+ printf("===ASM TEMPLATE%s===\n", hasGoto ? " (WITH GOTO)" : "");
+ Template = clang_Cursor_getGCCAssemblyTemplate(cursor);
+ printf("%s", clang_getCString(Template));
+ clang_disposeString(Template);
+ printf("\n===ASM TEMPLATE END===\n");
+
+ printf("volatile: %s\n",
+ clang_Cursor_isGCCAssemblyVolatile(cursor) ? "true" : "false");
+ printf("simple: %s\n",
+ clang_Cursor_isGCCAssemblySimple(cursor) ? "true" : "false");
+
+ for (i = 0, e = clang_Cursor_getGCCAssemblyNumOutputs(cursor); i < e; ++i) {
+ clang_Cursor_getGCCAssemblyOutput(cursor, i, &Constraint, &Expr);
+ printf("Output #%d Constraint (%s): ", i, clang_getCString(Constraint));
+ PrintCursor(Expr, NULL);
+ printf("\n");
+ clang_disposeString(Constraint);
+ }
+ for (i = 0, e = clang_Cursor_getGCCAssemblyNumInputs(cursor); i < e; ++i) {
+ clang_Cursor_getGCCAssemblyInput(cursor, i, &Constraint, &Expr);
+ printf("Input #%d Constraint (%s): ", i, clang_getCString(Constraint));
+ PrintCursor(Expr, NULL);
+ printf("\n");
+ clang_disposeString(Constraint);
+ }
+ for (i = 0, e = clang_Cursor_getGCCAssemblyNumClobbers(cursor); i < e; ++i) {
+ Clobber = clang_Cursor_getGCCAssemblyClobber(cursor, i);
+ printf("Clobber #%d: %s\n", i, clang_getCString(Clobber));
+ clang_disposeString(Constraint);
+ }
+ printf("===ASM END===\n");
+ return CXChildVisit_Recurse;
+}
+
/******************************************************************************/
/* Target information testing. */
/******************************************************************************/
@@ -5010,6 +5057,7 @@ static void print_usage(void) {
" c-index-test -test-annotate-tokens=<range> {<args>}*\n"
" c-index-test -test-inclusion-stack-source {<args>}*\n"
" c-index-test -test-inclusion-stack-tu <AST file>\n");
+ fprintf(stderr, " c-index-test -test-inline-assembly <AST file>\n");
fprintf(stderr,
" c-index-test -test-print-linkage-source {<args>}*\n"
" c-index-test -test-print-visibility {<args>}*\n"
@@ -5167,6 +5215,10 @@ int cindextest_main(int argc, const char **argv) {
else if (argc > 2 && strstr(argv[1], "-single-symbol-sgf-for=") == argv[1])
return perform_test_single_symbol_sgf(argv[1], argc - 2, argv + 2);
+ if (argc > 2 && strstr(argv[1], "-test-inline-assembly") == argv[1])
+ return perform_test_load_source(argc - 2, argv + 2, "all",
+ PrintGCCInlineAssembly, NULL);
+
print_usage();
return 1;
}
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 3068621d9c004..3d7aa19c10645 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -2141,8 +2141,7 @@ class EnqueueVisitor : public ConstStmtVisitor<EnqueueVisitor, void>,
void VisitBlockExpr(const BlockExpr *B);
void VisitCompoundLiteralExpr(const CompoundLiteralExpr *E);
void VisitCompoundStmt(const CompoundStmt *S);
- void VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) { /* Do nothing. */
- }
+ void VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) { /* Do nothing. */ }
void VisitMSDependentExistsStmt(const MSDependentExistsStmt *S);
void VisitCXXDependentScopeMemberExpr(const CXXDependentScopeMemberExpr *E);
void VisitCXXNewExpr(const CXXNewExpr *E);
@@ -2252,8 +2251,8 @@ class EnqueueVisitor : public ConstStmtVisitor<EnqueueVisitor, void>,
void VisitOMPMaskedTaskLoopDirective(const OMPMaskedTaskLoopDirective *D);
void
VisitOMPMasterTaskLoopSimdDirective(const OMPMasterTaskLoopSimdDirective *D);
- void VisitOMPMaskedTaskLoopSimdDirective(
- const OMPMaskedTaskLoopSimdDirective *D);
+ void
+ VisitOMPMaskedTaskLoopSimdDirective(const OMPMaskedTaskLoopSimdDirective *D);
void VisitOMPParallelMasterTaskLoopDirective(
const OMPParallelMasterTaskLoopDirective *D);
void VisitOMPParallelMaskedTaskLoopDirective(
@@ -2811,8 +2810,7 @@ void OMPClauseEnqueue::VisitOMPXDynCGroupMemClause(
void OMPClauseEnqueue::VisitOMPDoacrossClause(const OMPDoacrossClause *C) {
VisitOMPClauseList(C);
}
-void OMPClauseEnqueue::VisitOMPXAttributeClause(const OMPXAttributeClause *C) {
-}
+void OMPClauseEnqueue::VisitOMPXAttributeClause(const OMPXAttributeClause *C) {}
void OMPClauseEnqueue::VisitOMPXBareClause(const OMPXBareClause *C) {}
} // namespace
@@ -5290,7 +5288,7 @@ typedef struct _CXChildVisitResult {
int reserved;
enum CXChildVisitResult (*invoke)(struct _CXChildVisitResult *, CXCursor,
CXCursor);
-} * CXCursorVisitorBlock;
+} *CXCursorVisitorBlock;
static enum CXChildVisitResult visitWithBlock(CXCursor cursor, CXCursor parent,
CXClientData client_data) {
@@ -8648,6 +8646,119 @@ void clang_annotateTokens(CXTranslationUnit TU, CXToken *Tokens,
}
}
+//===----------------------------------------------------------------------===//
+// Operations for querying information of a GCC inline assembly block under a
+// cursor.
+//===----------------------------------------------------------------------===//
+CXString clang_Cursor_getGCCAssemblyTemplate(CXCursor Cursor) {
+ if (!clang_isStatement(Cursor.kind))
+ return cxstring::createEmpty();
+ if (auto const *Stmt = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor))) {
+ auto const &C = getCursorContext(Cursor);
+ auto AsmTemplate = Stmt->generateAsmString(C);
+ return cxstring::createDup(AsmTemplate);
+ }
+ return cxstring::createEmpty();
+}
+
+unsigned clang_Cursor_isGCCAssemblyHasGoto(CXCursor Cursor) {
+ if (!clang_isStatement(Cursor.kind))
+ return 0;
+ if (auto const *Stmt = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor))) {
+ return Stmt->isAsmGoto();
+ }
+ return 0;
+}
+
+unsigned clang_Cursor_getGCCAssemblyNumOutputs(CXCursor Cursor) {
+ if (!clang_isStatement(Cursor.kind))
+ return 0;
+ if (auto const *Stmt = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor))) {
+ return Stmt->getNumOutputs();
+ }
+ return 0;
+}
+
+unsigned clang_Cursor_getGCCAssemblyNumInputs(CXCursor Cursor) {
+ if (!clang_isStatement(Cursor.kind))
+ return 0;
+ if (auto const *Stmt = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor))) {
+ return Stmt->getNumInputs();
+ }
+ return 0;
+}
+
+unsigned clang_Cursor_getGCCAssemblyInput(CXCursor Cursor, unsigned Index,
+ CXString *Constraint,
+ CXCursor *Expr) {
+ if (!clang_isStatement(Cursor.kind) || !Constraint || !Expr)
+ return 0;
+ if (auto const *Stmt = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor));
+ Stmt && Index < Stmt->getNumInputs()) {
+ auto Constraint_ = Stmt->getInputConstraint(Index);
+ auto const *Expr_ = Stmt->getInputExpr(Index);
+ *Constraint = cxstring::createDup(Constraint_);
+ *Expr = MakeCXCursor(Expr_, getCursorDecl(Cursor),
+ cxcursor::getCursorTU(Cursor));
+ return 1;
+ }
+ return 0;
+}
+
+unsigned clang_Cursor_getGCCAssemblyOutput(CXCursor Cursor, unsigned Index,
+ CXString *Constraint,
+ CXCursor *Expr) {
+ if (!clang_isStatement(Cursor.kind) || !Constraint || !Expr)
+ return 0;
+ if (auto const *Stmt = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor));
+ Stmt && Index < Stmt->getNumOutputs()) {
+ auto Constraint_ = Stmt->getOutputConstraint(Index);
+ auto const *Expr_ = Stmt->getOutputExpr(Index);
+ *Constraint = cxstring::createDup(Constraint_);
+ *Expr = MakeCXCursor(Expr_, getCursorDecl(Cursor),
+ cxcursor::getCursorTU(Cursor));
+ return 1;
+ }
+ return 0;
+}
+
+unsigned clang_Cursor_getGCCAssemblyNumClobbers(CXCursor Cursor) {
+ if (!clang_isStatement(Cursor.kind))
+ return 0;
+ if (auto const *Stmt = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor))) {
+ return Stmt->getNumClobbers();
+ }
+ return 0;
+}
+
+CXString clang_Cursor_getGCCAssemblyClobber(CXCursor Cursor, unsigned Index) {
+ if (!clang_isStatement(Cursor.kind))
+ return cxstring::createEmpty();
+ if (auto const *Stmt = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor));
+ Stmt && Stmt->getNumClobbers()) {
+ return cxstring::createDup(Stmt->getClobber(Index));
+ }
+ return cxstring::createEmpty();
+}
+
+unsigned clang_Cursor_isGCCAssemblySimple(CXCursor Cursor) {
+ if (!clang_isStatement(Cursor.kind))
+ return 0;
+ if (auto const *Stmt = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor))) {
+ return Stmt->isSimple();
+ }
+ return 0;
+}
+
+unsigned clang_Cursor_isGCCAssemblyVolatile(CXCursor Cursor) {
+ if (!clang_isStatement(Cursor.kind))
+ return 0;
+ if (auto const *Stmt = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor))) {
+ return Stmt->isVolatile();
+ }
+ return 0;
+}
+
//===----------------------------------------------------------------------===//
// Operations for querying linkage of a cursor.
//===----------------------------------------------------------------------===//
diff --git a/clang/tools/libclang/libclang.map b/clang/tools/libclang/libclang.map
index f08d13c3da9e1..b3a12e9e9834b 100644
--- a/clang/tools/libclang/libclang.map
+++ b/clang/tools/libclang/libclang.map
@@ -441,6 +441,16 @@ LLVM_20 {
LLVM_21 {
global:
clang_getFullyQualifiedName;
+ clang_Cursor_getGCCAssemblyTemplate;
+ clang_Cursor_isGCCAssemblyHasGoto;
+ clang_Cursor_getGCCAssemblyNumOutputs;
+ clang_Cursor_getGCCAssemblyNumInputs;
+ clang_Cursor_getGCCAssemblyInput;
+ clang_Cursor_getGCCAssemblyOutput;
+ clang_Cursor_getGCCAssemblyNumClobbers;
+ clang_Cursor_getGCCAssemblyClobber;
+ clang_Cursor_isGCCAssemblySimple;
+ clang_Cursor_isGCCAssemblyVolatile;
};
# Example of how to add a new symbol version entry. If you do add a new symbol
More information about the cfe-commits
mailing list