[cfe-dev] Basic source-to-source transformation with Clang

Satya Prakash Prasad satyaprakash.prasad at gmail.com
Mon Jun 18 01:16:59 PDT 2012


I just downloaded LLVM and CLANG and trying to write a basic
source-to-source transformation tool.

My requirement is to transform the below sample code:

int inc(int& p)
{
        p++;
        printf("In inc [%d]\n", p);
        return p;
}
int main()
{
        int i = 0;
        int y,z;
        if(y == 0)
                print(inc(i) , inc(i));
        else
        {
                print(inc(i) , inc(i));
        }
        printf("y = [%d] z = [%d]\n", y , z);
        return 0;
}


To:

int inc(int& p)
{
        p++;
        printf("%s %d", __FILE__, __LINE__);
        printf("In inc [%d]\n", p);
        printf("%s %d", __FILE__, __LINE__);
        return p;
}

int main()
{
        int i = 0;
        printf("%s %d", __FILE__, __LINE__);
        int y,z;
        printf("%s %d", __FILE__, __LINE__);
        if(y == 0)
                print(inc(i) , inc(i));
        else
        {
                print(inc(i) , inc(i));
                printf("%s %d", __FILE__, __LINE__);
        }
        printf("y = [%d] z = [%d]\n", y , z);
        printf("%s %d", __FILE__, __LINE__);
        return 0;
}


This what I am able to develop till now via Internet Help:

#include <cstdio>
#include <string>
#include <sstream>

#include "clang/AST/ASTConsumer.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetOptions.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Parse/ParseAST.h"
#include "clang/Rewrite/Rewriter.h"
#include "clang/Rewrite/Rewriters.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/raw_ostream.h"

using namespace clang;
using namespace std;

// By implementing RecursiveASTVisitor, we can specify which AST nodes
// we're interested in by overriding relevant methods.
class MyASTVisitor : public RecursiveASTVisitor<MyASTVisitor>
{
public:
    MyASTVisitor(Rewriter &R)
        : TheRewriter(R)
    {}

    bool VisitStmt(Stmt *s) {
   if (isa<CompoundStmt>(s)) {
    CompoundStmt *Statement = cast<CompoundStmt>(s);
    TheRewriter.InsertText(Statement->getLocStart(),
             "printf(\"%s %d\", __FILE__, __LINE__);\n",
             true, true);
    }

        return true;
    }

    bool VisitFunctionDecl(FunctionDecl *f) {
        // Only function definitions (with bodies), not declarations.
        if (f->hasBody()) {
            Stmt *FuncBody = f->getBody();

            // Type name as string
            QualType QT = f->getResultType();
            string TypeStr = QT.getAsString();

            // Function name
            DeclarationName DeclName = f->getNameInfo().getName();
            string FuncName = DeclName.getAsString();

            // Add comment before
            stringstream SSBefore;
            SSBefore << "// Begin function " << FuncName << " returning "
                     << TypeStr << "\n";
            SourceLocation ST = f->getSourceRange().getBegin();
            TheRewriter.InsertText(ST, SSBefore.str(), true, true);

            // And after
            stringstream SSAfter;
            SSAfter << "\n// End function " << FuncName << "\n";
            ST = FuncBody->getLocEnd().getLocWithOffset(1);
            TheRewriter.InsertText(ST, SSAfter.str(), true, true);
        }

        return true;
    }

private:
    void AddBraces(Stmt *s);

    Rewriter &TheRewriter;
};

// Implementation of the ASTConsumer interface for reading an AST produced
// by the Clang parser.
class MyASTConsumer : public ASTConsumer
{
public:
    MyASTConsumer(Rewriter &R)
        : Visitor(R)
    {}

    // Override the method that gets called for each parsed top-level
    // declaration.
    virtual bool HandleTopLevelDecl(DeclGroupRef DR) {
        for (DeclGroupRef::iterator b = DR.begin(), e = DR.end();
             b != e; ++b)
            // Traverse the declaration using our AST visitor.
            Visitor.TraverseDecl(*b);
        return true;
    }

private:
    MyASTVisitor Visitor;
};

int main(int argc, char *argv[])
{
    if (argc != 2) {
        llvm::errs() << "Usage: rewritersample <filename>\n";
        return 1;
    }

    // CompilerInstance will hold the instance of the Clang compiler for us,
    // managing the various objects needed to run the compiler.
    CompilerInstance TheCompInst;
    TheCompInst.createDiagnostics(0, 0);

    // Initialize target info with the default triple for our platform.
    TargetOptions TO;
    TO.Triple = llvm::sys::getDefaultTargetTriple();
    TargetInfo *TI = TargetInfo::CreateTargetInfo(
        TheCompInst.getDiagnostics(), TO);
    TheCompInst.setTarget(TI);

    TheCompInst.createFileManager();
    FileManager &FileMgr = TheCompInst.getFileManager();
    TheCompInst.createSourceManager(FileMgr);
    SourceManager &SourceMgr = TheCompInst.getSourceManager();
    TheCompInst.createPreprocessor();
    TheCompInst.createASTContext();

    // A Rewriter helps us manage the code rewriting task.
    Rewriter TheRewriter;
    TheRewriter.setSourceMgr(SourceMgr, TheCompInst.getLangOpts());

    // Set the main file handled by the source manager to the input file.
    const FileEntry *FileIn = FileMgr.getFile(argv[1]);
    SourceMgr.createMainFileID(FileIn);
    TheCompInst.getDiagnosticClient().BeginSourceFile(
        TheCompInst.getLangOpts(),
        &TheCompInst.getPreprocessor());

    // Create an AST consumer instance which is going to get called by
    // ParseAST.
    MyASTConsumer TheConsumer(TheRewriter);

    // Parse the file to AST, registering our consumer as the AST consumer.
    ParseAST(TheCompInst.getPreprocessor(), &TheConsumer,
             TheCompInst.getASTContext());

    // At this point the rewriter's buffer should be full with the rewritten
    // file contents.
    const RewriteBuffer *RewriteBuf =
        TheRewriter.getRewriteBufferFor(SourceMgr.getMainFileID());
    llvm::outs() << string(RewriteBuf->begin(), RewriteBuf->end());

    return 0;
}


Below is the output I receive:

llvm/build/Release+Asserts/examples 1224> rewritersample test.cpp
test.cpp:1:12: error: expected ')'
int inc(int& p)
           ^
test.cpp:1:8: note: to match this '('
int inc(int& p)
       ^
test.cpp:1:12: error: parameter name omitted
int inc(int& p)
           ^
test.cpp:3:2: error: use of undeclared identifier 'p'
        p++;
        ^
test.cpp:4:26: error: use of undeclared identifier 'p'
        printf("In inc [%d]\n", p);
                                ^
test.cpp:5:9: error: use of undeclared identifier 'p'
        return p;
               ^
// Begin function inc returning int
int inc(int& p)
printf("%s %d", __FILE__, __LINE__);
{
   p++;
   printf("In inc [%d]\n", p);
   return p;
}
// End function inc


// Begin function main returning int
int main()
printf("%s %d", __FILE__, __LINE__);
{
   int i = 0;
   int y,z;
   if(y == 0)
    print(inc(i) , inc(i));
   else
    print(inc(i) , inc(i));
   printf("y = [%d] z = [%d]\n", y , z);
   return 0;
}
// End function main

Please let me know how
a) Stop the generation of 'error'(s)
b) Add statements in compound block as suggested above

Lastly it is a great tool to use. How should one get expertise using
llvm, clang as there are very less tutorials and examples on the same?

Regards,
Prakash



More information about the cfe-dev mailing list