[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