Skip to content

[clang-c] introduce queries on GCC-style inline assembly statements #143424

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 93 additions & 1 deletion clang/include/clang-c/Index.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -4495,6 +4495,98 @@ 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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this return on error?


/**
* 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.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What kind of things does the function return via its return value?

*/

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,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question here.

unsigned Index,
CXString *Constraint,
CXCursor *Expr);

/**
* Given a CXCursor_GCCAsmStmt cursor, count the clobbers in it.
*/

CINDEX_LINKAGE unsigned clang_Cursor_getGCCAssemblyNumClobbers(CXCursor Cursor);

/**
* Given a CXCursor_GCCAsmStmt cursor, get the Index-th clobber of it.
*/

CINDEX_LINKAGE CXString clang_Cursor_getGCCAssemblyClobber(CXCursor Cursor,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this return on error? An empty string? Something else?

unsigned Index);

/**
* Given a CXCursor_GCCAsmStmt cursor, check if the inline assembly is simple.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does it mean for assembly to be "simple"?

*/

CINDEX_LINKAGE unsigned clang_Cursor_isGCCAssemblySimple(CXCursor Cursor);

/**
* Given a CXCursor_GCCAsmStmt cursor, check if the inline assembly is
* `volatile`.
*/

CINDEX_LINKAGE unsigned clang_Cursor_isGCCAssemblyVolatile(CXCursor Cursor);

/**
* @}
*/
Expand Down
48 changes: 48 additions & 0 deletions clang/test/Index/inline-assembly.c
Original file line number Diff line number Diff line change
@@ -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 | 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===
52 changes: 52 additions & 0 deletions clang/tools/c-index-test/c-index-test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
/******************************************************************************/
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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;
}
Expand Down
125 changes: 118 additions & 7 deletions clang/tools/libclang/CIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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))) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stmt is a type name, better to pick a name which doesn't conflict with a type (this helps folks who have editors that don't handle conflicts with type and object names for things like autocomplete). Same issue happens in a bunch of other functions as well.

auto const &C = getCursorContext(Cursor);
auto AsmTemplate = Stmt->generateAsmString(C);
Comment on lines +8657 to +8658
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't use auto unless the types are spelled out in the initialization.

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();
}
Comment on lines +8667 to +8669
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (auto const *Stmt = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor))) {
return Stmt->isAsmGoto();
}
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();
}
Comment on lines +8676 to +8678
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (auto const *Stmt = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor))) {
return Stmt->getNumOutputs();
}
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();
}
Comment on lines +8685 to +8687
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (auto const *Stmt = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor))) {
return Stmt->getNumInputs();
}
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) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expr is a type name same as Stmt.

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);
Comment on lines +8698 to +8699
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here about auto, but also, try to pick names without a trailing underscore a well (which don't conflict with type names, like Expr).

*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) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here as above.

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);
Comment on lines +8715 to +8716
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here as above.

*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();
}
Comment on lines +8728 to +8730
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (auto const *Stmt = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor))) {
return Stmt->getNumClobbers();
}
if (auto const *Stmt = dyn_cast_or_null<GCCAsmStmt>(getCursorStmt(Cursor)))
return Stmt->getNumClobbers();

I'll stop commenting on this -- basically, single-line substatements don't get curly braces per our (odd) coding convention.

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.
//===----------------------------------------------------------------------===//
Expand Down
Loading
Loading