Author: wieDasDing Date: 2025-06-25T10:08:56-04:00 New Revision: d76fdf7f536c45adc08bdbeaf8b78439d604faf5
URL: https://github.com/llvm/llvm-project/commit/d76fdf7f536c45adc08bdbeaf8b78439d604faf5 DIFF: https://github.com/llvm/llvm-project/commit/d76fdf7f536c45adc08bdbeaf8b78439d604faf5.diff LOG: [clang-c] introduce queries on GCC-style inline assembly statements (#143424) [Discourse link](https://discourse.llvm.org/t/a-small-proposal-for-extraction-of-inline-assembly-block-information/86658) 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. These APIs opens new opportunities for `rust-bindgen` to translate inline assemblies in reasonably cases into Rust inline assembly blocks, which would further aid better interoperability with other existing code. --------- Signed-off-by: Xiangfei Ding <dingxiangfei2...@protonmail.ch> Added: clang/test/Index/inline-assembly.c Modified: clang/docs/ReleaseNotes.rst clang/include/clang-c/Index.h clang/tools/c-index-test/c-index-test.c clang/tools/libclang/CIndex.cpp clang/tools/libclang/libclang.map Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index e1fe22393eebb..047b1b929c0d9 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -329,6 +329,9 @@ Non-comprehensive list of changes in this release ``__reference_constructs_from_temporary`` should be used instead. (#GH44056) - Added `__builtin_get_vtable_pointer` to directly load the primary vtable pointer from a polymorphic object. +- ``libclang`` receives a family of new bindings to query basic facts about + GCC-style inline assembly blocks, including whether the block is ``volatile`` + and its template string following the LLVM IR ``asm`` format. (#GH143424) - Clang no longer rejects reinterpret_cast conversions between indirect ARC-managed pointers and other pointer types. The prior behavior was overly strict and inconsistent with the ARC specification. diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h index e4cb4327fbaac..c35311c886413 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,129 @@ 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. + * + * This function also returns a valid empty string if the cursor does not point + * at a GCC inline assembly block. + * + * Users are responsible for releasing the allocation of returned string via + * \c clang_disposeString. + */ + +CINDEX_LINKAGE CXString clang_Cursor_getGCCAssemblyTemplate(CXCursor); + +/** + * Given a CXCursor_GCCAsmStmt cursor, check if the assembly block has goto + * labels. + * This function also returns 0 if the cursor does not point at a GCC inline + * assembly block. + */ + +CINDEX_LINKAGE unsigned clang_Cursor_isGCCAssemblyHasGoto(CXCursor); + +/** + * Given a CXCursor_GCCAsmStmt cursor, count the number of outputs. + * This function also returns 0 if the cursor does not point at a GCC inline + * assembly block. + */ + +CINDEX_LINKAGE unsigned clang_Cursor_getGCCAssemblyNumOutputs(CXCursor); + +/** + * Given a CXCursor_GCCAsmStmt cursor, count the number of inputs. + * This function also returns 0 if the cursor does not point at a GCC inline + * assembly block. + */ + +CINDEX_LINKAGE unsigned clang_Cursor_getGCCAssemblyNumInputs(CXCursor); + +/** + * Given a CXCursor_GCCAsmStmt cursor, get the constraint and expression cursor + * to the Index-th input. + * This function returns 1 when the cursor points at a GCC inline assembly + * statement, `Index` is within bounds and both the `Constraint` and `Expr` are + * not NULL. + * Otherwise, this function returns 0 but leaves `Constraint` and `Expr` + * intact. + * + * Users are responsible for releasing the allocation of `Constraint` via + * \c clang_disposeString. + */ + +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. + * This function returns 1 when the cursor points at a GCC inline assembly + * statement, `Index` is within bounds and both the `Constraint` and `Expr` are + * not NULL. + * Otherwise, this function returns 0 but leaves `Constraint` and `Expr` + * intact. + * + * Users are responsible for releasing the allocation of `Constraint` via + * \c clang_disposeString. + */ + +CINDEX_LINKAGE unsigned clang_Cursor_getGCCAssemblyOutput(CXCursor Cursor, + unsigned Index, + CXString *Constraint, + CXCursor *Expr); + +/** + * Given a CXCursor_GCCAsmStmt cursor, count the clobbers in it. + * This function also returns 0 if the cursor does not point at a GCC inline + * assembly block. + */ + +CINDEX_LINKAGE unsigned clang_Cursor_getGCCAssemblyNumClobbers(CXCursor Cursor); + +/** + * Given a CXCursor_GCCAsmStmt cursor, get the Index-th clobber of it. + * This function returns a valid empty string if the cursor does not point + * at a GCC inline assembly block or `Index` is out of bounds. + * + * Users are responsible for releasing the allocation of returned string via + * \c clang_disposeString. + */ + +CINDEX_LINKAGE CXString clang_Cursor_getGCCAssemblyClobber(CXCursor Cursor, + unsigned Index); + +/** + * Given a CXCursor_GCCAsmStmt cursor, check if the inline assembly is + * `volatile`. + * This function returns 0 if the cursor does not point at a GCC inline + * assembly block. + */ + +CINDEX_LINKAGE 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..64a7ce03852c9 --- /dev/null +++ b/clang/test/Index/inline-assembly.c @@ -0,0 +1,46 @@ +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: 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: Output #0 Constraint (=&r): DeclRefExpr=tmp:27:9 +// CHECK: Input #0 Constraint (r): UnexposedExpr=tmp:27: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..cb3245756a394 100644 --- a/clang/tools/c-index-test/c-index-test.c +++ b/clang/tools/c-index-test/c-index-test.c @@ -1988,6 +1988,51 @@ 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"); + + 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(Clobber); + } + printf("===ASM END===\n"); + return CXChildVisit_Recurse; +} + /******************************************************************************/ /* Target information testing. */ /******************************************************************************/ @@ -5010,6 +5055,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 +5213,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..e239ffae547aa 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -8648,6 +8648,100 @@ 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 *S = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor))) { + ASTContext const &C = getCursorContext(Cursor); + std::string AsmTemplate = S->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 *S = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor))) + return S->isAsmGoto(); + return 0; +} + +unsigned clang_Cursor_getGCCAssemblyNumOutputs(CXCursor Cursor) { + if (!clang_isStatement(Cursor.kind)) + return 0; + if (auto const *S = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor))) + return S->getNumOutputs(); + return 0; +} + +unsigned clang_Cursor_getGCCAssemblyNumInputs(CXCursor Cursor) { + if (!clang_isStatement(Cursor.kind)) + return 0; + if (auto const *S = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor))) + return S->getNumInputs(); + return 0; +} + +unsigned clang_Cursor_getGCCAssemblyInput(CXCursor Cursor, unsigned Index, + CXString *Constraint, + CXCursor *ExprCursor) { + if (!clang_isStatement(Cursor.kind) || !Constraint || !ExprCursor) + return 0; + if (auto const *S = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor)); + S && Index < S->getNumInputs()) { + *Constraint = cxstring::createDup(S->getInputConstraint(Index)); + *ExprCursor = MakeCXCursor(S->getInputExpr(Index), getCursorDecl(Cursor), + cxcursor::getCursorTU(Cursor)); + return 1; + } + return 0; +} + +unsigned clang_Cursor_getGCCAssemblyOutput(CXCursor Cursor, unsigned Index, + CXString *Constraint, + CXCursor *ExprCursor) { + if (!clang_isStatement(Cursor.kind) || !Constraint || !ExprCursor) + return 0; + if (auto const *S = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor)); + S && Index < S->getNumOutputs()) { + *Constraint = cxstring::createDup(S->getOutputConstraint(Index)); + *ExprCursor = MakeCXCursor(S->getOutputExpr(Index), 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 *S = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor))) + return S->getNumClobbers(); + return 0; +} + +CXString clang_Cursor_getGCCAssemblyClobber(CXCursor Cursor, unsigned Index) { + if (!clang_isStatement(Cursor.kind)) + return cxstring::createEmpty(); + if (auto const *S = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor)); + S && Index < S->getNumClobbers()) + return cxstring::createDup(S->getClobber(Index)); + return cxstring::createEmpty(); +} + +unsigned clang_Cursor_isGCCAssemblyVolatile(CXCursor Cursor) { + if (!clang_isStatement(Cursor.kind)) + return 0; + if (auto const *S = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor))) + return S->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..d140a71e771a0 100644 --- a/clang/tools/libclang/libclang.map +++ b/clang/tools/libclang/libclang.map @@ -441,6 +441,15 @@ 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_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