[cfe-commits] r56266 - in /cfe/trunk: Driver/ASTConsumers.h Driver/RewriteBlocks.cpp clang.xcodeproj/project.pbxproj
Steve Naroff
snaroff at apple.com
Tue Sep 16 17:13:27 PDT 2008
Author: snaroff
Date: Tue Sep 16 19:13:27 2008
New Revision: 56266
URL: http://llvm.org/viewvc/llvm-project?rev=56266&view=rev
Log:
Add support for rewriting blocks...
Added:
cfe/trunk/Driver/RewriteBlocks.cpp
Modified:
cfe/trunk/Driver/ASTConsumers.h
cfe/trunk/clang.xcodeproj/project.pbxproj
Modified: cfe/trunk/Driver/ASTConsumers.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/Driver/ASTConsumers.h?rev=56266&r1=56265&r2=56266&view=diff
==============================================================================
--- cfe/trunk/Driver/ASTConsumers.h (original)
+++ cfe/trunk/Driver/ASTConsumers.h Tue Sep 16 19:13:27 2008
@@ -58,6 +58,9 @@
const std::string& EmitDir,
Diagnostic &Diags);
+ASTConsumer *CreateBlockRewriter(const std::string& InFile,
+ Diagnostic &Diags,
+ const LangOptions &LangOpts);
} // end clang namespace
#include "AnalysisConsumer.h"
Added: cfe/trunk/Driver/RewriteBlocks.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/Driver/RewriteBlocks.cpp?rev=56266&view=auto
==============================================================================
--- cfe/trunk/Driver/RewriteBlocks.cpp (added)
+++ cfe/trunk/Driver/RewriteBlocks.cpp Tue Sep 16 19:13:27 2008
@@ -0,0 +1,907 @@
+//===--- RewriteBlocks.cpp ----------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Hacks and fun related to the closure rewriter.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTConsumers.h"
+#include "clang/Rewrite/Rewriter.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/LangOptions.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include <sstream>
+
+using namespace clang;
+using llvm::utostr;
+
+namespace {
+
+class RewriteBlocks : public ASTConsumer {
+ Rewriter Rewrite;
+ Diagnostic &Diags;
+ const LangOptions &LangOpts;
+ unsigned RewriteFailedDiag;
+ unsigned NoNestedBlockCalls;
+
+ ASTContext *Context;
+ SourceManager *SM;
+ unsigned MainFileID;
+ const char *MainFileStart, *MainFileEnd;
+
+ // Block expressions.
+ llvm::SmallVector<BlockExpr *, 32> Blocks;
+ llvm::SmallVector<BlockDeclRefExpr *, 32> BlockDeclRefs;
+ llvm::DenseMap<BlockDeclRefExpr *, CallExpr *> BlockCallExprs;
+
+ // Block related declarations.
+ llvm::SmallPtrSet<ValueDecl *, 8> BlockByCopyDecls;
+ llvm::SmallPtrSet<ValueDecl *, 8> BlockByRefDecls;
+
+ // The function/method we are rewriting.
+ FunctionDecl *CurFunctionDef;
+ ObjCMethodDecl *CurMethodDef;
+
+ bool IsHeader;
+public:
+ RewriteBlocks(bool isHeader, Diagnostic &D, const LangOptions &LOpts) :
+ Diags(D), LangOpts(LOpts) {
+ IsHeader = isHeader;
+ CurFunctionDef = 0;
+ CurMethodDef = 0;
+ RewriteFailedDiag = Diags.getCustomDiagID(Diagnostic::Warning,
+ "rewriting failed");
+ NoNestedBlockCalls = Diags.getCustomDiagID(Diagnostic::Warning,
+ "Rewrite support for closure calls nested within closure blocks is incomplete");
+ }
+ ~RewriteBlocks() {
+ // Get the buffer corresponding to MainFileID.
+ // If we haven't changed it, then we are done.
+ if (const RewriteBuffer *RewriteBuf =
+ Rewrite.getRewriteBufferFor(MainFileID)) {
+ std::string S(RewriteBuf->begin(), RewriteBuf->end());
+ printf("%s\n", S.c_str());
+ } else {
+ printf("No changes\n");
+ }
+ }
+
+ void Initialize(ASTContext &context);
+
+ void InsertText(SourceLocation Loc, const char *StrData, unsigned StrLen);
+ void ReplaceText(SourceLocation Start, unsigned OrigLength,
+ const char *NewStr, unsigned NewLength);
+
+ // Top Level Driver code.
+ virtual void HandleTopLevelDecl(Decl *D);
+ void HandleDeclInMainFile(Decl *D);
+
+ // Top level
+ Stmt *RewriteFunctionBody(Stmt *S);
+ void InsertBlockLiteralsWithinFunction(FunctionDecl *FD);
+ void InsertBlockLiteralsWithinMethod(ObjCMethodDecl *MD);
+
+ // Block specific rewrite rules.
+ void RewriteBlockStmtExpr(BlockStmtExpr *Exp);
+
+ void RewriteBlockCall(CallExpr *Exp);
+ void RewriteBlockPointerDecl(NamedDecl *VD);
+ void RewriteBlockPointerFunctionArgs(FunctionDecl *FD);
+
+ std::string SynthesizeBlockFunc(BlockExpr *CE, int i,
+ const char *funcName, std::string Tag);
+ std::string SynthesizeBlockImpl(BlockExpr *CE, std::string Tag);
+ std::string SynthesizeBlockCall(CallExpr *Exp);
+ void SynthesizeBlockLiterals(SourceLocation FunLocStart,
+ const char *FunName);
+
+ void GetBlockDeclRefExprs(Stmt *S);
+ void GetBlockCallExprs(Stmt *S);
+
+ // We avoid calling Type::isBlockPointerType(), since it operates on the
+ // canonical type. We only care if the top-level type is a closure pointer.
+ bool isBlockPointerType(QualType T) { return isa<BlockPointerType>(T); }
+
+ // FIXME: This predicate seems like it would be useful to add to ASTContext.
+ bool isObjCType(QualType T) {
+ if (!LangOpts.ObjC1 && !LangOpts.ObjC2)
+ return false;
+
+ QualType OCT = Context->getCanonicalType(T).getUnqualifiedType();
+
+ if (OCT == Context->getCanonicalType(Context->getObjCIdType()) ||
+ OCT == Context->getCanonicalType(Context->getObjCClassType()))
+ return true;
+
+ if (const PointerType *PT = OCT->getAsPointerType()) {
+ if (isa<ObjCInterfaceType>(PT->getPointeeType()) ||
+ isa<ObjCQualifiedIdType>(PT->getPointeeType()))
+ return true;
+ }
+ return false;
+ }
+ // ObjC rewrite methods.
+ void RewriteInterfaceDecl(ObjCInterfaceDecl *ClassDecl);
+ void RewriteCategoryDecl(ObjCCategoryDecl *CatDecl);
+ void RewriteProtocolDecl(ObjCProtocolDecl *PDecl);
+ void RewriteMethodDecl(ObjCMethodDecl *MDecl);
+};
+
+}
+
+static bool IsHeaderFile(const std::string &Filename) {
+ std::string::size_type DotPos = Filename.rfind('.');
+
+ if (DotPos == std::string::npos) {
+ // no file extension
+ return false;
+ }
+
+ std::string Ext = std::string(Filename.begin()+DotPos+1, Filename.end());
+ // C header: .h
+ // C++ header: .hh or .H;
+ return Ext == "h" || Ext == "hh" || Ext == "H";
+}
+
+ASTConsumer *clang::CreateBlockRewriter(const std::string& InFile,
+ Diagnostic &Diags,
+ const LangOptions &LangOpts) {
+ return new RewriteBlocks(IsHeaderFile(InFile), Diags, LangOpts);
+}
+
+void RewriteBlocks::Initialize(ASTContext &context) {
+ Context = &context;
+ SM = &Context->getSourceManager();
+
+ // Get the ID and start/end of the main file.
+ MainFileID = SM->getMainFileID();
+ const llvm::MemoryBuffer *MainBuf = SM->getBuffer(MainFileID);
+ MainFileStart = MainBuf->getBufferStart();
+ MainFileEnd = MainBuf->getBufferEnd();
+
+ Rewrite.setSourceMgr(Context->getSourceManager());
+
+ const char *s = "#pragma once\n"
+ "#ifndef CLOSURE_IMPL\n"
+ "struct __closure_impl {\n"
+ " long Reserved;\n"
+ " int Flags;\n"
+ " int Size;\n"
+ " void *Invoke;\n"
+ "};\n"
+ "enum {\n"
+ " HAS_NONPOD = (1<<25),\n"
+ " HAS_BYREF = (1<<26)\n"
+ "};\n"
+ "#define CLOSURE_IMPL\n"
+ "#endif\n";
+ if (IsHeader) {
+ // insert the whole string when rewriting a header file
+ InsertText(SourceLocation::getFileLoc(MainFileID, 0), s, strlen(s));
+ }
+ else {
+ // Not rewriting header, exclude the #pragma once pragma
+ const char *p = s + strlen("#pragma once\n");
+ InsertText(SourceLocation::getFileLoc(MainFileID, 0), p, strlen(p));
+ }
+}
+
+void RewriteBlocks::InsertText(SourceLocation Loc, const char *StrData,
+ unsigned StrLen)
+{
+ if (!Rewrite.InsertText(Loc, StrData, StrLen))
+ return;
+ Diags.Report(Context->getFullLoc(Loc), RewriteFailedDiag);
+}
+
+void RewriteBlocks::ReplaceText(SourceLocation Start, unsigned OrigLength,
+ const char *NewStr, unsigned NewLength) {
+ if (!Rewrite.ReplaceText(Start, OrigLength, NewStr, NewLength))
+ return;
+ Diags.Report(Context->getFullLoc(Start), RewriteFailedDiag);
+}
+
+void RewriteBlocks::RewriteMethodDecl(ObjCMethodDecl *Method) {
+ bool haveBlockPtrs = false;
+ for (ObjCMethodDecl::param_iterator I = Method->param_begin(),
+ E = Method->param_end(); I != E; ++I)
+ if (isBlockPointerType((*I)->getType()))
+ haveBlockPtrs = true;
+
+ if (!haveBlockPtrs)
+ return;
+
+ // Do a fuzzy rewrite.
+ // We have 1 or more arguments that have closure pointers.
+ SourceLocation Loc = Method->getLocStart();
+ SourceLocation LocEnd = Method->getLocEnd();
+ const char *startBuf = SM->getCharacterData(Loc);
+ const char *endBuf = SM->getCharacterData(LocEnd);
+
+ const char *methodPtr = startBuf;
+ std::string Tag = "struct __closure_impl *";
+
+ while (*methodPtr++ && (methodPtr != endBuf)) {
+ switch (*methodPtr) {
+ case ':':
+ methodPtr++;
+ if (*methodPtr == '(') {
+ const char *scanType = ++methodPtr;
+ bool foundBlockPointer = false;
+ unsigned parenCount = 1;
+
+ while (parenCount) {
+ switch (*scanType) {
+ case '(':
+ parenCount++;
+ break;
+ case ')':
+ parenCount--;
+ break;
+ case '^':
+ foundBlockPointer = true;
+ break;
+ }
+ scanType++;
+ }
+ if (foundBlockPointer) {
+ // advance the location to startArgList.
+ Loc = Loc.getFileLocWithOffset(methodPtr-startBuf);
+ assert((Loc.isValid()) && "Invalid Loc");
+ ReplaceText(Loc, scanType-methodPtr-1, Tag.c_str(), Tag.size());
+
+ // Advance startBuf. Since the underlying buffer has changed,
+ // it's very important to advance startBuf (so we can correctly
+ // compute a relative Loc the next time around).
+ startBuf = methodPtr;
+ }
+ // Advance the method ptr to the end of the type.
+ methodPtr = scanType;
+ }
+ break;
+ }
+ }
+ return;
+}
+
+void RewriteBlocks::RewriteInterfaceDecl(ObjCInterfaceDecl *ClassDecl) {
+ for (ObjCInterfaceDecl::instmeth_iterator I = ClassDecl->instmeth_begin(),
+ E = ClassDecl->instmeth_end(); I != E; ++I)
+ RewriteMethodDecl(*I);
+ for (ObjCInterfaceDecl::classmeth_iterator I = ClassDecl->classmeth_begin(),
+ E = ClassDecl->classmeth_end(); I != E; ++I)
+ RewriteMethodDecl(*I);
+}
+
+void RewriteBlocks::RewriteCategoryDecl(ObjCCategoryDecl *CatDecl) {
+ for (ObjCCategoryDecl::instmeth_iterator I = CatDecl->instmeth_begin(),
+ E = CatDecl->instmeth_end(); I != E; ++I)
+ RewriteMethodDecl(*I);
+ for (ObjCCategoryDecl::classmeth_iterator I = CatDecl->classmeth_begin(),
+ E = CatDecl->classmeth_end(); I != E; ++I)
+ RewriteMethodDecl(*I);
+}
+
+void RewriteBlocks::RewriteProtocolDecl(ObjCProtocolDecl *PDecl) {
+ for (ObjCProtocolDecl::instmeth_iterator I = PDecl->instmeth_begin(),
+ E = PDecl->instmeth_end(); I != E; ++I)
+ RewriteMethodDecl(*I);
+ for (ObjCProtocolDecl::classmeth_iterator I = PDecl->classmeth_begin(),
+ E = PDecl->classmeth_end(); I != E; ++I)
+ RewriteMethodDecl(*I);
+}
+
+//===----------------------------------------------------------------------===//
+// Top Level Driver Code
+//===----------------------------------------------------------------------===//
+
+void RewriteBlocks::HandleTopLevelDecl(Decl *D) {
+ // Two cases: either the decl could be in the main file, or it could be in a
+ // #included file. If the former, rewrite it now. If the later, check to see
+ // if we rewrote the #include/#import.
+ SourceLocation Loc = D->getLocation();
+ Loc = SM->getLogicalLoc(Loc);
+
+ // If this is for a builtin, ignore it.
+ if (Loc.isInvalid()) return;
+
+ if (ObjCInterfaceDecl *MD = dyn_cast<ObjCInterfaceDecl>(D))
+ RewriteInterfaceDecl(MD);
+ else if (ObjCCategoryDecl *CD = dyn_cast<ObjCCategoryDecl>(D))
+ RewriteCategoryDecl(CD);
+ else if (ObjCProtocolDecl *PD = dyn_cast<ObjCProtocolDecl>(D))
+ RewriteProtocolDecl(PD);
+
+ // If we have a decl in the main file, see if we should rewrite it.
+ if (SM->getDecomposedFileLoc(Loc).first == MainFileID)
+ HandleDeclInMainFile(D);
+ return;
+}
+
+std::string RewriteBlocks::SynthesizeBlockFunc(BlockExpr *CE, int i,
+ const char *funcName,
+ std::string Tag) {
+ const FunctionType *AFT = CE->getFunctionType();
+ QualType RT = AFT->getResultType();
+ std::string S = "static " + RT.getAsString() + " __" +
+ funcName + "_" + "closure_" + utostr(i);
+
+ if (isa<FunctionTypeNoProto>(AFT)) {
+ S += "()";
+ } else if (CE->arg_empty()) {
+ S += "(" + Tag + " *__cself)";
+ } else {
+ const FunctionTypeProto *FT = cast<FunctionTypeProto>(AFT);
+ assert(FT && "SynthesizeBlockFunc: No function proto");
+ S += '(';
+ // first add the implicit argument.
+ S += Tag + " *__cself, ";
+ std::string ParamStr;
+ for (BlockStmtExpr::arg_iterator AI = CE->arg_begin(),
+ E = CE->arg_end(); AI != E; ++AI) {
+ if (AI != CE->arg_begin()) S += ", ";
+ ParamStr = (*AI)->getName();
+ (*AI)->getType().getAsStringInternal(ParamStr);
+ S += ParamStr;
+ }
+ if (FT->isVariadic()) {
+ if (!CE->arg_empty()) S += ", ";
+ S += "...";
+ }
+ S += ')';
+ }
+ S += " {\n";
+
+ bool haveByRefDecls = false;
+
+ // Create local declarations to avoid rewriting all closure decl ref exprs.
+ // First, emit a declaration for all "by ref" decls.
+ for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = BlockByRefDecls.begin(),
+ E = BlockByRefDecls.end(); I != E; ++I) {
+ // Note: It is not possible to have "by ref" closure pointer decls.
+ haveByRefDecls = true;
+ S += " ";
+ std::string Name = (*I)->getName();
+ Context->getPointerType((*I)->getType()).getAsStringInternal(Name);
+ S += Name + " = __cself->" + (*I)->getName() + "; // bound by ref\n";
+ }
+ // Next, emit a declaration for all "by copy" declarations.
+ for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = BlockByCopyDecls.begin(),
+ E = BlockByCopyDecls.end(); I != E; ++I) {
+ S += " ";
+ std::string Name = (*I)->getName();
+ // Handle nested closure invocation. For example:
+ //
+ // void (^myImportedClosure)(void);
+ // myImportedClosure = ^(void) { setGlobalInt(x + y); };
+ //
+ // void (^anotherClosure)(void);
+ // anotherClosure = ^(void) {
+ // myImportedClosure(); // import and invoke the closure
+ // };
+ //
+ if (isBlockPointerType((*I)->getType()))
+ S += "struct __closure_impl *";
+ else
+ (*I)->getType().getAsStringInternal(Name);
+ S += Name + " = __cself->" + (*I)->getName() + "; // bound by copy\n";
+ }
+ if (BlockStmtExpr *CBE = dyn_cast<BlockStmtExpr>(CE)) {
+ std::string BodyBuf;
+
+ SourceLocation BodyLocStart = CBE->getBody()->getLocStart();
+ SourceLocation BodyLocEnd = CBE->getBody()->getLocEnd();
+ const char *BodyStartBuf = SM->getCharacterData(BodyLocStart);
+ const char *BodyEndBuf = SM->getCharacterData(BodyLocEnd);
+
+ BodyBuf.append(BodyStartBuf, BodyEndBuf-BodyStartBuf+1);
+
+ if (BlockDeclRefs.size()) {
+ unsigned int nCharsAdded = 0;
+ for (unsigned i = 0; i < BlockDeclRefs.size(); i++) {
+ if (BlockDeclRefs[i]->isByRef()) {
+ // Add a level of indirection! The code below assumes
+ // the closure decl refs/locations are in strictly ascending
+ // order. The traversal performed by GetBlockDeclRefExprs()
+ // currently does this. FIXME: Wrap the *x with parens,
+ // just in case x is a more complex expression, like x->member,
+ // which needs to be rewritten to (*x)->member.
+ SourceLocation StarLoc = BlockDeclRefs[i]->getLocStart();
+ const char *StarBuf = SM->getCharacterData(StarLoc);
+ BodyBuf.insert(StarBuf-BodyStartBuf+nCharsAdded, 1, '*');
+ // Get a fresh buffer, the insert might have caused it to grow.
+ BodyStartBuf = SM->getCharacterData(BodyLocStart);
+ nCharsAdded++;
+ } else if (isBlockPointerType(BlockDeclRefs[i]->getType())) {
+ Diags.Report(NoNestedBlockCalls);
+
+ GetBlockCallExprs(CE);
+
+ // Rewrite the closure in place.
+ // The character based equivalent of RewriteBlockCall().
+ // Need to get the CallExpr associated with this BlockDeclRef.
+ std::string BlockCall = SynthesizeBlockCall(BlockCallExprs[BlockDeclRefs[i]]);
+
+ SourceLocation CallLocStart = BlockCallExprs[BlockDeclRefs[i]]->getLocStart();
+ SourceLocation CallLocEnd = BlockCallExprs[BlockDeclRefs[i]]->getLocEnd();
+ const char *CallStart = SM->getCharacterData(CallLocStart);
+ const char *CallEnd = SM->getCharacterData(CallLocEnd);
+ unsigned CallBytes = CallEnd-CallStart;
+ BodyBuf.replace(CallStart-BodyStartBuf, CallBytes, BlockCall.c_str());
+ nCharsAdded += CallBytes;
+ }
+ }
+ }
+ if (haveByRefDecls) {
+ // Remove |...|.
+ const char *firstBarPtr = strchr(BodyStartBuf, '|');
+ const char *secondBarPtr = strchr(firstBarPtr+1, '|');
+ BodyBuf.replace(firstBarPtr-BodyStartBuf, secondBarPtr-firstBarPtr+1, "");
+ }
+ S += " ";
+ S += BodyBuf;
+ }
+ S += "\n}\n";
+ return S;
+}
+
+std::string RewriteBlocks::SynthesizeBlockImpl(BlockExpr *CE,
+ std::string Tag) {
+ std::string S = Tag + " {\n struct __closure_impl impl;\n";
+
+ GetBlockDeclRefExprs(CE);
+ if (BlockDeclRefs.size()) {
+ // Unique all "by copy" declarations.
+ for (unsigned i = 0; i < BlockDeclRefs.size(); i++)
+ if (!BlockDeclRefs[i]->isByRef())
+ BlockByCopyDecls.insert(BlockDeclRefs[i]->getDecl());
+ // Unique all "by ref" declarations.
+ for (unsigned i = 0; i < BlockDeclRefs.size(); i++)
+ if (BlockDeclRefs[i]->isByRef())
+ BlockByRefDecls.insert(BlockDeclRefs[i]->getDecl());
+
+ // Output all "by copy" declarations.
+ for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = BlockByCopyDecls.begin(),
+ E = BlockByCopyDecls.end(); I != E; ++I) {
+ S += " ";
+ std::string Name = (*I)->getName();
+ // Handle nested closure invocation. For example:
+ //
+ // void (^myImportedBlock)(void);
+ // myImportedBlock = ^(void) { setGlobalInt(x + y); };
+ //
+ // void (^anotherBlock)(void);
+ // anotherBlock = ^(void) {
+ // myImportedBlock(); // import and invoke the closure
+ // };
+ //
+ if (isBlockPointerType((*I)->getType()))
+ S += "struct __closure_impl *";
+ else
+ (*I)->getType().getAsStringInternal(Name);
+ S += Name + ";\n";
+ }
+ // Output all "by ref" declarations.
+ for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = BlockByRefDecls.begin(),
+ E = BlockByRefDecls.end(); I != E; ++I) {
+ S += " ";
+ std::string Name = (*I)->getName();
+ if (isBlockPointerType((*I)->getType()))
+ S += "struct __closure_impl *";
+ else
+ Context->getPointerType((*I)->getType()).getAsStringInternal(Name);
+ S += Name + "; // by ref\n";
+ }
+ }
+ S += "};\n";
+ return S;
+}
+
+void RewriteBlocks::SynthesizeBlockLiterals(SourceLocation FunLocStart,
+ const char *FunName) {
+ // Insert closures that were part of the function.
+ for (unsigned i = 0; i < Blocks.size(); i++) {
+
+ std::string Tag = "struct __" + std::string(FunName) +
+ "_closure_impl_" + utostr(i);
+
+ std::string CI = SynthesizeBlockImpl(Blocks[i], Tag);
+
+ InsertText(FunLocStart, CI.c_str(), CI.size());
+
+ std::string CF = SynthesizeBlockFunc(Blocks[i], i, FunName, Tag);
+
+ InsertText(FunLocStart, CF.c_str(), CF.size());
+
+ BlockDeclRefs.clear();
+ BlockByRefDecls.clear();
+ BlockByCopyDecls.clear();
+ BlockCallExprs.clear();
+ }
+ Blocks.clear();
+}
+
+void RewriteBlocks::InsertBlockLiteralsWithinFunction(FunctionDecl *FD) {
+ SourceLocation FunLocStart = FD->getLocation();
+ const char *FuncName = FD->getName();
+
+ SynthesizeBlockLiterals(FunLocStart, FuncName);
+}
+
+void RewriteBlocks::InsertBlockLiteralsWithinMethod(ObjCMethodDecl *MD) {
+ SourceLocation FunLocStart = MD->getLocStart();
+ std::string FuncName = std::string(MD->getSelector().getName());
+ // Convert colons to underscores.
+ std::string::size_type loc = 0;
+ while ((loc = FuncName.find(":", loc)) != std::string::npos)
+ FuncName.replace(loc, 1, "_");
+
+ SynthesizeBlockLiterals(FunLocStart, FuncName.c_str());
+}
+
+/// HandleDeclInMainFile - This is called for each top-level decl defined in the
+/// main file of the input.
+void RewriteBlocks::HandleDeclInMainFile(Decl *D) {
+ if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+
+ // Since function prototypes don't have ParmDecl's, we check the function
+ // prototype. This enables us to rewrite function declarations and
+ // definitions using the same code.
+ QualType funcType = FD->getType();
+
+ if (FunctionTypeProto *fproto = dyn_cast<FunctionTypeProto>(funcType)) {
+ for (FunctionTypeProto::arg_type_iterator I = fproto->arg_type_begin(),
+ E = fproto->arg_type_end(); I && (I != E); ++I)
+ if (isBlockPointerType(*I)) {
+ // All the args are checked/rewritten. Don't call twice!
+ RewriteBlockPointerDecl(FD);
+ break;
+ }
+ }
+ if (Stmt *Body = FD->getBody()) {
+ CurFunctionDef = FD;
+ FD->setBody(RewriteFunctionBody(Body));
+ InsertBlockLiteralsWithinFunction(FD);
+ CurFunctionDef = 0;
+ }
+ return;
+ }
+ if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
+ RewriteMethodDecl(MD);
+ if (Stmt *Body = MD->getBody()) {
+ CurMethodDef = MD;
+ RewriteFunctionBody(Body);
+ InsertBlockLiteralsWithinMethod(MD);
+ CurMethodDef = 0;
+ }
+ }
+ if (ValueDecl *ND = dyn_cast<ValueDecl>(D)) {
+ if (isBlockPointerType(ND->getType()))
+ RewriteBlockPointerDecl(ND);
+ return;
+ }
+ if (TypedefDecl *TD = dyn_cast<TypedefDecl>(D)) {
+ if (isBlockPointerType(TD->getUnderlyingType()))
+ RewriteBlockPointerDecl(TD);
+ return;
+ }
+}
+
+void RewriteBlocks::GetBlockDeclRefExprs(Stmt *S) {
+ for (Stmt::child_iterator CI = S->child_begin(), E = S->child_end();
+ CI != E; ++CI)
+ if (*CI)
+ GetBlockDeclRefExprs(*CI);
+
+ // Handle specific things.
+ if (BlockDeclRefExpr *CDRE = dyn_cast<BlockDeclRefExpr>(S))
+ // FIXME: Handle enums.
+ if (!isa<FunctionDecl>(CDRE->getDecl()))
+ BlockDeclRefs.push_back(CDRE);
+ return;
+}
+
+void RewriteBlocks::GetBlockCallExprs(Stmt *S) {
+ for (Stmt::child_iterator CI = S->child_begin(), E = S->child_end();
+ CI != E; ++CI)
+ if (*CI)
+ GetBlockCallExprs(*CI);
+
+ if (CallExpr *CE = dyn_cast<CallExpr>(S)) {
+ if (CE->getCallee()->getType()->isBlockPointerType())
+ BlockCallExprs[dyn_cast<BlockDeclRefExpr>(CE->getCallee())] = CE;
+ }
+ return;
+}
+
+//===----------------------------------------------------------------------===//
+// Function Body / Expression rewriting
+//===----------------------------------------------------------------------===//
+
+Stmt *RewriteBlocks::RewriteFunctionBody(Stmt *S) {
+ // Start by rewriting all children.
+ for (Stmt::child_iterator CI = S->child_begin(), E = S->child_end();
+ CI != E; ++CI)
+ if (*CI) {
+ if (BlockStmtExpr *CBE = dyn_cast<BlockStmtExpr>(*CI)) {
+ // We intentionally avoid rewritting the contents of a closure block
+ // expr. InsertBlockLiteralsWithinFunction() will rewrite the body.
+ RewriteBlockStmtExpr(CBE);
+ } else {
+ Stmt *newStmt = RewriteFunctionBody(*CI);
+ if (newStmt)
+ *CI = newStmt;
+ }
+ }
+ // Handle specific things.
+ if (CallExpr *CE = dyn_cast<CallExpr>(S)) {
+ if (CE->getCallee()->getType()->isBlockPointerType())
+ RewriteBlockCall(CE);
+ }
+ if (DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
+ ScopedDecl *SD = DS->getDecl();
+ if (ValueDecl *ND = dyn_cast<ValueDecl>(SD)) {
+ if (isBlockPointerType(ND->getType()))
+ RewriteBlockPointerDecl(ND);
+ }
+ if (TypedefDecl *TD = dyn_cast<TypedefDecl>(SD)) {
+ if (isBlockPointerType(TD->getUnderlyingType()))
+ RewriteBlockPointerDecl(TD);
+ }
+ }
+ // Return this stmt unmodified.
+ return S;
+}
+
+std::string RewriteBlocks::SynthesizeBlockCall(CallExpr *Exp) {
+ // Navigate to relevant type information.
+ const char *closureName;
+ const BlockPointerType *CPT;
+
+ if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Exp->getCallee())) {
+ closureName = DRE->getDecl()->getName();
+ CPT = DRE->getType()->getAsBlockPointerType();
+ } else if (BlockDeclRefExpr *CDRE = dyn_cast<BlockDeclRefExpr>(Exp->getCallee())) {
+ closureName = CDRE->getDecl()->getName();
+ CPT = CDRE->getType()->getAsBlockPointerType();
+ } else {
+ assert(1 && "RewriteBlockClass: Bad type");
+ }
+ assert(CPT && "RewriteBlockClass: Bad type");
+ const FunctionType *FT = CPT->getPointeeType()->getAsFunctionType();
+ assert(FT && "RewriteBlockClass: Bad type");
+ const FunctionTypeProto *FTP = dyn_cast<FunctionTypeProto>(FT);
+ // FTP will be null for closures that don't take arguments.
+
+ // Build a closure call - start with a paren expr to enforce precedence.
+ std::string BlockCall = "(";
+
+ // Synthesize the cast.
+ BlockCall += "(" + Exp->getType().getAsString() + "(*)";
+ BlockCall += "(struct __closure_impl *";
+ if (FTP) {
+ for (FunctionTypeProto::arg_type_iterator I = FTP->arg_type_begin(),
+ E = FTP->arg_type_end(); I && (I != E); ++I)
+ BlockCall += ", " + (*I).getAsString();
+ }
+ BlockCall += "))"; // close the argument list and paren expression.
+
+ // Invoke the closure.
+ BlockCall += closureName;
+ BlockCall += "->Invoke)";
+
+ // Add the arguments.
+ BlockCall += "(";
+ BlockCall += closureName;
+ for (CallExpr::arg_iterator I = Exp->arg_begin(),
+ E = Exp->arg_end(); I != E; ++I) {
+ std::string syncExprBufS;
+ llvm::raw_string_ostream Buf(syncExprBufS);
+ (*I)->printPretty(Buf);
+ BlockCall += ", " + Buf.str();
+ }
+ return BlockCall;
+}
+
+void RewriteBlocks::RewriteBlockCall(CallExpr *Exp) {
+ std::string BlockCall = SynthesizeBlockCall(Exp);
+
+ const char *startBuf = SM->getCharacterData(Exp->getLocStart());
+ const char *endBuf = SM->getCharacterData(Exp->getLocEnd());
+
+ ReplaceText(Exp->getLocStart(), endBuf-startBuf,
+ BlockCall.c_str(), BlockCall.size());
+}
+
+void RewriteBlocks::RewriteBlockPointerFunctionArgs(FunctionDecl *FD) {
+ SourceLocation DeclLoc = FD->getLocation();
+ unsigned parenCount = 0, nArgs = 0;
+
+ // We have 1 or more arguments that have closure pointers.
+ const char *startBuf = SM->getCharacterData(DeclLoc);
+ const char *startArgList = strchr(startBuf, '(');
+
+ assert((*startArgList == '(') && "Rewriter fuzzy parser confused");
+
+ parenCount++;
+ // advance the location to startArgList.
+ DeclLoc = DeclLoc.getFileLocWithOffset(startArgList-startBuf+1);
+ assert((DeclLoc.isValid()) && "Invalid DeclLoc");
+
+ const char *topLevelCommaCursor = 0;
+ const char *argPtr = startArgList;
+ bool scannedBlockDecl = false;
+ std::string Tag = "struct __closure_impl *";
+
+ while (*argPtr++ && parenCount) {
+ switch (*argPtr) {
+ case '^':
+ scannedBlockDecl = true;
+ break;
+ case '(':
+ parenCount++;
+ break;
+ case ')':
+ parenCount--;
+ if (parenCount == 0) {
+ if (scannedBlockDecl) {
+ // If we are rewriting a definition, don't forget the arg name.
+ if (FD->getBody())
+ Tag += FD->getParamDecl(nArgs)->getName();
+ // The last argument is a closure pointer decl, rewrite it!
+ if (topLevelCommaCursor)
+ ReplaceText(DeclLoc, argPtr-topLevelCommaCursor-2, Tag.c_str(), Tag.size());
+ else
+ ReplaceText(DeclLoc, argPtr-startArgList-1, Tag.c_str(), Tag.size());
+ scannedBlockDecl = false; // reset.
+ }
+ nArgs++;
+ }
+ break;
+ case ',':
+ if (parenCount == 1) {
+ // Make sure the function takes more than one argument.
+ assert((FD->getNumParams() > 1) && "Rewriter fuzzy parser confused");
+ if (scannedBlockDecl) {
+ // If we are rewriting a definition, don't forget the arg name.
+ if (FD->getBody())
+ Tag += FD->getParamDecl(nArgs)->getName();
+ // The current argument is a closure pointer decl, rewrite it!
+ if (topLevelCommaCursor)
+ ReplaceText(DeclLoc, argPtr-topLevelCommaCursor-1, Tag.c_str(), Tag.size());
+ else
+ ReplaceText(DeclLoc, argPtr-startArgList-1, Tag.c_str(), Tag.size());
+ scannedBlockDecl = false;
+ }
+ nArgs++;
+ // advance the location to topLevelCommaCursor.
+ if (topLevelCommaCursor)
+ DeclLoc = DeclLoc.getFileLocWithOffset(argPtr-topLevelCommaCursor);
+ else
+ DeclLoc = DeclLoc.getFileLocWithOffset(argPtr-startArgList+1);
+ topLevelCommaCursor = argPtr;
+ assert((DeclLoc.isValid()) && "Invalid DeclLoc");
+ }
+ break;
+ }
+ }
+ return;
+}
+
+void RewriteBlocks::RewriteBlockPointerDecl(NamedDecl *ND) {
+ SourceLocation DeclLoc = ND->getLocation();
+ const char *startBuf, *endBuf;
+
+ if (FunctionDecl *FD = dyn_cast<FunctionDecl>(ND)) {
+ RewriteBlockPointerFunctionArgs(FD);
+ return;
+ } else if (VarDecl *VD = dyn_cast<VarDecl>(ND)) {
+ DeclLoc = VD->getLocation();
+ startBuf = SM->getCharacterData(DeclLoc);
+ endBuf = startBuf;
+ } else if (TypedefDecl *TDD = dyn_cast<TypedefDecl>(ND)) {
+ DeclLoc = TDD->getLocation();
+ startBuf = SM->getCharacterData(DeclLoc);
+ if (!strncmp("typedef ", startBuf, 8)) {
+ startBuf += 8; // skip the typedef...
+ DeclLoc = DeclLoc.getFileLocWithOffset(8);
+ }
+ endBuf = startBuf;
+ }
+ // FIXME: need to skip past the argument list...then check for ','.
+ while (*endBuf && *endBuf != '=' && *endBuf != ';')
+ endBuf++;
+
+ SourceLocation DeclEndLoc = DeclLoc.getFileLocWithOffset(endBuf-startBuf);
+
+ std::string Tag = "struct __closure_impl *" + std::string(ND->getName());
+ ReplaceText(DeclLoc, endBuf-startBuf, Tag.c_str(), Tag.size());
+ return;
+}
+
+void RewriteBlocks::RewriteBlockStmtExpr(BlockStmtExpr *Exp) {
+ Blocks.push_back(Exp);
+ bool haveByRefDecls = false;
+
+ // Add initializers for any closure decl refs.
+ GetBlockDeclRefExprs(Exp);
+ if (BlockDeclRefs.size()) {
+ // Unique all "by copy" declarations.
+ for (unsigned i = 0; i < BlockDeclRefs.size(); i++)
+ if (!BlockDeclRefs[i]->isByRef())
+ BlockByCopyDecls.insert(BlockDeclRefs[i]->getDecl());
+ // Unique all "by ref" declarations.
+ for (unsigned i = 0; i < BlockDeclRefs.size(); i++)
+ if (BlockDeclRefs[i]->isByRef()) {
+ haveByRefDecls = true;
+ BlockByRefDecls.insert(BlockDeclRefs[i]->getDecl());
+ }
+ }
+ std::string FuncName;
+
+ if (CurFunctionDef)
+ FuncName = std::string(CurFunctionDef->getName());
+ else if (CurMethodDef) {
+ FuncName = std::string(CurMethodDef->getSelector().getName());
+ // Convert colons to underscores.
+ std::string::size_type loc = 0;
+ while ((loc = FuncName.find(":", loc)) != std::string::npos)
+ FuncName.replace(loc, 1, "_");
+ }
+ std::string BlockNumber = utostr(Blocks.size()-1);
+
+ std::string Tag = "struct __" + FuncName + "_closure_impl_" + BlockNumber;
+ std::string Func = "__" + FuncName + "_" + "closure_" + BlockNumber;
+
+ // Rewrite the closure block with a compound literal. The first cast is
+ // to prevent warnings from the C compiler.
+ std::string Init = "(struct __closure_impl *)&(" + Tag + "){{0,";
+
+ // Initialize the Flags, Size, and Invoke fields.
+ Init += (haveByRefDecls ? "HAS_BYREF," : "0,");
+ Init += "sizeof(" + Tag + ")," + Func + "}";
+
+ // Add initializers for any closure decl refs.
+ if (BlockDeclRefs.size()) {
+ // Output all "by copy" declarations.
+ for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = BlockByCopyDecls.begin(),
+ E = BlockByCopyDecls.end(); I != E; ++I) {
+ Init += ",";
+ if (isObjCType((*I)->getType())) {
+ Init += "[[";
+ Init += (*I)->getName();
+ Init += " retain] autorelease]";
+ } else {
+ Init += (*I)->getName();
+ }
+ }
+ // Output all "by ref" declarations.
+ for (llvm::SmallPtrSet<ValueDecl*,8>::iterator I = BlockByRefDecls.begin(),
+ E = BlockByRefDecls.end(); I != E; ++I) {
+ Init += ",&";
+ Init += (*I)->getName();
+ }
+ }
+ Init += "}";
+ BlockDeclRefs.clear();
+ BlockByRefDecls.clear();
+ BlockByCopyDecls.clear();
+
+ // Do the rewrite.
+ const char *startBuf = SM->getCharacterData(Exp->getLocStart());
+ const char *endBuf = SM->getCharacterData(Exp->getLocEnd());
+ ReplaceText(Exp->getLocStart(), endBuf-startBuf+1, Init.c_str(), Init.size());
+ return;
+}
Modified: cfe/trunk/clang.xcodeproj/project.pbxproj
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/clang.xcodeproj/project.pbxproj?rev=56266&r1=56265&r2=56266&view=diff
==============================================================================
--- cfe/trunk/clang.xcodeproj/project.pbxproj (original)
+++ cfe/trunk/clang.xcodeproj/project.pbxproj Tue Sep 16 19:13:27 2008
@@ -65,6 +65,7 @@
84AF36A10CB17A3B00C820A5 /* DeclObjC.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 84AF36A00CB17A3B00C820A5 /* DeclObjC.h */; };
84D9A8880C1A57E100AC7ABC /* AttributeList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84D9A8870C1A57E100AC7ABC /* AttributeList.cpp */; };
84D9A88C0C1A581300AC7ABC /* AttributeList.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 84D9A88B0C1A581300AC7ABC /* AttributeList.h */; };
+ 9030C10A0E807A9300941490 /* RewriteBlocks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9030C1090E807A9300941490 /* RewriteBlocks.cpp */; };
DE01DA490B12ADA300AC22CE /* PPCallbacks.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = DE01DA480B12ADA300AC22CE /* PPCallbacks.h */; };
DE06756C0C051CFE00EBBFD8 /* ParseExprCXX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DE06756B0C051CFE00EBBFD8 /* ParseExprCXX.cpp */; };
DE06B73E0A8307640050E87E /* LangOptions.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = DE06B73D0A8307640050E87E /* LangOptions.h */; };
@@ -366,6 +367,7 @@
84D9A8870C1A57E100AC7ABC /* AttributeList.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = AttributeList.cpp; path = lib/Parse/AttributeList.cpp; sourceTree = "<group>"; };
84D9A88B0C1A581300AC7ABC /* AttributeList.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = AttributeList.h; path = clang/Parse/AttributeList.h; sourceTree = "<group>"; };
8DD76F6C0486A84900D96B5E /* clang */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = clang; sourceTree = BUILT_PRODUCTS_DIR; };
+ 9030C1090E807A9300941490 /* RewriteBlocks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RewriteBlocks.cpp; path = Driver/RewriteBlocks.cpp; sourceTree = "<group>"; };
DE01DA480B12ADA300AC22CE /* PPCallbacks.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PPCallbacks.h; sourceTree = "<group>"; };
DE06756B0C051CFE00EBBFD8 /* ParseExprCXX.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ParseExprCXX.cpp; path = lib/Parse/ParseExprCXX.cpp; sourceTree = "<group>"; };
DE06B73D0A8307640050E87E /* LangOptions.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = LangOptions.h; sourceTree = "<group>"; tabWidth = 2; };
@@ -846,6 +848,7 @@
DEAEECAE0A5AF0FA0045101B /* Driver */ = {
isa = PBXGroup;
children = (
+ 9030C1090E807A9300941490 /* RewriteBlocks.cpp */,
DE5932CD0AD60FF400BC794C /* clang.cpp */,
DE5932CE0AD60FF400BC794C /* clang.h */,
359DBBE20E1ACD4700F43FA0 /* AnalysisConsumer.h */,
@@ -1221,6 +1224,7 @@
359763260E633C7500B68AB7 /* HTMLDiagnostics.cpp in Sources */,
358CFBB80E65AB04002A8E19 /* BasicConstraintManager.cpp in Sources */,
35475B200E79973F0000BFE4 /* CGCall.cpp in Sources */,
+ 9030C10A0E807A9300941490 /* RewriteBlocks.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
More information about the cfe-commits
mailing list