I am working on creating a tool to detect parallelism in a function, I am using checkerContext, to find the for loop inside my function and then traversing through the body of the loop to detect any parallelism and generate a bugReport if there is no parallelism in the function. The checker code and my testcases code is attached below. I am not able to figure out why none of the debugging output lines that is llvm::errs() are not executed. Nothing is being printed on the console.
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/Expr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include <iostream>
using namespace clang;
using namespace ento;
namespace {
class DataDependencyVisitor : public RecursiveASTVisitor<DataDependencyVisitor> {
const VarDecl *LoopVar;
bool &HasDataDependency;
const SourceManager &SM;
public:
DataDependencyVisitor(const VarDecl *LoopVar, bool &HasDataDependency, const SourceManager &SM)
: LoopVar(LoopVar), HasDataDependency(HasDataDependency), SM(SM) {}
bool VisitArraySubscriptExpr(ArraySubscriptExpr *ASE) {
llvm::errs() << "Visiting array subscript: "
<< ASE->getSourceRange().printToString(SM) << "n";
const Expr *IndexExpr = ASE->getIdx()->IgnoreParenCasts();
if (const DeclRefExpr *IndexDeclRef = dyn_cast<DeclRefExpr>(IndexExpr)) {
if (const VarDecl *IndexVar = dyn_cast<VarDecl>(IndexDeclRef->getDecl())) {
if (IndexVar == LoopVar) {
llvm::errs() << "Loop variable used directly as array index. No dependency.n";
return true; // No dependency found
}
}
} else if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(IndexExpr)) {
if (BO->getOpcode() == BO_Add || BO->getOpcode() == BO_Sub) {
const DeclRefExpr *LHSDeclRef = dyn_cast<DeclRefExpr>(BO->getLHS());
if (LHSDeclRef && LHSDeclRef->getDecl() == LoopVar) {
if (isa<IntegerLiteral>(BO->getRHS())) {
llvm::errs() << "Index is loop variable +/- constant. No dependency.n";
return true; // No dependency found
}
}
}
}
llvm::errs() << "Indexing indicates potential cross-iteration dependency.n";
HasDataDependency = true; // Potential dependency detected
return false; // Stop further traversal
}
bool VisitBinaryOperator(BinaryOperator *BO) {
llvm::errs() << "Visiting binary operator: " << BO->getOpcodeStr() << "n";
if (BO->isAssignmentOp()) {
if (ArraySubscriptExpr *ASE = dyn_cast<ArraySubscriptExpr>(BO->getLHS())) {
TraverseStmt(ASE->getIdx()); // Check the index for dependencies
TraverseStmt(BO->getRHS()); // Traverse the RHS to check if it involves the loop variable
} else {
TraverseStmt(BO->getRHS()); // Traverse the entire RHS if it's not a simple array subscript
}
}
return true; // Continue traversal
}
bool VisitUnaryOperator(UnaryOperator *UO) {
llvm::errs() << "Visiting unary operator: " << UO->getOpcodeStr(UO->getOpcode()) << "n";
if (UO->isIncrementDecrementOp()) {
if (ArraySubscriptExpr *ASE = dyn_cast<ArraySubscriptExpr>(UO->getSubExpr())) {
TraverseStmt(ASE->getIdx()); // Check the index for dependencies
if (HasDataDependency) {
llvm::errs() << "Dependency found in unary operator.n";
return false; // Stop if dependency found
}
}
}
return true; // Continue traversal
}
bool TraverseStmt(Stmt *S) {
if (!S)
return true;
llvm::errs() << "Traversing statement of type: " << S->getStmtClassName() << "n";
return RecursiveASTVisitor<DataDependencyVisitor>::TraverseStmt(S);
}
};
class DataParallelismChecker : public Checker<check::PreStmt<ForStmt>> {
const BugType ParallelismBugType{
this, "Potential Data Parallelism", "Performance"};
public:
void checkPreStmt(const ForStmt *FS, CheckerContext &C) const;
};
void DataParallelismChecker::checkPreStmt(const ForStmt *FS, CheckerContext &C) const {
llvm::errs() << "Checking loop at line: " << FS->getBeginLoc().printToString(C.getSourceManager()) << "n";
const DeclStmt *InitStmt = dyn_cast<DeclStmt>(FS->getInit());
if (!InitStmt || InitStmt->isSingleDecl() == false) return;
const VarDecl *LoopVar = dyn_cast<VarDecl>(InitStmt->getSingleDecl());
if (!LoopVar) return;
bool HasDataDependency = false;
DataDependencyVisitor Visitor(LoopVar, HasDataDependency, C.getSourceManager());
Visitor.TraverseStmt(const_cast<Stmt *>(FS->getBody())); // Traverse the body of the loop
llvm::errs() << "Data dependency found: " << (HasDataDependency ? "Yes" : "No") << "n";
if (!HasDataDependency) {
llvm::errs() << "No data dependency found. Emitting warning...n";
ExplodedNode *N = C.generateNonFatalErrorNode();
if (!N)
return;
auto Report = std::make_unique<PathSensitiveBugReport>(ParallelismBugType, "This loop can be parallelized", N);
Report->addRange(FS->getSourceRange());
C.emitReport(std::move(Report));
}
}
} // end anonymous namespace
void ento::registerDataParallelismChecker(CheckerManager &mgr) {
mgr.registerChecker<DataParallelismChecker>();
}
// This checker should be enabled regardless of how language options are set.
bool ento::shouldRegisterDataParallelismChecker(const CheckerManager &mgr) {
return true;
}
Above code is my checker code Name of file DataParallelismChecker.cpp
// RUN: %clang_analyze_cc1 -analyzer-checker=example.DataParallelismChecker -isystem /usr/include -isystem /usr/lib/gcc/x86_64-linux-gnu/11/include -verify %s
#include <math.h>
void test() {
for (int i = 0; i < 10; ++i) {
int j=i;
}
}
void test1() {
int arr[100];
for (int i = 0; i < 100; ++i) {
arr[i] = arr[i - 1] + 1; // No warning because of the dependency
}
}
void test2(float* data, int N) {
for (int i = 0; i < N; ++i) {
data[i] = sin(data[i]) + cos(data[i]); // expected-warning{{This loop can be parallelized}}
}
}
void test3(float* data, int N) {
for (int i = 0; i < N; ++i) {
if (data[i] > 0) {
data[i] = sqrt(data[i]); // expected-warning{{This loop can be parallelized}}
}
}
}
void test4() {
int arr[100];
for (int i = 0; i < 100; ++i) {
arr[i] = i * i; // expected-warning{{This loop can be parallelized}}
}
}
void compute(float* data, int N) {
for (int i = 0; i < N; ++i) {
data[i] = sin(data[i]) + cos(data[i]); // expected-warning{{This loop can be parallelized}}
}
}
void non_parallel_loop(float* data, int N) {
for (int i = 1; i < N; ++i) {
data[i] = data[i - 1] + sin(data[i]); // No warning expected due to dependency
}
}
This code is my test cases Name of file data-parallelism.c
I want to generate bug reports when parallelism is possible