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 <dingxiangfei2...@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 <dingxiangfei2...@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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits