Skip to content

Commit d76fdf7

Browse files
[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 <[email protected]>
1 parent fd3cc20 commit d76fdf7

File tree

6 files changed

+326
-1
lines changed

6 files changed

+326
-1
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,9 @@ Non-comprehensive list of changes in this release
329329
``__reference_constructs_from_temporary`` should be used instead. (#GH44056)
330330
- Added `__builtin_get_vtable_pointer` to directly load the primary vtable pointer from a
331331
polymorphic object.
332+
- ``libclang`` receives a family of new bindings to query basic facts about
333+
GCC-style inline assembly blocks, including whether the block is ``volatile``
334+
and its template string following the LLVM IR ``asm`` format. (#GH143424)
332335
- Clang no longer rejects reinterpret_cast conversions between indirect
333336
ARC-managed pointers and other pointer types. The prior behavior was overly
334337
strict and inconsistent with the ARC specification.

clang/include/clang-c/Index.h

Lines changed: 124 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
#define CINDEX_VERSION_MAJOR 0
3737
#define CINDEX_VERSION_MINOR 64
3838

39-
#define CINDEX_VERSION_ENCODE(major, minor) (((major)*10000) + ((minor)*1))
39+
#define CINDEX_VERSION_ENCODE(major, minor) (((major) * 10000) + ((minor) * 1))
4040

4141
#define CINDEX_VERSION \
4242
CINDEX_VERSION_ENCODE(CINDEX_VERSION_MAJOR, CINDEX_VERSION_MINOR)
@@ -4495,6 +4495,129 @@ CINDEX_LINKAGE CXStringSet *clang_Cursor_getCXXManglings(CXCursor);
44954495
*/
44964496
CINDEX_LINKAGE CXStringSet *clang_Cursor_getObjCManglings(CXCursor);
44974497

4498+
/**
4499+
* @}
4500+
*/
4501+
4502+
/**
4503+
* \defgroup CINDEX_MODULE Inline Assembly introspection
4504+
*
4505+
* The functions in this group provide access to information about GCC-style
4506+
* inline assembly statements.
4507+
*
4508+
* @{
4509+
*/
4510+
4511+
/**
4512+
* Given a CXCursor_GCCAsmStmt cursor, return the assembly template string.
4513+
* As per LLVM IR Assembly Template language, template placeholders for
4514+
* inputs and outputs are either of the form $N where N is a decimal number
4515+
* as an index into the input-output specification,
4516+
* or ${N:M} where N is a decimal number also as an index into the
4517+
* input-output specification and M is the template argument modifier.
4518+
* The index N in both cases points into the the total inputs and outputs,
4519+
* or more specifically, into the list of outputs followed by the inputs,
4520+
* starting from index 0 as the first available template argument.
4521+
*
4522+
* This function also returns a valid empty string if the cursor does not point
4523+
* at a GCC inline assembly block.
4524+
*
4525+
* Users are responsible for releasing the allocation of returned string via
4526+
* \c clang_disposeString.
4527+
*/
4528+
4529+
CINDEX_LINKAGE CXString clang_Cursor_getGCCAssemblyTemplate(CXCursor);
4530+
4531+
/**
4532+
* Given a CXCursor_GCCAsmStmt cursor, check if the assembly block has goto
4533+
* labels.
4534+
* This function also returns 0 if the cursor does not point at a GCC inline
4535+
* assembly block.
4536+
*/
4537+
4538+
CINDEX_LINKAGE unsigned clang_Cursor_isGCCAssemblyHasGoto(CXCursor);
4539+
4540+
/**
4541+
* Given a CXCursor_GCCAsmStmt cursor, count the number of outputs.
4542+
* This function also returns 0 if the cursor does not point at a GCC inline
4543+
* assembly block.
4544+
*/
4545+
4546+
CINDEX_LINKAGE unsigned clang_Cursor_getGCCAssemblyNumOutputs(CXCursor);
4547+
4548+
/**
4549+
* Given a CXCursor_GCCAsmStmt cursor, count the number of inputs.
4550+
* This function also returns 0 if the cursor does not point at a GCC inline
4551+
* assembly block.
4552+
*/
4553+
4554+
CINDEX_LINKAGE unsigned clang_Cursor_getGCCAssemblyNumInputs(CXCursor);
4555+
4556+
/**
4557+
* Given a CXCursor_GCCAsmStmt cursor, get the constraint and expression cursor
4558+
* to the Index-th input.
4559+
* This function returns 1 when the cursor points at a GCC inline assembly
4560+
* statement, `Index` is within bounds and both the `Constraint` and `Expr` are
4561+
* not NULL.
4562+
* Otherwise, this function returns 0 but leaves `Constraint` and `Expr`
4563+
* intact.
4564+
*
4565+
* Users are responsible for releasing the allocation of `Constraint` via
4566+
* \c clang_disposeString.
4567+
*/
4568+
4569+
CINDEX_LINKAGE unsigned clang_Cursor_getGCCAssemblyInput(CXCursor Cursor,
4570+
unsigned Index,
4571+
CXString *Constraint,
4572+
CXCursor *Expr);
4573+
4574+
/**
4575+
* Given a CXCursor_GCCAsmStmt cursor, get the constraint and expression cursor
4576+
* to the Index-th output.
4577+
* This function returns 1 when the cursor points at a GCC inline assembly
4578+
* statement, `Index` is within bounds and both the `Constraint` and `Expr` are
4579+
* not NULL.
4580+
* Otherwise, this function returns 0 but leaves `Constraint` and `Expr`
4581+
* intact.
4582+
*
4583+
* Users are responsible for releasing the allocation of `Constraint` via
4584+
* \c clang_disposeString.
4585+
*/
4586+
4587+
CINDEX_LINKAGE unsigned clang_Cursor_getGCCAssemblyOutput(CXCursor Cursor,
4588+
unsigned Index,
4589+
CXString *Constraint,
4590+
CXCursor *Expr);
4591+
4592+
/**
4593+
* Given a CXCursor_GCCAsmStmt cursor, count the clobbers in it.
4594+
* This function also returns 0 if the cursor does not point at a GCC inline
4595+
* assembly block.
4596+
*/
4597+
4598+
CINDEX_LINKAGE unsigned clang_Cursor_getGCCAssemblyNumClobbers(CXCursor Cursor);
4599+
4600+
/**
4601+
* Given a CXCursor_GCCAsmStmt cursor, get the Index-th clobber of it.
4602+
* This function returns a valid empty string if the cursor does not point
4603+
* at a GCC inline assembly block or `Index` is out of bounds.
4604+
*
4605+
* Users are responsible for releasing the allocation of returned string via
4606+
* \c clang_disposeString.
4607+
*/
4608+
4609+
CINDEX_LINKAGE CXString clang_Cursor_getGCCAssemblyClobber(CXCursor Cursor,
4610+
unsigned Index);
4611+
4612+
/**
4613+
* Given a CXCursor_GCCAsmStmt cursor, check if the inline assembly is
4614+
* `volatile`.
4615+
* This function returns 0 if the cursor does not point at a GCC inline
4616+
* assembly block.
4617+
*/
4618+
4619+
CINDEX_LINKAGE unsigned clang_Cursor_isGCCAssemblyVolatile(CXCursor Cursor);
4620+
44984621
/**
44994622
* @}
45004623
*/

clang/test/Index/inline-assembly.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
static void inline_assembly_template_regardless_of_target_machine() {
2+
int tmp;
3+
asm volatile (
4+
"nop\n"
5+
"a_value %w[v]\n"
6+
"o_value %w[o]"
7+
: [v] "=&r" (tmp)
8+
: [o] "r" (tmp)
9+
: "cc", "memory"
10+
);
11+
}
12+
13+
// RUN: c-index-test -test-inline-assembly %s 2>&1 | FileCheck %s
14+
// CHECK: ===ASM TEMPLATE===
15+
// CHECK: nop
16+
// CHECK: a_value ${0:w}
17+
// CHECK: o_value ${1:w}
18+
// CHECK: ===ASM TEMPLATE END===
19+
// CHECK: volatile: true
20+
// CHECK: Output #0 Constraint (=&r): DeclRefExpr=tmp:2:9
21+
// CHECK: Input #0 Constraint (r): UnexposedExpr=tmp:2:9
22+
// CHECK: Clobber #0: cc
23+
// CHECK: Clobber #1: memory
24+
// CHECK: ===ASM END===
25+
26+
static void inline_assembly_valid_x86_example() {
27+
int tmp;
28+
asm (
29+
"nop\n"
30+
"mov %w[o], %w[v]"
31+
: [v] "=&r" (tmp)
32+
: [o] "r" (tmp)
33+
: "cc", "memory"
34+
);
35+
}
36+
37+
// CHECK: ===ASM TEMPLATE===
38+
// CHECK: nop
39+
// CHECK: mov ${1:w}, ${0:w}
40+
// CHECK: ===ASM TEMPLATE END===
41+
// CHECK: volatile: false
42+
// CHECK: Output #0 Constraint (=&r): DeclRefExpr=tmp:27:9
43+
// CHECK: Input #0 Constraint (r): UnexposedExpr=tmp:27:9
44+
// CHECK: Clobber #0: cc
45+
// CHECK: Clobber #1: memory
46+
// CHECK: ===ASM END===

clang/tools/c-index-test/c-index-test.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1988,6 +1988,51 @@ static enum CXChildVisitResult PrintDeclAttributes(CXCursor cursor, CXCursor p,
19881988
return CXChildVisit_Continue;
19891989
}
19901990

1991+
/******************************************************************************/
1992+
/* Inline assembly cursor testing */
1993+
/******************************************************************************/
1994+
1995+
static enum CXChildVisitResult
1996+
PrintGCCInlineAssembly(CXCursor cursor, CXCursor p, CXClientData d) {
1997+
CXString Constraint, Template, Clobber;
1998+
CXCursor Expr;
1999+
unsigned hasGoto, i, e;
2000+
if (clang_getCursorKind(cursor) != CXCursor_AsmStmt)
2001+
return CXChildVisit_Recurse;
2002+
2003+
hasGoto = clang_Cursor_isGCCAssemblyHasGoto(cursor);
2004+
printf("===ASM TEMPLATE%s===\n", hasGoto ? " (WITH GOTO)" : "");
2005+
Template = clang_Cursor_getGCCAssemblyTemplate(cursor);
2006+
printf("%s", clang_getCString(Template));
2007+
clang_disposeString(Template);
2008+
printf("\n===ASM TEMPLATE END===\n");
2009+
2010+
printf("volatile: %s\n",
2011+
clang_Cursor_isGCCAssemblyVolatile(cursor) ? "true" : "false");
2012+
2013+
for (i = 0, e = clang_Cursor_getGCCAssemblyNumOutputs(cursor); i < e; ++i) {
2014+
clang_Cursor_getGCCAssemblyOutput(cursor, i, &Constraint, &Expr);
2015+
printf("Output #%d Constraint (%s): ", i, clang_getCString(Constraint));
2016+
PrintCursor(Expr, NULL);
2017+
printf("\n");
2018+
clang_disposeString(Constraint);
2019+
}
2020+
for (i = 0, e = clang_Cursor_getGCCAssemblyNumInputs(cursor); i < e; ++i) {
2021+
clang_Cursor_getGCCAssemblyInput(cursor, i, &Constraint, &Expr);
2022+
printf("Input #%d Constraint (%s): ", i, clang_getCString(Constraint));
2023+
PrintCursor(Expr, NULL);
2024+
printf("\n");
2025+
clang_disposeString(Constraint);
2026+
}
2027+
for (i = 0, e = clang_Cursor_getGCCAssemblyNumClobbers(cursor); i < e; ++i) {
2028+
Clobber = clang_Cursor_getGCCAssemblyClobber(cursor, i);
2029+
printf("Clobber #%d: %s\n", i, clang_getCString(Clobber));
2030+
clang_disposeString(Clobber);
2031+
}
2032+
printf("===ASM END===\n");
2033+
return CXChildVisit_Recurse;
2034+
}
2035+
19912036
/******************************************************************************/
19922037
/* Target information testing. */
19932038
/******************************************************************************/
@@ -5010,6 +5055,7 @@ static void print_usage(void) {
50105055
" c-index-test -test-annotate-tokens=<range> {<args>}*\n"
50115056
" c-index-test -test-inclusion-stack-source {<args>}*\n"
50125057
" c-index-test -test-inclusion-stack-tu <AST file>\n");
5058+
fprintf(stderr, " c-index-test -test-inline-assembly <AST file>\n");
50135059
fprintf(stderr,
50145060
" c-index-test -test-print-linkage-source {<args>}*\n"
50155061
" c-index-test -test-print-visibility {<args>}*\n"
@@ -5167,6 +5213,10 @@ int cindextest_main(int argc, const char **argv) {
51675213
else if (argc > 2 && strstr(argv[1], "-single-symbol-sgf-for=") == argv[1])
51685214
return perform_test_single_symbol_sgf(argv[1], argc - 2, argv + 2);
51695215

5216+
if (argc > 2 && strstr(argv[1], "-test-inline-assembly") == argv[1])
5217+
return perform_test_load_source(argc - 2, argv + 2, "all",
5218+
PrintGCCInlineAssembly, NULL);
5219+
51705220
print_usage();
51715221
return 1;
51725222
}

clang/tools/libclang/CIndex.cpp

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8648,6 +8648,100 @@ void clang_annotateTokens(CXTranslationUnit TU, CXToken *Tokens,
86488648
}
86498649
}
86508650

8651+
//===----------------------------------------------------------------------===//
8652+
// Operations for querying information of a GCC inline assembly block under a
8653+
// cursor.
8654+
//===----------------------------------------------------------------------===//
8655+
CXString clang_Cursor_getGCCAssemblyTemplate(CXCursor Cursor) {
8656+
if (!clang_isStatement(Cursor.kind))
8657+
return cxstring::createEmpty();
8658+
if (auto const *S = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor))) {
8659+
ASTContext const &C = getCursorContext(Cursor);
8660+
std::string AsmTemplate = S->generateAsmString(C);
8661+
return cxstring::createDup(AsmTemplate);
8662+
}
8663+
return cxstring::createEmpty();
8664+
}
8665+
8666+
unsigned clang_Cursor_isGCCAssemblyHasGoto(CXCursor Cursor) {
8667+
if (!clang_isStatement(Cursor.kind))
8668+
return 0;
8669+
if (auto const *S = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor)))
8670+
return S->isAsmGoto();
8671+
return 0;
8672+
}
8673+
8674+
unsigned clang_Cursor_getGCCAssemblyNumOutputs(CXCursor Cursor) {
8675+
if (!clang_isStatement(Cursor.kind))
8676+
return 0;
8677+
if (auto const *S = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor)))
8678+
return S->getNumOutputs();
8679+
return 0;
8680+
}
8681+
8682+
unsigned clang_Cursor_getGCCAssemblyNumInputs(CXCursor Cursor) {
8683+
if (!clang_isStatement(Cursor.kind))
8684+
return 0;
8685+
if (auto const *S = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor)))
8686+
return S->getNumInputs();
8687+
return 0;
8688+
}
8689+
8690+
unsigned clang_Cursor_getGCCAssemblyInput(CXCursor Cursor, unsigned Index,
8691+
CXString *Constraint,
8692+
CXCursor *ExprCursor) {
8693+
if (!clang_isStatement(Cursor.kind) || !Constraint || !ExprCursor)
8694+
return 0;
8695+
if (auto const *S = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor));
8696+
S && Index < S->getNumInputs()) {
8697+
*Constraint = cxstring::createDup(S->getInputConstraint(Index));
8698+
*ExprCursor = MakeCXCursor(S->getInputExpr(Index), getCursorDecl(Cursor),
8699+
cxcursor::getCursorTU(Cursor));
8700+
return 1;
8701+
}
8702+
return 0;
8703+
}
8704+
8705+
unsigned clang_Cursor_getGCCAssemblyOutput(CXCursor Cursor, unsigned Index,
8706+
CXString *Constraint,
8707+
CXCursor *ExprCursor) {
8708+
if (!clang_isStatement(Cursor.kind) || !Constraint || !ExprCursor)
8709+
return 0;
8710+
if (auto const *S = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor));
8711+
S && Index < S->getNumOutputs()) {
8712+
*Constraint = cxstring::createDup(S->getOutputConstraint(Index));
8713+
*ExprCursor = MakeCXCursor(S->getOutputExpr(Index), getCursorDecl(Cursor),
8714+
cxcursor::getCursorTU(Cursor));
8715+
return 1;
8716+
}
8717+
return 0;
8718+
}
8719+
8720+
unsigned clang_Cursor_getGCCAssemblyNumClobbers(CXCursor Cursor) {
8721+
if (!clang_isStatement(Cursor.kind))
8722+
return 0;
8723+
if (auto const *S = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor)))
8724+
return S->getNumClobbers();
8725+
return 0;
8726+
}
8727+
8728+
CXString clang_Cursor_getGCCAssemblyClobber(CXCursor Cursor, unsigned Index) {
8729+
if (!clang_isStatement(Cursor.kind))
8730+
return cxstring::createEmpty();
8731+
if (auto const *S = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor));
8732+
S && Index < S->getNumClobbers())
8733+
return cxstring::createDup(S->getClobber(Index));
8734+
return cxstring::createEmpty();
8735+
}
8736+
8737+
unsigned clang_Cursor_isGCCAssemblyVolatile(CXCursor Cursor) {
8738+
if (!clang_isStatement(Cursor.kind))
8739+
return 0;
8740+
if (auto const *S = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor)))
8741+
return S->isVolatile();
8742+
return 0;
8743+
}
8744+
86518745
//===----------------------------------------------------------------------===//
86528746
// Operations for querying linkage of a cursor.
86538747
//===----------------------------------------------------------------------===//

clang/tools/libclang/libclang.map

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,15 @@ LLVM_20 {
441441
LLVM_21 {
442442
global:
443443
clang_getFullyQualifiedName;
444+
clang_Cursor_getGCCAssemblyTemplate;
445+
clang_Cursor_isGCCAssemblyHasGoto;
446+
clang_Cursor_getGCCAssemblyNumOutputs;
447+
clang_Cursor_getGCCAssemblyNumInputs;
448+
clang_Cursor_getGCCAssemblyInput;
449+
clang_Cursor_getGCCAssemblyOutput;
450+
clang_Cursor_getGCCAssemblyNumClobbers;
451+
clang_Cursor_getGCCAssemblyClobber;
452+
clang_Cursor_isGCCAssemblyVolatile;
444453
};
445454

446455
# Example of how to add a new symbol version entry. If you do add a new symbol

0 commit comments

Comments
 (0)