[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 <dingxiangfei2009@protonmail.ch>
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
46
clang/test/Index/inline-assembly.c
Normal file
46
clang/test/Index/inline-assembly.c
Normal file
@@ -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===
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user