1

I'm trying to write a linter for openmp code, but libclang hides the body of openmp directives. My problem is essentially the same as this unanswered issue from the llvm mailing list.

I took a look at clang's textual ast-dump, and the information I need seems to be present. However, when I use the binary dump to create a CXCursor to iterate over the tree using the libclang library, the subtrees captured by openmp directives seem to turn into "unexposed statements."

I have tried using clang's parseTranslationUnit function to generate the AST internally, but the result is the same. Is this a limitation of libclang, or could I be doing something wrong? Below are snapshots of my code, the textual AST dump and the command I used to generate it, and the output I get from walking the AST with libclang. Thanks!

AST Walker:

#include <iostream>
#include <stdlib.h>
#include <clang-c/Index.h>
using namespace std;

int main(int argc, char** argv)
{
  CXIndex index = clang_createIndex(0, 0);
  CXTranslationUnit unit = clang_createTranslationUnit(index, argv[1]); //.ast filename
  if (unit == nullptr)
  {
    cerr << "Unable to parse translation unit. Quitting." << endl;
    exit(-1);
  }
  printf("%d\n",clang_getNumDiagnostics(unit));
  for (unsigned I = 0, N = clang_getNumDiagnostics(unit); I != N; ++I) { 
    CXDiagnostic Diag = clang_getDiagnostic(unit, I);
    CXString String = clang_formatDiagnostic(Diag, clang_defaultDiagnosticDisplayOptions());
    fprintf(stderr, "%s\n", clang_getCString(String));
    clang_disposeString(String);
}
  CXCursor cursor = clang_getTranslationUnitCursor(unit);
  clang_visitChildren(
    cursor,
    [](CXCursor c, CXCursor parent, CXClientData client_data)
    {
      if(clang_Location_isFromMainFile(clang_getCursorLocation(c))){
        unsigned loc = -1;
        clang_getExpansionLocation(clang_getCursorLocation(c),NULL,&loc,NULL,NULL);
        printf("%d: ",loc);
        cout << clang_getCString(clang_getCursorSpelling(c))<< '+' << clang_getCString(clang_getCursorKindSpelling(clang_getCursorKind(c))) << endl;
      }
      return CXChildVisit_Recurse;
    },
    nullptr);
  clang_disposeTranslationUnit(unit);
  clang_disposeIndex(index);
}

Test File:

#include <omp.h>
#include <stdio.h>
#include <stdlib.h>

int main (int argc, char *argv[]) 
{
int nthreads, tid;

/* Fork a team of threads giving them their own copies of variables */
#pragma omp parallel
  {

  /* Obtain thread number */
  tid = omp_get_thread_num();
  printf("Hello World from thread = %d\n", tid);

  /* Only master thread does this */
  if (tid == 0) 
    {
    nthreads = omp_get_num_threads();
    printf("Number of threads = %d\n", nthreads);
    }

  }  /* All threads join master thread and disband */

}

Textual AST:

`-FunctionDecl 0x7fbcd785a140 <helloworld.c:17:1, line:38:1> line:17:5 main 'int (int, char **)'
  |-ParmVarDecl 0x7fbcd7859f38 <col:11, col:15> col:15 argc 'int'
  |-ParmVarDecl 0x7fbcd785a020 <col:21, col:32> col:27 argv 'char **':'char **'
  `-CompoundStmt 0x7fbcd785cd18 <line:18:1, line:38:1>
    |-DeclStmt 0x7fbcd785a308 <line:19:1, col:18>
    | |-VarDecl 0x7fbcd785a208 <col:1, col:5> col:5 used nthreads 'int'
    | `-VarDecl 0x7fbcd785a288 <col:1, col:15> col:15 used tid 'int'
    `-OMPParallelDirective 0x7fbcd785cce0 <line:22:1, col:44>
      |-OMPPrivateClause 0x7fbcd785c538 <col:22, col:43>
      | |-DeclRefExpr 0x7fbcd785a320 <col:30> 'int' lvalue Var 0x7fbcd785a208 'nthreads' 'int'
      | `-DeclRefExpr 0x7fbcd785a340 <col:40> 'int' lvalue Var 0x7fbcd785a288 'tid' 'int'
      `-CapturedStmt 0x7fbcd785ccb8 <line:23:3, line:36:3>
        `-CapturedDecl 0x7fbcd785c650 <<invalid sloc>> <invalid sloc> nothrow
          |-CompoundStmt 0x7fbcd785cc90 <line:23:3, line:36:3> openmp_structured_block
          | |-BinaryOperator 0x7fbcd785c8f0 <line:26:3, col:28> 'int' '='
          | | |-DeclRefExpr 0x7fbcd785c848 <col:3> 'int' lvalue Var 0x7fbcd785a288 'tid' 'int'
          | | `-CallExpr 0x7fbcd785c8d0 <col:9, col:28> 'int'
          | |   `-ImplicitCastExpr 0x7fbcd785c8b8 <col:9> 'int (*)(void)' <FunctionToPointerDecay>
          | |     `-DeclRefExpr 0x7fbcd785c868 <col:9> 'int (void)' Function 0x7fbcd781f590 'omp_get_thread_num' 'int (void)'
          | |-CallExpr 0x7fbcd785c9d0 <line:27:3, col:47> 'int'
          | | |-ImplicitCastExpr 0x7fbcd785c9b8 <col:3> 'int (*)(const char *, ...)' <FunctionToPointerDecay>
          | | | `-DeclRefExpr 0x7fbcd785c910 <col:3> 'int (const char *, ...)' Function 0x7fbcd7847550 'printf' 'int (const char *, ...)'
          | | |-ImplicitCastExpr 0x7fbcd785ca18 <col:10> 'const char *' <NoOp>
          | | | `-ImplicitCastExpr 0x7fbcd785ca00 <col:10> 'char *' <ArrayToPointerDecay>
          | | |   `-StringLiteral 0x7fbcd785c930 <col:10> 'char [30]' lvalue "Hello World from thread = %d\n"
          | | `-ImplicitCastExpr 0x7fbcd785ca30 <col:44> 'int' <LValueToRValue>
          | |   `-DeclRefExpr 0x7fbcd785c968 <col:44> 'int' lvalue Var 0x7fbcd785a288 'tid' 'int'
          | `-IfStmt 0x7fbcd785cc78 <line:30:3, line:34:5>
          |   |-BinaryOperator 0x7fbcd785caa0 <line:30:7, col:14> 'int' '=='
          |   | |-ImplicitCastExpr 0x7fbcd785ca88 <col:7> 'int' <LValueToRValue>
          |   | | `-DeclRefExpr 0x7fbcd785ca48 <col:7> 'int' lvalue Var 0x7fbcd785a288 'tid' 'int'
          |   | `-IntegerLiteral 0x7fbcd785ca68 <col:14> 'int' 0
          |   `-CompoundStmt 0x7fbcd785cc58 <line:31:5, line:34:5>
          |     |-BinaryOperator 0x7fbcd785cb38 <line:32:5, col:36> 'int' '='
          |     | |-DeclRefExpr 0x7fbcd785cac0 <col:5> 'int' lvalue Var 0x7fbcd785a208 'nthreads' 'int'
          |     | `-CallExpr 0x7fbcd785cb18 <col:16, col:36> 'int'
          |     |   `-ImplicitCastExpr 0x7fbcd785cb00 <col:16> 'int (*)(void)' <FunctionToPointerDecay>
          |     |     `-DeclRefExpr 0x7fbcd785cae0 <col:16> 'int (void)' Function 0x7fbcd781f090 'omp_get_num_threads' 'int (void)'
          |     `-CallExpr 0x7fbcd785cbe0 <line:33:5, col:48> 'int'
          |       |-ImplicitCastExpr 0x7fbcd785cbc8 <col:5> 'int (*)(const char *, ...)' <FunctionToPointerDecay>
          |       | `-DeclRefExpr 0x7fbcd785cb58 <col:5> 'int (const char *, ...)' Function 0x7fbcd7847550 'printf' 'int (const char *, ...)'
          |       |-ImplicitCastExpr 0x7fbcd785cc28 <col:12> 'const char *' <NoOp>
          |       | `-ImplicitCastExpr 0x7fbcd785cc10 <col:12> 'char *' <ArrayToPointerDecay>
          |       |   `-StringLiteral 0x7fbcd785cb78 <col:12> 'char [24]' lvalue "Number of threads = %d\n"
          |       `-ImplicitCastExpr 0x7fbcd785cc40 <col:40> 'int' <LValueToRValue>

Command to generate AST (change to emit-ast for the binary file):

clang -fopenmp -Xclang -ast-dump -L/usr/local/lib helloworld.c

Program Output:

0
13: omp.h+inclusion directive
14: stdio.h+inclusion directive
15: stdlib.h+inclusion directive
17: main+FunctionDecl
17: argc+ParmDecl
17: argv+ParmDecl
18: +CompoundStmt
19: +DeclStmt
19: nthreads+VarDecl
19: tid+VarDecl
22: +OMPParallelDirective
23: +UnexposedStmt
26: tid+DeclRefExpr
32: nthreads+DeclRefExpr
D. Tastet
  • 11
  • 2
  • Not your issue, but your test code is not doing what you think it is. Variables are shared by default, so you need #pragma omp parallel default(private) to do what your comment says ("/* Fork a team of threads giving them their own copies of variables */"). Normally it's better to declare variables in their minimal required scope, then sharing often falls out naturally. – Jim Cownie Nov 15 '19 at 11:15
  • Right, I should have mentioned that I found the test file out there on the internet. It originally had a private clause, but I removed it to see if there was any effect on the AST. – D. Tastet Nov 17 '19 at 21:03

0 Answers0