Compare commits

..

39 Commits

Author SHA1 Message Date
Tanya Lattner
45d5bd27cb 2.0 Release docs
llvm-svn: 37312
2007-05-23 18:12:40 +00:00
Tanya Lattner
755a21f3ae Fixing patch mistake.
llvm-svn: 37303
2007-05-23 06:07:37 +00:00
Chris Lattner
688d4d27cf final updates to release notes
llvm-svn: 37300
2007-05-23 04:41:25 +00:00
Tanya Lattner
0df7b6319f Merge from mainline
llvm-svn: 37298
2007-05-23 04:22:41 +00:00
Tanya Lattner
5764e55d63 Merge from mainline
llvm-svn: 37279
2007-05-22 06:23:12 +00:00
Tanya Lattner
b5c059b832 Merge from mainline
llvm-svn: 37276
2007-05-22 05:38:40 +00:00
Chris Lattner
bd43fcf5c5 merge portability fix from mainline
llvm-svn: 37222
2007-05-18 07:07:27 +00:00
Chris Lattner
1dc0dbf64d first cut of llvm 2.0 release notes
llvm-svn: 37220
2007-05-18 06:39:06 +00:00
Tanya Lattner
8c32fe0e61 Merge from mainline
llvm-svn: 37217
2007-05-18 06:21:50 +00:00
Tanya Lattner
2e406e7d52 Merge from mainline.
llvm-svn: 37216
2007-05-18 06:20:09 +00:00
Tanya Lattner
1f6d36ff77 Merge from mainline
llvm-svn: 37215
2007-05-18 06:08:42 +00:00
Tanya Lattner
a5bea192e3 Merge from mainline.
llvm-svn: 37214
2007-05-18 06:03:56 +00:00
Tanya Lattner
28c87da11f Merge from mainline.
llvm-svn: 37213
2007-05-18 06:03:55 +00:00
Tanya Lattner
539eba2303 Merging from mainline.
llvm-svn: 37212
2007-05-18 05:59:52 +00:00
Tanya Lattner
16c912d5f0 Merging from mainline (inline asm fix)
llvm-svn: 37211
2007-05-18 05:55:42 +00:00
Chris Lattner
5e6d2c1282 merge in from mainline
llvm-svn: 37210
2007-05-18 05:51:57 +00:00
Tanya Lattner
ac864c0f53 Merge inline asm fix from mainline.
llvm-svn: 37209
2007-05-18 05:51:20 +00:00
Chris Lattner
35fb6225a6 merge from mainline
llvm-svn: 37208
2007-05-18 05:48:38 +00:00
Reid Spencer
b73b303f76 Regenerate these for the release. They weren't up to date and would
cause non-bison builds to fail.

llvm-svn: 37189
2007-05-17 22:59:30 +00:00
Reid Spencer
25c7375e28 Fix the rules for handling the case when BISON is not available. We want
this to succeed by copying the .h.cvs file to .h and the .cpp.cvs file to
.cpp.

llvm-svn: 37187
2007-05-17 22:42:28 +00:00
Reid Spencer
9a8bae25b7 Merge from mainline to require Perl for build system.
llvm-svn: 37167
2007-05-17 18:12:30 +00:00
Reid Spencer
9eb9228a7b Make Perl required - merge from mainline.
llvm-svn: 37165
2007-05-17 18:07:24 +00:00
Reid Spencer
cbf56c683f Merge arbitrary precision integer documentation from mainline.
llvm-svn: 37110
2007-05-16 18:46:48 +00:00
Tanya Lattner
407e88833c Remove from release.
llvm-svn: 37079
2007-05-15 05:18:30 +00:00
Tanya Lattner
70c8b4ce40 Merge from mainline.
llvm-svn: 37078
2007-05-15 05:15:10 +00:00
Tanya Lattner
9a3b80ebb8 Merging from mainline.
llvm-svn: 37077
2007-05-15 05:08:55 +00:00
Tanya Lattner
d1e8ff86b5 Merge from mainline.
llvm-svn: 37076
2007-05-15 05:00:54 +00:00
Tanya Lattner
05c9c7203d Merge from mainline
PR1413

llvm-svn: 37075
2007-05-15 04:57:33 +00:00
Tanya Lattner
990c707d71 Merging from mainline
llvm-svn: 36970
2007-05-10 04:52:54 +00:00
Tanya Lattner
43195b190b Merging from mainline PR 1403
llvm-svn: 36969
2007-05-10 04:47:38 +00:00
Tanya Lattner
d29e402cca Merge from mainline PR1403
llvm-svn: 36968
2007-05-10 04:42:32 +00:00
Tanya Lattner
02ac6f3362 file 2007-05-09-JumpTables.ll was added on branch release_20 on 2007-05-10 04:47:38 +0000
llvm-svn: 36961
2007-05-09 20:07:09 +00:00
CVS to SVN Conversion
ad4bf2f82b This commit was manufactured by cvs2svn to create branch 'release_20'.
llvm-svn: 36960
2007-05-09 20:07:08 +00:00
Tanya Lattner
200b3953c2 Merging from mainline. PR1399 fix.
llvm-svn: 36949
2007-05-08 23:41:39 +00:00
Tanya Lattner
e43c50a1a4 Merge from mainline
llvm-svn: 36932
2007-05-08 06:37:35 +00:00
Tanya Lattner
09489a21d9 Merging from mainline.
llvm-svn: 36931
2007-05-08 06:36:05 +00:00
Tanya Lattner
1c81a16ffc Setting version number
llvm-svn: 36927
2007-05-08 04:45:26 +00:00
Tanya Lattner
357259b227 Setting version number.
llvm-svn: 36926
2007-05-08 04:45:12 +00:00
CVS to SVN Conversion
15fe03a879 This commit was manufactured by cvs2svn to create branch 'release_20'.
llvm-svn: 36925
2007-05-08 04:45:12 +00:00
26136 changed files with 393140 additions and 3989243 deletions

View File

@@ -1,6 +0,0 @@
add_subdirectory(remove-cstr-calls)
add_subdirectory(tool-template)
add_subdirectory(loop-convert)
# Add the common testsuite after all the tools.
add_subdirectory(test)

View File

@@ -1,63 +0,0 @@
==============================================================================
LLVM Release License
==============================================================================
University of Illinois/NCSA
Open Source License
Copyright (c) 2007-2012 University of Illinois at Urbana-Champaign.
All rights reserved.
Developed by:
LLVM Team
University of Illinois at Urbana-Champaign
http://llvm.org
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal with
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimers.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimers in the
documentation and/or other materials provided with the distribution.
* Neither the names of the LLVM Team, University of Illinois at
Urbana-Champaign, nor the names of its contributors may be used to
endorse or promote products derived from this Software without specific
prior written permission.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
SOFTWARE.
==============================================================================
The LLVM software contains code written by third parties. Such software will
have its own individual LICENSE.TXT file in the directory in which it appears.
This file will describe the copyrights, license, and restrictions which apply
to that code.
The disclaimer of warranty in the University of Illinois Open Source License
applies to all code in the LLVM Distribution, and nothing in any of the
other licenses gives permission to use the names of the LLVM Team or the
University of Illinois to endorse or promote products derived from this
Software.
The following pieces of software have additional or alternate copyrights,
licenses, and/or restrictions:
Program Directory
------- ---------
<none yet>

View File

@@ -1,40 +0,0 @@
##===- tools/extra/Makefile --------------------------------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##
CLANG_LEVEL := ../..
include $(CLANG_LEVEL)/../../Makefile.config
PARALLEL_DIRS := remove-cstr-calls tool-template loop-convert
include $(CLANG_LEVEL)/Makefile
###
# Handle the nested test suite.
ifneq ($(PROJ_SRC_ROOT),$(PROJ_OBJ_ROOT))
$(RecursiveTargets)::
$(Verb) for dir in test; do \
if [ -f $(PROJ_SRC_DIR)/$${dir}/Makefile ] && [ ! -f $${dir}/Makefile ]; then \
$(MKDIR) $${dir}; \
$(CP) $(PROJ_SRC_DIR)/$${dir}/Makefile $${dir}/Makefile; \
fi \
done
endif
test::
@ $(MAKE) -C test
report::
@ $(MAKE) -C test report
clean::
@ $(MAKE) -C test clean
.PHONY: test report clean

View File

@@ -1,22 +0,0 @@
//===----------------------------------------------------------------------===//
// Clang Tools repository
//===----------------------------------------------------------------------===//
Welcome to the repository of extra Clang Tools. This repository holds tools
that are developed as part of the LLVM compiler infrastructure project and the
Clang frontend. These tools are kept in a separate "extra" repository to
allow lighter weight checkouts of the core Clang codebase.
This repository is only intended to be checked out inside of a full LLVM+Clang
tree, and in the 'tools/extra' subdirectory of the Clang checkout.
All discussion regarding Clang, Clang-based tools, and code in this repository
should be held using the standard Clang mailing lists:
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev
Code review for this tree should take place on the standard Clang patch and
commit lists:
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
If you find a bug in these tools, please file it in the LLVM bug tracker:
http://llvm.org/bugs/

View File

@@ -1,20 +0,0 @@
set(LLVM_LINK_COMPONENTS support)
set(LLVM_USED_LIBS clangTooling clangBasic clangAST)
add_clang_executable(loop-convert
LoopConvert.cpp
LoopActions.cpp
LoopActions.h
LoopMatchers.cpp
LoopMatchers.h
StmtAncestor.cpp
StmtAncestor.h
VariableNaming.cpp
VariableNaming.h
)
target_link_libraries(loop-convert
clangTooling
clangBasic
clangASTMatchers
)

View File

@@ -1,991 +0,0 @@
//===-- loop-convert/LoopActions.cpp - C++11 For loop migration -*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines matchers and callbacks for use in migrating C++ for loops.
//
//===----------------------------------------------------------------------===//
#include "LoopActions.h"
#include "LoopMatchers.h"
#include "VariableNaming.h"
#include "clang/Lex/Lexer.h"
namespace clang {
namespace loop_migrate {
using namespace clang::ast_matchers;
using namespace clang::tooling;
/// \brief The information needed to describe a valid convertible usage
/// of an array index or iterator.
struct Usage {
const Expr *E;
bool IsArrow;
SourceRange Range;
explicit Usage(const Expr *E)
: E(E), IsArrow(false), Range(E->getSourceRange()) { }
Usage(const Expr *E, bool IsArrow, SourceRange Range)
: E(E), IsArrow(IsArrow), Range(Range) { }
};
/// \brief A class to encapsulate lowering of the tool's confidence level.
class Confidence {
public:
/// \brief Initialize the default confidence level to the maximum value
/// (TCK_Safe).
explicit Confidence(TranslationConfidenceKind Level) :
CurrentLevel(Level) {}
/// \brief Lower the internal confidence level to Level, but do not raise it.
void lowerTo(TranslationConfidenceKind Level) {
CurrentLevel = std::min(Level, CurrentLevel);
}
/// \brief Return the internal confidence level.
TranslationConfidenceKind get() const { return CurrentLevel; }
/// \brief Set the confidence level unconditionally.
void resetTo(TranslationConfidenceKind Level) { CurrentLevel = Level; }
private:
TranslationConfidenceKind CurrentLevel;
};
/// \brief Discover usages of expressions consisting of index or iterator
/// access.
///
/// Given an index variable, recursively crawls a for loop to discover if the
/// index variable is used in a way consistent with range-based for loop access.
class ForLoopIndexUseVisitor
: public RecursiveASTVisitor<ForLoopIndexUseVisitor> {
public:
ForLoopIndexUseVisitor(ASTContext *Context, const VarDecl *IndexVar,
const VarDecl *EndVar, const Expr *ContainerExpr,
const Expr *ArrayBoundExpr,
bool ContainerNeedsDereference) :
Context(Context), IndexVar(IndexVar), EndVar(EndVar),
ContainerExpr(ContainerExpr), ArrayBoundExpr(ArrayBoundExpr),
ContainerNeedsDereference(ContainerNeedsDereference),
OnlyUsedAsIndex(true), AliasDecl(NULL), ConfidenceLevel(TCK_Safe) {
if (ContainerExpr) {
addComponent(ContainerExpr);
llvm::FoldingSetNodeID ID;
const Expr *E = ContainerExpr->IgnoreParenImpCasts();
E->Profile(ID, *Context, true);
}
}
/// \brief Finds all uses of IndexVar in Body, placing all usages in Usages,
/// and returns true if IndexVar was only used in a way consistent with a
/// range-based for loop.
///
/// The general strategy is to reject any DeclRefExprs referencing IndexVar,
/// with the exception of certain acceptable patterns.
/// For arrays, the DeclRefExpr for IndexVar must appear as the index of an
/// ArraySubscriptExpression. Iterator-based loops may dereference
/// IndexVar or call methods through operator-> (builtin or overloaded).
/// Array-like containers may use IndexVar as a parameter to the at() member
/// function and in overloaded operator[].
bool findAndVerifyUsages(const Stmt *Body) {
TraverseStmt(const_cast<Stmt *>(Body));
return OnlyUsedAsIndex && ContainerExpr;
}
/// \brief Add a set of components that we should consider relevant to the
/// container.
void addComponents(const ComponentVector &Components) {
// FIXME: add sort(on ID)+unique to avoid extra work.
for (ComponentVector::const_iterator I = Components.begin(),
E = Components.end(); I != E; ++I)
addComponent(*I);
}
/// \brief Accessor for Usages.
const UsageResult &getUsages() const { return Usages; }
/// \brief Get the container indexed by IndexVar, if any.
const Expr *getContainerIndexed() const {
return ContainerExpr;
}
/// \brief Returns the statement declaring the variable created as an alias
/// for the loop element, if any.
const DeclStmt *getAliasDecl() const { return AliasDecl; }
/// \brief Accessor for ConfidenceLevel.
TranslationConfidenceKind getConfidenceLevel() const {
return ConfidenceLevel.get();
}
private:
/// Typedef used in CRTP functions.
typedef RecursiveASTVisitor<ForLoopIndexUseVisitor> VisitorBase;
friend class RecursiveASTVisitor<ForLoopIndexUseVisitor>;
/// Overriden methods for RecursiveASTVisitor's traversal.
bool TraverseArraySubscriptExpr(ArraySubscriptExpr *ASE);
bool TraverseCXXMemberCallExpr(CXXMemberCallExpr *MemberCall);
bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr *OpCall);
bool TraverseMemberExpr(MemberExpr *Member);
bool TraverseUnaryDeref(UnaryOperator *Uop);
bool VisitDeclRefExpr(DeclRefExpr *DRE);
bool VisitDeclStmt(DeclStmt *DS);
/// \brief Add an expression to the list of expressions on which the container
/// expression depends.
void addComponent(const Expr *E) {
llvm::FoldingSetNodeID ID;
const Expr *Node = E->IgnoreParenImpCasts();
Node->Profile(ID, *Context, true);
DependentExprs.push_back(std::make_pair(Node, ID));
}
// Input member variables:
ASTContext *Context;
/// The index variable's VarDecl.
const VarDecl *IndexVar;
/// The loop's 'end' variable, which cannot be mentioned at all.
const VarDecl *EndVar;
/// The Expr which refers to the container.
const Expr *ContainerExpr;
/// The Expr which refers to the terminating condition for array-based loops.
const Expr *ArrayBoundExpr;
bool ContainerNeedsDereference;
// Output member variables:
/// A container which holds all usages of IndexVar as the index of
/// ArraySubscriptExpressions.
UsageResult Usages;
bool OnlyUsedAsIndex;
/// The DeclStmt for an alias to the container element.
const DeclStmt *AliasDecl;
Confidence ConfidenceLevel;
/// \brief A list of expressions on which ContainerExpr depends.
///
/// If any of these expressions are encountered outside of an acceptable usage
/// of the loop element, lower our confidence level.
llvm::SmallVector<
std::pair<const Expr *, llvm::FoldingSetNodeID>, 16> DependentExprs;
};
/// \brief Obtain the original source code text from a SourceRange.
static StringRef getStringFromRange(SourceManager &SourceMgr,
const LangOptions &LangOpts,
SourceRange Range) {
if (SourceMgr.getFileID(Range.getBegin()) !=
SourceMgr.getFileID(Range.getEnd()))
return NULL;
CharSourceRange SourceChars(Range, true);
return Lexer::getSourceText(SourceChars, SourceMgr, LangOpts);
}
/// \brief Returns the DeclRefExpr represented by E, or NULL if there isn't one.
static const DeclRefExpr *getDeclRef(const Expr *E) {
return dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts());
}
/// \brief If the given expression is actually a DeclRefExpr, find and return
/// the underlying VarDecl; otherwise, return NULL.
static const VarDecl *getReferencedVariable(const Expr *E) {
if (const DeclRefExpr *DRE = getDeclRef(E))
return dyn_cast<VarDecl>(DRE->getDecl());
return NULL;
}
/// \brief Returns true when the given expression is a member expression
/// whose base is `this` (implicitly or not).
static bool isDirectMemberExpr(const Expr *E) {
if (const MemberExpr *Member = dyn_cast<MemberExpr>(E->IgnoreParenImpCasts()))
return isa<CXXThisExpr>(Member->getBase()->IgnoreParenImpCasts());
return false;
}
/// \brief Returns true when two ValueDecls are the same variable.
static bool areSameVariable(const ValueDecl *First, const ValueDecl *Second) {
return First && Second &&
First->getCanonicalDecl() == Second->getCanonicalDecl();
}
/// \brief Determines if an expression is a declaration reference to a
/// particular variable.
static bool exprReferencesVariable(const ValueDecl *Target, const Expr *E) {
if (!Target || !E)
return false;
const DeclRefExpr *DRE = getDeclRef(E);
return DRE && areSameVariable(Target, DRE->getDecl());
}
/// \brief Returns true when two Exprs are equivalent.
static bool areSameExpr(ASTContext* Context, const Expr *First,
const Expr *Second) {
if (!First || !Second)
return false;
llvm::FoldingSetNodeID FirstID, SecondID;
First->Profile(FirstID, *Context, true);
Second->Profile(SecondID, *Context, true);
return FirstID == SecondID;
}
/// \brief Look through conversion/copy constructors to find the explicit
/// initialization expression, returning it is found.
///
/// The main idea is that given
/// vector<int> v;
/// we consider either of these initializations
/// vector<int>::iterator it = v.begin();
/// vector<int>::iterator it(v.begin());
/// and retrieve `v.begin()` as the expression used to initialize `it` but do
/// not include
/// vector<int>::iterator it;
/// vector<int>::iterator it(v.begin(), 0); // if this constructor existed
/// as being initialized from `v.begin()`
static const Expr *digThroughConstructors(const Expr *E) {
if (!E)
return NULL;
E = E->IgnoreParenImpCasts();
if (const CXXConstructExpr *ConstructExpr = dyn_cast<CXXConstructExpr>(E)) {
// The initial constructor must take exactly one parameter, but base class
// and deferred constructors can take more.
if (ConstructExpr->getNumArgs() != 1 ||
ConstructExpr->getConstructionKind() != CXXConstructExpr::CK_Complete)
return NULL;
E = ConstructExpr->getArg(0);
if (const MaterializeTemporaryExpr *MTE =
dyn_cast<MaterializeTemporaryExpr>(E))
E = MTE->GetTemporaryExpr();
return digThroughConstructors(E);
}
return E;
}
/// \brief If the expression is a dereference or call to operator*(), return the
/// operand. Otherwise, return NULL.
static const Expr *getDereferenceOperand(const Expr *E) {
if (const UnaryOperator *Uop = dyn_cast<UnaryOperator>(E))
return Uop->getOpcode() == UO_Deref ? Uop->getSubExpr() : NULL;
if (const CXXOperatorCallExpr *OpCall = dyn_cast<CXXOperatorCallExpr>(E))
return OpCall->getOperator() == OO_Star && OpCall->getNumArgs() == 1 ?
OpCall->getArg(0) : NULL;
return NULL;
}
/// \brief Returns true when the Container contains an Expr equivalent to E.
template<typename ContainerT>
static bool containsExpr(ASTContext *Context, const ContainerT *Container,
const Expr *E) {
llvm::FoldingSetNodeID ID;
E->Profile(ID, *Context, true);
for (typename ContainerT::const_iterator I = Container->begin(),
End = Container->end(); I != End; ++I)
if (ID == I->second)
return true;
return false;
}
/// \brief Returns true when the index expression is a declaration reference to
/// IndexVar.
///
/// If the index variable is `index`, this function returns true on
/// arrayExpression[index];
/// containerExpression[index];
/// but not
/// containerExpression[notIndex];
static bool isIndexInSubscriptExpr(const Expr *IndexExpr,
const VarDecl *IndexVar) {
const DeclRefExpr *Idx = getDeclRef(IndexExpr);
return Idx && Idx->getType()->isIntegerType()
&& areSameVariable(IndexVar, Idx->getDecl());
}
/// \brief Returns true when the index expression is a declaration reference to
/// IndexVar, Obj is the same expression as SourceExpr after all parens and
/// implicit casts are stripped off.
///
/// If PermitDeref is true, IndexExpression may
/// be a dereference (overloaded or builtin operator*).
///
/// This function is intended for array-like containers, as it makes sure that
/// both the container and the index match.
/// If the loop has index variable `index` and iterates over `container`, then
/// isIndexInSubscriptExpr returns true for
/// \code
/// container[index]
/// container.at(index)
/// container->at(index)
/// \endcode
/// but not for
/// \code
/// container[notIndex]
/// notContainer[index]
/// \endcode
/// If PermitDeref is true, then isIndexInSubscriptExpr additionally returns
/// true on these expressions:
/// \code
/// (*container)[index]
/// (*container).at(index)
/// \endcode
static bool isIndexInSubscriptExpr(ASTContext *Context, const Expr *IndexExpr,
const VarDecl *IndexVar, const Expr *Obj,
const Expr *SourceExpr, bool PermitDeref) {
if (!SourceExpr || !Obj || !isIndexInSubscriptExpr(IndexExpr, IndexVar))
return false;
if (areSameExpr(Context, SourceExpr->IgnoreParenImpCasts(),
Obj->IgnoreParenImpCasts()))
return true;
if (const Expr *InnerObj = getDereferenceOperand(Obj->IgnoreParenImpCasts()))
if (PermitDeref && areSameExpr(Context, SourceExpr->IgnoreParenImpCasts(),
InnerObj->IgnoreParenImpCasts()))
return true;
return false;
}
/// \brief Returns true when Opcall is a call a one-parameter dereference of
/// IndexVar.
///
/// For example, if the index variable is `index`, returns true for
/// *index
/// but not
/// index
/// *notIndex
static bool isDereferenceOfOpCall(const CXXOperatorCallExpr *OpCall,
const VarDecl *IndexVar) {
return OpCall->getOperator() == OO_Star && OpCall->getNumArgs() == 1 &&
exprReferencesVariable(IndexVar, OpCall->getArg(0));
}
/// \brief Returns true when Uop is a dereference of IndexVar.
///
/// For example, if the index variable is `index`, returns true for
/// *index
/// but not
/// index
/// *notIndex
static bool isDereferenceOfUop(const UnaryOperator *Uop,
const VarDecl *IndexVar) {
return Uop->getOpcode() == UO_Deref &&
exprReferencesVariable(IndexVar, Uop->getSubExpr());
}
/// \brief Determines whether the given Decl defines a variable initialized to
/// the loop object.
///
/// This is intended to find cases such as
/// \code
/// for (int i = 0; i < arraySize(arr); ++i) {
/// T t = arr[i];
/// // use t, do not use i
/// }
/// \endcode
/// and
/// \code
/// for (iterator i = container.begin(), e = container.end(); i != e; ++i) {
/// T t = *i;
/// // use t, do not use i
/// }
/// \code
static bool isAliasDecl(const Decl *TheDecl, const VarDecl *IndexVar) {
const VarDecl *VDecl = dyn_cast<VarDecl>(TheDecl);
if (!VDecl)
return false;
if (!VDecl->hasInit())
return false;
const Expr *Init =
digThroughConstructors(VDecl->getInit()->IgnoreParenImpCasts());
if (!Init)
return false;
switch (Init->getStmtClass()) {
case Stmt::ArraySubscriptExprClass: {
const ArraySubscriptExpr *ASE = cast<ArraySubscriptExpr>(Init);
// We don't really care which array is used here. We check to make sure
// it was the correct one later, since the AST will traverse it next.
return isIndexInSubscriptExpr(ASE->getIdx(), IndexVar);
}
case Stmt::UnaryOperatorClass:
return isDereferenceOfUop(cast<UnaryOperator>(Init), IndexVar);
case Stmt::CXXOperatorCallExprClass: {
const CXXOperatorCallExpr *OpCall = cast<CXXOperatorCallExpr>(Init);
if (OpCall->getOperator() == OO_Star)
return isDereferenceOfOpCall(OpCall, IndexVar);
break;
}
default:
break;
}
return false;
}
/// \brief Determines whether the bound of a for loop condition expression is
/// the same as the statically computable size of ArrayType.
///
/// Given
/// \code
/// const int N = 5;
/// int arr[N];
/// \endcode
/// This is intended to permit
/// \code
/// for (int i = 0; i < N; ++i) { /* use arr[i] */ }
/// for (int i = 0; i < arraysize(arr); ++i) { /* use arr[i] */ }
/// \endcode
static bool arrayMatchesBoundExpr(ASTContext *Context,
const QualType &ArrayType,
const Expr *ConditionExpr) {
if (!ConditionExpr || ConditionExpr->isValueDependent())
return false;
const ConstantArrayType *CAT = Context->getAsConstantArrayType(ArrayType);
if (!CAT)
return false;
llvm::APSInt ConditionSize;
if (!ConditionExpr->isIntegerConstantExpr(ConditionSize, *Context))
return false;
llvm::APSInt ArraySize(CAT->getSize());
return llvm::APSInt::isSameValue(ConditionSize, ArraySize);
}
/// \brief If the unary operator is a dereference of IndexVar, include it
/// as a valid usage and prune the traversal.
///
/// For example, if container.begin() and container.end() both return pointers
/// to int, this makes sure that the initialization for `k` is not counted as an
/// unconvertible use of the iterator `i`.
/// \code
/// for (int *i = container.begin(), *e = container.end(); i != e; ++i) {
/// int k = *i + 2;
/// }
/// \endcode
bool ForLoopIndexUseVisitor::TraverseUnaryDeref(UnaryOperator *Uop) {
// If we dereference an iterator that's actually a pointer, count the
// occurrence.
if (isDereferenceOfUop(Uop, IndexVar)) {
Usages.push_back(Usage(Uop));
return true;
}
return VisitorBase::TraverseUnaryOperator(Uop);
}
/// \brief If the member expression is operator-> (overloaded or not) on
/// IndexVar, include it as a valid usage and prune the traversal.
///
/// For example, given
/// \code
/// struct Foo { int bar(); int x; };
/// vector<Foo> v;
/// \endcode
/// the following uses will be considered convertible:
/// \code
/// for (vector<Foo>::iterator i = v.begin(), e = v.end(); i != e; ++i) {
/// int b = i->bar();
/// int k = i->x + 1;
/// }
/// \endcode
/// though
/// \code
/// for (vector<Foo>::iterator i = v.begin(), e = v.end(); i != e; ++i) {
/// int k = i.insert(1);
/// }
/// for (vector<Foo>::iterator i = v.begin(), e = v.end(); i != e; ++i) {
/// int b = e->bar();
/// }
/// \endcode
/// will not.
bool ForLoopIndexUseVisitor::TraverseMemberExpr(MemberExpr *Member) {
const Expr *Base = Member->getBase();
const DeclRefExpr *Obj = getDeclRef(Base);
const Expr *ResultExpr = Member;
QualType ExprType;
if (const CXXOperatorCallExpr *Call =
dyn_cast<CXXOperatorCallExpr>(Base->IgnoreParenImpCasts())) {
// If operator->() is a MemberExpr containing a CXXOperatorCallExpr, then
// the MemberExpr does not have the expression we want. We therefore catch
// that instance here.
// For example, if vector<Foo>::iterator defines operator->(), then the
// example `i->bar()` at the top of this function is a CXXMemberCallExpr
// referring to `i->` as the member function called. We want just `i`, so
// we take the argument to operator->() as the base object.
if(Call->getOperator() == OO_Arrow) {
assert(Call->getNumArgs() == 1 &&
"Operator-> takes more than one argument");
Obj = getDeclRef(Call->getArg(0));
ResultExpr = Obj;
ExprType = Call->getCallReturnType();
}
}
if (Member->isArrow() && Obj && exprReferencesVariable(IndexVar, Obj)) {
if (ExprType.isNull())
ExprType = Obj->getType();
assert(ExprType->isPointerType() && "Operator-> returned non-pointer type");
// FIXME: This works around not having the location of the arrow operator.
// Consider adding OperatorLoc to MemberExpr?
SourceLocation ArrowLoc =
Lexer::getLocForEndOfToken(Base->getExprLoc(), 0,
Context->getSourceManager(),
Context->getLangOpts());
// If something complicated is happening (i.e. the next token isn't an
// arrow), give up on making this work.
if (!ArrowLoc.isInvalid()) {
Usages.push_back(Usage(ResultExpr, /*IsArrow=*/true,
SourceRange(Base->getExprLoc(), ArrowLoc)));
return true;
}
}
return TraverseStmt(Member->getBase());
}
/// \brief If a member function call is the at() accessor on the container with
/// IndexVar as the single argument, include it as a valid usage and prune
/// the traversal.
///
/// Member calls on other objects will not be permitted.
/// Calls on the iterator object are not permitted, unless done through
/// operator->(). The one exception is allowing vector::at() for pseudoarrays.
bool ForLoopIndexUseVisitor::TraverseCXXMemberCallExpr(
CXXMemberCallExpr *MemberCall) {
MemberExpr *Member =
dyn_cast<MemberExpr>(MemberCall->getCallee()->IgnoreParenImpCasts());
if (!Member)
return VisitorBase::TraverseCXXMemberCallExpr(MemberCall);
// We specifically allow an accessor named "at" to let STL in, though
// this is restricted to pseudo-arrays by requiring a single, integer
// argument.
const IdentifierInfo *Ident = Member->getMemberDecl()->getIdentifier();
if (Ident && Ident->isStr("at") && MemberCall->getNumArgs() == 1) {
if (isIndexInSubscriptExpr(Context, MemberCall->getArg(0), IndexVar,
Member->getBase(), ContainerExpr,
ContainerNeedsDereference)) {
Usages.push_back(Usage(MemberCall));
return true;
}
}
if (containsExpr(Context, &DependentExprs, Member->getBase()))
ConfidenceLevel.lowerTo(TCK_Risky);
return VisitorBase::TraverseCXXMemberCallExpr(MemberCall);
}
/// \brief If an overloaded operator call is a dereference of IndexVar or
/// a subscript of a the container with IndexVar as the single argument,
/// include it as a valid usage and prune the traversal.
///
/// For example, given
/// \code
/// struct Foo { int bar(); int x; };
/// vector<Foo> v;
/// void f(Foo);
/// \endcode
/// the following uses will be considered convertible:
/// \code
/// for (vector<Foo>::iterator i = v.begin(), e = v.end(); i != e; ++i) {
/// f(*i);
/// }
/// for (int i = 0; i < v.size(); ++i) {
/// int i = v[i] + 1;
/// }
/// \endcode
bool ForLoopIndexUseVisitor::TraverseCXXOperatorCallExpr(
CXXOperatorCallExpr *OpCall) {
switch (OpCall->getOperator()) {
case OO_Star:
if (isDereferenceOfOpCall(OpCall, IndexVar)) {
Usages.push_back(Usage(OpCall));
return true;
}
break;
case OO_Subscript:
if (OpCall->getNumArgs() != 2)
break;
if (isIndexInSubscriptExpr(Context, OpCall->getArg(1), IndexVar,
OpCall->getArg(0), ContainerExpr,
ContainerNeedsDereference)) {
Usages.push_back(Usage(OpCall));
return true;
}
break;
default:
break;
}
return VisitorBase::TraverseCXXOperatorCallExpr(OpCall);
}
/// \brief If we encounter an array with IndexVar as the index of an
/// ArraySubsriptExpression, note it as a consistent usage and prune the
/// AST traversal.
///
/// For example, given
/// \code
/// const int N = 5;
/// int arr[N];
/// \endcode
/// This is intended to permit
/// \code
/// for (int i = 0; i < N; ++i) { /* use arr[i] */ }
/// \endcode
/// but not
/// \code
/// for (int i = 0; i < N; ++i) { /* use notArr[i] */ }
/// \endcode
/// and further checking needs to be done later to ensure that exactly one array
/// is referenced.
bool ForLoopIndexUseVisitor::TraverseArraySubscriptExpr(
ArraySubscriptExpr *ASE) {
Expr *Arr = ASE->getBase();
if (!isIndexInSubscriptExpr(ASE->getIdx(), IndexVar))
return VisitorBase::TraverseArraySubscriptExpr(ASE);
if ((ContainerExpr && !areSameExpr(Context, Arr->IgnoreParenImpCasts(),
ContainerExpr->IgnoreParenImpCasts()))
|| !arrayMatchesBoundExpr(Context, Arr->IgnoreImpCasts()->getType(),
ArrayBoundExpr)) {
// If we have already discovered the array being indexed and this isn't it
// or this array doesn't match, mark this loop as unconvertible.
OnlyUsedAsIndex = false;
return VisitorBase::TraverseArraySubscriptExpr(ASE);
}
if (!ContainerExpr)
ContainerExpr = Arr;
Usages.push_back(Usage(ASE));
return true;
}
/// \brief If we encounter a reference to IndexVar in an unpruned branch of the
/// traversal, mark this loop as unconvertible.
///
/// This implements the whitelist for convertible loops: any usages of IndexVar
/// not explicitly considered convertible by this traversal will be caught by
/// this function.
///
/// Additionally, if the container expression is more complex than just a
/// DeclRefExpr, and some part of it is appears elsewhere in the loop, lower
/// our confidence in the transformation.
///
/// For example, these are not permitted:
/// \code
/// for (int i = 0; i < N; ++i) { printf("arr[%d] = %d", i, arr[i]); }
/// for (vector<int>::iterator i = container.begin(), e = container.end();
/// i != e; ++i)
/// i.insert(0);
/// for (vector<int>::iterator i = container.begin(), e = container.end();
/// i != e; ++i)
/// i.insert(0);
/// for (vector<int>::iterator i = container.begin(), e = container.end();
/// i != e; ++i)
/// if (i + 1 != e)
/// printf("%d", *i);
/// \endcode
///
/// And these will raise the risk level:
/// \code
/// int arr[10][20];
/// int l = 5;
/// for (int j = 0; j < 20; ++j)
/// int k = arr[l][j] + l; // using l outside arr[l] is considered risky
/// for (int i = 0; i < obj.getVector().size(); ++i)
/// obj.foo(10); // using `obj` is considered risky
/// \endcode
bool ForLoopIndexUseVisitor::VisitDeclRefExpr(DeclRefExpr *DRE) {
const ValueDecl *TheDecl = DRE->getDecl();
if (areSameVariable(IndexVar, TheDecl) || areSameVariable(EndVar, TheDecl))
OnlyUsedAsIndex = false;
if (containsExpr(Context, &DependentExprs, DRE))
ConfidenceLevel.lowerTo(TCK_Risky);
return true;
}
/// \brief If we find that another variable is created just to refer to the loop
/// element, note it for reuse as the loop variable.
///
/// See the comments for isAliasDecl.
bool ForLoopIndexUseVisitor::VisitDeclStmt(DeclStmt *DS) {
if (!AliasDecl && DS->isSingleDecl() &&
isAliasDecl(DS->getSingleDecl(), IndexVar))
AliasDecl = DS;
return true;
}
//// \brief Apply the source transformations necessary to migrate the loop!
void LoopFixer::doConversion(ASTContext *Context,
const VarDecl *IndexVar,
const VarDecl *MaybeContainer,
StringRef ContainerString,
const UsageResult &Usages,
const DeclStmt *AliasDecl, const ForStmt *TheLoop,
bool ContainerNeedsDereference) {
std::string VarName;
if (Usages.size() == 1 && AliasDecl) {
const VarDecl *AliasVar = cast<VarDecl>(AliasDecl->getSingleDecl());
VarName = AliasVar->getName().str();
// We keep along the entire DeclStmt to keep the correct range here.
const SourceRange &ReplaceRange = AliasDecl->getSourceRange();
if (!CountOnly)
Replace->insert(
Replacement(Context->getSourceManager(),
CharSourceRange::getTokenRange(ReplaceRange), ""));
// No further replacements are made to the loop, since the iterator or index
// was used exactly once - in the initialization of AliasVar.
} else {
VariableNamer Namer(GeneratedDecls, &ParentFinder->getStmtToParentStmtMap(),
TheLoop, IndexVar, MaybeContainer);
VarName = Namer.createIndexName();
// First, replace all usages of the array subscript expression with our new
// variable.
for (UsageResult::const_iterator I = Usages.begin(), E = Usages.end();
I != E; ++I) {
std::string ReplaceText = I->IsArrow ? VarName + "." : VarName;
ReplacedVarRanges->insert(std::make_pair(TheLoop, IndexVar));
if (!CountOnly)
Replace->insert(
Replacement(Context->getSourceManager(),
CharSourceRange::getTokenRange(I->Range),
ReplaceText));
}
}
// Now, we need to construct the new range expresion.
SourceRange ParenRange(TheLoop->getLParenLoc(), TheLoop->getRParenLoc());
QualType AutoRefType =
Context->getLValueReferenceType(Context->getAutoDeductType());
std::string MaybeDereference = ContainerNeedsDereference ? "*" : "";
std::string TypeString = AutoRefType.getAsString();
std::string Range = ("(" + TypeString + " " + VarName + " : "
+ MaybeDereference + ContainerString + ")").str();
if (!CountOnly)
Replace->insert(Replacement(Context->getSourceManager(),
CharSourceRange::getTokenRange(ParenRange),
Range));
GeneratedDecls->insert(make_pair(TheLoop, VarName));
}
/// \brief Determine whether Init appears to be an initializing an iterator.
///
/// If it is, returns the object whose begin() or end() method is called, and
/// the output parameter isArrow is set to indicate whether the initialization
/// is called via . or ->.
static const Expr *getContainerFromBeginEndCall(const Expr* Init, bool IsBegin,
bool *IsArrow) {
// FIXME: Maybe allow declaration/initialization outside of the for loop?
const CXXMemberCallExpr *TheCall =
dyn_cast_or_null<CXXMemberCallExpr>(digThroughConstructors(Init));
if (!TheCall || TheCall->getNumArgs() != 0)
return NULL;
const MemberExpr *Member = dyn_cast<MemberExpr>(TheCall->getCallee());
if (!Member)
return NULL;
const std::string Name = Member->getMemberDecl()->getName();
const std::string TargetName = IsBegin ? "begin" : "end";
if (Name != TargetName)
return NULL;
const Expr *SourceExpr = Member->getBase();
if (!SourceExpr)
return NULL;
*IsArrow = Member->isArrow();
return SourceExpr;
}
/// \brief Determines the container whose begin() and end() functions are called
/// for an iterator-based loop.
///
/// BeginExpr must be a member call to a function named "begin()", and EndExpr
/// must be a member .
static const Expr *findContainer(ASTContext *Context, const Expr *BeginExpr,
const Expr *EndExpr,
bool *ContainerNeedsDereference) {
// Now that we know the loop variable and test expression, make sure they are
// valid.
bool BeginIsArrow = false;
bool EndIsArrow = false;
const Expr *BeginContainerExpr =
getContainerFromBeginEndCall(BeginExpr, /*IsBegin=*/true, &BeginIsArrow);
if (!BeginContainerExpr)
return NULL;
const Expr *EndContainerExpr =
getContainerFromBeginEndCall(EndExpr, /*IsBegin=*/false, &EndIsArrow);
// Disallow loops that try evil things like this (note the dot and arrow):
// for (IteratorType It = Obj.begin(), E = Obj->end(); It != E; ++It) { }
if (!EndContainerExpr || BeginIsArrow != EndIsArrow ||
!areSameExpr(Context, EndContainerExpr, BeginContainerExpr))
return NULL;
*ContainerNeedsDereference = BeginIsArrow;
return BeginContainerExpr;
}
StringRef LoopFixer::checkDeferralsAndRejections(ASTContext *Context,
const Expr *ContainerExpr,
Confidence ConfidenceLevel,
const ForStmt *TheLoop) {
// If we already modified the range of this for loop, don't do any further
// updates on this iteration.
// FIXME: Once Replacements can detect conflicting edits, replace this
// implementation and rely on conflicting edit detection instead.
if (ReplacedVarRanges->count(TheLoop)) {
++*DeferredChanges;
return "";
}
ParentFinder->gatherAncestors(Context->getTranslationUnitDecl());
// Ensure that we do not try to move an expression dependent on a local
// variable declared inside the loop outside of it!
DependencyFinderASTVisitor
DependencyFinder(&ParentFinder->getStmtToParentStmtMap(),
&ParentFinder->getDeclToParentStmtMap(),
ReplacedVarRanges, TheLoop);
// Not all of these are actually deferred changes.
// FIXME: Determine when the external dependency isn't an expression converted
// by another loop.
if (DependencyFinder.dependsOnInsideVariable(ContainerExpr)) {
++*DeferredChanges;
return "";
}
if (ConfidenceLevel.get() < RequiredConfidenceLevel) {
++*RejectedChanges;
return "";
}
StringRef ContainerString =
getStringFromRange(Context->getSourceManager(), Context->getLangOpts(),
ContainerExpr->getSourceRange());
// In case someone is using an evil macro, reject this change.
if (ContainerString.empty())
++*RejectedChanges;
return ContainerString;
}
/// \brief Given that we have verified that the loop's header appears to be
/// convertible, run the complete analysis on the loop to determine if the
/// loop's body is convertible.
void LoopFixer::findAndVerifyUsages(ASTContext *Context,
const VarDecl *LoopVar,
const VarDecl *EndVar,
const Expr *ContainerExpr,
const Expr *BoundExpr,
bool ContainerNeedsDereference,
const ForStmt *TheLoop,
Confidence ConfidenceLevel) {
ForLoopIndexUseVisitor Finder(Context, LoopVar, EndVar, ContainerExpr,
BoundExpr, ContainerNeedsDereference);
if (ContainerExpr) {
ComponentFinderASTVisitor ComponentFinder;
ComponentFinder.findExprComponents(ContainerExpr->IgnoreParenImpCasts());
Finder.addComponents(ComponentFinder.getComponents());
}
if (!Finder.findAndVerifyUsages(TheLoop->getBody()))
return;
ConfidenceLevel.lowerTo(Finder.getConfidenceLevel());
if (FixerKind == LFK_Array) {
// The array being indexed by IndexVar was discovered during traversal.
ContainerExpr = Finder.getContainerIndexed()->IgnoreParenImpCasts();
// Very few loops are over expressions that generate arrays rather than
// array variables. Consider loops over arrays that aren't just represented
// by a variable to be risky conversions.
if (!getReferencedVariable(ContainerExpr) &&
!isDirectMemberExpr(ContainerExpr))
ConfidenceLevel.lowerTo(TCK_Risky);
}
std::string ContainerString =
checkDeferralsAndRejections(Context, ContainerExpr,
ConfidenceLevel, TheLoop);
if (ContainerString.empty())
return;
doConversion(Context, LoopVar, getReferencedVariable(ContainerExpr),
ContainerString, Finder.getUsages(),
Finder.getAliasDecl(), TheLoop, ContainerNeedsDereference);
++*AcceptedChanges;
}
/// \brief The LoopFixer callback, which determines if loops discovered by the
/// matchers are convertible, printing information about the loops if so.
void LoopFixer::run(const MatchFinder::MatchResult &Result) {
const BoundNodes &Nodes = Result.Nodes;
Confidence ConfidenceLevel(TCK_Safe);
ASTContext *Context = Result.Context;
const ForStmt *TheLoop = Nodes.getStmtAs<ForStmt>(LoopName);
if (!Context->getSourceManager().isFromMainFile(TheLoop->getForLoc()))
return;
// Check that we have exactly one index variable and at most one end variable.
const VarDecl *LoopVar = Nodes.getDeclAs<VarDecl>(IncrementVarName);
const VarDecl *CondVar = Nodes.getDeclAs<VarDecl>(ConditionVarName);
const VarDecl *InitVar = Nodes.getDeclAs<VarDecl>(InitVarName);
if (!areSameVariable(LoopVar, CondVar) || !areSameVariable(LoopVar, InitVar))
return;
const VarDecl *EndVar = Nodes.getDeclAs<VarDecl>(EndVarName);
const VarDecl *ConditionEndVar =
Nodes.getDeclAs<VarDecl>(ConditionEndVarName);
if (EndVar && !areSameVariable(EndVar, ConditionEndVar))
return;
// If the end comparison isn't a variable, we can try to work with the
// expression the loop variable is being tested against instead.
const CXXMemberCallExpr *EndCall =
Nodes.getStmtAs<CXXMemberCallExpr>(EndCallName);
const Expr *BoundExpr = Nodes.getStmtAs<Expr>(ConditionBoundName);
// If the loop calls end()/size() after each iteration, lower our confidence
// level.
if (FixerKind != LFK_Array && !EndVar)
ConfidenceLevel.lowerTo(TCK_Reasonable);
const Expr *ContainerExpr = NULL;
bool ContainerNeedsDereference = false;
// FIXME: Try to put most of this logic inside a matcher. Currently, matchers
// don't allow the right-recursive checks in digThroughConstructors.
if (FixerKind == LFK_Iterator)
ContainerExpr = findContainer(Context, LoopVar->getInit(),
EndVar ? EndVar->getInit() : EndCall,
&ContainerNeedsDereference);
else if (FixerKind == LFK_PseudoArray) {
if (!EndCall)
return;
ContainerExpr = EndCall->getImplicitObjectArgument();
const MemberExpr *Member = dyn_cast<MemberExpr>(EndCall->getCallee());
if (!Member)
return;
ContainerNeedsDereference = Member->isArrow();
}
// We must know the container or an array length bound.
if (!ContainerExpr && !BoundExpr)
return;
findAndVerifyUsages(Context, LoopVar, EndVar, ContainerExpr, BoundExpr,
ContainerNeedsDereference, TheLoop, ConfidenceLevel);
}
} // namespace loop_migrate
} // namespace clang

View File

@@ -1,108 +0,0 @@
//===-- loop-convert/LoopActions.h - C++11 For loop migration ---*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file declares matchers and callbacks for use in migrating C++ for loops.
//
//===----------------------------------------------------------------------===//
#ifndef _LLVM_TOOLS_CLANG_TOOLS_EXTRA_LOOP_CONVERT_LOOPACTIONS_H_
#define _LLVM_TOOLS_CLANG_TOOLS_EXTRA_LOOP_CONVERT_LOOPACTIONS_H_
#include "StmtAncestor.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
namespace clang {
namespace loop_migrate {
struct Usage;
class Confidence;
// The main computational result of ForLoopIndexUseVisitor.
typedef llvm::SmallVector<Usage, 8> UsageResult;
/// \brief The level of safety to require of transformations.
enum TranslationConfidenceKind {
TCK_Risky,
TCK_Reasonable,
TCK_Safe
};
enum LoopFixerKind {
LFK_Array,
LFK_Iterator,
LFK_PseudoArray
};
/// \brief The callback to be used for loop migration matchers.
///
/// The callback does extra checking not possible in matchers, and attempts to
/// convert the for loop, if possible.
class LoopFixer : public ast_matchers::MatchFinder::MatchCallback {
public:
LoopFixer(StmtAncestorASTVisitor *ParentFinder,
tooling::Replacements *Replace,
StmtGeneratedVarNameMap *GeneratedDecls,
ReplacedVarsMap *ReplacedVarRanges,
unsigned *AcceptedChanges, unsigned *DeferredChanges,
unsigned *RejectedChanges, bool CountOnly,
TranslationConfidenceKind RequiredConfidenceLevel,
LoopFixerKind FixerKind) :
ParentFinder(ParentFinder), Replace(Replace),
GeneratedDecls(GeneratedDecls), ReplacedVarRanges(ReplacedVarRanges),
AcceptedChanges(AcceptedChanges), DeferredChanges(DeferredChanges),
RejectedChanges(RejectedChanges), CountOnly(CountOnly),
RequiredConfidenceLevel(RequiredConfidenceLevel), FixerKind(FixerKind) { }
virtual void run(const ast_matchers::MatchFinder::MatchResult &Result);
private:
StmtAncestorASTVisitor *ParentFinder;
tooling::Replacements *Replace;
StmtGeneratedVarNameMap *GeneratedDecls;
ReplacedVarsMap *ReplacedVarRanges;
unsigned *AcceptedChanges;
unsigned *DeferredChanges;
unsigned *RejectedChanges;
bool CountOnly;
TranslationConfidenceKind RequiredConfidenceLevel;
LoopFixerKind FixerKind;
/// \brief Computes the changes needed to convert a given for loop, and
/// applies it if this->CountOnly is false.
void doConversion(ASTContext *Context,
const VarDecl *IndexVar,
const VarDecl *MaybeContainer,
StringRef ContainerString,
const UsageResult &Usages,
const DeclStmt *AliasDecl, const ForStmt *TheLoop,
bool ContainerNeedsDereference);
/// \brief Given a loop header that would be convertible, discover all usages
/// of the index variable and convert the loop if possible.
void findAndVerifyUsages(ASTContext *Context,
const VarDecl *LoopVar,
const VarDecl *EndVar,
const Expr *ContainerExpr,
const Expr *BoundExpr,
bool ContainerNeedsDereference,
const ForStmt *TheLoop,
Confidence ConfidenceLevel);
/// \brief Determine if the change should be deferred or rejected, returning
/// text which refers to the container iterated over if the change should
/// proceed.
StringRef checkDeferralsAndRejections(ASTContext *Context,
const Expr *ContainerExpr,
Confidence ConfidenceLevel,
const ForStmt *TheLoop);
};
} // namespace loop_migrate
} // namespace clang
#endif // _LLVM_TOOLS_CLANG_TOOLS_EXTRA_LOOP_CONVERT_LOOPACTIONS_H_

View File

@@ -1,137 +0,0 @@
//===-- loop-convert/LoopConvert.cpp - C++11 For loop migration -*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements a tool that migrates for loops to take advantage of the
// range-basead syntax new to C++11.
//
// Usage:
// loop-convert <cmake-output-dir> <file1> <file2> ...
//
// Where <cmake-output-dir> is a CMake build directory containing a file named
// compile_commands.json.
//
// <file1>... specify the pahs of files in the CMake source tree, with the same
// requirements as other tools built on LibTooling.
//
//===----------------------------------------------------------------------===//
#include "LoopActions.h"
#include "LoopMatchers.h"
#include "clang/Basic/FileManager.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/Tooling.h"
#include "clang/Tooling/Refactoring.h"
using clang::ast_matchers::MatchFinder;
namespace cl = llvm::cl;
using namespace clang::tooling;
using namespace clang::loop_migrate;
static cl::opt<std::string> BuildPath(
cl::Positional,
cl::desc("<build-path>"));
static cl::list<std::string> SourcePaths(
cl::Positional,
cl::desc("<source0> [... <sourceN>]"),
cl::OneOrMore);
// General options go here:
static cl::opt<bool> CountOnly(
"count-only", cl::desc("Do not apply transformations; only count them."));
static cl::opt<TranslationConfidenceKind> TransformationLevel(
cl::desc("Choose safety requirements for transformations:"),
cl::values(clEnumValN(TCK_Safe, "A0", "Enable safe transformations"),
clEnumValN(TCK_Reasonable, "A1",
"Enable transformations that might change semantics "
"(default)"),
clEnumValN(TCK_Risky, "A2",
"Enable transformations that are likely "
"to change semantics"),
clEnumValEnd),
cl::init(TCK_Reasonable));
int main(int argc, const char **argv) {
llvm::OwningPtr<CompilationDatabase> Compilations(
FixedCompilationDatabase::loadFromCommandLine(argc, argv));
cl::ParseCommandLineOptions(argc, argv);
if (!Compilations) {
std::string ErrorMessage;
Compilations.reset(
!BuildPath.empty() ?
CompilationDatabase::autoDetectFromDirectory(BuildPath, ErrorMessage) :
CompilationDatabase::autoDetectFromSource(SourcePaths[0],
ErrorMessage));
if (!Compilations)
llvm::report_fatal_error(ErrorMessage);
}
ClangTool SyntaxTool(*Compilations, SourcePaths);
// First, let's check to make sure there were no errors.
if (int result =
SyntaxTool.run(newFrontendActionFactory<clang::SyntaxOnlyAction>())) {
llvm::errs() << "Error compiling files.\n";
return result;
}
RefactoringTool LoopTool(*Compilations, SourcePaths);
StmtAncestorASTVisitor ParentFinder;
StmtGeneratedVarNameMap GeneratedDecls;
ReplacedVarsMap ReplacedVars;
unsigned AcceptedChanges = 0;
unsigned DeferredChanges = 0;
unsigned RejectedChanges = 0;
MatchFinder Finder;
LoopFixer ArrayLoopFixer(&ParentFinder, &LoopTool.getReplacements(),
&GeneratedDecls, &ReplacedVars, &AcceptedChanges,
&DeferredChanges, &RejectedChanges,
CountOnly, TransformationLevel, LFK_Array);
Finder.addMatcher(makeArrayLoopMatcher(), &ArrayLoopFixer);
LoopFixer IteratorLoopFixer(&ParentFinder, &LoopTool.getReplacements(),
&GeneratedDecls, &ReplacedVars, &AcceptedChanges,
&DeferredChanges, &RejectedChanges,
CountOnly, TransformationLevel, LFK_Iterator);
Finder.addMatcher(makeIteratorLoopMatcher(), &IteratorLoopFixer);
LoopFixer PseudoarrrayLoopFixer(&ParentFinder, &LoopTool.getReplacements(),
&GeneratedDecls, &ReplacedVars,
&AcceptedChanges, &DeferredChanges,
&RejectedChanges, CountOnly,
TransformationLevel, LFK_PseudoArray);
Finder.addMatcher(makePseudoArrayLoopMatcher(), &PseudoarrrayLoopFixer);
if (int result = LoopTool.run(newFrontendActionFactory(&Finder))) {
llvm::errs() << "Error encountered during translation.\n";
return result;
}
llvm::outs() << "\nFor Loop Conversion:\n\t" << AcceptedChanges
<< " converted loop(s)\n\t" << DeferredChanges
<< " potentially conflicting change(s) deferred.\n\t"
<< RejectedChanges << " change(s) rejected.\n";
if (DeferredChanges > 0)
llvm::outs() << "Re-run this tool to attempt applying deferred changes.\n";
if (RejectedChanges > 0)
llvm::outs() << "Re-run this tool with a lower required confidence level "
"to apply rejected changes.\n";
if (AcceptedChanges > 0) {
// Check to see if the changes introduced any new errors.
ClangTool EndSyntaxTool(*Compilations, SourcePaths);
if (int result = EndSyntaxTool.run(
newFrontendActionFactory<clang::SyntaxOnlyAction>())) {
llvm::errs() << "Error compiling files after translation.\n";
return result;
}
}
return 0;
}

View File

@@ -1,235 +0,0 @@
//===-- loop-convert/LoopMatchers.h - Matchers for for loops ----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains definitions of the matchers for use in migrating
// C++ for loops.
//
//===----------------------------------------------------------------------===//
#include "LoopMatchers.h"
namespace clang {
namespace loop_migrate {
using namespace clang::ast_matchers;
const char LoopName[] = "forLoop";
const char ConditionBoundName[] = "conditionBound";
const char ConditionVarName[] = "conditionVar";
const char IncrementVarName[] = "incrementVar";
const char InitVarName[] = "initVar";
const char EndCallName[] = "endCall";
const char ConditionEndVarName[] = "conditionEndVar";
const char EndVarName[] = "endVar";
// shared matchers
static const TypeMatcher AnyType = anything();
static const StatementMatcher IntegerComparisonMatcher =
expr(ignoringParenImpCasts(declRefExpr(to(
varDecl(hasType(isInteger())).bind(ConditionVarName)))));
static const DeclarationMatcher InitToZeroMatcher =
varDecl(hasInitializer(ignoringParenImpCasts(
integerLiteral(equals(0))))).bind(InitVarName);
static const StatementMatcher IncrementVarMatcher =
declRefExpr(to(
varDecl(hasType(isInteger())).bind(IncrementVarName)));
// FIXME: How best to document complicated matcher expressions? They're fairly
// self-documenting...but there may be some unintuitive parts.
/// \brief The matcher for loops over arrays.
///
/// In this general example, assuming 'j' and 'k' are of integral type:
/// \code
/// for (int i = 0; j < 3 + 2; ++k) { ... }
/// \endcode
/// The following string identifers are bound to the parts of the AST:
/// ConditionVarName: 'j' (as a VarDecl)
/// ConditionBoundName: '3 + 2' (as an Expr)
/// InitVarName: 'i' (as a VarDecl)
/// IncrementVarName: 'k' (as a VarDecl)
/// LoopName: The entire for loop (as a ForStmt)
///
/// Client code will need to make sure that:
/// - The three index variables identified by the matcher are the same
/// VarDecl.
/// - The index variable is only used as an array index.
/// - All arrays indexed by the loop are the same.
StatementMatcher makeArrayLoopMatcher() {
StatementMatcher ArrayBoundMatcher =
expr(hasType(isInteger())).bind(ConditionBoundName);
return forStmt(
hasLoopInit(declStmt(hasSingleDecl(InitToZeroMatcher))),
hasCondition(anyOf(binaryOperator(hasOperatorName("<"),
hasLHS(IntegerComparisonMatcher),
hasRHS(ArrayBoundMatcher)),
binaryOperator(hasOperatorName(">"),
hasLHS(ArrayBoundMatcher),
hasRHS(IntegerComparisonMatcher)))),
hasIncrement(unaryOperator(hasOperatorName("++"),
hasUnaryOperand(IncrementVarMatcher))))
.bind(LoopName);
}
/// \brief The matcher used for iterator-based for loops.
///
/// This matcher is more flexible than array-based loops. It will match
/// catch loops of the following textual forms (regardless of whether the
/// iterator type is actually a pointer type or a class type):
///
/// Assuming f, g, and h are of type containerType::iterator,
/// \code
/// for (containerType::iterator it = container.begin(),
/// e = createIterator(); f != g; ++h) { ... }
/// for (containerType::iterator it = container.begin();
/// f != anotherContainer.end(); ++h) { ... }
/// \endcode
/// The following string identifiers are bound to the parts of the AST:
/// InitVarName: 'it' (as a VarDecl)
/// ConditionVarName: 'f' (as a VarDecl)
/// LoopName: The entire for loop (as a ForStmt)
/// In the first example only:
/// EndVarName: 'e' (as a VarDecl)
/// ConditionEndVarName: 'g' (as a VarDecl)
/// In the second example only:
/// EndCallName: 'container.end()' (as a CXXMemberCallExpr)
///
/// Client code will need to make sure that:
/// - The iterator variables 'it', 'f', and 'h' are the same
/// - The two containers on which 'begin' and 'end' are called are the same
/// - If the end iterator variable 'g' is defined, it is the same as 'f'
StatementMatcher makeIteratorLoopMatcher() {
StatementMatcher BeginCallMatcher =
memberCallExpr(argumentCountIs(0), callee(methodDecl(hasName("begin"))));
DeclarationMatcher InitDeclMatcher =
varDecl(hasInitializer(anything())).bind(InitVarName);
DeclarationMatcher EndDeclMatcher =
varDecl(hasInitializer(anything())).bind(EndVarName);
StatementMatcher EndCallMatcher =
memberCallExpr(argumentCountIs(0), callee(methodDecl(hasName("end"))));
StatementMatcher IteratorBoundMatcher =
expr(anyOf(ignoringParenImpCasts(declRefExpr(to(
varDecl().bind(ConditionEndVarName)))),
ignoringParenImpCasts(
expr(EndCallMatcher).bind(EndCallName)),
materializeTemporaryExpr(ignoringParenImpCasts(
expr(EndCallMatcher).bind(EndCallName)))));
StatementMatcher IteratorComparisonMatcher =
expr(ignoringParenImpCasts(declRefExpr(to(
varDecl().bind(ConditionVarName)))));
StatementMatcher OverloadedNEQMatcher = operatorCallExpr(
hasOverloadedOperatorName("!="),
argumentCountIs(2),
hasArgument(0, IteratorComparisonMatcher),
hasArgument(1, IteratorBoundMatcher));
return forStmt(
hasLoopInit(anyOf(
declStmt(declCountIs(2),
containsDeclaration(0, InitDeclMatcher),
containsDeclaration(1, EndDeclMatcher)),
declStmt(hasSingleDecl(InitDeclMatcher)))),
hasCondition(anyOf(
binaryOperator(hasOperatorName("!="),
hasLHS(IteratorComparisonMatcher),
hasRHS(IteratorBoundMatcher)),
binaryOperator(hasOperatorName("!="),
hasLHS(IteratorBoundMatcher),
hasRHS(IteratorComparisonMatcher)),
OverloadedNEQMatcher)),
hasIncrement(anyOf(
unaryOperator(hasOperatorName("++"),
hasUnaryOperand(declRefExpr(to(
varDecl(hasType(pointsTo(AnyType)))
.bind(IncrementVarName))))),
operatorCallExpr(
hasOverloadedOperatorName("++"),
hasArgument(0, declRefExpr(to(
varDecl().bind(IncrementVarName))))))))
.bind(LoopName);
}
/// \brief The matcher used for array-like containers (pseudoarrays).
///
/// This matcher is more flexible than array-based loops. It will match
/// loops of the following textual forms (regardless of whether the
/// iterator type is actually a pointer type or a class type):
///
/// Assuming f, g, and h are of type containerType::iterator,
/// \code
/// for (int i = 0, j = container.size(); f < g; ++h) { ... }
/// for (int i = 0; f < container.size(); ++h) { ... }
/// \endcode
/// The following string identifiers are bound to the parts of the AST:
/// InitVarName: 'i' (as a VarDecl)
/// ConditionVarName: 'f' (as a VarDecl)
/// LoopName: The entire for loop (as a ForStmt)
/// In the first example only:
/// EndVarName: 'j' (as a VarDecl)
/// ConditionEndVarName: 'g' (as a VarDecl)
/// In the second example only:
/// EndCallName: 'container.size()' (as a CXXMemberCallExpr)
///
/// Client code will need to make sure that:
/// - The index variables 'i', 'f', and 'h' are the same
/// - The containers on which 'size()' is called is the container indexed
/// - The index variable is only used in overloaded operator[] or
/// container.at()
/// - If the end iterator variable 'g' is defined, it is the same as 'j'
/// - The container's iterators would not be invalidated during the loop
StatementMatcher makePseudoArrayLoopMatcher() {
StatementMatcher SizeCallMatcher =
memberCallExpr(argumentCountIs(0),
callee(methodDecl(anyOf(hasName("size"),
hasName("length")))));
StatementMatcher EndInitMatcher =
expr(anyOf(
ignoringParenImpCasts(expr(SizeCallMatcher).bind(EndCallName)),
explicitCastExpr(hasSourceExpression(ignoringParenImpCasts(
expr(SizeCallMatcher).bind(EndCallName))))));
DeclarationMatcher EndDeclMatcher =
varDecl(hasInitializer(EndInitMatcher)).bind(EndVarName);
StatementMatcher IndexBoundMatcher =
expr(anyOf(
ignoringParenImpCasts(declRefExpr(to(
varDecl(hasType(isInteger())).bind(ConditionEndVarName)))),
EndInitMatcher));
return forStmt(
hasLoopInit(anyOf(
declStmt(declCountIs(2),
containsDeclaration(0, InitToZeroMatcher),
containsDeclaration(1, EndDeclMatcher)),
declStmt(hasSingleDecl(InitToZeroMatcher)))),
hasCondition(anyOf(
binaryOperator(hasOperatorName("<"),
hasLHS(IntegerComparisonMatcher),
hasRHS(IndexBoundMatcher)),
binaryOperator(hasOperatorName(">"),
hasLHS(IndexBoundMatcher),
hasRHS(IntegerComparisonMatcher)))),
hasIncrement(unaryOperator(
hasOperatorName("++"),
hasUnaryOperand(IncrementVarMatcher))))
.bind(LoopName);
}
} // namespace loop_migrate
} // namespace clang

View File

@@ -1,43 +0,0 @@
//===-- loop-convert/LoopMatchers.h - Matchers for for loops ----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains declarations of the matchers for use in migrating
// C++ for loops. The matchers are responsible for checking the general shape of
// the for loop, namely the init, condition, and increment portions.
// Further analysis will be needed to confirm that the loop is in fact
// convertible in the matcher callback.
//
//===----------------------------------------------------------------------===//
#ifndef _LLVM_TOOLS_CLANG_TOOLS_EXTRA_LOOP_CONVERT_LOOP_MATCHERS_H_
#define _LLVM_TOOLS_CLANG_TOOLS_EXTRA_LOOP_CONVERT_LOOP_MATCHERS_H_
#include "clang/ASTMatchers/ASTMatchers.h"
namespace clang {
namespace loop_migrate {
// Constants used for matcher name bindings
extern const char LoopName[];
extern const char ConditionBoundName[];
extern const char ConditionVarName[];
extern const char ConditionEndVarName[];
extern const char IncrementVarName[];
extern const char InitVarName[];
extern const char EndExprName[];
extern const char EndCallName[];
extern const char EndVarName[];
ast_matchers::StatementMatcher makeArrayLoopMatcher();
ast_matchers::StatementMatcher makeIteratorLoopMatcher();
ast_matchers::StatementMatcher makePseudoArrayLoopMatcher();
} //namespace loop_migrate
} //namespace clang
#endif //_LLVM_TOOLS_CLANG_TOOLS_EXTRA_LOOP_CONVERT_LOOP_MATCHERS_H_

View File

@@ -1,26 +0,0 @@
##===- tools/extra/loop-convert/Makefile ----sssss----------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##
CLANG_LEVEL := ../../..
TOOLNAME = loop-convert
NO_INSTALL = 1
# No plugins, optimize startup time.
TOOL_NO_EXPORTS = 1
include $(CLANG_LEVEL)/../../Makefile.config
LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser support mc
USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \
clangRewriteFrontend.a clangRewriteCore.a clangParse.a \
clangSema.a clangAnalysis.a \
clangAST.a clangASTMatchers.a clangEdit.a clangLex.a clangBasic.a
include $(CLANG_LEVEL)/Makefile

View File

@@ -1,120 +0,0 @@
//===-- loop-convert/StmtAncestor.cpp - AST property visitors ---*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains the definitions of several RecursiveASTVisitors used to
// build and check data structures used in loop migration.
//
//===----------------------------------------------------------------------===//
#include "StmtAncestor.h"
namespace clang {
namespace loop_migrate {
/// \brief Tracks a stack of parent statements during traversal.
///
/// All this really does is inject push_back() before running
/// RecursiveASTVisitor::TraverseStmt() and pop_back() afterwards. The Stmt atop
/// the stack is the parent of the current statement (NULL for the topmost
/// statement).
bool StmtAncestorASTVisitor::TraverseStmt(Stmt *Statement) {
StmtAncestors.insert(std::make_pair(Statement, StmtStack.back()));
StmtStack.push_back(Statement);
RecursiveASTVisitor<StmtAncestorASTVisitor>::TraverseStmt(Statement);
StmtStack.pop_back();
return true;
}
/// \brief Keep track of the DeclStmt associated with each VarDecl.
///
/// Combined with StmtAncestors, this provides roughly the same information as
/// Scope, as we can map a VarDecl to its DeclStmt, then walk up the parent tree
/// using StmtAncestors.
bool StmtAncestorASTVisitor::VisitDeclStmt(DeclStmt *Decls) {
for (DeclStmt::const_decl_iterator I = Decls->decl_begin(),
E = Decls->decl_end(); I != E; ++I)
if (const VarDecl *VD = dyn_cast<VarDecl>(*I))
DeclParents.insert(std::make_pair(VD, Decls));
return true;
}
/// \brief record the DeclRefExpr as part of the parent expression.
bool ComponentFinderASTVisitor::VisitDeclRefExpr(DeclRefExpr *E) {
Components.push_back(E);
return true;
}
/// \brief record the MemberExpr as part of the parent expression.
bool ComponentFinderASTVisitor::VisitMemberExpr(MemberExpr *Member) {
Components.push_back(Member);
return true;
}
/// \brief Forward any DeclRefExprs to a check on the referenced variable
/// declaration.
bool DependencyFinderASTVisitor::VisitDeclRefExpr(DeclRefExpr *DRE) {
if (VarDecl *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl()))
return VisitVarDecl(VD);
return true;
}
/// \brief Determine if any this variable is declared inside the ContainingStmt.
bool DependencyFinderASTVisitor::VisitVarDecl(VarDecl *VD) {
const Stmt *Curr = DeclParents->lookup(VD);
// First, see if the variable was declared within an inner scope of the loop.
while (Curr != NULL) {
if (Curr == ContainingStmt) {
DependsOnInsideVariable = true;
return false;
}
Curr = StmtParents->lookup(Curr);
}
// Next, check if the variable was removed from existence by an earlier
// iteration.
for (ReplacedVarsMap::const_iterator I = ReplacedVars->begin(),
E = ReplacedVars->end(); I != E; ++I)
if ((*I).second == VD) {
DependsOnInsideVariable = true;
return false;
}
return true;
}
/// \brief If we already created a variable for TheLoop, check to make sure
/// that the name was not already taken.
bool DeclFinderASTVisitor::VisitForStmt(ForStmt *TheLoop) {
StmtGeneratedVarNameMap::const_iterator I = GeneratedDecls->find(TheLoop);
if (I != GeneratedDecls->end() && I->second == Name) {
Found = true;
return false;
}
return true;
}
/// \brief If any named declaration within the AST subtree has the same name,
/// then consider Name already taken.
bool DeclFinderASTVisitor::VisitNamedDecl(NamedDecl *ND) {
const IdentifierInfo *Ident = ND->getIdentifier();
if (Ident && Ident->getName() == Name) {
Found = true;
return false;
}
return true;
}
/// \brief Forward any declaration references to the actual check on the
/// referenced declaration.
bool DeclFinderASTVisitor::VisitDeclRefExpr(DeclRefExpr *DRE) {
if (NamedDecl *ND = dyn_cast<NamedDecl>(DRE->getDecl()))
return VisitNamedDecl(ND);
return true;
}
} // namespace clang
} // namespace for_migrate

View File

@@ -1,199 +0,0 @@
//===-- loop-convert/StmtAncestor.h - AST property visitors -----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains the declarations of several RecursiveASTVisitors used to
// build and check data structures used in loop migration.
//
//===----------------------------------------------------------------------===//
#ifndef _LLVM_TOOLS_CLANG_TOOLS_EXTRA_LOOP_CONVERT_STMT_ANCESTOR_H_
#define _LLVM_TOOLS_CLANG_TOOLS_EXTRA_LOOP_CONVERT_STMT_ANCESTOR_H_
#include "clang/AST/RecursiveASTVisitor.h"
namespace clang {
namespace loop_migrate {
/// A map used to walk the AST in reverse: maps child Stmt to parent Stmt.
typedef llvm::DenseMap<const Stmt*, const Stmt*> StmtParentMap;
/// A map used to walk the AST in reverse:
/// maps VarDecl to the to parent DeclStmt.
typedef llvm::DenseMap<const VarDecl*, const DeclStmt*> DeclParentMap;
/// A map used to track which variables have been removed by a refactoring pass.
/// It maps the parent ForStmt to the removed index variable's VarDecl.
typedef llvm::DenseMap<const ForStmt*, const VarDecl *> ReplacedVarsMap;
/// A map used to remember the variable names generated in a Stmt
typedef llvm::DenseMap<const Stmt*, std::string> StmtGeneratedVarNameMap;
/// A vector used to store the AST subtrees of an Expr.
typedef llvm::SmallVector<const Expr *, 16> ComponentVector;
/// \brief Class used build the reverse AST properties needed to detect
/// name conflicts and free variables.
class StmtAncestorASTVisitor :
public RecursiveASTVisitor<StmtAncestorASTVisitor> {
public:
StmtAncestorASTVisitor() {
StmtStack.push_back(NULL);
}
/// \brief Run the analysis on the TranslationUnitDecl.
///
/// In case we're running this analysis multiple times, don't repeat the work.
void gatherAncestors(const TranslationUnitDecl *TUD) {
if (StmtAncestors.empty())
TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
}
/// Accessor for StmtAncestors.
const StmtParentMap &getStmtToParentStmtMap() {
return StmtAncestors;
}
/// Accessor for DeclParents.
const DeclParentMap &getDeclToParentStmtMap() {
return DeclParents;
}
friend class RecursiveASTVisitor<StmtAncestorASTVisitor>;
private:
StmtParentMap StmtAncestors;
DeclParentMap DeclParents;
llvm::SmallVector<const Stmt *, 16> StmtStack;
bool TraverseStmt(Stmt *Statement);
bool VisitDeclStmt(DeclStmt *Statement);
};
/// Class used to find the variables and member expressions on which an
/// arbitrary expression depends.
class ComponentFinderASTVisitor :
public RecursiveASTVisitor<ComponentFinderASTVisitor> {
public:
ComponentFinderASTVisitor() { }
/// Find the components of an expression and place them in a ComponentVector.
void findExprComponents(const Expr *SourceExpr) {
Expr *E = const_cast<Expr *>(SourceExpr);
RecursiveASTVisitor<ComponentFinderASTVisitor>::TraverseStmt(E);
}
/// Accessor for Components.
const ComponentVector &getComponents() {
return Components;
}
friend class RecursiveASTVisitor<ComponentFinderASTVisitor>;
private:
ComponentVector Components;
bool VisitDeclRefExpr(DeclRefExpr *E);
bool VisitMemberExpr(MemberExpr *Member);
};
/// Class used to determine if an expression is dependent on a variable declared
/// inside of the loop where it would be used.
class DependencyFinderASTVisitor :
public RecursiveASTVisitor<DependencyFinderASTVisitor> {
public:
DependencyFinderASTVisitor(const StmtParentMap *StmtParents,
const DeclParentMap *DeclParents,
const ReplacedVarsMap *ReplacedVars,
const Stmt *ContainingStmt) :
StmtParents(StmtParents), DeclParents(DeclParents),
ContainingStmt(ContainingStmt), ReplacedVars(ReplacedVars) { }
/// \brief Run the analysis on Body, and return true iff the expression
/// depends on some variable declared within ContainingStmt.
///
/// This is intended to protect against hoisting the container expression
/// outside of an inner context if part of that expression is declared in that
/// inner context.
///
/// For example,
/// \code
/// const int N = 10, M = 20;
/// int arr[N][M];
/// int getRow();
///
/// for (int i = 0; i < M; ++i) {
/// int k = getRow();
/// printf("%d:", arr[k][i]);
/// }
/// \endcode
/// At first glance, this loop looks like it could be changed to
/// \code
/// for (int elem : arr[k]) {
/// int k = getIndex();
/// printf("%d:", elem);
/// }
/// \endcode
/// But this is malformed, since `k` is used before it is defined!
///
/// In order to avoid this, this class looks at the container expression
/// `arr[k]` and decides whether or not it contains a sub-expression declared
/// within the the loop body.
bool dependsOnInsideVariable(const Stmt *Body) {
DependsOnInsideVariable = false;
TraverseStmt(const_cast<Stmt *>(Body));
return DependsOnInsideVariable;
}
friend class RecursiveASTVisitor<DependencyFinderASTVisitor>;
private:
const StmtParentMap *StmtParents;
const DeclParentMap *DeclParents;
const Stmt *ContainingStmt;
const ReplacedVarsMap *ReplacedVars;
bool DependsOnInsideVariable;
bool VisitVarDecl(VarDecl *VD);
bool VisitDeclRefExpr(DeclRefExpr *DRE);
};
/// Class used to determine if any declarations used in a Stmt would conflict
/// with a particular identifier. This search includes the names that don't
/// actually appear in the AST (i.e. created by a refactoring tool) by including
/// a map from Stmts to generated names associated with those stmts.
class DeclFinderASTVisitor : public RecursiveASTVisitor<DeclFinderASTVisitor> {
public:
DeclFinderASTVisitor(const std::string &Name,
const StmtGeneratedVarNameMap *GeneratedDecls) :
Name(Name), GeneratedDecls(GeneratedDecls), Found(false) { }
/// Attempts to find any usages of variables name Name in Body, returning
/// true when it is used in Body. This includes the generated loop variables
/// of ForStmts which have already been transformed.
bool findUsages(const Stmt *Body) {
Found = false;
TraverseStmt(const_cast<Stmt *>(Body));
return Found;
}
friend class RecursiveASTVisitor<DeclFinderASTVisitor>;
private:
std::string Name;
/// GeneratedDecls keeps track of ForStmts which have been tranformed, mapping
/// each modified ForStmt to the variable generated in the loop.
const StmtGeneratedVarNameMap *GeneratedDecls;
bool Found;
bool VisitForStmt(ForStmt *FS);
bool VisitNamedDecl(NamedDecl *ND);
bool VisitDeclRefExpr(DeclRefExpr *DRE);
};
} // namespace for_migrate
} // namespace clang
#endif // _LLVM_TOOLS_CLANG_TOOLS_EXTRA_LOOP_CONVERT_STMT_ANCESTOR_H_

View File

@@ -1,84 +0,0 @@
//===-- loop-convert/VariableNaming.h - Gererate variable names -*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains the definitino of the VariableNamer class, which is
// responsible for generating new variable names and ensuring that they do not
// conflict with existing ones.
//
//===----------------------------------------------------------------------===//
#include "VariableNaming.h"
namespace clang {
namespace loop_migrate {
std::string VariableNamer::createIndexName() {
// FIXME: Add in naming conventions to handle:
// - Uppercase/lowercase indices
// - How to handle conflicts
// - An interactive process for naming
std::string IteratorName;
std::string ContainerName;
if (TheContainer)
ContainerName = TheContainer->getName().str();
size_t Len = ContainerName.length();
if (Len > 1 && ContainerName[Len - 1] == 's')
IteratorName = ContainerName.substr(0, Len - 1);
else
IteratorName = "elem";
if (!declarationExists(IteratorName))
return IteratorName;
IteratorName = ContainerName + "_" + OldIndex->getName().str();
if (!declarationExists(IteratorName))
return IteratorName;
IteratorName = ContainerName + "_elem";
if (!declarationExists(IteratorName))
return IteratorName;
IteratorName += "_elem";
if (!declarationExists(IteratorName))
return IteratorName;
IteratorName = "_elem_";
// Someone defeated my naming scheme...
while (declarationExists(IteratorName))
IteratorName += "i";
return IteratorName;
}
/// \brief Determines whether or not the the name Symbol exists in LoopContext,
/// any of its parent contexts, or any of its child statements.
///
/// We also check to see if the same identifier was generated by this loop
/// converter in a loop nested within SourceStmt.
bool VariableNamer::declarationExists(const StringRef Symbol) {
// Determine if the symbol was generated in a parent context.
for (const Stmt *S = SourceStmt; S != NULL; S = ReverseAST->lookup(S)) {
StmtGeneratedVarNameMap::const_iterator I = GeneratedDecls->find(S);
if (I != GeneratedDecls->end() && I->second == Symbol)
return true;
}
// FIXME: Rather than detecting conflicts at their usages, we should check the
// parent context.
// For some reason, lookup() always returns the pair (NULL, NULL) because its
// StoredDeclsMap is not initialized (i.e. LookupPtr.getInt() is false inside
// of DeclContext::lookup()). Why is this?
// Finally, determine if the symbol was used in the loop or a child context.
DeclFinderASTVisitor DeclFinder(Symbol, GeneratedDecls);
return DeclFinder.findUsages(SourceStmt);
}
} // namespace loop_migrate
} // namespace clang

View File

@@ -1,59 +0,0 @@
//===-- loop-convert/VariableNaming.h - Gererate variable names -*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains the declaration of the VariableNamer class, which is
// responsible for generating new variable names and ensuring that they do not
// conflict with existing ones.
//
//===----------------------------------------------------------------------===//
#ifndef _LLVM_TOOLS_CLANG_TOOLS_EXTRA_LOOP_VARIABLE_NAMING_H_
#define _LLVM_TOOLS_CLANG_TOOLS_EXTRA_LOOP_VARIABLE_NAMING_H_
#include "StmtAncestor.h"
#include "clang/AST/ASTContext.h"
namespace clang {
namespace loop_migrate {
/// \brief Create names for generated variables within a particular statement.
///
/// VariableNamer uses a DeclContext as a reference point, checking for any
/// conflicting declarations higher up in the context or within SourceStmt.
/// It creates a variable name using hints from a source container and the old
/// index, if they exist.
class VariableNamer {
public:
VariableNamer(StmtGeneratedVarNameMap *GeneratedDecls,
const StmtParentMap *ReverseAST, const Stmt *SourceStmt,
const VarDecl *OldIndex, const VarDecl *TheContainer) :
GeneratedDecls(GeneratedDecls), ReverseAST(ReverseAST),
SourceStmt(SourceStmt), OldIndex(OldIndex), TheContainer(TheContainer) { }
/// \brief Generate a new index name.
///
/// Generates the name to be used for an inserted iterator. It relies on
/// declarationExists() to determine that there are no naming conflicts, and
/// tries to use some hints from the container name and the old index name.
std::string createIndexName();
private:
StmtGeneratedVarNameMap *GeneratedDecls;
const StmtParentMap *ReverseAST;
const Stmt *SourceStmt;
const VarDecl *OldIndex;
const VarDecl *TheContainer;
// Determine whether or not a declaration that would conflict with Symbol
// exists in an outer context or in any statement contained in SourceStmt.
bool declarationExists(const StringRef Symbol);
};
} // namespace loop_migrate
} // namespace clang
#endif // _LLVM_TOOLS_CLANG_TOOLS_EXTRA_LOOP_VARIABLE_NAMING_H_

View File

@@ -1,6 +0,0 @@
add_clang_executable(remove-cstr-calls
RemoveCStrCalls.cpp
)
target_link_libraries(remove-cstr-calls
clangEdit clangTooling clangBasic clangAST clangASTMatchers)

View File

@@ -1,25 +0,0 @@
##===- tools/extra/remove-cstr-calls/Makefile --------------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##
CLANG_LEVEL := ../../..
TOOLNAME = remove-cstr-calls
NO_INSTALL = 1
# No plugins, optimize startup time.
TOOL_NO_EXPORTS = 1
include $(CLANG_LEVEL)/../../Makefile.config
LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser support mc
USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \
clangRewriteFrontend.a clangRewriteCore.a \
clangParse.a clangSema.a clangAnalysis.a \
clangAST.a clangASTMatchers.a clangEdit.a clangLex.a clangBasic.a
include $(CLANG_LEVEL)/Makefile

View File

@@ -1,236 +0,0 @@
//===- examples/Tooling/RemoveCStrCalls.cpp - Redundant c_str call removal ===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements a tool that prints replacements that remove redundant
// calls of c_str() on strings.
//
// Usage:
// remove-cstr-calls <cmake-output-dir> <file1> <file2> ...
//
// Where <cmake-output-dir> is a CMake build directory in which a file named
// compile_commands.json exists (enable -DCMAKE_EXPORT_COMPILE_COMMANDS in
// CMake to get this output).
//
// <file1> ... specify the paths of files in the CMake source tree. This path
// is looked up in the compile command database. If the path of a file is
// absolute, it needs to point into CMake's source tree. If the path is
// relative, the current working directory needs to be in the CMake source
// tree and the file must be in a subdirectory of the current working
// directory. "./" prefixes in the relative files will be automatically
// removed, but the rest of a relative path must be a suffix of a path in
// the compile command line database.
//
// For example, to use remove-cstr-calls on all files in a subtree of the
// source tree, use:
//
// /path/in/subtree $ find . -name '*.cpp'|
// xargs remove-cstr-calls /path/to/build
//
//===----------------------------------------------------------------------===//
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Lex/Lexer.h"
#include "clang/Tooling/CompilationDatabase.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/system_error.h"
using namespace clang;
using namespace clang::ast_matchers;
using namespace llvm;
using clang::tooling::newFrontendActionFactory;
using clang::tooling::Replacement;
using clang::tooling::CompilationDatabase;
// FIXME: Pull out helper methods in here into more fitting places.
// Returns the text that makes up 'node' in the source.
// Returns an empty string if the text cannot be found.
template <typename T>
static std::string getText(const SourceManager &SourceManager, const T &Node) {
SourceLocation StartSpellingLocatino =
SourceManager.getSpellingLoc(Node.getLocStart());
SourceLocation EndSpellingLocation =
SourceManager.getSpellingLoc(Node.getLocEnd());
if (!StartSpellingLocatino.isValid() || !EndSpellingLocation.isValid()) {
return std::string();
}
bool Invalid = true;
const char *Text =
SourceManager.getCharacterData(StartSpellingLocatino, &Invalid);
if (Invalid) {
return std::string();
}
std::pair<FileID, unsigned> Start =
SourceManager.getDecomposedLoc(StartSpellingLocatino);
std::pair<FileID, unsigned> End =
SourceManager.getDecomposedLoc(Lexer::getLocForEndOfToken(
EndSpellingLocation, 0, SourceManager, LangOptions()));
if (Start.first != End.first) {
// Start and end are in different files.
return std::string();
}
if (End.second < Start.second) {
// Shuffling text with macros may cause this.
return std::string();
}
return std::string(Text, End.second - Start.second);
}
// Return true if expr needs to be put in parens when it is an
// argument of a prefix unary operator, e.g. when it is a binary or
// ternary operator syntactically.
static bool needParensAfterUnaryOperator(const Expr &ExprNode) {
if (dyn_cast<clang::BinaryOperator>(&ExprNode) ||
dyn_cast<clang::ConditionalOperator>(&ExprNode)) {
return true;
}
if (const CXXOperatorCallExpr *op =
dyn_cast<CXXOperatorCallExpr>(&ExprNode)) {
return op->getNumArgs() == 2 &&
op->getOperator() != OO_PlusPlus &&
op->getOperator() != OO_MinusMinus &&
op->getOperator() != OO_Call &&
op->getOperator() != OO_Subscript;
}
return false;
}
// Format a pointer to an expression: prefix with '*' but simplify
// when it already begins with '&'. Return empty string on failure.
static std::string formatDereference(const SourceManager &SourceManager,
const Expr &ExprNode) {
if (const clang::UnaryOperator *Op =
dyn_cast<clang::UnaryOperator>(&ExprNode)) {
if (Op->getOpcode() == UO_AddrOf) {
// Strip leading '&'.
return getText(SourceManager, *Op->getSubExpr()->IgnoreParens());
}
}
const std::string Text = getText(SourceManager, ExprNode);
if (Text.empty()) return std::string();
// Add leading '*'.
if (needParensAfterUnaryOperator(ExprNode)) {
return std::string("*(") + Text + ")";
}
return std::string("*") + Text;
}
namespace {
class FixCStrCall : public ast_matchers::MatchFinder::MatchCallback {
public:
FixCStrCall(tooling::Replacements *Replace)
: Replace(Replace) {}
virtual void run(const ast_matchers::MatchFinder::MatchResult &Result) {
const CallExpr *Call =
Result.Nodes.getStmtAs<CallExpr>("call");
const Expr *Arg =
Result.Nodes.getStmtAs<Expr>("arg");
const bool Arrow =
Result.Nodes.getStmtAs<MemberExpr>("member")->isArrow();
// Replace the "call" node with the "arg" node, prefixed with '*'
// if the call was using '->' rather than '.'.
const std::string ArgText = Arrow ?
formatDereference(*Result.SourceManager, *Arg) :
getText(*Result.SourceManager, *Arg);
if (ArgText.empty()) return;
Replace->insert(Replacement(*Result.SourceManager, Call, ArgText));
}
private:
tooling::Replacements *Replace;
};
} // end namespace
const char *StringConstructor =
"::std::basic_string<char, std::char_traits<char>, std::allocator<char> >"
"::basic_string";
const char *StringCStrMethod =
"::std::basic_string<char, std::char_traits<char>, std::allocator<char> >"
"::c_str";
cl::opt<std::string> BuildPath(
cl::Positional,
cl::desc("<build-path>"));
cl::list<std::string> SourcePaths(
cl::Positional,
cl::desc("<source0> [... <sourceN>]"),
cl::OneOrMore);
int main(int argc, const char **argv) {
llvm::OwningPtr<CompilationDatabase> Compilations(
tooling::FixedCompilationDatabase::loadFromCommandLine(argc, argv));
cl::ParseCommandLineOptions(argc, argv);
if (!Compilations) {
std::string ErrorMessage;
Compilations.reset(
CompilationDatabase::loadFromDirectory(BuildPath, ErrorMessage));
if (!Compilations)
llvm::report_fatal_error(ErrorMessage);
}
tooling::RefactoringTool Tool(*Compilations, SourcePaths);
ast_matchers::MatchFinder Finder;
FixCStrCall Callback(&Tool.getReplacements());
Finder.addMatcher(
constructExpr(
hasDeclaration(methodDecl(hasName(StringConstructor))),
argumentCountIs(2),
// The first argument must have the form x.c_str() or p->c_str()
// where the method is string::c_str(). We can use the copy
// constructor of string instead (or the compiler might share
// the string object).
hasArgument(
0,
id("call", memberCallExpr(
callee(id("member", memberExpr())),
callee(methodDecl(hasName(StringCStrMethod))),
on(id("arg", expr()))))),
// The second argument is the alloc object which must not be
// present explicitly.
hasArgument(
1,
defaultArgExpr())),
&Callback);
Finder.addMatcher(
constructExpr(
// Implicit constructors of these classes are overloaded
// wrt. string types and they internally make a StringRef
// referring to the argument. Passing a string directly to
// them is preferred to passing a char pointer.
hasDeclaration(methodDecl(anyOf(
hasName("::llvm::StringRef::StringRef"),
hasName("::llvm::Twine::Twine")))),
argumentCountIs(1),
// The only argument must have the form x.c_str() or p->c_str()
// where the method is string::c_str(). StringRef also has
// a constructor from string which is more efficient (avoids
// strlen), so we can construct StringRef from the string
// directly.
hasArgument(
0,
id("call", memberCallExpr(
callee(id("member", memberExpr())),
callee(methodDecl(hasName(StringCStrMethod))),
on(id("arg", expr())))))),
&Callback);
return Tool.run(newFrontendActionFactory(&Finder));
}

View File

@@ -1,34 +0,0 @@
# Test runner infrastructure for Clang-based tools. This configures the Clang
# test trees for use by Lit, and delegates to LLVM's lit test handlers.
#
# Note that currently we don't support stand-alone builds of Clang, you must
# be building Clang from within a combined LLVM+Clang checkout..
set(CLANG_TOOLS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..")
set(CLANG_TOOLS_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/..")
configure_lit_site_cfg(
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
)
option(CLANG_TOOLS_TEST_USE_VG "Run Clang tools' tests under Valgrind" OFF)
if(CLANG_TOOLS_TEST_USE_VG)
set(CLANG_TOOLS_TEST_EXTRA_ARGS ${CLANG_TEST_EXTRA_ARGS} "--vg")
endif()
set(CLANG_TOOLS_TEST_DEPS
# Base line deps.
clang clang-headers FileCheck count not
# Individual tools we test.
remove-cstr-calls loop-convert
)
add_lit_testsuite(check-clang-tools "Running the Clang extra tools' regression tests"
${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${CLANG_TOOLS_TEST_DEPS}
ARGS ${CLANG_TOOLS_TEST_EXTRA_ARGS}
)
set_target_properties(check-clang-tools PROPERTIES FOLDER "Clang extra tools' tests")

View File

@@ -1,63 +0,0 @@
##===- tools/extra/test/Makefile ---------------------------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##
CLANG_LEVEL := ../../..
include $(CLANG_LEVEL)/Makefile
# Test in all immediate subdirectories if unset.
ifdef TESTSUITE
TESTDIRS := $(TESTSUITE:%=$(PROJ_SRC_DIR)/%)
else
TESTDIRS ?= $(PROJ_SRC_DIR)
endif
# 'lit' wants objdir paths, so it will pick up the lit.site.cfg.
TESTDIRS := $(TESTDIRS:$(PROJ_SRC_DIR)%=$(PROJ_OBJ_DIR)%)
# Allow EXTRA_TESTDIRS to provide additional test directories.
TESTDIRS += $(EXTRA_TESTDIRS)
ifndef TESTARGS
ifdef VERBOSE
TESTARGS = -v
else
TESTARGS = -s -v
endif
endif
# Make sure any extra test suites can find the main site config.
LIT_ARGS := --param clang_site_config=$(PROJ_OBJ_DIR)/lit.site.cfg
ifdef VG
LIT_ARGS += "--vg"
endif
all:: lit.site.cfg
@ echo '--- Running the Clang extra tools tests for $(TARGET_TRIPLE) ---'
@ $(PYTHON) $(LLVM_SRC_ROOT)/utils/lit/lit.py \
$(LIT_ARGS) $(TESTARGS) $(TESTDIRS)
FORCE:
lit.site.cfg: FORCE
@echo "Making Clang extra tools' 'lit.site.cfg' file..."
@$(ECHOPATH) s=@LLVM_SOURCE_DIR@=$(LLVM_SRC_ROOT)=g > lit.tmp
@$(ECHOPATH) s=@LLVM_BINARY_DIR@=$(LLVM_OBJ_ROOT)=g >> lit.tmp
@$(ECHOPATH) s=@LLVM_TOOLS_DIR@=$(ToolDir)=g >> lit.tmp
@$(ECHOPATH) s=@LLVM_LIBS_DIR@=$(LibDir)=g >> lit.tmp
@$(ECHOPATH) s=@CLANG_TOOLS_SOURCE_DIR@=$(PROJ_SRC_DIR)/..=g >> lit.tmp
@$(ECHOPATH) s=@CLANG_TOOLS_BINARY_DIR@=$(PROJ_OBJ_DIR)/..=g >> lit.tmp
@$(ECHOPATH) s=@TARGET_TRIPLE@=$(TARGET_TRIPLE)=g >> lit.tmp
@sed -f lit.tmp $(PROJ_SRC_DIR)/lit.site.cfg.in > $@
@-rm -f lit.tmp
clean::
@ find . -name Output | xargs rm -fr
.PHONY: all report clean

View File

@@ -1,224 +0,0 @@
# -*- Python -*-
import os
import platform
import re
import subprocess
# Configuration file for the 'lit' test runner.
# name: The name of this test suite.
config.name = 'Clang Tools'
# Tweak PATH for Win32
if platform.system() == 'Windows':
# Seek sane tools in directories and set to $PATH.
path = getattr(config, 'lit_tools_dir', None)
path = lit.getToolsPath(path,
config.environment['PATH'],
['cmp.exe', 'grep.exe', 'sed.exe'])
if path is not None:
path = os.path.pathsep.join((path,
config.environment['PATH']))
config.environment['PATH'] = path
# testFormat: The test format to use to interpret tests.
#
# For now we require '&&' between commands, until they get globally killed and
# the test runner updated.
execute_external = (platform.system() != 'Windows'
or lit.getBashPath() not in [None, ""])
config.test_format = lit.formats.ShTest(execute_external)
# suffixes: A list of file extensions to treat as test files.
config.suffixes = ['.c', '.cpp', '.m', '.mm', '.cu', '.ll', '.cl', '.s']
# test_source_root: The root path where tests are located.
config.test_source_root = os.path.dirname(__file__)
# test_exec_root: The root path where tests should be run.
clang_tools_binary_dir = getattr(config, 'clang_tools_binary_dir', None)
if clang_tools_binary_dir is not None:
config.test_exec_root = os.path.join(clang_tools_binary_dir, 'test')
# Clear some environment variables that might affect Clang.
#
# This first set of vars are read by Clang, but shouldn't affect tests
# that aren't specifically looking for these features, or are required
# simply to run the tests at all.
#
# FIXME: Should we have a tool that enforces this?
# safe_env_vars = ('TMPDIR', 'TEMP', 'TMP', 'USERPROFILE', 'PWD',
# 'MACOSX_DEPLOYMENT_TARGET', 'IPHONEOS_DEPLOYMENT_TARGET',
# 'IOS_SIMULATOR_DEPLOYMENT_TARGET',
# 'VCINSTALLDIR', 'VC100COMNTOOLS', 'VC90COMNTOOLS',
# 'VC80COMNTOOLS')
possibly_dangerous_env_vars = ['COMPILER_PATH', 'RC_DEBUG_OPTIONS',
'CINDEXTEST_PREAMBLE_FILE', 'LIBRARY_PATH',
'CPATH', 'C_INCLUDE_PATH', 'CPLUS_INCLUDE_PATH',
'OBJC_INCLUDE_PATH', 'OBJCPLUS_INCLUDE_PATH',
'LIBCLANG_TIMING', 'LIBCLANG_OBJTRACKING',
'LIBCLANG_LOGGING', 'LIBCLANG_BGPRIO_INDEX',
'LIBCLANG_BGPRIO_EDIT', 'LIBCLANG_NOTHREADS',
'LIBCLANG_RESOURCE_USAGE',
'LIBCLANG_CODE_COMPLETION_LOGGING']
# Clang/Win32 may refer to %INCLUDE%. vsvarsall.bat sets it.
if platform.system() != 'Windows':
possibly_dangerous_env_vars.append('INCLUDE')
for name in possibly_dangerous_env_vars:
if name in config.environment:
del config.environment[name]
# Tweak the PATH to include the tools dir and the scripts dir.
if clang_tools_binary_dir is not None:
llvm_tools_dir = getattr(config, 'llvm_tools_dir', None)
if not llvm_tools_dir:
lit.fatal('No LLVM tools dir set!')
path = os.path.pathsep.join((llvm_tools_dir, config.environment['PATH']))
config.environment['PATH'] = path
llvm_libs_dir = getattr(config, 'llvm_libs_dir', None)
if not llvm_libs_dir:
lit.fatal('No LLVM libs dir set!')
path = os.path.pathsep.join((llvm_libs_dir,
config.environment.get('LD_LIBRARY_PATH','')))
config.environment['LD_LIBRARY_PATH'] = path
###
# Check that the object root is known.
if config.test_exec_root is None:
# Otherwise, we haven't loaded the site specific configuration (the user is
# probably trying to run on a test file directly, and either the site
# configuration hasn't been created by the build system, or we are in an
# out-of-tree build situation).
# Check for 'clang_site_config' user parameter, and use that if available.
site_cfg = lit.params.get('clang_tools_extra_site_config', None)
if site_cfg and os.path.exists(site_cfg):
lit.load_config(config, site_cfg)
raise SystemExit
# Try to detect the situation where we are using an out-of-tree build by
# looking for 'llvm-config'.
#
# FIXME: I debated (i.e., wrote and threw away) adding logic to
# automagically generate the lit.site.cfg if we are in some kind of fresh
# build situation. This means knowing how to invoke the build system though,
# and I decided it was too much magic. We should solve this by just having
# the .cfg files generated during the configuration step.
llvm_config = lit.util.which('llvm-config', config.environment['PATH'])
if not llvm_config:
lit.fatal('No site specific configuration available!')
# Get the source and object roots.
llvm_src_root = lit.util.capture(['llvm-config', '--src-root']).strip()
llvm_obj_root = lit.util.capture(['llvm-config', '--obj-root']).strip()
clang_src_root = os.path.join(llvm_src_root, "tools", "clang")
clang_obj_root = os.path.join(llvm_obj_root, "tools", "clang")
clang_tools_extra_src_root = os.path.join(clang_src_root, "tools", "extra")
clang_tools_extra_obj_root = os.path.join(clang_obj_root, "tools", "extra")
# Validate that we got a tree which points to here, using the standard
# tools/clang layout.
this_src_root = os.path.dirname(config.test_source_root)
if os.path.realpath(clang_tools_extra_src_root) != os.path.realpath(this_src_root):
lit.fatal('No site specific configuration available!')
# Check that the site specific configuration exists.
site_cfg = os.path.join(clang_tools_extra_obj_root, 'test', 'lit.site.cfg')
if not os.path.exists(site_cfg):
lit.fatal('No site specific configuration available! You may need to '
'run "make test" in your Clang build directory.')
# Okay, that worked. Notify the user of the automagic, and reconfigure.
lit.note('using out-of-tree build at %r' % clang_obj_root)
lit.load_config(config, site_cfg)
raise SystemExit
###
# Discover the 'clang' and 'clangcc' to use.
import os
def inferClang(PATH):
# Determine which clang to use.
clang = os.getenv('CLANG')
# If the user set clang in the environment, definitely use that and don't
# try to validate.
if clang:
return clang
# Otherwise look in the path.
clang = lit.util.which('clang', PATH)
if not clang:
lit.fatal("couldn't find 'clang' program, try setting "
"CLANG in your environment")
return clang
# When running under valgrind, we mangle '-vg' onto the end of the triple so we
# can check it with XFAIL and XTARGET.
if lit.useValgrind:
config.target_triple += '-vg'
config.clang = inferClang(config.environment['PATH']).replace('\\', '/')
if not lit.quiet:
lit.note('using clang: %r' % config.clang)
# Note that when substituting %clang_cc1 also fill in the include directory of
# the builtin headers. Those are part of even a freestanding environment, but
# Clang relies on the driver to locate them.
def getClangBuiltinIncludeDir(clang):
# FIXME: Rather than just getting the version, we should have clang print
# out its resource dir here in an easy to scrape form.
cmd = subprocess.Popen([clang, '-print-file-name=include'],
stdout=subprocess.PIPE)
if not cmd.stdout:
lit.fatal("Couldn't find the include dir for Clang ('%s')" % clang)
return cmd.stdout.read().strip()
config.substitutions.append( ('%clang_cc1', '%s -cc1 -internal-isystem %s'
% (config.clang,
getClangBuiltinIncludeDir(config.clang))) )
config.substitutions.append( ('%clangxx', ' ' + config.clang +
' -ccc-clang-cxx -ccc-cxx '))
config.substitutions.append( ('%clang', ' ' + config.clang + ' ') )
# FIXME: Find nicer way to prohibit this.
config.substitutions.append(
(' clang ', """*** Do not use 'clang' in tests, use '%clang'. ***""") )
config.substitutions.append(
(' clang\+\+ ', """*** Do not use 'clang++' in tests, use '%clangxx'. ***"""))
config.substitutions.append(
(' clang-cc ',
"""*** Do not use 'clang-cc' in tests, use '%clang_cc1'. ***""") )
config.substitutions.append(
(' clang -cc1 ',
"""*** Do not use 'clang -cc1' in tests, use '%clang_cc1'. ***""") )
config.substitutions.append(
(' %clang-cc1 ',
"""*** invalid substitution, use '%clang_cc1'. ***""") )
###
# Set available features we allow tests to conditionalize on.
#
# As of 2011.08, crash-recovery tests still do not pass on FreeBSD.
if platform.system() not in ['FreeBSD']:
config.available_features.add('crash-recovery')
# Shell execution
if platform.system() not in ['Windows'] or lit.getBashPath() != '':
config.available_features.add('shell')
# ANSI escape sequences in non-dumb terminal
if platform.system() not in ['Windows']:
config.available_features.add('ansi-escape-sequences')

View File

@@ -1,21 +0,0 @@
## Autogenerated by LLVM/Clang configuration.
# Do not edit!
config.llvm_src_root = "@LLVM_SOURCE_DIR@"
config.llvm_obj_root = "@LLVM_BINARY_DIR@"
config.llvm_tools_dir = "@LLVM_TOOLS_DIR@"
config.llvm_libs_dir = "@LLVM_LIBS_DIR@"
config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@"
config.clang_tools_binary_dir = "@CLANG_TOOLS_BINARY_DIR@"
config.target_triple = "@TARGET_TRIPLE@"
# Support substitution of the tools and libs dirs with user parameters. This is
# used when we can't determine the tool dir at configuration time.
try:
config.llvm_tools_dir = config.llvm_tools_dir % lit.params
config.llvm_libs_dir = config.llvm_libs_dir % lit.params
except KeyError,e:
key, = e.args
lit.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key,key))
# Let the main config do the real work.
lit.load_config(config, "@CLANG_TOOLS_SOURCE_DIR@/test/lit.cfg")

View File

@@ -1,14 +0,0 @@
#ifndef _CLANG_TOOLS_EXTRA_H_
#define _CLANG_TOOLS_EXTRA_H_
// Single FileCheck line to make sure that no loops are converted.
// CHECK-NOT: for ({{.*[^:]:[^:].*}})
static void loopInHeader() {
const int N = 10;
int arr[N];
int sum = 0;
for (int i = 0; i < N; ++i)
sum += arr[i];
}
#endif //_CLANG_TOOLS_EXTRA_H_

View File

@@ -1,140 +0,0 @@
#ifndef _LLVM_TOOLS_CLANG_TOOLS_TESTS_TOOLING_STRUCTURES_H_
#define _LLVM_TOOLS_CLANG_TOOLS_TESTS_TOOLING_STRUCTURES_H_
extern "C" {
extern int printf(const char *restrict, ...);
}
struct Val {int x; void g(); };
struct MutableVal {
void constFun(int) const;
void nonConstFun(int, int);
void constFun(MutableVal &) const;
void constParamFun(const MutableVal &) const;
void nonConstParamFun(const MutableVal &);
int x;
};
struct S {
typedef MutableVal *iterator;
typedef const MutableVal *const_iterator;
const_iterator begin() const;
const_iterator end() const;
iterator begin();
iterator end();
};
struct T {
struct iterator {
int& operator*();
const int& operator*()const;
iterator& operator ++();
bool operator!=(const iterator &other);
void insert(int);
int x;
};
iterator begin();
iterator end();
};
struct U {
struct iterator {
Val& operator*();
const Val& operator*()const;
iterator& operator ++();
bool operator!=(const iterator &other);
Val *operator->();
};
iterator begin();
iterator end();
int x;
};
struct X {
S s;
T t;
U u;
S getS();
};
template<typename ElemType>
class dependent{
public:
struct iterator_base {
const ElemType& operator*()const;
iterator_base& operator ++();
bool operator!=(const iterator_base &other) const;
const ElemType *operator->() const;
};
struct iterator : iterator_base {
ElemType& operator*();
iterator& operator ++();
ElemType *operator->();
};
typedef iterator_base const_iterator;
const_iterator begin() const;
const_iterator end() const;
iterator begin();
iterator end();
unsigned size() const;
ElemType & operator[](unsigned);
const ElemType & operator[](unsigned) const;
ElemType & at(unsigned);
const ElemType & at(unsigned) const;
// Intentionally evil.
dependent<ElemType> operator*();
void foo();
void constFoo() const;
};
template<typename First, typename Second>
class doublyDependent{
public:
struct Value {
First first;
Second second;
};
struct iterator_base {
const Value& operator*()const;
iterator_base& operator ++();
bool operator!=(const iterator_base &other) const;
const Value *operator->() const;
};
struct iterator : iterator_base {
Value& operator*();
Value& operator ++();
Value *operator->();
};
typedef iterator_base const_iterator;
const_iterator begin() const;
const_iterator end() const;
iterator begin();
iterator end();
};
template<typename Contained>
class transparent {
public:
Contained *at();
Contained *operator->();
Contained operator*();
};
template<typename IteratorType>
struct Nested {
typedef IteratorType* iterator;
IteratorType *operator->();
IteratorType operator*();
iterator begin();
iterator end();
};
#endif // _LLVM_TOOLS_CLANG_TOOLS_TESTS_TOOLING_STRUCTURES_H_

View File

@@ -1,155 +0,0 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: cp %t.cpp %t.base
// RUN: loop-convert . %t.cpp -- -I %S/Inputs
// RUN: FileCheck -input-file=%t.cpp %s
// RUN: cp %t.base %t.cpp
// RUN: loop-convert -count-only . %t.cpp -- -I %S/Inputs > %T/out
// RUN: FileCheck -check-prefix=COUNTONLY -input-file=%T/out %s
// RUN: diff %t.cpp %t.base
#include "structures.h"
const int N = 6;
const int NMinusOne = N - 1;
int arr[N] = {1, 2, 3, 4, 5, 6};
int (*pArr)[N] = &arr;
void f() {
int sum = 0;
// Update the number of correctly converted loops as this test changes:
// COUNTONLY: 15 converted
// COUNTONLY-NEXT: 0 potentially conflicting
// COUNTONLY-NEXT: 0 change(s) rejected
for (int i = 0; i < N; ++i) {
sum += arr[i];
int k;
}
// CHECK: for (auto & [[VAR:[a-z_]+]] : arr) {
// CHECK-NEXT: sum += [[VAR]];
// CHECK-NEXT: int k;
// CHECK-NEXT: }
for (int i = 0; i < N; ++i) {
printf("Fibonacci number is %d\n", arr[i]);
sum += arr[i] + 2;
}
// CHECK: for (auto & [[VAR:[a-z_]+]] : arr)
// CHECK-NEXT: printf("Fibonacci number is %d\n", [[VAR]]);
// CHECK-NEXT: sum += [[VAR]] + 2;
for (int i = 0; i < N; ++i) {
int x = arr[i];
int y = arr[i] + 2;
}
// CHECK: for (auto & [[VAR:[a-z_]+]] : arr)
// CHECK-NEXT: int x = [[VAR]];
// CHECK-NEXT: int y = [[VAR]] + 2;
for (int i = 0; i < N; ++i) {
int x = N;
x = arr[i];
}
// CHECK: for (auto & [[VAR:[a-z_]+]] : arr)
// CHECK-NEXT: int x = N;
// CHECK-NEXT: x = [[VAR]];
for (int i = 0; i < N; ++i) {
arr[i] += 1;
}
// CHECK: for (auto & [[VAR:[a-z_]+]] : arr) {
// CHECK-NEXT: [[VAR]] += 1;
// CHECK-NEXT: }
for (int i = 0; i < N; ++i) {
int x = arr[i] + 2;
arr[i] ++;
}
// CHECK: for (auto & [[VAR:[a-z_]+]] : arr)
// CHECK-NEXT: int x = [[VAR]] + 2;
// CHECK-NEXT: [[VAR]] ++;
for (int i = 0; i < N; ++i) {
arr[i] = 4 + arr[i];
}
// CHECK: for (auto & [[VAR:[a-z_]+]] : arr)
// CHECK-NEXT: [[VAR]] = 4 + [[VAR]];
for (int i = 0; i < NMinusOne + 1; ++i) {
sum += arr[i];
}
// CHECK: for (auto & [[VAR:[a-z_]+]] : arr) {
// CHECK-NEXT: sum += [[VAR]];
// CHECK-NEXT: }
for (int i = 0; i < N; ++i) {
printf("Fibonacci number %d has address %p\n", arr[i], &arr[i]);
sum += arr[i] + 2;
}
// CHECK: for (auto & [[VAR:[a-z_]+]] : arr)
// CHECK-NEXT: printf("Fibonacci number %d has address %p\n", [[VAR]], &[[VAR]]);
// CHECK-NEXT: sum += [[VAR]] + 2;
Val teas[N];
for (int i = 0; i < N; ++i) {
teas[i].g();
}
// CHECK: for (auto & [[VAR:[a-z_]+]] : teas) {
// CHECK-NEXT: [[VAR]].g();
// CHECK-NEXT: }
}
struct HasArr {
int Arr[N];
Val ValArr[N];
void implicitThis() {
for (int i = 0; i < N; ++i) {
printf("%d", Arr[i]);
}
// CHECK: for (auto & [[VAR:[a-z_]+]] : Arr) {
// CHECK-NEXT: printf("%d", [[VAR]]);
// CHECK-NEXT: }
for (int i = 0; i < N; ++i) {
printf("%d", ValArr[i].x);
}
// CHECK: for (auto & [[VAR:[a-z_]+]] : ValArr) {
// CHECK-NEXT: printf("%d", [[VAR]].x);
// CHECK-NEXT: }
}
void explicitThis() {
for (int i = 0; i < N; ++i) {
printf("%d", this->Arr[i]);
}
// CHECK: for (auto & [[VAR:[a-z_]+]] : this->Arr) {
// CHECK-NEXT: printf("%d", [[VAR]]);
// CHECK-NEXT: }
for (int i = 0; i < N; ++i) {
printf("%d", this->ValArr[i].x);
}
// CHECK: for (auto & [[VAR:[a-z_]+]] : this->ValArr) {
// CHECK-NEXT: printf("%d", [[VAR]].x);
// CHECK-NEXT: }
}
};
// Loops whose bounds are value-dependent shold not be converted.
template<int N>
void dependentExprBound() {
for (int i = 0; i < N; ++i)
arr[i] = 0;
// CHECK: for (int i = 0; i < N; ++i)
// CHECK-NEXT: arr[i] = 0;
}
template void dependentExprBound<20>();
void memberFunctionPointer() {
Val v;
void (Val::*mfpArr[N])(void) = { &Val::g };
for (int i = 0; i < N; ++i)
(v.*mfpArr[i])();
// CHECK: for (auto & [[VAR:[a-z_]+]] : mfpArr)
// CHECK-NEXT: (v.*[[VAR]])();
}

View File

@@ -1,35 +0,0 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: loop-convert . %t.cpp -- -I %S/Inputs
// RUN: FileCheck -input-file=%t.cpp %s
// RUN: loop-convert . %t.cpp -A2 -- -I %S/Inputs
// RUN: FileCheck -check-prefix=RISKY -input-file=%t.cpp %s
#include "structures.h"
void f() {
const int N = 5;
const int M = 7;
int (*pArr)[N];
int Arr[N][M];
int sum = 0;
for (int i = 0; i < M; ++i) {
sum += Arr[0][i];
}
// CHECK: for (int i = 0; i < M; ++i) {
// CHECK-NEXT: sum += Arr[0][i];
// CHECK-NEXT: }
// RISKY: for (auto & [[VAR:[a-z_]+]] : Arr[0]) {
// RISKY-NEXT: sum += [[VAR]];
// RISKY-NEXT: }
for (int i = 0; i < N; ++i) {
sum += (*pArr)[i];
}
// RISKY: for (auto & [[VAR:[a-z_]+]] : *pArr) {
// RISKY-NEXT: sum += [[VAR]];
// RISKY-NEXT: }
// CHECK: for (int i = 0; i < N; ++i) {
// CHECK-NEXT: sum += (*pArr)[i];
// CHECK-NEXT: }
}

View File

@@ -1,26 +0,0 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: loop-convert . %t.cpp -- && FileCheck -input-file=%t.cpp %s
void f() {
const int N = 6;
const int M = 8;
int arr[N][M];
for (int i = 0; i < N; ++i) {
int a = 0;
int b = arr[i][a];
}
// CHECK: for (auto & [[VAR:[a-z_]+]] : arr) {
// CHECK-NEXT: int a = 0;
// CHECK-NEXT: int b = [[VAR]][a];
// CHECK-NEXT: }
for (int j = 0; j < M; ++j) {
int a = 0;
int b = arr[a][j];
}
// CHECK: for (int j = 0; j < M; ++j) {
// CHECK-NEXT: int a = 0;
// CHECK-NEXT: int b = arr[a][j];
// CHECK-NEXT: }
}

View File

@@ -1,103 +0,0 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: loop-convert . %t.cpp -- -I %S/Inputs
// RUN: FileCheck -input-file=%t.cpp %s
#include "structures.h"
void f() {
/// begin()/end() - based for loops here:
T t;
for (T::iterator it = t.begin(), e = t.end(); it != e; ++it) {
printf("I found %d\n", *it);
}
// CHECK: for ({{[a-zA-Z_ ]+&? ?}}[[VAR:[a-z_]+]] : t)
// CHECK-NEXT: printf("I found %d\n", [[VAR]]);
T *pt;
for (T::iterator it = pt->begin(), e = pt->end(); it != e; ++it) {
printf("I found %d\n", *it);
}
// CHECK: for ({{[a-zA-Z_ ]+&? ?}}[[VAR:[a-z_]+]] : *pt)
// CHECK-NEXT: printf("I found %d\n", [[VAR]]);
S s;
for (S::const_iterator it = s.begin(), e = s.end(); it != e; ++it) {
printf("s has value %d\n", (*it).x);
}
// CHECK: for ({{[a-zA-Z_ ]*&? ?}}[[VAR:[a-z_]+]] : s)
// CHECK-NEXT: printf("s has value %d\n", ([[VAR]]).x);
S *ps;
for (S::const_iterator it = ps->begin(), e = ps->end(); it != e; ++it) {
printf("s has value %d\n", (*it).x);
}
// CHECK: for ({{[a-zA-Z_ ]*&? ?}}[[VAR:[a-z_]+]] : *ps)
// CHECK-NEXT: printf("s has value %d\n", ([[VAR]]).x);
for (S::const_iterator it = s.begin(), e = s.end(); it != e; ++it) {
printf("s has value %d\n", it->x);
}
// CHECK: for ({{[a-zA-Z_ ]*&? ?}}[[VAR:[a-z_]+]] : s)
// CHECK-NEXT: printf("s has value %d\n", [[VAR]].x);
for (S::iterator it = s.begin(), e = s.end(); it != e; ++it) {
it->x = 3;
}
// CHECK: for ({{[a-zA-Z_ ]*&? ?}}[[VAR:[a-z_]+]] : s)
// CHECK-NEXT: [[VAR]].x = 3;
for (S::iterator it = s.begin(), e = s.end(); it != e; ++it) {
(*it).x = 3;
}
// CHECK: for ({{[a-zA-Z_ ]*&? ?}}[[VAR:[a-z_]+]] : s)
// CHECK-NEXT: ([[VAR]]).x = 3;
for (S::iterator it = s.begin(), e = s.end(); it != e; ++it) {
it->nonConstFun(4, 5);
}
// CHECK: for ({{[a-zA-Z_ ]*&? ?}}[[VAR:[a-z_]+]] : s)
// CHECK-NEXT: [[VAR]].nonConstFun(4, 5);
U u;
for (U::iterator it = u.begin(), e = u.end(); it != e; ++it) {
printf("s has value %d\n", it->x);
}
// CHECK: for ({{[a-zA-Z_ ]*&? ?}}[[VAR:[a-z_]+]] : u)
// CHECK-NEXT: printf("s has value %d\n", [[VAR]].x);
for (U::iterator it = u.begin(), e = u.end(); it != e; ++it) {
printf("s has value %d\n", (*it).x);
}
// CHECK: for ({{[a-zA-Z_ ]*&? ?}}[[VAR:[a-z_]+]] : u)
// CHECK-NEXT: printf("s has value %d\n", ([[VAR]]).x);
U::iterator A;
for (U::iterator i = u.begin(), e = u.end(); i != e; ++i)
int k = A->x + i->x;
// CHECK: for ({{[a-zA-Z_ ]*&? ?}}[[VAR:[a-z_]+]] : u)
// CHECK-NEXT: int k = A->x + [[VAR]].x;
dependent<int> v;
for (dependent<int>::const_iterator it = v.begin(), e = v.end();
it != e; ++it) {
printf("Fibonacci number is %d\n", *it);
}
// CHECK: for ({{[a-zA-Z_ ]*&? ?}}[[VAR:[a-z_]+]] : v)
// CHECK-NEXT: printf("Fibonacci number is %d\n", [[VAR]]);
for (dependent<int>::const_iterator it(v.begin()), e = v.end();
it != e; ++it) {
printf("Fibonacci number is %d\n", *it);
}
// CHECK: for ({{[a-zA-Z_ ]*&? ?}}[[VAR:[a-z_]+]] : v)
// CHECK-NEXT: printf("Fibonacci number is %d\n", [[VAR]]);
doublyDependent<int,int> intmap;
for (doublyDependent<int,int>::iterator it = intmap.begin(), e = intmap.end();
it != e; ++it) {
printf("intmap[%d] = %d", it->first, it->second);
}
// CHECK: for ({{[a-zA-Z_ ]*&? ?}}[[VAR:[a-z_]+]] : intmap)
// CHECK-NEXT: printf("intmap[%d] = %d", [[VAR]].first, [[VAR]].second);
}

View File

@@ -1,67 +0,0 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: loop-convert . %t.cpp -- -I %S/Inputs
// RUN: FileCheck -input-file=%t.cpp %s
#include "structures.h"
const int N = 10;
int nums[N];
int sum = 0;
Val Arr[N];
Val &func(Val &);
void aliasing() {
// The extra blank braces are left as a placeholder for after the variable
// declaration is deleted.
for (int i = 0; i < N; ++i) {
Val &t = Arr[i]; { }
int y = t.x;
}
// CHECK: for (auto & t : Arr)
// CHECK-NEXT: { }
// CHECK-NEXT: int y = t.x;
for (int i = 0; i < N; ++i) {
Val &t = Arr[i];
int y = t.x;
int z = Arr[i].x + t.x;
}
// CHECK: for (auto & [[VAR:[a-z_]+]] : Arr)
// CHECK-NEXT: Val &t = [[VAR]];
// CHECK-NEXT: int y = t.x;
// CHECK-NEXT: int z = [[VAR]].x + t.x;
for (int i = 0; i < N; ++i) {
Val t = Arr[i];
int y = t.x;
int z = Arr[i].x + t.x;
}
// CHECK: for (auto & [[VAR:[a-z_]+]] : Arr)
// CHECK-NEXT: Val t = [[VAR]];
// CHECK-NEXT: int y = t.x;
// CHECK-NEXT: int z = [[VAR]].x + t.x;
for (int i = 0; i < N; ++i) {
Val &t = func(Arr[i]);
int y = t.x;
}
// CHECK: for (auto & [[VAR:[a-z_]+]] : Arr)
// CHECK-NEXT: Val &t = func([[VAR]]);
// CHECK-NEXT: int y = t.x;
}
void sameNames() {
int num = 0;
for (int i = 0; i < N; ++i) {
printf("Fibonacci number is %d\n", nums[i]);
sum += nums[i] + 2 + num;
(void) nums[i];
}
// CHECK: int num = 0;
// CHECK-NEXT: for (auto & [[VAR:[a-z_]+]] : nums)
// CHECK-NEXT: printf("Fibonacci number is %d\n", [[VAR]]);
// CHECK-NEXT: sum += [[VAR]] + 2 + num;
// CHECK-NOT: (void) num;
// CHECK: }
}

View File

@@ -1,160 +0,0 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: loop-convert . %t.cpp -- -I %S/Inputs
// RUN: FileCheck -input-file=%t.cpp %s
#include "structures.h"
// Single FileCheck line to make sure that no loops are converted.
// CHECK-NOT: for ({{.*[^:]:[^:].*}})
S s;
T t;
U u;
struct BadBeginEnd : T {
iterator notBegin();
iterator notEnd();
};
void notBeginOrEnd() {
BadBeginEnd Bad;
for (T::iterator i = Bad.notBegin(), e = Bad.end(); i != e; ++i)
int k = *i;
for (T::iterator i = Bad.begin(), e = Bad.notEnd(); i != e; ++i)
int k = *i;
}
void badLoopShapes() {
for (T::iterator i = t.begin(), e = t.end(), f = e; i != e; ++i)
int k = *i;
for (T::iterator i = t.begin(), e = t.end(); i != e; )
int k = *i;
for (T::iterator i = t.begin(), e = t.end(); ; ++i)
int k = *i;
T::iterator outsideI;
T::iterator outsideE;
for (; outsideI != outsideE ; ++outsideI)
int k = *outsideI;
}
void iteratorArrayMix() {
int lower;
const int N = 6;
for (T::iterator i = t.begin(), e = t.end(); lower < N; ++i)
int k = *i;
for (T::iterator i = t.begin(), e = t.end(); lower < N; ++lower)
int k = *i;
}
struct ExtraConstructor : T::iterator {
ExtraConstructor(T::iterator, int);
explicit ExtraConstructor(T::iterator);
};
void badConstructor() {
for (T::iterator i = ExtraConstructor(t.begin(), 0), e = t.end();
i != e; ++i)
int k = *i;
for (T::iterator i = ExtraConstructor(t.begin()), e = t.end(); i != e; ++i)
int k = *i;
}
void iteratorMemberUsed() {
for (T::iterator i = t.begin(), e = t.end(); i != e; ++i)
i.x = *i;
for (T::iterator i = t.begin(), e = t.end(); i != e; ++i)
int k = i.x + *i;
for (T::iterator i = t.begin(), e = t.end(); i != e; ++i)
int k = e.x + *i;
}
void iteratorMethodCalled() {
for (T::iterator i = t.begin(), e = t.end(); i != e; ++i)
i.insert(3);
for (T::iterator i = t.begin(), e = t.end(); i != e; ++i)
if (i != i)
int k = 3;
}
void iteratorOperatorCalled() {
for (T::iterator i = t.begin(), e = t.end(); i != e; ++i)
int k = *(++i);
for (S::iterator i = s.begin(), e = s.end(); i != e; ++i)
MutableVal k = *(++i);
}
void differentContainers() {
T other;
for (T::iterator i = t.begin(), e = other.end(); i != e; ++i)
int k = *i;
for (T::iterator i = other.begin(), e = t.end(); i != e; ++i)
int k = *i;
S otherS;
for (S::iterator i = s.begin(), e = otherS.end(); i != e; ++i)
MutableVal k = *i;
for (S::iterator i = otherS.begin(), e = s.end(); i != e; ++i)
MutableVal k = *i;
}
void wrongIterators() {
T::iterator other;
for (T::iterator i = t.begin(), e = t.end(); i != other; ++i)
int k = *i;
}
struct EvilArrow : U {
// Please, no one ever write code like this.
U* operator->();
};
void differentMemberAccessTypes() {
EvilArrow A;
for (EvilArrow::iterator i = A.begin(), e = A->end(); i != e; ++i)
Val k = *i;
for (EvilArrow::iterator i = A->begin(), e = A.end(); i != e; ++i)
Val k = *i;
}
void f(const T::iterator &it, int);
void f(const T &it, int);
void g(T &it, int);
void iteratorPassedToFunction() {
for (T::iterator i = t.begin(), e = t.end(); i != e; ++i)
f(i, *i);
}
// FIXME: Disallow this except for containers passed by value and/or const
// reference. Or maybe this is correct enough for any container?
void containerPassedToFunction() {
// for (T::iterator i = t.begin(), e = t.end(); i != e; ++i)
// f(t, *i);
// for (T::iterator i = t.begin(), e = t.end(); i != e; ++i)
// g(t, *i);
}
// FIXME: These tests can be removed if this tool ever does enough analysis to
// decide that this is a safe transformation.
// Until then, we don't want it applied.
void iteratorDefinedOutside() {
T::iterator theEnd = t.end();
for (T::iterator i = t.begin(); i != theEnd; ++i)
int k = *i;
T::iterator theBegin = t.begin();
for (T::iterator e = t.end(); theBegin != e; ++theBegin)
int k = *theBegin;
}

View File

@@ -1,64 +0,0 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: loop-convert -A0 . %t.cpp -- -I %S/Inputs
// RUN: FileCheck -input-file=%t.cpp %s
#include "structures.h"
// Single FileCheck line to make sure that no loops are converted.
// CHECK-NOT: for ({{.*[^:]:[^:].*}})
S s;
T t;
U u;
void multipleEnd() {
for (S::iterator i = s.begin(); i != s.end(); ++i)
MutableVal k = *i;
for (T::iterator i = t.begin(); i != t.end(); ++i)
int k = *i;
for (U::iterator i = u.begin(); i != u.end(); ++i)
Val k = *i;
}
void f(X);
void f(S);
void f(T);
void complexContainer() {
X x;
for (S::iterator i = x.s.begin(), e = x.s.end(); i != e; ++i) {
f(x);
MutableVal k = *i;
}
for (T::iterator i = x.t.begin(), e = x.t.end(); i != e; ++i) {
f(x);
int k = *i;
}
for (S::iterator i = x.s.begin(), e = x.s.end(); i != e; ++i) {
f(x.s);
MutableVal k = *i;
}
for (T::iterator i = x.t.begin(), e = x.t.end(); i != e; ++i) {
f(x.t);
int k = *i;
}
for (S::iterator i = x.getS().begin(), e = x.getS().end(); i != e; ++i) {
f(x.getS());
MutableVal k = *i;
}
X exes[5];
int index = 0;
for (S::iterator i = exes[index].getS().begin(),
e = exes[index].getS().end(); i != e; ++i) {
index++;
MutableVal k = *i;
}
}

View File

@@ -1,29 +0,0 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: loop-convert -A1 . %t.cpp -- -I %S/Inputs
// RUN: FileCheck -input-file=%t.cpp %s
#include "structures.h"
// Single FileCheck line to make sure that no loops are converted.
// CHECK-NOT: for ({{.*[^:]:[^:].*}})
const int N = 6;
dependent<int> v;
dependent<int> *pv;
int sum = 0;
// Checks to see that non-const member functions are not called on the container
// object.
// These could be conceivably allowed with a lower required confidence level.
void memberFunctionCalled() {
for (int i = 0; i < v.size(); ++i) {
sum += v[i];
v.foo();
}
for (int i = 0; i < v.size(); ++i) {
sum += v[i];
dependent<int>::iterator it = v.begin();
}
}

View File

@@ -1,129 +0,0 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: loop-convert -A1 . %t.cpp -- -I %S/Inputs
// RUN: FileCheck -input-file=%t.cpp %s
#include "structures.h"
// Single FileCheck line to make sure that no loops are converted.
// CHECK-NOT: for ({{.*[^:]:[^:].*}})
const int N = 6;
dependent<int> v;
dependent<int> *pv;
transparent<dependent<int> > cv;
int sum = 0;
// Checks for the index start and end:
void indexStartAndEnd() {
for (int i = 0; i < v.size() + 1; ++i)
sum += v[i];
for (int i = 0; i < v.size() - 1; ++i)
sum += v[i];
for (int i = 1; i < v.size(); ++i)
sum += v[i];
for (int i = 1; i < v.size(); ++i)
sum += v[i];
for (int i = 0; ; ++i)
sum += (*pv)[i];
}
// Checks for invalid increment steps:
void increment() {
for (int i = 0; i < v.size(); --i)
sum += v[i];
for (int i = 0; i < v.size(); i)
sum += v[i];
for (int i = 0; i < v.size();)
sum += v[i];
for (int i = 0; i < v.size(); i += 2)
sum ++;
}
// Checks to make sure that the index isn't used outside of the container:
void indexUse() {
for (int i = 0; i < v.size(); ++i)
v[i] += 1 + i;
}
// Checks for incorrect loop variables.
void mixedVariables() {
int badIndex;
for (int i = 0; badIndex < v.size(); ++i)
sum += v[i];
for (int i = 0; i < v.size(); ++badIndex)
sum += v[i];
for (int i = 0; badIndex < v.size(); ++badIndex)
sum += v[i];
for (int i = 0; badIndex < v.size(); ++badIndex)
sum += v[badIndex];
}
// Checks for an array indexed in addition to the container.
void multipleArrays() {
int badArr[N];
for (int i = 0; i < v.size(); ++i)
sum += v[i] + badArr[i];
for (int i = 0; i < v.size(); ++i)
sum += badArr[i];
for (int i = 0; i < v.size(); ++i) {
int k = badArr[i];
sum += k + 2;
}
for (int i = 0; i < v.size(); ++i) {
int k = badArr[i];
sum += v[i] + k;
}
}
// Checks for multiple containers being indexed container.
void multipleContainers() {
dependent<int> badArr;
for (int i = 0; i < v.size(); ++i)
sum += v[i] + badArr[i];
for (int i = 0; i < v.size(); ++i)
sum += badArr[i];
for (int i = 0; i < v.size(); ++i) {
int k = badArr[i];
sum += k + 2;
}
for (int i = 0; i < v.size(); ++i) {
int k = badArr[i];
sum += v[i] + k;
}
}
// Check to make sure that dereferenced pointers-to-containers behave nicely
void derefContainer() {
// Note the dependent<T>::operator*() returns another dependent<T>.
// This test makes sure that we don't allow an arbitrary number of *'s.
for (int i = 0; i < pv->size(); ++i)
sum += (**pv).at(i);
for (int i = 0; i < pv->size(); ++i)
sum += (**pv)[i];
}
void wrongEnd() {
int bad;
for (int i = 0, e = v.size(); i < bad; ++i)
sum += v[i];
}

View File

@@ -1,123 +0,0 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: grep -Ev "// *[A-Z-]+:" %S/Inputs/negative-header.h > \
// RUN: %T/negative-header.h
// RUN: loop-convert . %t.cpp -- -I %S/Inputs/
// RUN: FileCheck -input-file=%t.cpp %s
// RUN: FileCheck -input-file=%T/negative-header.h %S/Inputs/negative-header.h
#include "negative-header.h"
#include "structures.h"
// Single FileCheck line to make sure that no loops are converted.
// CHECK-NOT: for ({{.*[^:]:[^:].*}})
const int N = 6;
int arr[N] = {1, 2, 3, 4, 5, 6};
int (*pArr)[N] = &arr;
int sum = 0;
// Checks for the index start and end:
void indexStartAndEnd() {
for (int i = 0; i < N + 1; ++i)
sum += arr[i];
for (int i = 0; i < N - 1; ++i)
sum += arr[i];
for (int i = 1; i < N; ++i)
sum += arr[i];
for (int i = 1; i < N; ++i)
sum += arr[i];
for (int i = 0; ; ++i)
sum += (*pArr)[i];
}
// Checks for invalid increment steps:
void increment() {
for (int i = 0; i < N; --i)
sum += arr[i];
for (int i = 0; i < N; i)
sum += arr[i];
for (int i = 0; i < N;)
sum += arr[i];
for (int i = 0; i < N; i += 2)
sum ++;
}
// Checks to make sure that the index isn't used outside of the array:
void indexUse() {
for (int i = 0; i < N; ++i)
arr[i] += 1 + i;
}
// Check for loops that don't mention arrays
void noArray() {
for (int i = 0; i < N; ++i)
sum += i;
for (int i = 0; i < N; ++i) { }
for (int i = 0; i < N; ++i) ;
}
// Checks for incorrect loop variables.
void mixedVariables() {
int badIndex;
for (int i = 0; badIndex < N; ++i)
sum += arr[i];
for (int i = 0; i < N; ++badIndex)
sum += arr[i];
for (int i = 0; badIndex < N; ++badIndex)
sum += arr[i];
for (int i = 0; badIndex < N; ++badIndex)
sum += arr[badIndex];
}
// Checks for multiple arrays indexed.
void multipleArrays() {
int badArr[N];
for (int i = 0; i < N; ++i)
sum += arr[i] + badArr[i];
for (int i = 0; i < N; ++i) {
int k = badArr[i];
sum += arr[i] + k;
}
}
struct HasArr {
int Arr[N];
Val ValArr[N];
};
struct HasIndirectArr {
HasArr HA;
void implicitThis() {
for (int i = 0; i < N; ++i) {
printf("%d", HA.Arr[i]);
}
for (int i = 0; i < N; ++i) {
printf("%d", HA.ValArr[i].x);
}
}
void explicitThis() {
for (int i = 0; i < N; ++i) {
printf("%d", this->HA.Arr[i]);
}
for (int i = 0; i < N; ++i) {
printf("%d", this->HA.ValArr[i].x);
}
}
};

View File

@@ -1,57 +0,0 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: loop-convert . %t.cpp -- -I %S/Inputs
// RUN: FileCheck -input-file=%t.cpp %s
#include "structures.h"
void f() {
const int N = 10;
const int M = 15;
Val Arr[N];
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j) {
int k = Arr[i].x + Arr[j].x;
// The repeat is there to allow FileCheck to make sure the two variable
// names aren't the same.
int l = Arr[i].x + Arr[j].x;
}
}
// CHECK: for (auto & [[VAR:[a-zA-Z_]+]] : Arr)
// CHECK-NEXT: for (auto & [[INNERVAR:[a-zA-Z_]+]] : Arr)
// CHECK-NEXT: int k = [[VAR]].x + [[INNERVAR]].x;
// CHECK-NOT: int l = [[VAR]].x + [[VAR]].x;
Val Nest[N][M];
for (int i = 0; i < N; ++i) {
for (int j = 0; j < M; ++j) {
printf("Got item %d", Nest[i][j].x);
}
}
// The inner loop is also convertible, but doesn't need to be converted
// immediately. Update this test when that changes!
// CHECK: for (auto & [[VAR:[a-zA-Z_]+]] : Nest)
// CHECK-NEXT: for (int j = 0; j < M; ++j)
// CHECK-NEXT: printf("Got item %d", [[VAR]][j].x);
// Note that the order of M and N are switched for this test.
for (int j = 0; j < M; ++j) {
for (int i = 0; i < N; ++i) {
printf("Got item %d", Nest[i][j].x);
}
}
// CHECK-NOT: for (auto & {{[a-zA-Z_]+}} : Nest[i])
// CHECK: for (int j = 0; j < M; ++j)
// CHECK-NEXT: for (auto & [[VAR:[a-zA-Z_]+]] : Nest)
// CHECK-NEXT: printf("Got item %d", [[VAR]][j].x);
Nested<T> NestT;
for (Nested<T>::iterator I = NestT.begin(), E = NestT.end(); I != E; ++I) {
for (T::iterator TI = (*I).begin(), TE = (*I).end(); TI != TE; ++TI) {
printf("%d", *TI);
}
}
// The inner loop is also convertible, but doesn't need to be converted
// immediately. Update this test when that changes!
// CHECK: for (auto & [[VAR:[a-zA-Z_]+]] : NestT) {
// CHECK-NEXT: for (T::iterator TI = ([[VAR]]).begin(), TE = ([[VAR]]).end(); TI != TE; ++TI) {
// CHECK-NEXT: printf("%d", *TI);
}

View File

@@ -1,21 +0,0 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: not loop-convert . %t.cpp -- -I %S/Inputs
// RUN: FileCheck -input-file=%t.cpp %s
void valid() {
const int arr[5];
int sum = 0;
for (int i = 0; i < 5; ++i) {
sum += arr[i];
}
}
void hasSyntaxError = 3;
// CHECK: void valid() {
// CHECK-NEXT: const int arr[5];
// CHECK-NEXT: int sum = 0;
// CHECK-NEXT: for (int i = 0; i < 5; ++i) {
// CHECK-NEXT: sum += arr[i];
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: void hasSyntaxError = 3;

View File

@@ -1,66 +0,0 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: loop-convert . %t.cpp -- -I %S/Inputs
// RUN: FileCheck -input-file=%t.cpp %s
#include "structures.h"
const int N = 6;
dependent<int> v;
dependent<int> *pv;
transparent<dependent<int> > cv;
void f() {
int sum = 0;
for (int i = 0, e = v.size(); i < e; ++i) {
printf("Fibonacci number is %d\n", v[i]);
sum += v[i] + 2;
}
// CHECK: for (auto & [[VAR:[a-z_]+]] : v)
// CHECK-NEXT: printf("Fibonacci number is %d\n", [[VAR]]);
// CHECK-NEXT: sum += [[VAR]] + 2;
for (int i = 0, e = v.size(); i < e; ++i) {
printf("Fibonacci number is %d\n", v.at(i));
sum += v.at(i) + 2;
}
// CHECK: for (auto & [[VAR:[a-z_]+]] : v)
// CHECK-NEXT: printf("Fibonacci number is %d\n", [[VAR]]);
// CHECK-NEXT: sum += [[VAR]] + 2;
for (int i = 0, e = pv->size(); i < e; ++i) {
printf("Fibonacci number is %d\n", pv->at(i));
sum += pv->at(i) + 2;
}
// CHECK: for (auto & [[VAR:[a-z_]+]] : *pv)
// CHECK-NEXT: printf("Fibonacci number is %d\n", [[VAR]]);
// CHECK-NEXT: sum += [[VAR]] + 2;
// This test will fail if size() isn't called repeatedly, since it
// returns unsigned int, and 0 is deduced to be signed int.
// FIXME: Insert the necessary explicit conversion, or write out the types
// explicitly.
for (int i = 0; i < pv->size(); ++i) {
printf("Fibonacci number is %d\n", (*pv).at(i));
sum += (*pv)[i] + 2;
}
// CHECK: for (auto & [[VAR:[a-z_]+]] : *pv)
// CHECK-NEXT: printf("Fibonacci number is %d\n", [[VAR]]);
// CHECK-NEXT: sum += [[VAR]] + 2;
for (int i = 0; i < cv->size(); ++i) {
printf("Fibonacci number is %d\n", cv->at(i));
sum += cv->at(i) + 2;
}
// CHECK: for (auto & [[VAR:[a-z_]+]] : *cv)
// CHECK-NEXT: printf("Fibonacci number is %d\n", [[VAR]]);
// CHECK-NEXT: sum += [[VAR]] + 2;
}
// Check for loops that don't mention containers
void noContainer() {
for (auto i = 0; i < v.size(); ++i) { }
// CHECK: for (auto & [[VAR:[a-z_]+]] : v) { }
for (auto i = 0; i < v.size(); ++i) ;
// CHECK: for (auto & [[VAR:[a-z_]+]] : v) ;
}

View File

@@ -1,115 +0,0 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: loop-convert . %t.cpp -- -I %S/Inputs
// RUN: FileCheck -input-file=%t.cpp %s
#include "structures.h"
void complexContainer() {
X exes[5];
int index = 0;
for (S::iterator i = exes[index].getS().begin(), e = exes[index].getS().end(); i != e; ++i) {
MutableVal k = *i;
MutableVal j = *i;
}
// CHECK: for ({{[a-zA-Z_ ]+&? ?}}[[VAR:[a-z_]+]] : exes[index].getS())
// CHECK-NEXT: MutableVal k = [[VAR]];
// CHECK-NEXT: MutableVal j = [[VAR]];
}
void f() {
/// begin()/end() - based for loops here:
T t;
for (T::iterator it = t.begin(); it != t.end(); ++it) {
printf("I found %d\n", *it);
}
// CHECK: for ({{[a-zA-Z_ ]+&? ?}}[[VAR:[a-z_]+]] : t)
// CHECK-NEXT: printf("I found %d\n", [[VAR]]);
T *pt;
for (T::iterator it = pt->begin(); it != pt->end(); ++it) {
printf("I found %d\n", *it);
}
// CHECK: for ({{[a-zA-Z_ ]+&? ?}}[[VAR:[a-z_]+]] : *pt)
// CHECK-NEXT: printf("I found %d\n", [[VAR]]);
S s;
for (S::const_iterator it = s.begin(); it != s.end(); ++it) {
printf("s has value %d\n", (*it).x);
}
// CHECK: for ({{[a-zA-Z_ ]*&? ?}}[[VAR:[a-z_]+]] : s)
// CHECK-NEXT: printf("s has value %d\n", ([[VAR]]).x);
S *ps;
for (S::const_iterator it = ps->begin(); it != ps->end(); ++it) {
printf("s has value %d\n", (*it).x);
}
// CHECK: for ({{[a-zA-Z_ ]*&? ?}}[[VAR:[a-z_]+]] : *ps)
// CHECK-NEXT: printf("s has value %d\n", ([[VAR]]).x);
for (S::const_iterator it = s.begin(); it != s.end(); ++it) {
printf("s has value %d\n", it->x);
}
// CHECK: for ({{[a-zA-Z_ ]*&? ?}}[[VAR:[a-z_]+]] : s)
// CHECK-NEXT: printf("s has value %d\n", [[VAR]].x);
for (S::iterator it = s.begin(); it != s.end(); ++it) {
it->x = 3;
}
// CHECK: for ({{[a-zA-Z_ ]*&? ?}}[[VAR:[a-z_]+]] : s)
// CHECK-NEXT: [[VAR]].x = 3;
for (S::iterator it = s.begin(); it != s.end(); ++it) {
(*it).x = 3;
}
// CHECK: for ({{[a-zA-Z_ ]*&? ?}}[[VAR:[a-z_]+]] : s)
// CHECK-NEXT: ([[VAR]]).x = 3;
for (S::iterator it = s.begin(); it != s.end(); ++it) {
it->nonConstFun(4, 5);
}
// CHECK: for ({{[a-zA-Z_ ]*&? ?}}[[VAR:[a-z_]+]] : s)
// CHECK-NEXT: [[VAR]].nonConstFun(4, 5);
U u;
for (U::iterator it = u.begin(); it != u.end(); ++it) {
printf("s has value %d\n", it->x);
}
// CHECK: for ({{[a-zA-Z_ ]*&? ?}}[[VAR:[a-z_]+]] : u)
// CHECK-NEXT: printf("s has value %d\n", [[VAR]].x);
for (U::iterator it = u.begin(); it != u.end(); ++it) {
printf("s has value %d\n", (*it).x);
}
// CHECK: for ({{[a-zA-Z_ ]*&? ?}}[[VAR:[a-z_]+]] : u)
// CHECK-NEXT: printf("s has value %d\n", ([[VAR]]).x);
U::iterator A;
for (U::iterator i = u.begin(); i != u.end(); ++i)
int k = A->x + i->x;
// CHECK: for ({{[a-zA-Z_ ]*&? ?}}[[VAR:[a-z_]+]] : u)
// CHECK-NEXT: int k = A->x + [[VAR]].x;
dependent<int> v;
for (dependent<int>::const_iterator it = v.begin();
it != v.end(); ++it) {
printf("Fibonacci number is %d\n", *it);
}
// CHECK: for ({{[a-zA-Z_ ]*&? ?}}[[VAR:[a-z_]+]] : v)
// CHECK-NEXT: printf("Fibonacci number is %d\n", [[VAR]]);
for (dependent<int>::const_iterator it(v.begin());
it != v.end(); ++it) {
printf("Fibonacci number is %d\n", *it);
}
// CHECK: for ({{[a-zA-Z_ ]*&? ?}}[[VAR:[a-z_]+]] : v)
// CHECK-NEXT: printf("Fibonacci number is %d\n", [[VAR]]);
doublyDependent<int,int> intmap;
for (doublyDependent<int,int>::iterator it = intmap.begin();
it != intmap.end(); ++it) {
printf("intmap[%d] = %d", it->first, it->second);
}
// CHECK: for ({{[a-zA-Z_ ]*&? ?}}[[VAR:[a-z_]+]] : intmap)
// CHECK-NEXT: printf("intmap[%d] = %d", [[VAR]].first, [[VAR]].second);
}

View File

@@ -1,34 +0,0 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: remove-cstr-calls . %t.cpp --
// RUN: FileCheck -input-file=%t.cpp %s
// REQUIRES: shell
namespace std {
template<typename T> class allocator {};
template<typename T> class char_traits {};
template<typename C, typename T, typename A> struct basic_string {
basic_string();
basic_string(const C *p, const A& a = A());
const C *c_str() const;
};
typedef basic_string<char, std::char_traits<char>, std::allocator<char> > string;
}
namespace llvm { struct StringRef { StringRef(const char *p); }; }
void f1(const std::string &s) {
f1(s.c_str());
// CHECK: void f1
// CHECK-NEXT: f1(s)
}
void f2(const llvm::StringRef r) {
std::string s;
f2(s.c_str());
// CHECK: std::string s;
// CHECK-NEXT: f2(s)
}
void f3(const llvm::StringRef &r) {
std::string s;
f3(s.c_str());
// CHECK: std::string s;
// CHECK: f3(s)
}

View File

@@ -1,6 +0,0 @@
add_clang_executable(tool-template
ToolTemplate.cpp
)
target_link_libraries(tool-template
clangEdit clangTooling clangBasic clangAST clangASTMatchers)

View File

@@ -1,25 +0,0 @@
##===-------- tools/toolTemplate/Makefile ------------------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##
CLANG_LEVEL := ../../..
TOOLNAME = tool-template
NO_INSTALL = 1
# No plugins, optimize startup time.
TOOL_NO_EXPORTS = 1
include $(CLANG_LEVEL)/../../Makefile.config
LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser support mc
USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \
clangRewriteFrontend.a clangRewriteCore.a \
clangParse.a clangSema.a clangAnalysis.a \
clangAST.a clangASTMatchers.a clangEdit.a clangLex.a clangBasic.a
include $(CLANG_LEVEL)/Makefile

View File

@@ -1,106 +0,0 @@
//===---- tools/extra/ToolTemplate.cpp - Template for refactoring tool ----===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements an empty refactoring tool using the clang tooling.
// The goal is to lower the "barrier to entry" for writing refactoring tools.
//
// Usage:
// tool-template <cmake-output-dir> <file1> <file2> ...
//
// Where <cmake-output-dir> is a CMake build directory in which a file named
// compile_commands.json exists (enable -DCMAKE_EXPORT_COMPILE_COMMANDS in
// CMake to get this output).
//
// <file1> ... specify the paths of files in the CMake source tree. This path
// is looked up in the compile command database. If the path of a file is
// absolute, it needs to point into CMake's source tree. If the path is
// relative, the current working directory needs to be in the CMake source
// tree and the file must be in a subdirectory of the current working
// directory. "./" prefixes in the relative files will be automatically
// removed, but the rest of a relative path must be a suffix of a path in
// the compile command line database.
//
// For example, to use tool-template on all files in a subtree of the
// source tree, use:
//
// /path/in/subtree $ find . -name '*.cpp'|
// xargs tool-template /path/to/build
//
//===----------------------------------------------------------------------===//
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Lex/Lexer.h"
#include "clang/Tooling/CompilationDatabase.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MemoryBuffer.h"
using namespace clang;
using namespace clang::ast_matchers;
using namespace clang::tooling;
using namespace llvm;
namespace {
class ToolTemplateCallback : public MatchFinder::MatchCallback {
public:
ToolTemplateCallback(Replacements *Replace) : Replace(Replace) {}
virtual void run(const MatchFinder::MatchResult &Result) {
// TODO: This routine will get called for each thing that the matchers find.
// At this point, you can examine the match, and do whatever you want,
// including replacing the matched text with other text
(void) Replace; // This to prevent an "unused member variable" warning;
}
private:
Replacements *Replace;
};
} // end anonymous namespace
// Set up the command line options
cl::opt<std::string> BuildPath(
cl::Positional,
cl::desc("<build-path>"));
cl::list<std::string> SourcePaths(
cl::Positional,
cl::desc("<source0> [... <sourceN>]"),
cl::OneOrMore);
int main(int argc, const char **argv) {
llvm::OwningPtr<CompilationDatabase> Compilations(
FixedCompilationDatabase::loadFromCommandLine(argc, argv));
cl::ParseCommandLineOptions(argc, argv);
if (!Compilations) { // Couldn't find a compilation DB from the command line
std::string ErrorMessage;
Compilations.reset(
!BuildPath.empty() ?
CompilationDatabase::autoDetectFromDirectory(BuildPath, ErrorMessage) :
CompilationDatabase::autoDetectFromSource(SourcePaths[0], ErrorMessage)
);
// Still no compilation DB? - bail.
if (!Compilations)
llvm::report_fatal_error(ErrorMessage);
}
RefactoringTool Tool(*Compilations, SourcePaths);
ast_matchers::MatchFinder Finder;
ToolTemplateCallback Callback(&Tool.getReplacements());
// TODO: Put your matchers here.
// Use Finder.addMatcher(...) to define the patterns in the AST that you
// want to match against. You are not limited to just one matcher!
return Tool.run(newFrontendActionFactory(&Finder));
}

32
clang/.gitignore vendored
View File

@@ -1,32 +0,0 @@
#==============================================================================#
# This file specifies intentionally untracked files that git should ignore.
# See: http://www.kernel.org/pub/software/scm/git/docs/gitignore.html
#
# This file is intentionally different from the output of `git svn show-ignore`,
# as most of those are useless.
#==============================================================================#
#==============================================================================#
# File extensions to be ignored anywhere in the tree.
#==============================================================================#
# Temp files created by most text editors.
*~
# Merge files created by git.
*.orig
# Byte compiled python modules.
*.pyc
# vim swap files
.*.swp
.sw?
#==============================================================================#
# Explicit files to ignore (only matches one).
#==============================================================================#
cscope.files
cscope.out
#==============================================================================#
# Directories to ignore (do not add trailing '/'s, they skip symlinks).
#==============================================================================#
# Clang extra user tools, which is tracked independently (clang-tools-extra).
tools/extra

View File

@@ -1,295 +0,0 @@
# If we are not building as a part of LLVM, build Clang as an
# standalone project, using LLVM as an external library:
if( CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR )
project(Clang)
cmake_minimum_required(VERSION 2.8)
set(CLANG_PATH_TO_LLVM_SOURCE "" CACHE PATH
"Path to LLVM source code. Not necessary if using an installed LLVM.")
set(CLANG_PATH_TO_LLVM_BUILD "" CACHE PATH
"Path to the directory where LLVM was built or installed.")
if( CLANG_PATH_TO_LLVM_SOURCE )
if( NOT EXISTS "${CLANG_PATH_TO_LLVM_SOURCE}/cmake/config-ix.cmake" )
message(FATAL_ERROR "Please set CLANG_PATH_TO_LLVM_SOURCE to the root directory of LLVM source code.")
else()
get_filename_component(LLVM_MAIN_SRC_DIR ${CLANG_PATH_TO_LLVM_SOURCE}
ABSOLUTE)
list(APPEND CMAKE_MODULE_PATH "${LLVM_MAIN_SRC_DIR}/cmake/modules")
endif()
endif()
if( NOT EXISTS "${CLANG_PATH_TO_LLVM_BUILD}/bin/llvm-tblgen${CMAKE_EXECUTABLE_SUFFIX}" )
# Looking for bin/Debug/llvm-tblgen is a complete hack. How can we get
# around this?
if( NOT EXISTS "${CLANG_PATH_TO_LLVM_BUILD}/bin/Debug/llvm-tblgen${CMAKE_EXECUTABLE_SUFFIX}" )
message(FATAL_ERROR "Please set CLANG_PATH_TO_LLVM_BUILD to a directory containing a LLVM build.")
endif()
endif()
list(APPEND CMAKE_MODULE_PATH "${CLANG_PATH_TO_LLVM_BUILD}/share/llvm/cmake")
get_filename_component(PATH_TO_LLVM_BUILD ${CLANG_PATH_TO_LLVM_BUILD}
ABSOLUTE)
include(AddLLVM)
include(TableGen)
include("${CLANG_PATH_TO_LLVM_BUILD}/share/llvm/cmake/LLVMConfig.cmake")
include(HandleLLVMOptions)
set(PACKAGE_VERSION "${LLVM_PACKAGE_VERSION}")
set(LLVM_MAIN_INCLUDE_DIR "${LLVM_MAIN_SRC_DIR}/include")
set(LLVM_BINARY_DIR ${CMAKE_BINARY_DIR})
set(CMAKE_INCLUDE_CURRENT_DIR ON)
include_directories("${PATH_TO_LLVM_BUILD}/include" "${LLVM_MAIN_INCLUDE_DIR}")
link_directories("${PATH_TO_LLVM_BUILD}/lib")
if( EXISTS "${CLANG_PATH_TO_LLVM_BUILD}/bin/llvm-tblgen${CMAKE_EXECUTABLE_SUFFIX}" )
set(LLVM_TABLEGEN_EXE "${PATH_TO_LLVM_BUILD}/bin/llvm-tblgen${CMAKE_EXECUTABLE_SUFFIX}")
else()
# FIXME: This is an utter hack.
set(LLVM_TABLEGEN_EXE "${PATH_TO_LLVM_BUILD}/bin/Debug/llvm-tblgen${CMAKE_EXECUTABLE_SUFFIX}")
endif()
# Define the default arguments to use with 'lit', and an option for the user
# to override.
set(LIT_ARGS_DEFAULT "-sv")
if (MSVC OR XCODE)
set(LIT_ARGS_DEFAULT "${LIT_ARGS_DEFAULT} --no-progress-bar")
endif()
set(LLVM_LIT_ARGS "${LIT_ARGS_DEFAULT}" CACHE STRING "Default options for lit")
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin )
set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
set( CLANG_BUILT_STANDALONE 1 )
endif()
set(CLANG_RESOURCE_DIR "" CACHE STRING
"Relative directory from the Clang binary to its resource files.")
set(C_INCLUDE_DIRS "" CACHE STRING
"Colon separated list of directories clang will search for headers.")
set(GCC_INSTALL_PREFIX "" CACHE PATH "Directory where gcc is installed." )
set(DEFAULT_SYSROOT "" CACHE PATH
"Default <path> to all compiler invocations for --sysroot=<path>." )
set(CLANG_VENDOR "" CACHE STRING
"Vendor-specific text for showing with version information.")
if( CLANG_VENDOR )
add_definitions( -DCLANG_VENDOR="${CLANG_VENDOR} " )
endif()
set(CLANG_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(CLANG_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
if( CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR AND NOT MSVC_IDE )
message(FATAL_ERROR "In-source builds are not allowed. CMake would overwrite "
"the makefiles distributed with LLVM. Please create a directory and run cmake "
"from there, passing the path to this source directory as the last argument. "
"This process created the file `CMakeCache.txt' and the directory "
"`CMakeFiles'. Please delete them.")
endif()
if( NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR )
file(GLOB_RECURSE
tablegenned_files_on_include_dir
"${CLANG_SOURCE_DIR}/include/clang/*.inc")
if( tablegenned_files_on_include_dir )
message(FATAL_ERROR "Apparently there is a previous in-source build, "
"probably as the result of running `configure' and `make' on "
"${CLANG_SOURCE_DIR}. This may cause problems. The suspicious files are:\n"
"${tablegenned_files_on_include_dir}\nPlease clean the source directory.")
endif()
endif()
# Compute the Clang version from the LLVM version.
string(REGEX MATCH "[0-9]+\\.[0-9]+(\\.[0-9]+)?" CLANG_VERSION
${PACKAGE_VERSION})
message(STATUS "Clang version: ${CLANG_VERSION}")
string(REGEX REPLACE "([0-9]+)\\.[0-9]+(\\.[0-9]+)?" "\\1" CLANG_VERSION_MAJOR
${CLANG_VERSION})
string(REGEX REPLACE "[0-9]+\\.([0-9]+)(\\.[0-9]+)?" "\\1" CLANG_VERSION_MINOR
${CLANG_VERSION})
if (${CLANG_VERSION} MATCHES "[0-9]+\\.[0-9]+\\.[0-9]+")
set(CLANG_HAS_VERSION_PATCHLEVEL 1)
string(REGEX REPLACE "[0-9]+\\.[0-9]+\\.([0-9]+)" "\\1" CLANG_VERSION_PATCHLEVEL
${CLANG_VERSION})
else()
set(CLANG_HAS_VERSION_PATCHLEVEL 0)
endif()
# Configure the Version.inc file.
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/include/clang/Basic/Version.inc.in
${CMAKE_CURRENT_BINARY_DIR}/include/clang/Basic/Version.inc)
# Add appropriate flags for GCC
if (LLVM_COMPILER_IS_GCC_COMPATIBLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-common -Woverloaded-virtual -Wcast-qual -fno-strict-aliasing -pedantic -Wno-long-long -Wall -W -Wno-unused-parameter -Wwrite-strings")
endif ()
if (APPLE)
set(CMAKE_MODULE_LINKER_FLAGS "-Wl,-flat_namespace -Wl,-undefined -Wl,suppress")
endif ()
# libxml2 is an optional dependency, required only to run validation
# tests on XML output.
find_package(LibXml2)
configure_file(
${CLANG_SOURCE_DIR}/include/clang/Config/config.h.cmake
${CLANG_BINARY_DIR}/include/clang/Config/config.h)
include(LLVMParseArguments)
function(clang_tablegen)
# Syntax:
# clang_tablegen output-file [tablegen-arg ...] SOURCE source-file
# [[TARGET cmake-target-name] [DEPENDS extra-dependency ...]]
#
# Generates a custom command for invoking tblgen as
#
# tblgen source-file -o=output-file tablegen-arg ...
#
# and, if cmake-target-name is provided, creates a custom target for
# executing the custom command depending on output-file. It is
# possible to list more files to depend after DEPENDS.
parse_arguments( CTG "SOURCE;TARGET;DEPENDS" "" ${ARGN} )
if( NOT CTG_SOURCE )
message(FATAL_ERROR "SOURCE source-file required by clang_tablegen")
endif()
set( LLVM_TARGET_DEFINITIONS ${CTG_SOURCE} )
tablegen( CLANG ${CTG_DEFAULT_ARGS} )
list( GET CTG_DEFAULT_ARGS 0 output_file )
if( CTG_TARGET )
add_custom_target( ${CTG_TARGET} DEPENDS ${output_file} ${CTG_DEPENDS} )
set_target_properties( ${CTG_TARGET} PROPERTIES FOLDER "Clang tablegenning")
endif()
endfunction(clang_tablegen)
macro(add_clang_library name)
llvm_process_sources(srcs ${ARGN})
if(MSVC_IDE OR XCODE)
# Add public headers
file(RELATIVE_PATH lib_path
${CLANG_SOURCE_DIR}/lib/
${CMAKE_CURRENT_SOURCE_DIR}
)
if(NOT lib_path MATCHES "^[.][.]")
file( GLOB_RECURSE headers
${CLANG_SOURCE_DIR}/include/clang/${lib_path}/*.h
${CLANG_SOURCE_DIR}/include/clang/${lib_path}/*.def
)
set_source_files_properties(${headers} PROPERTIES HEADER_FILE_ONLY ON)
file( GLOB_RECURSE tds
${CLANG_SOURCE_DIR}/include/clang/${lib_path}/*.td
)
source_group("TableGen descriptions" FILES ${tds})
set_source_files_properties(${tds}} PROPERTIES HEADER_FILE_ONLY ON)
set(srcs ${srcs} ${headers} ${tds})
endif()
endif(MSVC_IDE OR XCODE)
if (MODULE)
set(libkind MODULE)
elseif (SHARED_LIBRARY)
set(libkind SHARED)
else()
set(libkind)
endif()
add_library( ${name} ${libkind} ${srcs} )
if( LLVM_COMMON_DEPENDS )
add_dependencies( ${name} ${LLVM_COMMON_DEPENDS} )
endif( LLVM_COMMON_DEPENDS )
llvm_config( ${name} ${LLVM_LINK_COMPONENTS} )
target_link_libraries( ${name} ${LLVM_COMMON_LIBS} )
link_system_libs( ${name} )
install(TARGETS ${name}
LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX}
ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX}
RUNTIME DESTINATION bin)
set_target_properties(${name} PROPERTIES FOLDER "Clang libraries")
endmacro(add_clang_library)
macro(add_clang_executable name)
add_llvm_executable( ${name} ${ARGN} )
set_target_properties(${name} PROPERTIES FOLDER "Clang executables")
endmacro(add_clang_executable)
include_directories(BEFORE
${CMAKE_CURRENT_BINARY_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/include
)
install(DIRECTORY include/
DESTINATION include
FILES_MATCHING
PATTERN "*.def"
PATTERN "*.h"
PATTERN "config.h" EXCLUDE
PATTERN ".svn" EXCLUDE
)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/
DESTINATION include
FILES_MATCHING
PATTERN "CMakeFiles" EXCLUDE
PATTERN "*.inc"
)
add_definitions( -D_GNU_SOURCE )
# Clang version information
set(CLANG_EXECUTABLE_VERSION
"${CLANG_VERSION_MAJOR}.${CLANG_VERSION_MINOR}" CACHE STRING
"Version number that will be placed into the clang executable, in the form XX.YY")
set(LIBCLANG_LIBRARY_VERSION
"${CLANG_VERSION_MAJOR}.${CLANG_VERSION_MINOR}" CACHE STRING
"Version number that will be placed into the libclang library , in the form XX.YY")
mark_as_advanced(CLANG_EXECUTABLE_VERSION LIBCLANG_LIBRARY_VERSION)
add_subdirectory(utils/TableGen)
add_subdirectory(include)
add_subdirectory(lib)
add_subdirectory(tools)
add_subdirectory(runtime)
option(CLANG_BUILD_EXAMPLES "Build CLANG example programs by default." OFF)
add_subdirectory(examples)
# TODO: docs.
add_subdirectory(test)
if( LLVM_INCLUDE_TESTS )
if( NOT CLANG_BUILT_STANDALONE )
add_subdirectory(unittests)
endif()
endif()
# Workaround for MSVS10 to avoid the Dialog Hell
# FIXME: This could be removed with future version of CMake.
if( CLANG_BUILT_STANDALONE AND MSVC_VERSION EQUAL 1600 )
set(CLANG_SLN_FILENAME "${CMAKE_CURRENT_BINARY_DIR}/Clang.sln")
if( EXISTS "${CLANG_SLN_FILENAME}" )
file(APPEND "${CLANG_SLN_FILENAME}" "\n# This should be regenerated!\n")
endif()
endif()
set(BUG_REPORT_URL "http://llvm.org/bugs/" CACHE STRING
"Default URL where bug reports are to be submitted.")

View File

@@ -1,2 +0,0 @@
#import <Cocoa/Cocoa.h>

View File

@@ -1,86 +0,0 @@
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cctype>
#include <cerrno>
#include <cfloat>
#include <ciso646>
#include <climits>
#include <clocale>
#include <cmath>
#include <complex>
#include <csetjmp>
#include <csignal>
#include <cstdarg>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cwchar>
#include <cwctype>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <iterator>
#include <limits>
#include <list>
#include <locale>
#include <map>
#include <memory>
#include <new>
#include <numeric>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#if __has_include(<strstream>)
#include <strstream>
#endif
#include <typeinfo>
#include <utility>
#include <valarray>
#include <vector>
#if __cplusplus >= 201103 || defined(__GXX_EXPERIMENTAL_CXX0X__)
#include <array>
#if __has_include(<atomic>)
#include <atomic>
#endif
#include <chrono>
#if __has_include(<codecvt>)
#include <codecvt>
#endif
#include <condition_variable>
#include <forward_list>
#if __has_include(<future>)
#include <future>
#endif
#include <initializer_list>
#include <mutex>
#include <random>
#include <ratio>
#include <regex>
#if __has_include(<scoped_allocator>)
#include <scoped_allocator>
#endif
#include <system_error>
#include <thread>
#include <tuple>
#include <type_traits>
#if __has_include(<typeindex>)
#include <typeindex>
#endif
#include <unordered_map>
#include <unordered_set>
#endif

View File

@@ -1,639 +0,0 @@
/* Test for integer constant types. */
/* Origin: Joseph Myers <jsm28@cam.ac.uk>. */
/* { dg-do compile } */
/* { dg-options "-std=iso9899:1999 -pedantic-errors" } */
#include <limits.h>
/* Assertion that constant C is of type T. */
#define ASSERT_CONST_TYPE(C, T) \
do { \
typedef T type; \
typedef type **typepp; \
typedef __typeof__((C)) ctype; \
typedef ctype **ctypepp; \
typepp x = 0; \
ctypepp y = 0; \
x = y; \
y = x; \
} while (0)
/* (T *) if E is zero, (void *) otherwise. */
#define type_if_not(T, E) __typeof__(0 ? (T *)0 : (void *)(E))
/* (T *) if E is nonzero, (void *) otherwise. */
#define type_if(T, E) type_if_not(T, !(E))
/* Combine pointer types, all but one (void *). */
#define type_comb2(T1, T2) __typeof__(0 ? (T1)0 : (T2)0)
#define type_comb3(T1, T2, T3) type_comb2(T1, type_comb2(T2, T3))
#define type_comb4(T1, T2, T3, T4) \
type_comb2(T1, type_comb2(T2, type_comb2(T3, T4)))
#define type_comb6(T1, T2, T3, T4, T5, T6) \
type_comb2(T1, \
type_comb2(T2, \
type_comb2(T3, \
type_comb2(T4, \
type_comb2(T5, T6)))))
/* (T1 *) if E1, otherwise (T2 *) if E2. */
#define first_of2p(T1, E1, T2, E2) type_comb2(type_if(T1, (E1)), \
type_if(T2, (!(E1) && (E2))))
/* (T1 *) if E1, otherwise (T2 *) if E2, otherwise (T3 *) if E3. */
#define first_of3p(T1, E1, T2, E2, T3, E3) \
type_comb3(type_if(T1, (E1)), \
type_if(T2, (!(E1) && (E2))), \
type_if(T3, (!(E1) && !(E2) && (E3))))
/* (T1 *) if E1, otherwise (T2 *) if E2, otherwise (T3 *) if E3, otherwise
(T4 *) if E4. */
#define first_of4p(T1, E1, T2, E2, T3, E3, T4, E4) \
type_comb4(type_if(T1, (E1)), \
type_if(T2, (!(E1) && (E2))), \
type_if(T3, (!(E1) && !(E2) && (E3))), \
type_if(T4, (!(E1) && !(E2) && !(E3) && (E4))))
/* (T1 *) if E1, otherwise (T2 *) if E2, otherwise (T3 *) if E3, otherwise
(T4 *) if E4, otherwise (T5 *) if E5, otherwise (T6 *) if E6. */
#define first_of6p(T1, E1, T2, E2, T3, E3, T4, E4, T5, E5, T6, E6) \
type_comb6(type_if(T1, (E1)), \
type_if(T2, (!(E1) && (E2))), \
type_if(T3, (!(E1) && !(E2) && (E3))), \
type_if(T4, (!(E1) && !(E2) && !(E3) && (E4))), \
type_if(T5, (!(E1) && !(E2) && !(E3) && !(E4) && (E5))), \
type_if(T6, (!(E1) && !(E2) && !(E3) \
&& !(E4) && !(E5) && (E6))))
/* Likewise, but return the original type rather than a pointer type. */
#define first_of2(T1, E1, T2, E2) \
__typeof__(*((first_of2p(T1, (E1), T2, (E2)))0))
#define first_of3(T1, E1, T2, E2, T3, E3) \
__typeof__(*((first_of3p(T1, (E1), T2, (E2), T3, (E3)))0))
#define first_of4(T1, E1, T2, E2, T3, E3, T4, E4) \
__typeof__(*((first_of4p(T1, (E1), T2, (E2), T3, (E3), T4, (E4)))0))
#define first_of6(T1, E1, T2, E2, T3, E3, T4, E4, T5, E5, T6, E6) \
__typeof__(*((first_of6p(T1, (E1), T2, (E2), T3, (E3), \
T4, (E4), T5, (E5), T6, (E6)))0))
/* Types of constants according to the C99 rules. */
#define C99_UNSUF_DEC_TYPE(C) \
first_of3(int, (C) <= INT_MAX, \
long int, (C) <= LONG_MAX, \
long long int, (C) <= LLONG_MAX)
#define C99_UNSUF_OCTHEX_TYPE(C) \
first_of6(int, (C) <= INT_MAX, \
unsigned int, (C) <= UINT_MAX, \
long int, (C) <= LONG_MAX, \
unsigned long int, (C) <= ULONG_MAX, \
long long int, (C) <= LLONG_MAX, \
unsigned long long int, (C) <= ULLONG_MAX)
#define C99_SUFu_TYPE(C) \
first_of3(unsigned int, (C) <= UINT_MAX, \
unsigned long int, (C) <= ULONG_MAX, \
unsigned long long int, (C) <= ULLONG_MAX)
#define C99_SUFl_DEC_TYPE(C) \
first_of2(long int, (C) <= LONG_MAX, \
long long int, (C) <= LLONG_MAX)
#define C99_SUFl_OCTHEX_TYPE(C) \
first_of4(long int, (C) <= LONG_MAX, \
unsigned long int, (C) <= ULONG_MAX, \
long long int, (C) <= LLONG_MAX, \
unsigned long long int, (C) <= ULLONG_MAX)
#define C99_SUFul_TYPE(C) \
first_of2(unsigned long int, (C) <= ULONG_MAX, \
unsigned long long int, (C) <= ULLONG_MAX)
#define C99_SUFll_OCTHEX_TYPE(C) \
first_of2(long long int, (C) <= LLONG_MAX, \
unsigned long long int, (C) <= ULLONG_MAX)
/* Checks that constants have correct type. */
#define CHECK_UNSUF_DEC_TYPE(C) ASSERT_CONST_TYPE((C), C99_UNSUF_DEC_TYPE((C)))
#define CHECK_UNSUF_OCTHEX_TYPE(C) \
ASSERT_CONST_TYPE((C), C99_UNSUF_OCTHEX_TYPE((C)))
#define CHECK_SUFu_TYPE(C) ASSERT_CONST_TYPE((C), C99_SUFu_TYPE((C)))
#define CHECK_SUFl_DEC_TYPE(C) ASSERT_CONST_TYPE((C), C99_SUFl_DEC_TYPE((C)))
#define CHECK_SUFl_OCTHEX_TYPE(C) \
ASSERT_CONST_TYPE((C), C99_SUFl_OCTHEX_TYPE((C)))
#define CHECK_SUFul_TYPE(C) ASSERT_CONST_TYPE((C), C99_SUFul_TYPE((C)))
#define CHECK_SUFll_DEC_TYPE(C) ASSERT_CONST_TYPE((C), long long int)
#define CHECK_SUFll_OCTHEX_TYPE(C) \
ASSERT_CONST_TYPE((C), C99_SUFll_OCTHEX_TYPE((C)))
#define CHECK_SUFull_TYPE(C) ASSERT_CONST_TYPE((C), unsigned long long int)
/* Check a decimal value, with all suffixes. */
#define CHECK_DEC_CONST(C) \
CHECK_UNSUF_DEC_TYPE(C); \
CHECK_SUFu_TYPE(C##u); \
CHECK_SUFu_TYPE(C##U); \
CHECK_SUFl_DEC_TYPE(C##l); \
CHECK_SUFl_DEC_TYPE(C##L); \
CHECK_SUFul_TYPE(C##ul); \
CHECK_SUFul_TYPE(C##uL); \
CHECK_SUFul_TYPE(C##Ul); \
CHECK_SUFul_TYPE(C##UL); \
CHECK_SUFll_DEC_TYPE(C##ll); \
CHECK_SUFll_DEC_TYPE(C##LL); \
CHECK_SUFull_TYPE(C##ull); \
CHECK_SUFull_TYPE(C##uLL); \
CHECK_SUFull_TYPE(C##Ull); \
CHECK_SUFull_TYPE(C##ULL);
/* Check an octal or hexadecimal value, with all suffixes. */
#define CHECK_OCTHEX_CONST(C) \
CHECK_UNSUF_OCTHEX_TYPE(C); \
CHECK_SUFu_TYPE(C##u); \
CHECK_SUFu_TYPE(C##U); \
CHECK_SUFl_OCTHEX_TYPE(C##l); \
CHECK_SUFl_OCTHEX_TYPE(C##L); \
CHECK_SUFul_TYPE(C##ul); \
CHECK_SUFul_TYPE(C##uL); \
CHECK_SUFul_TYPE(C##Ul); \
CHECK_SUFul_TYPE(C##UL); \
CHECK_SUFll_OCTHEX_TYPE(C##ll); \
CHECK_SUFll_OCTHEX_TYPE(C##LL); \
CHECK_SUFull_TYPE(C##ull); \
CHECK_SUFull_TYPE(C##uLL); \
CHECK_SUFull_TYPE(C##Ull); \
CHECK_SUFull_TYPE(C##ULL);
#define CHECK_OCT_CONST(C) CHECK_OCTHEX_CONST(C)
#define CHECK_HEX_CONST(C) \
CHECK_OCTHEX_CONST(0x##C); \
CHECK_OCTHEX_CONST(0X##C);
/* True iff "long long" is at least B bits. This presumes that (B-2)/3 is at
most 63. */
#define LLONG_AT_LEAST(B) \
(LLONG_MAX >> ((B)-2)/3 >> ((B)-2)/3 \
>> ((B)-2 - ((B)-2)/3 - ((B)-2)/3))
#define LLONG_HAS_BITS(B) (LLONG_AT_LEAST((B)) && !LLONG_AT_LEAST((B) + 1))
void
foo (void)
{
/* Decimal. */
/* Check all 2^n and 2^n - 1 up to 2^71 - 1. */
CHECK_DEC_CONST(1);
CHECK_DEC_CONST(2);
CHECK_DEC_CONST(3);
CHECK_DEC_CONST(4);
CHECK_DEC_CONST(7);
CHECK_DEC_CONST(8);
CHECK_DEC_CONST(15);
CHECK_DEC_CONST(16);
CHECK_DEC_CONST(31);
CHECK_DEC_CONST(32);
CHECK_DEC_CONST(63);
CHECK_DEC_CONST(64);
CHECK_DEC_CONST(127);
CHECK_DEC_CONST(128);
CHECK_DEC_CONST(255);
CHECK_DEC_CONST(256);
CHECK_DEC_CONST(511);
CHECK_DEC_CONST(512);
CHECK_DEC_CONST(1023);
CHECK_DEC_CONST(1024);
CHECK_DEC_CONST(2047);
CHECK_DEC_CONST(2048);
CHECK_DEC_CONST(4095);
CHECK_DEC_CONST(4096);
CHECK_DEC_CONST(8191);
CHECK_DEC_CONST(8192);
CHECK_DEC_CONST(16383);
CHECK_DEC_CONST(16384);
CHECK_DEC_CONST(32767);
CHECK_DEC_CONST(32768);
CHECK_DEC_CONST(65535);
CHECK_DEC_CONST(65536);
CHECK_DEC_CONST(131071);
CHECK_DEC_CONST(131072);
CHECK_DEC_CONST(262143);
CHECK_DEC_CONST(262144);
CHECK_DEC_CONST(524287);
CHECK_DEC_CONST(524288);
CHECK_DEC_CONST(1048575);
CHECK_DEC_CONST(1048576);
CHECK_DEC_CONST(2097151);
CHECK_DEC_CONST(2097152);
CHECK_DEC_CONST(4194303);
CHECK_DEC_CONST(4194304);
CHECK_DEC_CONST(8388607);
CHECK_DEC_CONST(8388608);
CHECK_DEC_CONST(16777215);
CHECK_DEC_CONST(16777216);
CHECK_DEC_CONST(33554431);
CHECK_DEC_CONST(33554432);
CHECK_DEC_CONST(67108863);
CHECK_DEC_CONST(67108864);
CHECK_DEC_CONST(134217727);
CHECK_DEC_CONST(134217728);
CHECK_DEC_CONST(268435455);
CHECK_DEC_CONST(268435456);
CHECK_DEC_CONST(536870911);
CHECK_DEC_CONST(536870912);
CHECK_DEC_CONST(1073741823);
CHECK_DEC_CONST(1073741824);
CHECK_DEC_CONST(2147483647);
CHECK_DEC_CONST(2147483648);
CHECK_DEC_CONST(4294967295);
CHECK_DEC_CONST(4294967296);
CHECK_DEC_CONST(8589934591);
CHECK_DEC_CONST(8589934592);
CHECK_DEC_CONST(17179869183);
CHECK_DEC_CONST(17179869184);
CHECK_DEC_CONST(34359738367);
CHECK_DEC_CONST(34359738368);
CHECK_DEC_CONST(68719476735);
CHECK_DEC_CONST(68719476736);
CHECK_DEC_CONST(137438953471);
CHECK_DEC_CONST(137438953472);
CHECK_DEC_CONST(274877906943);
CHECK_DEC_CONST(274877906944);
CHECK_DEC_CONST(549755813887);
CHECK_DEC_CONST(549755813888);
CHECK_DEC_CONST(1099511627775);
CHECK_DEC_CONST(1099511627776);
CHECK_DEC_CONST(2199023255551);
CHECK_DEC_CONST(2199023255552);
CHECK_DEC_CONST(4398046511103);
CHECK_DEC_CONST(4398046511104);
CHECK_DEC_CONST(8796093022207);
CHECK_DEC_CONST(8796093022208);
CHECK_DEC_CONST(17592186044415);
CHECK_DEC_CONST(17592186044416);
CHECK_DEC_CONST(35184372088831);
CHECK_DEC_CONST(35184372088832);
CHECK_DEC_CONST(70368744177663);
CHECK_DEC_CONST(70368744177664);
CHECK_DEC_CONST(140737488355327);
CHECK_DEC_CONST(140737488355328);
CHECK_DEC_CONST(281474976710655);
CHECK_DEC_CONST(281474976710656);
CHECK_DEC_CONST(562949953421311);
CHECK_DEC_CONST(562949953421312);
CHECK_DEC_CONST(1125899906842623);
CHECK_DEC_CONST(1125899906842624);
CHECK_DEC_CONST(2251799813685247);
CHECK_DEC_CONST(2251799813685248);
CHECK_DEC_CONST(4503599627370495);
CHECK_DEC_CONST(4503599627370496);
CHECK_DEC_CONST(9007199254740991);
CHECK_DEC_CONST(9007199254740992);
CHECK_DEC_CONST(18014398509481983);
CHECK_DEC_CONST(18014398509481984);
CHECK_DEC_CONST(36028797018963967);
CHECK_DEC_CONST(36028797018963968);
CHECK_DEC_CONST(72057594037927935);
CHECK_DEC_CONST(72057594037927936);
CHECK_DEC_CONST(144115188075855871);
CHECK_DEC_CONST(144115188075855872);
CHECK_DEC_CONST(288230376151711743);
CHECK_DEC_CONST(288230376151711744);
CHECK_DEC_CONST(576460752303423487);
CHECK_DEC_CONST(576460752303423488);
CHECK_DEC_CONST(1152921504606846975);
CHECK_DEC_CONST(1152921504606846976);
CHECK_DEC_CONST(2305843009213693951);
CHECK_DEC_CONST(2305843009213693952);
CHECK_DEC_CONST(4611686018427387903);
CHECK_DEC_CONST(4611686018427387904);
CHECK_DEC_CONST(9223372036854775807);
#if LLONG_AT_LEAST(65)
CHECK_DEC_CONST(9223372036854775808);
CHECK_DEC_CONST(18446744073709551615);
#endif
#if LLONG_AT_LEAST(66)
CHECK_DEC_CONST(18446744073709551616);
CHECK_DEC_CONST(36893488147419103231);
#endif
#if LLONG_AT_LEAST(67)
CHECK_DEC_CONST(36893488147419103232);
CHECK_DEC_CONST(73786976294838206463);
#endif
#if LLONG_AT_LEAST(68)
CHECK_DEC_CONST(73786976294838206464);
CHECK_DEC_CONST(147573952589676412927);
#endif
#if LLONG_AT_LEAST(69)
CHECK_DEC_CONST(147573952589676412928);
CHECK_DEC_CONST(295147905179352825855);
#endif
#if LLONG_AT_LEAST(70)
CHECK_DEC_CONST(295147905179352825856);
CHECK_DEC_CONST(590295810358705651711);
#endif
#if LLONG_AT_LEAST(71)
CHECK_DEC_CONST(590295810358705651712);
CHECK_DEC_CONST(1180591620717411303423);
#endif
#if LLONG_AT_LEAST(72)
CHECK_DEC_CONST(1180591620717411303424);
CHECK_DEC_CONST(2361183241434822606847);
#endif
/* Octal and hexadecimal. */
/* Check all 2^n and 2^n - 1 up to 2^72 - 1. */
CHECK_OCT_CONST(0);
CHECK_HEX_CONST(0);
CHECK_OCT_CONST(01);
CHECK_HEX_CONST(1);
CHECK_OCT_CONST(02);
CHECK_HEX_CONST(2);
CHECK_OCT_CONST(03);
CHECK_HEX_CONST(3);
CHECK_OCT_CONST(04);
CHECK_HEX_CONST(4);
CHECK_OCT_CONST(07);
CHECK_HEX_CONST(7);
CHECK_OCT_CONST(010);
CHECK_HEX_CONST(8);
CHECK_OCT_CONST(017);
CHECK_HEX_CONST(f);
CHECK_OCT_CONST(020);
CHECK_HEX_CONST(10);
CHECK_OCT_CONST(037);
CHECK_HEX_CONST(1f);
CHECK_OCT_CONST(040);
CHECK_HEX_CONST(20);
CHECK_OCT_CONST(077);
CHECK_HEX_CONST(3f);
CHECK_OCT_CONST(0100);
CHECK_HEX_CONST(40);
CHECK_OCT_CONST(0177);
CHECK_HEX_CONST(7f);
CHECK_OCT_CONST(0200);
CHECK_HEX_CONST(80);
CHECK_OCT_CONST(0377);
CHECK_HEX_CONST(ff);
CHECK_OCT_CONST(0400);
CHECK_HEX_CONST(100);
CHECK_OCT_CONST(0777);
CHECK_HEX_CONST(1ff);
CHECK_OCT_CONST(01000);
CHECK_HEX_CONST(200);
CHECK_OCT_CONST(01777);
CHECK_HEX_CONST(3ff);
CHECK_OCT_CONST(02000);
CHECK_HEX_CONST(400);
CHECK_OCT_CONST(03777);
CHECK_HEX_CONST(7ff);
CHECK_OCT_CONST(04000);
CHECK_HEX_CONST(800);
CHECK_OCT_CONST(07777);
CHECK_HEX_CONST(fff);
CHECK_OCT_CONST(010000);
CHECK_HEX_CONST(1000);
CHECK_OCT_CONST(017777);
CHECK_HEX_CONST(1fff);
CHECK_OCT_CONST(020000);
CHECK_HEX_CONST(2000);
CHECK_OCT_CONST(037777);
CHECK_HEX_CONST(3fff);
CHECK_OCT_CONST(040000);
CHECK_HEX_CONST(4000);
CHECK_OCT_CONST(077777);
CHECK_HEX_CONST(7fff);
CHECK_OCT_CONST(0100000);
CHECK_HEX_CONST(8000);
CHECK_OCT_CONST(0177777);
CHECK_HEX_CONST(ffff);
CHECK_OCT_CONST(0200000);
CHECK_HEX_CONST(10000);
CHECK_OCT_CONST(0377777);
CHECK_HEX_CONST(1ffff);
CHECK_OCT_CONST(0400000);
CHECK_HEX_CONST(20000);
CHECK_OCT_CONST(0777777);
CHECK_HEX_CONST(3ffff);
CHECK_OCT_CONST(01000000);
CHECK_HEX_CONST(40000);
CHECK_OCT_CONST(01777777);
CHECK_HEX_CONST(7ffff);
CHECK_OCT_CONST(02000000);
CHECK_HEX_CONST(80000);
CHECK_OCT_CONST(03777777);
CHECK_HEX_CONST(fffff);
CHECK_OCT_CONST(04000000);
CHECK_HEX_CONST(100000);
CHECK_OCT_CONST(07777777);
CHECK_HEX_CONST(1fffff);
CHECK_OCT_CONST(010000000);
CHECK_HEX_CONST(200000);
CHECK_OCT_CONST(017777777);
CHECK_HEX_CONST(3fffff);
CHECK_OCT_CONST(020000000);
CHECK_HEX_CONST(400000);
CHECK_OCT_CONST(037777777);
CHECK_HEX_CONST(7fffff);
CHECK_OCT_CONST(040000000);
CHECK_HEX_CONST(800000);
CHECK_OCT_CONST(077777777);
CHECK_HEX_CONST(ffffff);
CHECK_OCT_CONST(0100000000);
CHECK_HEX_CONST(1000000);
CHECK_OCT_CONST(0177777777);
CHECK_HEX_CONST(1ffffff);
CHECK_OCT_CONST(0200000000);
CHECK_HEX_CONST(2000000);
CHECK_OCT_CONST(0377777777);
CHECK_HEX_CONST(3ffffff);
CHECK_OCT_CONST(0400000000);
CHECK_HEX_CONST(4000000);
CHECK_OCT_CONST(0777777777);
CHECK_HEX_CONST(7ffffff);
CHECK_OCT_CONST(01000000000);
CHECK_HEX_CONST(8000000);
CHECK_OCT_CONST(01777777777);
CHECK_HEX_CONST(fffffff);
CHECK_OCT_CONST(02000000000);
CHECK_HEX_CONST(10000000);
CHECK_OCT_CONST(03777777777);
CHECK_HEX_CONST(1fffffff);
CHECK_OCT_CONST(04000000000);
CHECK_HEX_CONST(20000000);
CHECK_OCT_CONST(07777777777);
CHECK_HEX_CONST(3fffffff);
CHECK_OCT_CONST(010000000000);
CHECK_HEX_CONST(40000000);
CHECK_OCT_CONST(017777777777);
CHECK_HEX_CONST(7fffffff);
CHECK_OCT_CONST(020000000000);
CHECK_HEX_CONST(80000000);
CHECK_OCT_CONST(037777777777);
CHECK_HEX_CONST(ffffffff);
CHECK_OCT_CONST(040000000000);
CHECK_HEX_CONST(100000000);
CHECK_OCT_CONST(077777777777);
CHECK_HEX_CONST(1ffffffff);
CHECK_OCT_CONST(0100000000000);
CHECK_HEX_CONST(200000000);
CHECK_OCT_CONST(0177777777777);
CHECK_HEX_CONST(3ffffffff);
CHECK_OCT_CONST(0200000000000);
CHECK_HEX_CONST(400000000);
CHECK_OCT_CONST(0377777777777);
CHECK_HEX_CONST(7ffffffff);
CHECK_OCT_CONST(0400000000000);
CHECK_HEX_CONST(800000000);
CHECK_OCT_CONST(0777777777777);
CHECK_HEX_CONST(fffffffff);
CHECK_OCT_CONST(01000000000000);
CHECK_HEX_CONST(1000000000);
CHECK_OCT_CONST(01777777777777);
CHECK_HEX_CONST(1fffffffff);
CHECK_OCT_CONST(02000000000000);
CHECK_HEX_CONST(2000000000);
CHECK_OCT_CONST(03777777777777);
CHECK_HEX_CONST(3fffffffff);
CHECK_OCT_CONST(04000000000000);
CHECK_HEX_CONST(4000000000);
CHECK_OCT_CONST(07777777777777);
CHECK_HEX_CONST(7fffffffff);
CHECK_OCT_CONST(010000000000000);
CHECK_HEX_CONST(8000000000);
CHECK_OCT_CONST(017777777777777);
CHECK_HEX_CONST(ffffffffff);
CHECK_OCT_CONST(020000000000000);
CHECK_HEX_CONST(10000000000);
CHECK_OCT_CONST(037777777777777);
CHECK_HEX_CONST(1ffffffffff);
CHECK_OCT_CONST(040000000000000);
CHECK_HEX_CONST(20000000000);
CHECK_OCT_CONST(077777777777777);
CHECK_HEX_CONST(3ffffffffff);
CHECK_OCT_CONST(0100000000000000);
CHECK_HEX_CONST(40000000000);
CHECK_OCT_CONST(0177777777777777);
CHECK_HEX_CONST(7ffffffffff);
CHECK_OCT_CONST(0200000000000000);
CHECK_HEX_CONST(80000000000);
CHECK_OCT_CONST(0377777777777777);
CHECK_HEX_CONST(fffffffffff);
CHECK_OCT_CONST(0400000000000000);
CHECK_HEX_CONST(100000000000);
CHECK_OCT_CONST(0777777777777777);
CHECK_HEX_CONST(1fffffffffff);
CHECK_OCT_CONST(01000000000000000);
CHECK_HEX_CONST(200000000000);
CHECK_OCT_CONST(01777777777777777);
CHECK_HEX_CONST(3fffffffffff);
CHECK_OCT_CONST(02000000000000000);
CHECK_HEX_CONST(400000000000);
CHECK_OCT_CONST(03777777777777777);
CHECK_HEX_CONST(7fffffffffff);
CHECK_OCT_CONST(04000000000000000);
CHECK_HEX_CONST(800000000000);
CHECK_OCT_CONST(07777777777777777);
CHECK_HEX_CONST(ffffffffffff);
CHECK_OCT_CONST(010000000000000000);
CHECK_HEX_CONST(1000000000000);
CHECK_OCT_CONST(017777777777777777);
CHECK_HEX_CONST(1ffffffffffff);
CHECK_OCT_CONST(020000000000000000);
CHECK_HEX_CONST(2000000000000);
CHECK_OCT_CONST(037777777777777777);
CHECK_HEX_CONST(3ffffffffffff);
CHECK_OCT_CONST(040000000000000000);
CHECK_HEX_CONST(4000000000000);
CHECK_OCT_CONST(077777777777777777);
CHECK_HEX_CONST(7ffffffffffff);
CHECK_OCT_CONST(0100000000000000000);
CHECK_HEX_CONST(8000000000000);
CHECK_OCT_CONST(0177777777777777777);
CHECK_HEX_CONST(fffffffffffff);
CHECK_OCT_CONST(0200000000000000000);
CHECK_HEX_CONST(10000000000000);
CHECK_OCT_CONST(0377777777777777777);
CHECK_HEX_CONST(1fffffffffffff);
CHECK_OCT_CONST(0400000000000000000);
CHECK_HEX_CONST(20000000000000);
CHECK_OCT_CONST(0777777777777777777);
CHECK_HEX_CONST(3fffffffffffff);
CHECK_OCT_CONST(01000000000000000000);
CHECK_HEX_CONST(40000000000000);
CHECK_OCT_CONST(01777777777777777777);
CHECK_HEX_CONST(7fffffffffffff);
CHECK_OCT_CONST(02000000000000000000);
CHECK_HEX_CONST(80000000000000);
CHECK_OCT_CONST(03777777777777777777);
CHECK_HEX_CONST(ffffffffffffff);
CHECK_OCT_CONST(04000000000000000000);
CHECK_HEX_CONST(100000000000000);
CHECK_OCT_CONST(07777777777777777777);
CHECK_HEX_CONST(1ffffffffffffff);
CHECK_OCT_CONST(010000000000000000000);
CHECK_HEX_CONST(200000000000000);
CHECK_OCT_CONST(017777777777777777777);
CHECK_HEX_CONST(3ffffffffffffff);
CHECK_OCT_CONST(020000000000000000000);
CHECK_HEX_CONST(400000000000000);
CHECK_OCT_CONST(037777777777777777777);
CHECK_HEX_CONST(7ffffffffffffff);
CHECK_OCT_CONST(040000000000000000000);
CHECK_HEX_CONST(800000000000000);
CHECK_OCT_CONST(077777777777777777777);
CHECK_HEX_CONST(fffffffffffffff);
CHECK_OCT_CONST(0100000000000000000000);
CHECK_HEX_CONST(1000000000000000);
CHECK_OCT_CONST(0177777777777777777777);
CHECK_HEX_CONST(1fffffffffffffff);
CHECK_OCT_CONST(0200000000000000000000);
CHECK_HEX_CONST(2000000000000000);
CHECK_OCT_CONST(0377777777777777777777);
CHECK_HEX_CONST(3fffffffffffffff);
CHECK_OCT_CONST(0400000000000000000000);
CHECK_HEX_CONST(4000000000000000);
CHECK_OCT_CONST(0777777777777777777777);
CHECK_HEX_CONST(7fffffffffffffff);
CHECK_OCT_CONST(01000000000000000000000);
CHECK_HEX_CONST(8000000000000000);
CHECK_OCT_CONST(01777777777777777777777);
CHECK_HEX_CONST(ffffffffffffffff);
#if LLONG_AT_LEAST(65)
CHECK_OCT_CONST(02000000000000000000000);
CHECK_HEX_CONST(10000000000000000);
CHECK_OCT_CONST(03777777777777777777777);
CHECK_HEX_CONST(1ffffffffffffffff);
#endif
#if LLONG_AT_LEAST(66)
CHECK_OCT_CONST(04000000000000000000000);
CHECK_HEX_CONST(20000000000000000);
CHECK_OCT_CONST(07777777777777777777777);
CHECK_HEX_CONST(3ffffffffffffffff);
#endif
#if LLONG_AT_LEAST(67)
CHECK_OCT_CONST(010000000000000000000000);
CHECK_HEX_CONST(40000000000000000);
CHECK_OCT_CONST(017777777777777777777777);
CHECK_HEX_CONST(7ffffffffffffffff);
#endif
#if LLONG_AT_LEAST(68)
CHECK_OCT_CONST(020000000000000000000000);
CHECK_HEX_CONST(80000000000000000);
CHECK_OCT_CONST(037777777777777777777777);
CHECK_HEX_CONST(fffffffffffffffff);
#endif
#if LLONG_AT_LEAST(69)
CHECK_OCT_CONST(040000000000000000000000);
CHECK_HEX_CONST(100000000000000000);
CHECK_OCT_CONST(077777777777777777777777);
CHECK_HEX_CONST(1fffffffffffffffff);
#endif
#if LLONG_AT_LEAST(70)
CHECK_OCT_CONST(0100000000000000000000000);
CHECK_HEX_CONST(200000000000000000);
CHECK_OCT_CONST(0177777777777777777777777);
CHECK_HEX_CONST(3fffffffffffffffff);
#endif
#if LLONG_AT_LEAST(71)
CHECK_OCT_CONST(0200000000000000000000000);
CHECK_HEX_CONST(400000000000000000);
CHECK_OCT_CONST(0377777777777777777777777);
CHECK_HEX_CONST(7fffffffffffffffff);
#endif
#if LLONG_AT_LEAST(72)
CHECK_OCT_CONST(0400000000000000000000000);
CHECK_HEX_CONST(800000000000000000);
CHECK_OCT_CONST(0777777777777777777777777);
CHECK_HEX_CONST(ffffffffffffffffff);
#endif
}

View File

@@ -1,4 +0,0 @@
#include <Carbon/Carbon.h>
//#import<vecLib/vecLib.h>

View File

@@ -1,27 +0,0 @@
#define EXPAND_2_CASES(i, x, y) CASE(i, x, y); CASE(i + 1, x, y);
#define EXPAND_4_CASES(i, x, y) EXPAND_2_CASES(i, x, y) EXPAND_2_CASES(i + 2, x, y)
#define EXPAND_8_CASES(i, x, y) EXPAND_4_CASES(i, x, y) EXPAND_4_CASES(i + 4, x, y)
#define EXPAND_16_CASES(i, x, y) EXPAND_8_CASES(i, x, y) EXPAND_8_CASES(i + 8, x, y)
#define EXPAND_32_CASES(i, x, y) EXPAND_16_CASES(i, x, y) EXPAND_16_CASES(i + 16, x, y)
#define EXPAND_64_CASES(i, x, y) EXPAND_32_CASES(i, x, y) EXPAND_32_CASES(i + 32, x, y)
#define EXPAND_128_CASES(i, x, y) EXPAND_64_CASES(i, x, y) EXPAND_64_CASES(i + 64, x, y)
#define EXPAND_256_CASES(i, x, y) EXPAND_128_CASES(i, x, y) EXPAND_128_CASES(i + 128, x, y)
#define EXPAND_512_CASES(i, x, y) EXPAND_256_CASES(i, x, y) EXPAND_256_CASES(i + 256, x, y)
#define EXPAND_1024_CASES(i, x, y) EXPAND_512_CASES(i, x, y) EXPAND_512_CASES(i + 512, x, y)
#define EXPAND_2048_CASES(i, x, y) EXPAND_1024_CASES(i, x, y) EXPAND_1024_CASES(i + 1024, x, y)
#define EXPAND_4096_CASES(i, x, y) EXPAND_2048_CASES(i, x, y) EXPAND_2048_CASES(i + 2048, x, y)
// This has a *monstrous* single fan-out in the CFG, across 8000 blocks inside
// the while loop.
unsigned cfg_big_switch(int x) {
unsigned y = 0;
while (x > 0) {
switch(x) {
#define CASE(i, x, y) \
case i: { int case_var = 3*x + i; y += case_var - 1; break; }
EXPAND_4096_CASES(0, x, y);
}
--x;
}
return y;
}

View File

@@ -1,20 +0,0 @@
#define EXPAND_2_BRANCHES(i, x, y) BRANCH(i, x, y); BRANCH(i + 1, x, y);
#define EXPAND_4_BRANCHES(i, x, y) EXPAND_2_BRANCHES(i, x, y) EXPAND_2_BRANCHES(i + 2, x, y)
#define EXPAND_8_BRANCHES(i, x, y) EXPAND_4_BRANCHES(i, x, y) EXPAND_4_BRANCHES(i + 4, x, y)
#define EXPAND_16_BRANCHES(i, x, y) EXPAND_8_BRANCHES(i, x, y) EXPAND_8_BRANCHES(i + 8, x, y)
#define EXPAND_32_BRANCHES(i, x, y) EXPAND_16_BRANCHES(i, x, y) EXPAND_16_BRANCHES(i + 16, x, y)
#define EXPAND_64_BRANCHES(i, x, y) EXPAND_32_BRANCHES(i, x, y) EXPAND_32_BRANCHES(i + 32, x, y)
#define EXPAND_128_BRANCHES(i, x, y) EXPAND_64_BRANCHES(i, x, y) EXPAND_64_BRANCHES(i + 64, x, y)
#define EXPAND_256_BRANCHES(i, x, y) EXPAND_128_BRANCHES(i, x, y) EXPAND_128_BRANCHES(i + 128, x, y)
#define EXPAND_512_BRANCHES(i, x, y) EXPAND_256_BRANCHES(i, x, y) EXPAND_256_BRANCHES(i + 256, x, y)
#define EXPAND_1024_BRANCHES(i, x, y) EXPAND_512_BRANCHES(i, x, y) EXPAND_512_BRANCHES(i + 512, x, y)
#define EXPAND_2048_BRANCHES(i, x, y) EXPAND_1024_BRANCHES(i, x, y) EXPAND_1024_BRANCHES(i + 1024, x, y)
#define EXPAND_4096_BRANCHES(i, x, y) EXPAND_2048_BRANCHES(i, x, y) EXPAND_2048_BRANCHES(i + 2048, x, y)
unsigned cfg_long_chain_single_exit(unsigned x) {
unsigned y = 0;
#define BRANCH(i, x, y) if ((x % 13171) < i) { int var = x / 13171; y ^= var; }
EXPAND_4096_BRANCHES(1, x, y);
#undef BRANCH
return y;
}

View File

@@ -1,20 +0,0 @@
#define EXPAND_2_BRANCHES(i, x, y) BRANCH(i, x, y); BRANCH(i + 1, x, y);
#define EXPAND_4_BRANCHES(i, x, y) EXPAND_2_BRANCHES(i, x, y) EXPAND_2_BRANCHES(i + 2, x, y)
#define EXPAND_8_BRANCHES(i, x, y) EXPAND_4_BRANCHES(i, x, y) EXPAND_4_BRANCHES(i + 4, x, y)
#define EXPAND_16_BRANCHES(i, x, y) EXPAND_8_BRANCHES(i, x, y) EXPAND_8_BRANCHES(i + 8, x, y)
#define EXPAND_32_BRANCHES(i, x, y) EXPAND_16_BRANCHES(i, x, y) EXPAND_16_BRANCHES(i + 16, x, y)
#define EXPAND_64_BRANCHES(i, x, y) EXPAND_32_BRANCHES(i, x, y) EXPAND_32_BRANCHES(i + 32, x, y)
#define EXPAND_128_BRANCHES(i, x, y) EXPAND_64_BRANCHES(i, x, y) EXPAND_64_BRANCHES(i + 64, x, y)
#define EXPAND_256_BRANCHES(i, x, y) EXPAND_128_BRANCHES(i, x, y) EXPAND_128_BRANCHES(i + 128, x, y)
#define EXPAND_512_BRANCHES(i, x, y) EXPAND_256_BRANCHES(i, x, y) EXPAND_256_BRANCHES(i + 256, x, y)
#define EXPAND_1024_BRANCHES(i, x, y) EXPAND_512_BRANCHES(i, x, y) EXPAND_512_BRANCHES(i + 512, x, y)
#define EXPAND_2048_BRANCHES(i, x, y) EXPAND_1024_BRANCHES(i, x, y) EXPAND_1024_BRANCHES(i + 1024, x, y)
#define EXPAND_4096_BRANCHES(i, x, y) EXPAND_2048_BRANCHES(i, x, y) EXPAND_2048_BRANCHES(i + 2048, x, y)
unsigned cfg_long_chain_multiple_exit(unsigned x) {
unsigned y = 0;
#define BRANCH(i, x, y) if (((x % 13171) + ++y) < i) { int var = x / 13171 + y; return var; }
EXPAND_4096_BRANCHES(1, x, y);
#undef BRANCH
return 42;
}

View File

@@ -1,21 +0,0 @@
#define EXPAND_2_BRANCHES(i, x, y) BRANCH(i, x, y); BRANCH(i + 1, x, y);
#define EXPAND_4_BRANCHES(i, x, y) EXPAND_2_BRANCHES(i, x, y) EXPAND_2_BRANCHES(i + 2, x, y)
#define EXPAND_8_BRANCHES(i, x, y) EXPAND_4_BRANCHES(i, x, y) EXPAND_4_BRANCHES(i + 4, x, y)
#define EXPAND_16_BRANCHES(i, x, y) EXPAND_8_BRANCHES(i, x, y) EXPAND_8_BRANCHES(i + 8, x, y)
#define EXPAND_32_BRANCHES(i, x, y) EXPAND_16_BRANCHES(i, x, y) EXPAND_16_BRANCHES(i + 16, x, y)
#define EXPAND_64_BRANCHES(i, x, y) EXPAND_32_BRANCHES(i, x, y) EXPAND_32_BRANCHES(i + 32, x, y)
#define EXPAND_128_BRANCHES(i, x, y) EXPAND_64_BRANCHES(i, x, y) EXPAND_64_BRANCHES(i + 64, x, y)
#define EXPAND_256_BRANCHES(i, x, y) EXPAND_128_BRANCHES(i, x, y) EXPAND_128_BRANCHES(i + 128, x, y)
#define EXPAND_512_BRANCHES(i, x, y) EXPAND_256_BRANCHES(i, x, y) EXPAND_256_BRANCHES(i + 256, x, y)
#define EXPAND_1024_BRANCHES(i, x, y) EXPAND_512_BRANCHES(i, x, y) EXPAND_512_BRANCHES(i + 512, x, y)
#define EXPAND_2048_BRANCHES(i, x, y) EXPAND_1024_BRANCHES(i, x, y) EXPAND_1024_BRANCHES(i + 1024, x, y)
#define EXPAND_4096_BRANCHES(i, x, y) EXPAND_2048_BRANCHES(i, x, y) EXPAND_2048_BRANCHES(i + 2048, x, y)
unsigned cfg_long_chain_many_preds(unsigned x) {
unsigned y = 0;
#define BRANCH(i, x, y) if ((x % 13171) < i) { int var = x / 13171; y ^= var; } else
EXPAND_4096_BRANCHES(1, x, y);
#undef BRANCH
int var = x / 13171; y^= var;
return y;
}

View File

@@ -1,36 +0,0 @@
#define EXPAND_2_INNER_CASES(i, x, y) INNER_CASE(i, x, y); INNER_CASE(i + 1, x, y);
#define EXPAND_4_INNER_CASES(i, x, y) EXPAND_2_INNER_CASES(i, x, y) EXPAND_2_INNER_CASES(i + 2, x, y)
#define EXPAND_8_INNER_CASES(i, x, y) EXPAND_4_INNER_CASES(i, x, y) EXPAND_4_INNER_CASES(i + 4, x, y)
#define EXPAND_16_INNER_CASES(i, x, y) EXPAND_8_INNER_CASES(i, x, y) EXPAND_8_INNER_CASES(i + 8, x, y)
#define EXPAND_32_INNER_CASES(i, x, y) EXPAND_16_INNER_CASES(i, x, y) EXPAND_16_INNER_CASES(i + 16, x, y)
#define EXPAND_64_INNER_CASES(i, x, y) EXPAND_32_INNER_CASES(i, x, y) EXPAND_32_INNER_CASES(i + 32, x, y)
#define EXPAND_2_OUTER_CASES(i, x, y) OUTER_CASE(i, x, y); OUTER_CASE(i + 1, x, y);
#define EXPAND_4_OUTER_CASES(i, x, y) EXPAND_2_OUTER_CASES(i, x, y) EXPAND_2_OUTER_CASES(i + 2, x, y)
#define EXPAND_8_OUTER_CASES(i, x, y) EXPAND_4_OUTER_CASES(i, x, y) EXPAND_4_OUTER_CASES(i + 4, x, y)
#define EXPAND_16_OUTER_CASES(i, x, y) EXPAND_8_OUTER_CASES(i, x, y) EXPAND_8_OUTER_CASES(i + 8, x, y)
#define EXPAND_32_OUTER_CASES(i, x, y) EXPAND_16_OUTER_CASES(i, x, y) EXPAND_16_OUTER_CASES(i + 16, x, y)
#define EXPAND_64_OUTER_CASES(i, x, y) EXPAND_32_OUTER_CASES(i, x, y) EXPAND_32_OUTER_CASES(i + 32, x, y)
// Rather than a single monstrous fan-out, this fans out in smaller increments,
// but to a similar size.
unsigned cfg_nested_switch(int x) {
unsigned y = 0;
while (x > 0) {
switch (x) {
#define INNER_CASE(i, x, y) \
case i: { int case_var = 3*x + i; y += case_var - 1; break; }
#define OUTER_CASE(i, x, y) \
case i: { \
int case_var = y >> 8; \
switch (case_var) { \
EXPAND_64_INNER_CASES(0, x, y); \
} \
break; \
}
EXPAND_64_OUTER_CASES(0, x, y);
}
--x;
}
return y;
}

View File

@@ -1,59 +0,0 @@
// Hammer the CFG with large numbers of overlapping variable scopes, which
// implicit destructors triggered at each edge.
#define EXPAND_BASIC_STRUCT(i) struct X##i { X##i(int); ~X##i(); };
#define EXPAND_NORET_STRUCT(i) struct X##i { X##i(int); ~X##i() __attribute__((noreturn)); };
EXPAND_BASIC_STRUCT(0000); EXPAND_NORET_STRUCT(0001);
EXPAND_BASIC_STRUCT(0010); EXPAND_BASIC_STRUCT(0011);
EXPAND_BASIC_STRUCT(0100); EXPAND_NORET_STRUCT(0101);
EXPAND_NORET_STRUCT(0110); EXPAND_BASIC_STRUCT(0111);
EXPAND_BASIC_STRUCT(1000); EXPAND_NORET_STRUCT(1001);
EXPAND_BASIC_STRUCT(1010); EXPAND_BASIC_STRUCT(1011);
EXPAND_NORET_STRUCT(1100); EXPAND_NORET_STRUCT(1101);
EXPAND_BASIC_STRUCT(1110); EXPAND_BASIC_STRUCT(1111);
#define EXPAND_2_VARS(c, i, x) const X##i var_##c##_##i##0(x), &var_##c##_##i##1 = X##i(x)
#define EXPAND_4_VARS(c, i, x) EXPAND_2_VARS(c, i##0, x); EXPAND_2_VARS(c, i##1, x)
#define EXPAND_8_VARS(c, i, x) EXPAND_4_VARS(c, i##0, x); EXPAND_4_VARS(c, i##1, x)
#define EXPAND_16_VARS(c, i, x) EXPAND_8_VARS(c, i##0, x); EXPAND_8_VARS(c, i##1, x)
#define EXPAND_32_VARS(c, x) EXPAND_16_VARS(c, 0, x); EXPAND_16_VARS(c, 1, x)
#define EXPAND_2_INNER_CASES(i, x, y) INNER_CASE(i, x, y); INNER_CASE(i + 1, x, y);
#define EXPAND_4_INNER_CASES(i, x, y) EXPAND_2_INNER_CASES(i, x, y) EXPAND_2_INNER_CASES(i + 2, x, y)
#define EXPAND_8_INNER_CASES(i, x, y) EXPAND_4_INNER_CASES(i, x, y) EXPAND_4_INNER_CASES(i + 4, x, y)
#define EXPAND_16_INNER_CASES(i, x, y) EXPAND_8_INNER_CASES(i, x, y) EXPAND_8_INNER_CASES(i + 8, x, y)
#define EXPAND_32_INNER_CASES(i, x, y) EXPAND_16_INNER_CASES(i, x, y) EXPAND_16_INNER_CASES(i + 16, x, y)
#define EXPAND_2_OUTER_CASES(i, x, y) OUTER_CASE(i, x, y); OUTER_CASE(i + 1, x, y);
#define EXPAND_4_OUTER_CASES(i, x, y) EXPAND_2_OUTER_CASES(i, x, y) EXPAND_2_OUTER_CASES(i + 2, x, y)
#define EXPAND_8_OUTER_CASES(i, x, y) EXPAND_4_OUTER_CASES(i, x, y) EXPAND_4_OUTER_CASES(i + 4, x, y)
#define EXPAND_16_OUTER_CASES(i, x, y) EXPAND_8_OUTER_CASES(i, x, y) EXPAND_8_OUTER_CASES(i + 8, x, y)
#define EXPAND_32_OUTER_CASES(i, x, y) EXPAND_16_OUTER_CASES(i, x, y) EXPAND_16_OUTER_CASES(i + 16, x, y)
unsigned cfg_nested_vars(int x) {
int y = 0;
while (x > 0) {
EXPAND_32_VARS(a, x);
switch (x) {
#define INNER_CASE(i, x, y) \
case i: { \
int case_var = 3*x + i; \
EXPAND_32_VARS(c, case_var); \
y += case_var - 1; \
break; \
}
#define OUTER_CASE(i, x, y) \
case i: { \
int case_var = y >> 8; \
EXPAND_32_VARS(b, y); \
switch (case_var) { \
EXPAND_32_INNER_CASES(0, x, y); \
} \
break; \
}
EXPAND_32_OUTER_CASES(0, x, y);
}
--x;
}
return y;
}

View File

@@ -1,5 +0,0 @@
// clang -I/usr/include/c++/4.0.0 -I/usr/include/c++/4.0.0/powerpc-apple-darwin8 -I/usr/include/c++/4.0.0/backward INPUTS/iostream.cc -Eonly
#include <iostream>
#include <stdint.h>

View File

@@ -1,17 +0,0 @@
// This pounds on macro expansion for performance reasons. This is currently
// heavily constrained by darwin's malloc.
// Function-like macros.
#define A0(A, B) A B
#define A1(A, B) A0(A,B) A0(A,B) A0(A,B) A0(A,B) A0(A,B) A0(A,B)
#define A2(A, B) A1(A,B) A1(A,B) A1(A,B) A1(A,B) A1(A,B) A1(A,B)
#define A3(A, B) A2(A,B) A2(A,B) A2(A,B) A2(A,B) A2(A,B) A2(A,B)
#define A4(A, B) A3(A,B) A3(A,B) A3(A,B) A3(A,B) A3(A,B) A3(A,B)
#define A5(A, B) A4(A,B) A4(A,B) A4(A,B) A4(A,B) A4(A,B) A4(A,B)
#define A6(A, B) A5(A,B) A5(A,B) A5(A,B) A5(A,B) A5(A,B) A5(A,B)
#define A7(A, B) A6(A,B) A6(A,B) A6(A,B) A6(A,B) A6(A,B) A6(A,B)
#define A8(A, B) A7(A,B) A7(A,B) A7(A,B) A7(A,B) A7(A,B) A7(A,B)
A8(a, b)

View File

@@ -1,16 +0,0 @@
// This pounds on macro expansion for performance reasons. This is currently
// heavily constrained by darwin's malloc.
// Object-like expansions
#define A0 a b
#define A1 A0 A0 A0 A0 A0 A0
#define A2 A1 A1 A1 A1 A1 A1
#define A3 A2 A2 A2 A2 A2 A2
#define A4 A3 A3 A3 A3 A3 A3
#define A5 A4 A4 A4 A4 A4 A4
#define A6 A5 A5 A5 A5 A5 A5
#define A7 A6 A6 A6 A6 A6 A6
#define A8 A7 A7 A7 A7 A7 A7
A8

View File

@@ -1,47 +0,0 @@
#define __extension__
#define __stpcpy(dest, src) (__extension__ (__builtin_constant_p (src) ? (__string2_1bptr_p (src) && strlen (src) + 1 <= 8 ? __stpcpy_small (dest, __stpcpy_args (src), strlen (src) + 1) : ((char *) __mempcpy (dest, src, strlen (src) + 1) - 1)) : __stpcpy (dest, src)))
#define stpcpy(dest, src) __stpcpy (dest, src)
#define __stpcpy_args(src) __extension__ __STRING2_SMALL_GET16 (src, 0), __extension__ __STRING2_SMALL_GET16 (src, 4), __extension__ __STRING2_SMALL_GET32 (src, 0), __extension__ __STRING2_SMALL_GET32 (src, 4)
#define __mempcpy(dest, src, n) (__extension__ (__builtin_constant_p (src) && __builtin_constant_p (n) && __string2_1bptr_p (src) && n <= 8 ? __mempcpy_small (dest, __mempcpy_args (src), n) : __mempcpy (dest, src, n)))
#define mempcpy(dest, src, n) __mempcpy (dest, src, n)
#define __mempcpy_args(src) ((char *) (src))[0], ((char *) (src))[2], ((char *) (src))[4], ((char *) (src))[6], __extension__ __STRING2_SMALL_GET16 (src, 0), __extension__ __STRING2_SMALL_GET16 (src, 4), __extension__ __STRING2_SMALL_GET32 (src, 0), __extension__ __STRING2_SMALL_GET32 (src, 4)
#define __STRING2_SMALL_GET16(src, idx) (((__const unsigned char *) (__const char *) (src))[idx + 1] << 8 | ((__const unsigned char *) (__const char *) (src))[idx])
#define __STRING2_SMALL_GET32(src, idx) (((((__const unsigned char *) (__const char *) (src))[idx + 3] << 8 | ((__const unsigned char *) (__const char *) (src))[idx + 2]) << 8 | ((__const unsigned char *) (__const char *) (src))[idx + 1]) << 8 | ((__const unsigned char *) (__const char *) (src))[idx])
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)
stpcpy (stpcpy (stpcpy (stpcpy (a, b), c), d), e)

View File

@@ -1,49 +0,0 @@
//===----------------------------------------------------------------------===//
// Clang Installation Instructions
//===----------------------------------------------------------------------===//
These instructions describe how to build and install Clang.
//===----------------------------------------------------------------------===//
// Step 1: Organization
//===----------------------------------------------------------------------===//
Clang is designed to be built as part of an LLVM build. Assuming that the LLVM
source code is located at $LLVM_SRC_ROOT, then the clang source code should be
installed as:
$LLVM_SRC_ROOT/tools/clang
The directory is not required to be called clang, but doing so will allow the
LLVM build system to automatically recognize it and build it along with LLVM.
//===----------------------------------------------------------------------===//
// Step 2: Configure and Build LLVM
//===----------------------------------------------------------------------===//
Configure and build your copy of LLVM (see $LLVM_SRC_ROOT/GettingStarted.html
for more information).
Assuming you installed clang at $LLVM_SRC_ROOT/tools/clang then Clang will
automatically be built with LLVM. Otherwise, run 'make' in the Clang source
directory to build Clang.
//===----------------------------------------------------------------------===//
// Step 3: (Optional) Verify Your Build
//===----------------------------------------------------------------------===//
It is a good idea to run the Clang tests to make sure your build works
correctly. From inside the Clang build directory, run 'make test' to run the
tests.
//===----------------------------------------------------------------------===//
// Step 4: Install Clang
//===----------------------------------------------------------------------===//
From inside the Clang build directory, run 'make install' to install the Clang
compiler and header files into the prefix directory selected when LLVM was
configured.
The Clang compiler is available as 'clang' and supports a gcc like command line
interface. See the man page for clang (installed into $prefix/share/man/man1)
for more information.

View File

@@ -1,63 +0,0 @@
==============================================================================
LLVM Release License
==============================================================================
University of Illinois/NCSA
Open Source License
Copyright (c) 2007-2012 University of Illinois at Urbana-Champaign.
All rights reserved.
Developed by:
LLVM Team
University of Illinois at Urbana-Champaign
http://llvm.org
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal with
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimers.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimers in the
documentation and/or other materials provided with the distribution.
* Neither the names of the LLVM Team, University of Illinois at
Urbana-Champaign, nor the names of its contributors may be used to
endorse or promote products derived from this Software without specific
prior written permission.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
SOFTWARE.
==============================================================================
The LLVM software contains code written by third parties. Such software will
have its own individual LICENSE.TXT file in the directory in which it appears.
This file will describe the copyrights, license, and restrictions which apply
to that code.
The disclaimer of warranty in the University of Illinois Open Source License
applies to all code in the LLVM Distribution, and nothing in any of the
other licenses gives permission to use the names of the LLVM Team or the
University of Illinois to endorse or promote products derived from this
Software.
The following pieces of software have additional or alternate copyrights,
licenses, and/or restrictions:
Program Directory
------- ---------
<none yet>

View File

@@ -1,115 +0,0 @@
##===- Makefile --------------------------------------------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##
# If CLANG_LEVEL is not set, then we are the top-level Makefile. Otherwise, we
# are being included from a subdirectory makefile.
ifndef CLANG_LEVEL
IS_TOP_LEVEL := 1
CLANG_LEVEL := .
DIRS := utils/TableGen include lib tools runtime docs unittests
PARALLEL_DIRS :=
ifeq ($(BUILD_EXAMPLES),1)
PARALLEL_DIRS += examples
endif
endif
ifeq ($(MAKECMDGOALS),libs-only)
DIRS := $(filter-out tools docs, $(DIRS))
OPTIONAL_DIRS :=
endif
ifeq ($(BUILD_CLANG_ONLY),YES)
DIRS := $(filter-out docs unittests, $(DIRS))
OPTIONAL_DIRS :=
endif
###
# Common Makefile code, shared by all Clang Makefiles.
# Set LLVM source root level.
LEVEL := $(CLANG_LEVEL)/../..
# Include LLVM common makefile.
include $(LEVEL)/Makefile.common
ifneq ($(ENABLE_DOCS),1)
DIRS := $(filter-out docs, $(DIRS))
endif
# Set common Clang build flags.
CPP.Flags += -I$(PROJ_SRC_DIR)/$(CLANG_LEVEL)/include -I$(PROJ_OBJ_DIR)/$(CLANG_LEVEL)/include
ifdef CLANG_VENDOR
CPP.Flags += -DCLANG_VENDOR='"$(CLANG_VENDOR) "'
endif
ifdef CLANG_REPOSITORY_STRING
CPP.Flags += -DCLANG_REPOSITORY_STRING='"$(CLANG_REPOSITORY_STRING)"'
endif
# Disable -fstrict-aliasing. Darwin disables it by default (and LLVM doesn't
# work with it enabled with GCC), Clang/llvm-gcc don't support it yet, and newer
# GCC's have false positive warnings with it on Linux (which prove a pain to
# fix). For example:
# http://gcc.gnu.org/PR41874
# http://gcc.gnu.org/PR41838
#
# We can revisit this when LLVM/Clang support it.
CXX.Flags += -fno-strict-aliasing
# Set up Clang's tblgen.
ifndef CLANG_TBLGEN
ifeq ($(LLVM_CROSS_COMPILING),1)
CLANG_TBLGEN := $(BuildLLVMToolDir)/clang-tblgen$(BUILD_EXEEXT)
else
CLANG_TBLGEN := $(LLVMToolDir)/clang-tblgen$(EXEEXT)
endif
endif
ClangTableGen = $(CLANG_TBLGEN) $(TableGen.Flags)
###
# Clang Top Level specific stuff.
ifeq ($(IS_TOP_LEVEL),1)
ifneq ($(PROJ_SRC_ROOT),$(PROJ_OBJ_ROOT))
$(RecursiveTargets)::
$(Verb) for dir in test unittests; do \
if [ -f $(PROJ_SRC_DIR)/$${dir}/Makefile ] && [ ! -f $${dir}/Makefile ]; then \
$(MKDIR) $${dir}; \
$(CP) $(PROJ_SRC_DIR)/$${dir}/Makefile $${dir}/Makefile; \
fi \
done
endif
test::
@ $(MAKE) -C test
report::
@ $(MAKE) -C test report
clean::
@ $(MAKE) -C test clean
libs-only: all
tags::
$(Verb) etags `find . -type f -name '*.h' -or -name '*.cpp' | \
grep -v /lib/Headers | grep -v /test/`
cscope.files:
find tools lib include -name '*.cpp' \
-or -name '*.def' \
-or -name '*.td' \
-or -name '*.h' > cscope.files
.PHONY: test report clean cscope.files
endif

View File

@@ -1,5 +0,0 @@
# This file provides information for llvm-top
DepModule: llvm
ConfigCmd:
ConfigTest:
BuildCmd:

View File

@@ -1,114 +0,0 @@
//===---------------------------------------------------------------------===//
// Random Notes
//===---------------------------------------------------------------------===//
C90/C99/C++ Comparisons:
http://david.tribble.com/text/cdiffs.htm
//===---------------------------------------------------------------------===//
To time GCC preprocessing speed without output, use:
"time gcc -MM file"
This is similar to -Eonly.
//===---------------------------------------------------------------------===//
Creating and using a PTH file for performance measurement (use a release build).
$ clang -ccc-pch-is-pth -x objective-c-header INPUTS/Cocoa_h.m -o /tmp/tokencache
$ clang -cc1 -token-cache /tmp/tokencache INPUTS/Cocoa_h.m
//===---------------------------------------------------------------------===//
C++ Template Instantiation benchmark:
http://users.rcn.com/abrahams/instantiation_speed/index.html
//===---------------------------------------------------------------------===//
TODO: File Manager Speedup:
We currently do a lot of stat'ing for files that don't exist, particularly
when lots of -I paths exist (e.g. see the <iostream> example, check for
failures in stat in FileManager::getFile). It would be far better to make
the following changes:
1. FileEntry contains a sys::Path instead of a std::string for Name.
2. sys::Path contains timestamp and size, lazily computed. Eliminate from
FileEntry.
3. File UIDs are created on request, not when files are opened.
These changes make it possible to efficiently have FileEntry objects for
files that exist on the file system, but have not been used yet.
Once this is done:
1. DirectoryEntry gets a boolean value "has read entries". When false, not
all entries in the directory are in the file mgr, when true, they are.
2. Instead of stat'ing the file in FileManager::getFile, check to see if
the dir has been read. If so, fail immediately, if not, read the dir,
then retry.
3. Reading the dir uses the getdirentries syscall, creating a FileEntry
for all files found.
//===---------------------------------------------------------------------===//
// Specifying targets: -triple and -arch
//===---------------------------------------------------------------------===//
The clang supports "-triple" and "-arch" options. At most one -triple and one
-arch option may be specified. Both are optional.
The "selection of target" behavior is defined as follows:
(1) If the user does not specify -triple, we default to the host triple.
(2) If the user specifies a -arch, that overrides the arch in the host or
specified triple.
//===---------------------------------------------------------------------===//
verifyInputConstraint and verifyOutputConstraint should not return bool.
Instead we should return something like:
enum VerifyConstraintResult {
Valid,
// Output only
OutputOperandConstraintLacksEqualsCharacter,
MatchingConstraintNotValidInOutputOperand,
// Input only
InputOperandConstraintContainsEqualsCharacter,
MatchingConstraintReferencesInvalidOperandNumber,
// Both
PercentConstraintUsedWithLastOperand
};
//===---------------------------------------------------------------------===//
Blocks should not capture variables that are only used in dead code.
The rule that we came up with is that blocks are required to capture
variables if they're referenced in evaluated code, even if that code
doesn't actually rely on the value of the captured variable.
For example, this requires a capture:
(void) var;
But this does not:
if (false) puts(var);
Summary of <rdar://problem/9851835>: if we implement this, we should
warn about non-POD variables that are referenced but not captured, but
only if the non-reachability is not due to macro or template
metaprogramming.
//===---------------------------------------------------------------------===//
We can still apply a modified version of the constructor/destructor
delegation optimization in cases of virtual inheritance where:
- there is no function-try-block,
- the constructor signature is not variadic, and
- the parameter variables can safely be copied and repassed
to the base constructor because either
- they have not had their addresses taken by the vbase initializers or
- they were passed indirectly.
//===---------------------------------------------------------------------===//

View File

@@ -1,26 +0,0 @@
//===----------------------------------------------------------------------===//
// C Language Family Front-end
//===----------------------------------------------------------------------===//
Welcome to Clang. This is a compiler front-end for the C family of languages
(C, C++, Objective-C, and Objective-C++) which is built as part of the LLVM
compiler infrastructure project.
Unlike many other compiler frontends, Clang is useful for a number of things
beyond just compiling code: we intend for Clang to be host to a number of
different source level tools. One example of this is the Clang Static Analyzer.
If you're interested in more (including how to build Clang) it is best to read
the relevant web sites. Here are some pointers:
Information on Clang: http://clang.llvm.org/
Building and using Clang: http://clang.llvm.org/get_started.html
Clang Static Analyzer: http://clang-analyzer.llvm.org/
Information on the LLVM project: http://llvm.org/
If you have questions or comments about Clang, a great place to discuss them is
on the Clang development mailing list:
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev
If you find a bug in Clang, please file it in the LLVM bug tracker:
http://llvm.org/bugs/

View File

@@ -1,17 +0,0 @@
//===----------------------------------------------------------------------===//
// Clang Python Bindings
//===----------------------------------------------------------------------===//
This directory implements Python bindings for Clang.
You may need to alter LD_LIBRARY_PATH so that the Clang library can be
found. The unit tests are designed to be run with 'nosetests'. For example:
--
$ env PYTHONPATH=$(echo ~/llvm/tools/clang/bindings/python/) \
LD_LIBRARY_PATH=$(llvm-config --libdir) \
nosetests -v
tests.cindex.test_index.test_create ... ok
...
OK
--

View File

@@ -1,24 +0,0 @@
#===- __init__.py - Clang Python Bindings --------------------*- python -*--===#
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
#===------------------------------------------------------------------------===#
r"""
Clang Library Bindings
======================
This package provides access to the Clang compiler and libraries.
The available modules are:
cindex
Bindings for the Clang indexing library.
"""
__all__ = ['cindex']

File diff suppressed because it is too large Load Diff

View File

@@ -1,34 +0,0 @@
#===- enumerations.py - Python Enumerations ------------------*- python -*--===#
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
#===------------------------------------------------------------------------===#
"""
Clang Enumerations
==================
This module provides static definitions of enumerations that exist in libclang.
Enumerations are typically defined as a list of tuples. The exported values are
typically munged into other types or classes at module load time.
All enumerations are centrally defined in this file so they are all grouped
together and easier to audit. And, maybe even one day this file will be
automatically generated by scanning the libclang headers!
"""
# Maps to CXTokenKind. Note that libclang maintains a separate set of token
# enumerations from the C++ API.
TokenKinds = [
('PUNCTUATION', 0),
('KEYWORD', 1),
('IDENTIFIER', 2),
('LITERAL', 3),
('COMMENT', 4),
]
__all__ = ['TokenKinds']

View File

@@ -1,87 +0,0 @@
#!/usr/bin/env python
#===- cindex-dump.py - cindex/Python Source Dump -------------*- python -*--===#
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
#===------------------------------------------------------------------------===#
"""
A simple command line tool for dumping a source file using the Clang Index
Library.
"""
def get_diag_info(diag):
return { 'severity' : diag.severity,
'location' : diag.location,
'spelling' : diag.spelling,
'ranges' : diag.ranges,
'fixits' : diag.fixits }
def get_cursor_id(cursor, cursor_list = []):
if not opts.showIDs:
return None
if cursor is None:
return None
# FIXME: This is really slow. It would be nice if the index API exposed
# something that let us hash cursors.
for i,c in enumerate(cursor_list):
if cursor == c:
return i
cursor_list.append(cursor)
return len(cursor_list) - 1
def get_info(node, depth=0):
if opts.maxDepth is not None and depth >= opts.maxDepth:
children = None
else:
children = [get_info(c, depth+1)
for c in node.get_children()]
return { 'id' : get_cursor_id(node),
'kind' : node.kind,
'usr' : node.get_usr(),
'spelling' : node.spelling,
'location' : node.location,
'extent.start' : node.extent.start,
'extent.end' : node.extent.end,
'is_definition' : node.is_definition(),
'definition id' : get_cursor_id(node.get_definition()),
'children' : children }
def main():
from clang.cindex import Index
from pprint import pprint
from optparse import OptionParser, OptionGroup
global opts
parser = OptionParser("usage: %prog [options] {filename} [clang-args*]")
parser.add_option("", "--show-ids", dest="showIDs",
help="Don't compute cursor IDs (very slow)",
default=False)
parser.add_option("", "--max-depth", dest="maxDepth",
help="Limit cursor expansion to depth N",
metavar="N", type=int, default=None)
parser.disable_interspersed_args()
(opts, args) = parser.parse_args()
if len(args) == 0:
parser.error('invalid number arguments')
index = Index.create()
tu = index.parse(None, args)
if not tu:
parser.error("unable to load input")
pprint(('diags', map(get_diag_info, tu.diagnostics)))
pprint(('nodes', get_info(tu.cursor)))
if __name__ == '__main__':
main()

View File

@@ -1,58 +0,0 @@
#!/usr/bin/env python
#===- cindex-includes.py - cindex/Python Inclusion Graph -----*- python -*--===#
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
#===------------------------------------------------------------------------===#
"""
A simple command line tool for dumping a Graphviz description (dot) that
describes include dependencies.
"""
def main():
import sys
from clang.cindex import Index
from optparse import OptionParser, OptionGroup
parser = OptionParser("usage: %prog [options] {filename} [clang-args*]")
parser.disable_interspersed_args()
(opts, args) = parser.parse_args()
if len(args) == 0:
parser.error('invalid number arguments')
# FIXME: Add an output file option
out = sys.stdout
index = Index.create()
tu = index.parse(None, args)
if not tu:
parser.error("unable to load input")
# A helper function for generating the node name.
def name(f):
if f:
return "\"" + f.name + "\""
# Generate the include graph
out.write("digraph G {\n")
for i in tu.get_includes():
line = " ";
if i.is_input_file:
# Always write the input file as a node just in case it doesn't
# actually include anything. This would generate a 1 node graph.
line += name(i.include)
else:
line += '%s->%s' % (name(i.source), name(i.include))
line += "\n";
out.write(line)
out.write("}\n")
if __name__ == '__main__':
main()

View File

@@ -1,17 +0,0 @@
[
{
"directory": "/home/john.doe/MyProject",
"command": "clang++ -o project.o -c /home/john.doe/MyProject/project.cpp",
"file": "/home/john.doe/MyProject/project.cpp"
},
{
"directory": "/home/john.doe/MyProjectA",
"command": "clang++ -o project2.o -c /home/john.doe/MyProject/project2.cpp",
"file": "/home/john.doe/MyProject/project2.cpp"
},
{
"directory": "/home/john.doe/MyProjectB",
"command": "clang++ -DFEATURE=1 -o project2-feature.o -c /home/john.doe/MyProject/project2.cpp",
"file": "/home/john.doe/MyProject/project2.cpp"
}
]

View File

@@ -1,6 +0,0 @@
#ifndef HEADER1
#define HEADER1
#include "header3.h"
#endif

View File

@@ -1,6 +0,0 @@
#ifndef HEADER2
#define HEADER2
#include "header3.h"
#endif

View File

@@ -1,3 +0,0 @@
// Not a guarded header!
void f();

View File

@@ -1,6 +0,0 @@
#include "stdio.h"
int main(int argc, char* argv[]) {
printf("hello world\n");
return 0;
}

View File

@@ -1,5 +0,0 @@
#include "header1.h"
#include "header2.h"
#include "header1.h"
int main() { }

View File

@@ -1,2 +0,0 @@
int DECL_ONE = 1;
int DECL_TWO = 2;

View File

@@ -1,89 +0,0 @@
from clang.cindex import CompilationDatabase
from clang.cindex import CompilationDatabaseError
from clang.cindex import CompileCommands
from clang.cindex import CompileCommand
import os
import gc
kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS')
def test_create_fail():
"""Check we fail loading a database with an assertion"""
path = os.path.dirname(__file__)
try:
cdb = CompilationDatabase.fromDirectory(path)
except CompilationDatabaseError as e:
assert e.cdb_error == CompilationDatabaseError.ERROR_CANNOTLOADDATABASE
else:
assert False
def test_create():
"""Check we can load a compilation database"""
cdb = CompilationDatabase.fromDirectory(kInputsDir)
def test_lookup_fail():
"""Check file lookup failure"""
cdb = CompilationDatabase.fromDirectory(kInputsDir)
assert cdb.getCompileCommands('file_do_not_exist.cpp') == None
def test_lookup_succeed():
"""Check we get some results if the file exists in the db"""
cdb = CompilationDatabase.fromDirectory(kInputsDir)
cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp')
assert len(cmds) != 0
def test_1_compilecommand():
"""Check file with single compile command"""
cdb = CompilationDatabase.fromDirectory(kInputsDir)
cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp')
assert len(cmds) == 1
assert cmds[0].directory == '/home/john.doe/MyProject'
expected = [ 'clang++', '-o', 'project.o', '-c',
'/home/john.doe/MyProject/project.cpp']
for arg, exp in zip(cmds[0].arguments, expected):
assert arg == exp
def test_2_compilecommand():
"""Check file with 2 compile commands"""
cdb = CompilationDatabase.fromDirectory(kInputsDir)
cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project2.cpp')
assert len(cmds) == 2
expected = [
{ 'wd': '/home/john.doe/MyProjectA',
'line': ['clang++', '-o', 'project2.o', '-c',
'/home/john.doe/MyProject/project2.cpp']},
{ 'wd': '/home/john.doe/MyProjectB',
'line': ['clang++', '-DFEATURE=1', '-o', 'project2-feature.o', '-c',
'/home/john.doe/MyProject/project2.cpp']}
]
for i in range(len(cmds)):
assert cmds[i].directory == expected[i]['wd']
for arg, exp in zip(cmds[i].arguments, expected[i]['line']):
assert arg == exp
def test_compilecommand_iterator_stops():
"""Check that iterator stops after the correct number of elements"""
cdb = CompilationDatabase.fromDirectory(kInputsDir)
count = 0
for cmd in cdb.getCompileCommands('/home/john.doe/MyProject/project2.cpp'):
count += 1
assert count <= 2
def test_compilationDB_references():
"""Ensure CompilationsCommands are independent of the database"""
cdb = CompilationDatabase.fromDirectory(kInputsDir)
cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp')
del cdb
gc.collect()
workingdir = cmds[0].directory
def test_compilationCommands_references():
"""Ensure CompilationsCommand keeps a reference to CompilationCommands"""
cdb = CompilationDatabase.fromDirectory(kInputsDir)
cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp')
del cdb
cmd0 = cmds[0]
del cmds
gc.collect()
workingdir = cmd0.directory

View File

@@ -1,75 +0,0 @@
from clang.cindex import TranslationUnit
def check_completion_results(cr, expected):
assert cr is not None
assert len(cr.diagnostics) == 0
completions = [str(c) for c in cr.results]
for c in expected:
assert c in completions
def test_code_complete():
files = [('fake.c', """
/// Aaa.
int test1;
/// Bbb.
void test2(void);
void f() {
}
""")]
tu = TranslationUnit.from_source('fake.c', ['-std=c99'], unsaved_files=files,
options=TranslationUnit.PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION)
cr = tu.codeComplete('fake.c', 9, 1, unsaved_files=files, include_brief_comments=True)
expected = [
"{'int', ResultType} | {'test1', TypedText} || Priority: 50 || Availability: Available || Brief comment: Aaa.",
"{'void', ResultType} | {'test2', TypedText} | {'(', LeftParen} | {')', RightParen} || Priority: 50 || Availability: Available || Brief comment: Bbb.",
"{'return', TypedText} || Priority: 40 || Availability: Available || Brief comment: None"
]
check_completion_results(cr, expected)
def test_code_complete_availability():
files = [('fake.cpp', """
class P {
protected:
int member;
};
class Q : public P {
public:
using P::member;
};
void f(P x, Q y) {
x.; // member is inaccessible
y.; // member is accessible
}
""")]
tu = TranslationUnit.from_source('fake.cpp', ['-std=c++98'], unsaved_files=files)
cr = tu.codeComplete('fake.cpp', 12, 5, unsaved_files=files)
expected = [
"{'const', TypedText} || Priority: 40 || Availability: Available || Brief comment: None",
"{'volatile', TypedText} || Priority: 40 || Availability: Available || Brief comment: None",
"{'operator', TypedText} || Priority: 40 || Availability: Available || Brief comment: None",
"{'P', TypedText} | {'::', Text} || Priority: 75 || Availability: Available || Brief comment: None",
"{'Q', TypedText} | {'::', Text} || Priority: 75 || Availability: Available || Brief comment: None"
]
check_completion_results(cr, expected)
cr = tu.codeComplete('fake.cpp', 13, 5, unsaved_files=files)
expected = [
"{'P', TypedText} | {'::', Text} || Priority: 75 || Availability: Available || Brief comment: None",
"{'P &', ResultType} | {'operator=', TypedText} | {'(', LeftParen} | {'const P &', Placeholder} | {')', RightParen} || Priority: 34 || Availability: Available || Brief comment: None",
"{'int', ResultType} | {'member', TypedText} || Priority: 35 || Availability: NotAccessible || Brief comment: None",
"{'void', ResultType} | {'~P', TypedText} | {'(', LeftParen} | {')', RightParen} || Priority: 34 || Availability: Available || Brief comment: None"
]
check_completion_results(cr, expected)

View File

@@ -1,252 +0,0 @@
import gc
from clang.cindex import CursorKind
from clang.cindex import TranslationUnit
from clang.cindex import TypeKind
from .util import get_cursor
from .util import get_cursors
from .util import get_tu
kInput = """\
// FIXME: Find nicer way to drop builtins and other cruft.
int start_decl;
struct s0 {
int a;
int b;
};
struct s1;
void f0(int a0, int a1) {
int l0, l1;
if (a0)
return;
for (;;) {
break;
}
}
"""
def test_get_children():
tu = get_tu(kInput)
# Skip until past start_decl.
it = tu.cursor.get_children()
while it.next().spelling != 'start_decl':
pass
tu_nodes = list(it)
assert len(tu_nodes) == 3
for cursor in tu_nodes:
assert cursor.translation_unit is not None
assert tu_nodes[0] != tu_nodes[1]
assert tu_nodes[0].kind == CursorKind.STRUCT_DECL
assert tu_nodes[0].spelling == 's0'
assert tu_nodes[0].is_definition() == True
assert tu_nodes[0].location.file.name == 't.c'
assert tu_nodes[0].location.line == 4
assert tu_nodes[0].location.column == 8
assert tu_nodes[0].hash > 0
assert tu_nodes[0].translation_unit is not None
s0_nodes = list(tu_nodes[0].get_children())
assert len(s0_nodes) == 2
assert s0_nodes[0].kind == CursorKind.FIELD_DECL
assert s0_nodes[0].spelling == 'a'
assert s0_nodes[0].type.kind == TypeKind.INT
assert s0_nodes[1].kind == CursorKind.FIELD_DECL
assert s0_nodes[1].spelling == 'b'
assert s0_nodes[1].type.kind == TypeKind.INT
assert tu_nodes[1].kind == CursorKind.STRUCT_DECL
assert tu_nodes[1].spelling == 's1'
assert tu_nodes[1].displayname == 's1'
assert tu_nodes[1].is_definition() == False
assert tu_nodes[2].kind == CursorKind.FUNCTION_DECL
assert tu_nodes[2].spelling == 'f0'
assert tu_nodes[2].displayname == 'f0(int, int)'
assert tu_nodes[2].is_definition() == True
def test_references():
"""Ensure that references to TranslationUnit are kept."""
tu = get_tu('int x;')
cursors = list(tu.cursor.get_children())
assert len(cursors) > 0
cursor = cursors[0]
assert isinstance(cursor.translation_unit, TranslationUnit)
# Delete reference to TU and perform a full GC.
del tu
gc.collect()
assert isinstance(cursor.translation_unit, TranslationUnit)
# If the TU was destroyed, this should cause a segfault.
parent = cursor.semantic_parent
def test_canonical():
source = 'struct X; struct X; struct X { int member; };'
tu = get_tu(source)
cursors = []
for cursor in tu.cursor.get_children():
if cursor.spelling == 'X':
cursors.append(cursor)
assert len(cursors) == 3
assert cursors[1].canonical == cursors[2].canonical
def test_is_static_method():
"""Ensure Cursor.is_static_method works."""
source = 'class X { static void foo(); void bar(); };'
tu = get_tu(source, lang='cpp')
cls = get_cursor(tu, 'X')
foo = get_cursor(tu, 'foo')
bar = get_cursor(tu, 'bar')
assert cls is not None
assert foo is not None
assert bar is not None
assert foo.is_static_method()
assert not bar.is_static_method()
def test_underlying_type():
tu = get_tu('typedef int foo;')
typedef = get_cursor(tu, 'foo')
assert typedef is not None
assert typedef.kind.is_declaration()
underlying = typedef.underlying_typedef_type
assert underlying.kind == TypeKind.INT
kParentTest = """\
class C {
void f();
}
void C::f() { }
"""
def test_semantic_parent():
tu = get_tu(kParentTest, 'cpp')
curs = get_cursors(tu, 'f')
decl = get_cursor(tu, 'C')
assert(len(curs) == 2)
assert(curs[0].semantic_parent == curs[1].semantic_parent)
assert(curs[0].semantic_parent == decl)
def test_lexical_parent():
tu = get_tu(kParentTest, 'cpp')
curs = get_cursors(tu, 'f')
decl = get_cursor(tu, 'C')
assert(len(curs) == 2)
assert(curs[0].lexical_parent != curs[1].lexical_parent)
assert(curs[0].lexical_parent == decl)
assert(curs[1].lexical_parent == tu.cursor)
def test_enum_type():
tu = get_tu('enum TEST { FOO=1, BAR=2 };')
enum = get_cursor(tu, 'TEST')
assert enum is not None
assert enum.kind == CursorKind.ENUM_DECL
enum_type = enum.enum_type
assert enum_type.kind == TypeKind.UINT
def test_enum_type_cpp():
tu = get_tu('enum TEST : long long { FOO=1, BAR=2 };', lang="cpp")
enum = get_cursor(tu, 'TEST')
assert enum is not None
assert enum.kind == CursorKind.ENUM_DECL
assert enum.enum_type.kind == TypeKind.LONGLONG
def test_objc_type_encoding():
tu = get_tu('int i;', lang='objc')
i = get_cursor(tu, 'i')
assert i is not None
assert i.objc_type_encoding == 'i'
def test_enum_values():
tu = get_tu('enum TEST { SPAM=1, EGG, HAM = EGG * 20};')
enum = get_cursor(tu, 'TEST')
assert enum is not None
assert enum.kind == CursorKind.ENUM_DECL
enum_constants = list(enum.get_children())
assert len(enum_constants) == 3
spam, egg, ham = enum_constants
assert spam.kind == CursorKind.ENUM_CONSTANT_DECL
assert spam.enum_value == 1
assert egg.kind == CursorKind.ENUM_CONSTANT_DECL
assert egg.enum_value == 2
assert ham.kind == CursorKind.ENUM_CONSTANT_DECL
assert ham.enum_value == 40
def test_enum_values_cpp():
tu = get_tu('enum TEST : long long { SPAM = -1, HAM = 0x10000000000};', lang="cpp")
enum = get_cursor(tu, 'TEST')
assert enum is not None
assert enum.kind == CursorKind.ENUM_DECL
enum_constants = list(enum.get_children())
assert len(enum_constants) == 2
spam, ham = enum_constants
assert spam.kind == CursorKind.ENUM_CONSTANT_DECL
assert spam.enum_value == -1
assert ham.kind == CursorKind.ENUM_CONSTANT_DECL
assert ham.enum_value == 0x10000000000
def test_annotation_attribute():
tu = get_tu('int foo (void) __attribute__ ((annotate("here be annotation attribute")));')
foo = get_cursor(tu, 'foo')
assert foo is not None
for c in foo.get_children():
if c.kind == CursorKind.ANNOTATE_ATTR:
assert c.displayname == "here be annotation attribute"
break
else:
assert False, "Couldn't find annotation"
def test_result_type():
tu = get_tu('int foo();')
foo = get_cursor(tu, 'foo')
assert foo is not None
t = foo.result_type
assert t.kind == TypeKind.INT
def test_get_tokens():
"""Ensure we can map cursors back to tokens."""
tu = get_tu('int foo(int i);')
foo = get_cursor(tu, 'foo')
tokens = list(foo.get_tokens())
assert len(tokens) == 7
assert tokens[0].spelling == 'int'
assert tokens[1].spelling == 'foo'
def test_get_arguments():
tu = get_tu('void foo(int i, int j);')
foo = get_cursor(tu, 'foo')
arguments = list(foo.get_arguments())
assert len(arguments) == 2
assert arguments[0].spelling == "i"
assert arguments[1].spelling == "j"

View File

@@ -1,40 +0,0 @@
from clang.cindex import CursorKind
def test_name():
assert CursorKind.UNEXPOSED_DECL.name is 'UNEXPOSED_DECL'
def test_get_all_kinds():
assert CursorKind.UNEXPOSED_DECL in CursorKind.get_all_kinds()
assert CursorKind.TRANSLATION_UNIT in CursorKind.get_all_kinds()
def test_kind_groups():
"""Check that every kind classifies to exactly one group."""
assert CursorKind.UNEXPOSED_DECL.is_declaration()
assert CursorKind.TYPE_REF.is_reference()
assert CursorKind.DECL_REF_EXPR.is_expression()
assert CursorKind.UNEXPOSED_STMT.is_statement()
assert CursorKind.INVALID_FILE.is_invalid()
assert CursorKind.TRANSLATION_UNIT.is_translation_unit()
assert not CursorKind.TYPE_REF.is_translation_unit()
assert CursorKind.PREPROCESSING_DIRECTIVE.is_preprocessing()
assert not CursorKind.TYPE_REF.is_preprocessing()
assert CursorKind.UNEXPOSED_DECL.is_unexposed()
assert not CursorKind.TYPE_REF.is_unexposed()
for k in CursorKind.get_all_kinds():
group = [n for n in ('is_declaration', 'is_reference', 'is_expression',
'is_statement', 'is_invalid', 'is_attribute')
if getattr(k, n)()]
if k in ( CursorKind.TRANSLATION_UNIT,
CursorKind.MACRO_DEFINITION,
CursorKind.MACRO_INSTANTIATION,
CursorKind.INCLUSION_DIRECTIVE,
CursorKind.PREPROCESSING_DIRECTIVE):
assert len(group) == 0
else:
assert len(group) == 1

View File

@@ -1,82 +0,0 @@
from clang.cindex import *
from .util import get_tu
# FIXME: We need support for invalid translation units to test better.
def test_diagnostic_warning():
tu = get_tu('int f0() {}\n')
assert len(tu.diagnostics) == 1
assert tu.diagnostics[0].severity == Diagnostic.Warning
assert tu.diagnostics[0].location.line == 1
assert tu.diagnostics[0].location.column == 11
assert (tu.diagnostics[0].spelling ==
'control reaches end of non-void function')
def test_diagnostic_note():
# FIXME: We aren't getting notes here for some reason.
tu = get_tu('#define A x\nvoid *A = 1;\n')
assert len(tu.diagnostics) == 1
assert tu.diagnostics[0].severity == Diagnostic.Warning
assert tu.diagnostics[0].location.line == 2
assert tu.diagnostics[0].location.column == 7
assert 'incompatible' in tu.diagnostics[0].spelling
# assert tu.diagnostics[1].severity == Diagnostic.Note
# assert tu.diagnostics[1].location.line == 1
# assert tu.diagnostics[1].location.column == 11
# assert tu.diagnostics[1].spelling == 'instantiated from'
def test_diagnostic_fixit():
tu = get_tu('struct { int f0; } x = { f0 : 1 };')
assert len(tu.diagnostics) == 1
assert tu.diagnostics[0].severity == Diagnostic.Warning
assert tu.diagnostics[0].location.line == 1
assert tu.diagnostics[0].location.column == 26
assert tu.diagnostics[0].spelling.startswith('use of GNU old-style')
assert len(tu.diagnostics[0].fixits) == 1
assert tu.diagnostics[0].fixits[0].range.start.line == 1
assert tu.diagnostics[0].fixits[0].range.start.column == 26
assert tu.diagnostics[0].fixits[0].range.end.line == 1
assert tu.diagnostics[0].fixits[0].range.end.column == 30
assert tu.diagnostics[0].fixits[0].value == '.f0 = '
def test_diagnostic_range():
tu = get_tu('void f() { int i = "a" + 1; }')
assert len(tu.diagnostics) == 1
assert tu.diagnostics[0].severity == Diagnostic.Warning
assert tu.diagnostics[0].location.line == 1
assert tu.diagnostics[0].location.column == 16
assert tu.diagnostics[0].spelling.startswith('incompatible pointer to')
assert len(tu.diagnostics[0].fixits) == 0
assert len(tu.diagnostics[0].ranges) == 1
assert tu.diagnostics[0].ranges[0].start.line == 1
assert tu.diagnostics[0].ranges[0].start.column == 20
assert tu.diagnostics[0].ranges[0].end.line == 1
assert tu.diagnostics[0].ranges[0].end.column == 27
try:
tu.diagnostics[0].ranges[1].start.line
except IndexError:
assert True
else:
assert False
def test_diagnostic_category():
"""Ensure that category properties work."""
tu = get_tu('int f(int i) { return 7; }', all_warnings=True)
assert len(tu.diagnostics) == 1
d = tu.diagnostics[0]
assert d.severity == Diagnostic.Warning
assert d.location.line == 1
assert d.location.column == 11
assert d.category_number == 2
assert d.category_name == 'Semantic Issue'
def test_diagnostic_option():
"""Ensure that category option properties work."""
tu = get_tu('int f(int i) { return 7; }', all_warnings=True)
assert len(tu.diagnostics) == 1
d = tu.diagnostics[0]
assert d.option == '-Wunused-parameter'
assert d.disable_option == '-Wno-unused-parameter'

View File

@@ -1,9 +0,0 @@
from clang.cindex import Index, File
def test_file():
index = Index.create()
tu = index.parse('t.c', unsaved_files = [('t.c', "")])
file = File.from_name(tu, "t.c")
assert str(file) == "t.c"
assert file.name == "t.c"
assert repr(file) == "<File: t.c>"

View File

@@ -1,15 +0,0 @@
from clang.cindex import *
import os
kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS')
def test_create():
index = Index.create()
# FIXME: test Index.read
def test_parse():
index = Index.create()
assert isinstance(index, Index)
tu = index.parse(os.path.join(kInputsDir, 'hello.cpp'))
assert isinstance(tu, TranslationUnit)

View File

@@ -1,95 +0,0 @@
from clang.cindex import Cursor
from clang.cindex import File
from clang.cindex import SourceLocation
from clang.cindex import SourceRange
from .util import get_cursor
from .util import get_tu
baseInput="int one;\nint two;\n"
def assert_location(loc, line, column, offset):
assert loc.line == line
assert loc.column == column
assert loc.offset == offset
def test_location():
tu = get_tu(baseInput)
one = get_cursor(tu, 'one')
two = get_cursor(tu, 'two')
assert one is not None
assert two is not None
assert_location(one.location,line=1,column=5,offset=4)
assert_location(two.location,line=2,column=5,offset=13)
# adding a linebreak at top should keep columns same
tu = get_tu('\n' + baseInput)
one = get_cursor(tu, 'one')
two = get_cursor(tu, 'two')
assert one is not None
assert two is not None
assert_location(one.location,line=2,column=5,offset=5)
assert_location(two.location,line=3,column=5,offset=14)
# adding a space should affect column on first line only
tu = get_tu(' ' + baseInput)
one = get_cursor(tu, 'one')
two = get_cursor(tu, 'two')
assert_location(one.location,line=1,column=6,offset=5)
assert_location(two.location,line=2,column=5,offset=14)
# define the expected location ourselves and see if it matches
# the returned location
tu = get_tu(baseInput)
file = File.from_name(tu, 't.c')
location = SourceLocation.from_position(tu, file, 1, 5)
cursor = Cursor.from_location(tu, location)
one = get_cursor(tu, 'one')
assert one is not None
assert one == cursor
# Ensure locations referring to the same entity are equivalent.
location2 = SourceLocation.from_position(tu, file, 1, 5)
assert location == location2
location3 = SourceLocation.from_position(tu, file, 1, 4)
assert location2 != location3
offset_location = SourceLocation.from_offset(tu, file, 5)
cursor = Cursor.from_location(tu, offset_location)
verified = False
for n in [n for n in tu.cursor.get_children() if n.spelling == 'one']:
assert n == cursor
verified = True
assert verified
def test_extent():
tu = get_tu(baseInput)
one = get_cursor(tu, 'one')
two = get_cursor(tu, 'two')
assert_location(one.extent.start,line=1,column=1,offset=0)
assert_location(one.extent.end,line=1,column=8,offset=7)
assert baseInput[one.extent.start.offset:one.extent.end.offset] == "int one"
assert_location(two.extent.start,line=2,column=1,offset=9)
assert_location(two.extent.end,line=2,column=8,offset=16)
assert baseInput[two.extent.start.offset:two.extent.end.offset] == "int two"
file = File.from_name(tu, 't.c')
location1 = SourceLocation.from_position(tu, file, 1, 1)
location2 = SourceLocation.from_position(tu, file, 1, 8)
range1 = SourceRange.from_locations(location1, location2)
range2 = SourceRange.from_locations(location1, location2)
assert range1 == range2
location3 = SourceLocation.from_position(tu, file, 1, 6)
range3 = SourceRange.from_locations(location1, location3)
assert range1 != range3

View File

@@ -1,43 +0,0 @@
from clang.cindex import TokenKind
from nose.tools import eq_
from nose.tools import ok_
from nose.tools import raises
def test_constructor():
"""Ensure TokenKind constructor works as expected."""
t = TokenKind(5, 'foo')
eq_(t.value, 5)
eq_(t.name, 'foo')
@raises(ValueError)
def test_bad_register():
"""Ensure a duplicate value is rejected for registration."""
TokenKind.register(2, 'foo')
@raises(ValueError)
def test_unknown_value():
"""Ensure trying to fetch an unknown value raises."""
TokenKind.from_value(-1)
def test_registration():
"""Ensure that items registered appear as class attributes."""
ok_(hasattr(TokenKind, 'LITERAL'))
literal = TokenKind.LITERAL
ok_(isinstance(literal, TokenKind))
def test_from_value():
"""Ensure registered values can be obtained from from_value()."""
t = TokenKind.from_value(3)
ok_(isinstance(t, TokenKind))
eq_(t, TokenKind.LITERAL)
def test_repr():
"""Ensure repr() works."""
r = repr(TokenKind.LITERAL)
eq_(r, 'TokenKind.LITERAL')

View File

@@ -1,52 +0,0 @@
from clang.cindex import CursorKind
from clang.cindex import Index
from clang.cindex import SourceLocation
from clang.cindex import SourceRange
from clang.cindex import TokenKind
from nose.tools import eq_
from nose.tools import ok_
from .util import get_tu
def test_token_to_cursor():
"""Ensure we can obtain a Cursor from a Token instance."""
tu = get_tu('int i = 5;')
r = tu.get_extent('t.c', (0, 9))
tokens = list(tu.get_tokens(extent=r))
assert len(tokens) == 5
assert tokens[1].spelling == 'i'
assert tokens[1].kind == TokenKind.IDENTIFIER
cursor = tokens[1].cursor
assert cursor.kind == CursorKind.VAR_DECL
assert tokens[1].cursor == tokens[2].cursor
def test_token_location():
"""Ensure Token.location works."""
tu = get_tu('int foo = 10;')
r = tu.get_extent('t.c', (0, 11))
tokens = list(tu.get_tokens(extent=r))
eq_(len(tokens), 4)
loc = tokens[1].location
ok_(isinstance(loc, SourceLocation))
eq_(loc.line, 1)
eq_(loc.column, 5)
eq_(loc.offset, 4)
def test_token_extent():
"""Ensure Token.extent works."""
tu = get_tu('int foo = 10;')
r = tu.get_extent('t.c', (0, 11))
tokens = list(tu.get_tokens(extent=r))
eq_(len(tokens), 4)
extent = tokens[1].extent
ok_(isinstance(extent, SourceRange))
eq_(extent.start.offset, 4)
eq_(extent.end.offset, 7)

View File

@@ -1,241 +0,0 @@
import gc
import os
from clang.cindex import CursorKind
from clang.cindex import Cursor
from clang.cindex import File
from clang.cindex import Index
from clang.cindex import SourceLocation
from clang.cindex import SourceRange
from clang.cindex import TranslationUnitSaveError
from clang.cindex import TranslationUnit
from .util import get_cursor
from .util import get_tu
kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS')
def test_spelling():
path = os.path.join(kInputsDir, 'hello.cpp')
tu = TranslationUnit.from_source(path)
assert tu.spelling == path
def test_cursor():
path = os.path.join(kInputsDir, 'hello.cpp')
tu = get_tu(path)
c = tu.cursor
assert isinstance(c, Cursor)
assert c.kind is CursorKind.TRANSLATION_UNIT
def test_parse_arguments():
path = os.path.join(kInputsDir, 'parse_arguments.c')
tu = TranslationUnit.from_source(path, ['-DDECL_ONE=hello', '-DDECL_TWO=hi'])
spellings = [c.spelling for c in tu.cursor.get_children()]
assert spellings[-2] == 'hello'
assert spellings[-1] == 'hi'
def test_reparse_arguments():
path = os.path.join(kInputsDir, 'parse_arguments.c')
tu = TranslationUnit.from_source(path, ['-DDECL_ONE=hello', '-DDECL_TWO=hi'])
tu.reparse()
spellings = [c.spelling for c in tu.cursor.get_children()]
assert spellings[-2] == 'hello'
assert spellings[-1] == 'hi'
def test_unsaved_files():
tu = TranslationUnit.from_source('fake.c', ['-I./'], unsaved_files = [
('fake.c', """
#include "fake.h"
int x;
int SOME_DEFINE;
"""),
('./fake.h', """
#define SOME_DEFINE y
""")
])
spellings = [c.spelling for c in tu.cursor.get_children()]
assert spellings[-2] == 'x'
assert spellings[-1] == 'y'
def test_unsaved_files_2():
import StringIO
tu = TranslationUnit.from_source('fake.c', unsaved_files = [
('fake.c', StringIO.StringIO('int x;'))])
spellings = [c.spelling for c in tu.cursor.get_children()]
assert spellings[-1] == 'x'
def normpaths_equal(path1, path2):
""" Compares two paths for equality after normalizing them with
os.path.normpath
"""
return os.path.normpath(path1) == os.path.normpath(path2)
def test_includes():
def eq(expected, actual):
if not actual.is_input_file:
return normpaths_equal(expected[0], actual.source.name) and \
normpaths_equal(expected[1], actual.include.name)
else:
return normpaths_equal(expected[1], actual.include.name)
src = os.path.join(kInputsDir, 'include.cpp')
h1 = os.path.join(kInputsDir, "header1.h")
h2 = os.path.join(kInputsDir, "header2.h")
h3 = os.path.join(kInputsDir, "header3.h")
inc = [(src, h1), (h1, h3), (src, h2), (h2, h3)]
tu = TranslationUnit.from_source(src)
for i in zip(inc, tu.get_includes()):
assert eq(i[0], i[1])
def save_tu(tu):
"""Convenience API to save a TranslationUnit to a file.
Returns the filename it was saved to.
"""
# FIXME Generate a temp file path using system APIs.
base = 'TEMP_FOR_TRANSLATIONUNIT_SAVE.c'
path = os.path.join(kInputsDir, base)
# Just in case.
if os.path.exists(path):
os.unlink(path)
tu.save(path)
return path
def test_save():
"""Ensure TranslationUnit.save() works."""
tu = get_tu('int foo();')
path = save_tu(tu)
assert os.path.exists(path)
assert os.path.getsize(path) > 0
os.unlink(path)
def test_save_translation_errors():
"""Ensure that saving to an invalid directory raises."""
tu = get_tu('int foo();')
path = '/does/not/exist/llvm-test.ast'
assert not os.path.exists(os.path.dirname(path))
try:
tu.save(path)
assert False
except TranslationUnitSaveError as ex:
expected = TranslationUnitSaveError.ERROR_UNKNOWN
assert ex.save_error == expected
def test_load():
"""Ensure TranslationUnits can be constructed from saved files."""
tu = get_tu('int foo();')
assert len(tu.diagnostics) == 0
path = save_tu(tu)
assert os.path.exists(path)
assert os.path.getsize(path) > 0
tu2 = TranslationUnit.from_ast_file(filename=path)
assert len(tu2.diagnostics) == 0
foo = get_cursor(tu2, 'foo')
assert foo is not None
# Just in case there is an open file descriptor somewhere.
del tu2
os.unlink(path)
def test_index_parse():
path = os.path.join(kInputsDir, 'hello.cpp')
index = Index.create()
tu = index.parse(path)
assert isinstance(tu, TranslationUnit)
def test_get_file():
"""Ensure tu.get_file() works appropriately."""
tu = get_tu('int foo();')
f = tu.get_file('t.c')
assert isinstance(f, File)
assert f.name == 't.c'
try:
f = tu.get_file('foobar.cpp')
except:
pass
else:
assert False
def test_get_source_location():
"""Ensure tu.get_source_location() works."""
tu = get_tu('int foo();')
location = tu.get_location('t.c', 2)
assert isinstance(location, SourceLocation)
assert location.offset == 2
assert location.file.name == 't.c'
location = tu.get_location('t.c', (1, 3))
assert isinstance(location, SourceLocation)
assert location.line == 1
assert location.column == 3
assert location.file.name == 't.c'
def test_get_source_range():
"""Ensure tu.get_source_range() works."""
tu = get_tu('int foo();')
r = tu.get_extent('t.c', (1,4))
assert isinstance(r, SourceRange)
assert r.start.offset == 1
assert r.end.offset == 4
assert r.start.file.name == 't.c'
assert r.end.file.name == 't.c'
r = tu.get_extent('t.c', ((1,2), (1,3)))
assert isinstance(r, SourceRange)
assert r.start.line == 1
assert r.start.column == 2
assert r.end.line == 1
assert r.end.column == 3
assert r.start.file.name == 't.c'
assert r.end.file.name == 't.c'
start = tu.get_location('t.c', 0)
end = tu.get_location('t.c', 5)
r = tu.get_extent('t.c', (start, end))
assert isinstance(r, SourceRange)
assert r.start.offset == 0
assert r.end.offset == 5
assert r.start.file.name == 't.c'
assert r.end.file.name == 't.c'
def test_get_tokens_gc():
"""Ensures get_tokens() works properly with garbage collection."""
tu = get_tu('int foo();')
r = tu.get_extent('t.c', (0, 10))
tokens = list(tu.get_tokens(extent=r))
assert tokens[0].spelling == 'int'
gc.collect()
assert tokens[0].spelling == 'int'
del tokens[1]
gc.collect()
assert tokens[0].spelling == 'int'
# May trigger segfault if we don't do our job properly.
del tokens
gc.collect()
gc.collect() # Just in case.

View File

@@ -1,300 +0,0 @@
import gc
from clang.cindex import CursorKind
from clang.cindex import TranslationUnit
from clang.cindex import TypeKind
from nose.tools import raises
from .util import get_cursor
from .util import get_tu
kInput = """\
typedef int I;
struct teststruct {
int a;
I b;
long c;
unsigned long d;
signed long e;
const int f;
int *g;
int ***h;
};
"""
def test_a_struct():
tu = get_tu(kInput)
teststruct = get_cursor(tu, 'teststruct')
assert teststruct is not None, "Could not find teststruct."
fields = list(teststruct.get_children())
assert all(x.kind == CursorKind.FIELD_DECL for x in fields)
assert all(x.translation_unit is not None for x in fields)
assert fields[0].spelling == 'a'
assert not fields[0].type.is_const_qualified()
assert fields[0].type.kind == TypeKind.INT
assert fields[0].type.get_canonical().kind == TypeKind.INT
assert fields[1].spelling == 'b'
assert not fields[1].type.is_const_qualified()
assert fields[1].type.kind == TypeKind.TYPEDEF
assert fields[1].type.get_canonical().kind == TypeKind.INT
assert fields[1].type.get_declaration().spelling == 'I'
assert fields[2].spelling == 'c'
assert not fields[2].type.is_const_qualified()
assert fields[2].type.kind == TypeKind.LONG
assert fields[2].type.get_canonical().kind == TypeKind.LONG
assert fields[3].spelling == 'd'
assert not fields[3].type.is_const_qualified()
assert fields[3].type.kind == TypeKind.ULONG
assert fields[3].type.get_canonical().kind == TypeKind.ULONG
assert fields[4].spelling == 'e'
assert not fields[4].type.is_const_qualified()
assert fields[4].type.kind == TypeKind.LONG
assert fields[4].type.get_canonical().kind == TypeKind.LONG
assert fields[5].spelling == 'f'
assert fields[5].type.is_const_qualified()
assert fields[5].type.kind == TypeKind.INT
assert fields[5].type.get_canonical().kind == TypeKind.INT
assert fields[6].spelling == 'g'
assert not fields[6].type.is_const_qualified()
assert fields[6].type.kind == TypeKind.POINTER
assert fields[6].type.get_pointee().kind == TypeKind.INT
assert fields[7].spelling == 'h'
assert not fields[7].type.is_const_qualified()
assert fields[7].type.kind == TypeKind.POINTER
assert fields[7].type.get_pointee().kind == TypeKind.POINTER
assert fields[7].type.get_pointee().get_pointee().kind == TypeKind.POINTER
assert fields[7].type.get_pointee().get_pointee().get_pointee().kind == TypeKind.INT
def test_references():
"""Ensure that a Type maintains a reference to a TranslationUnit."""
tu = get_tu('int x;')
children = list(tu.cursor.get_children())
assert len(children) > 0
cursor = children[0]
t = cursor.type
assert isinstance(t.translation_unit, TranslationUnit)
# Delete main TranslationUnit reference and force a GC.
del tu
gc.collect()
assert isinstance(t.translation_unit, TranslationUnit)
# If the TU was destroyed, this should cause a segfault.
decl = t.get_declaration()
constarrayInput="""
struct teststruct {
void *A[2];
};
"""
def testConstantArray():
tu = get_tu(constarrayInput)
teststruct = get_cursor(tu, 'teststruct')
assert teststruct is not None, "Didn't find teststruct??"
fields = list(teststruct.get_children())
assert fields[0].spelling == 'A'
assert fields[0].type.kind == TypeKind.CONSTANTARRAY
assert fields[0].type.get_array_element_type() is not None
assert fields[0].type.get_array_element_type().kind == TypeKind.POINTER
assert fields[0].type.get_array_size() == 2
def test_equal():
"""Ensure equivalence operators work on Type."""
source = 'int a; int b; void *v;'
tu = get_tu(source)
a = get_cursor(tu, 'a')
b = get_cursor(tu, 'b')
v = get_cursor(tu, 'v')
assert a is not None
assert b is not None
assert v is not None
assert a.type == b.type
assert a.type != v.type
assert a.type != None
assert a.type != 'foo'
def test_typekind_spelling():
"""Ensure TypeKind.spelling works."""
tu = get_tu('int a;')
a = get_cursor(tu, 'a')
assert a is not None
assert a.type.kind.spelling == 'Int'
def test_function_argument_types():
"""Ensure that Type.argument_types() works as expected."""
tu = get_tu('void f(int, int);')
f = get_cursor(tu, 'f')
assert f is not None
args = f.type.argument_types()
assert args is not None
assert len(args) == 2
t0 = args[0]
assert t0 is not None
assert t0.kind == TypeKind.INT
t1 = args[1]
assert t1 is not None
assert t1.kind == TypeKind.INT
args2 = list(args)
assert len(args2) == 2
assert t0 == args2[0]
assert t1 == args2[1]
@raises(TypeError)
def test_argument_types_string_key():
"""Ensure that non-int keys raise a TypeError."""
tu = get_tu('void f(int, int);')
f = get_cursor(tu, 'f')
assert f is not None
args = f.type.argument_types()
assert len(args) == 2
args['foo']
@raises(IndexError)
def test_argument_types_negative_index():
"""Ensure that negative indexes on argument_types Raises an IndexError."""
tu = get_tu('void f(int, int);')
f = get_cursor(tu, 'f')
args = f.type.argument_types()
args[-1]
@raises(IndexError)
def test_argument_types_overflow_index():
"""Ensure that indexes beyond the length of Type.argument_types() raise."""
tu = get_tu('void f(int, int);')
f = get_cursor(tu, 'f')
args = f.type.argument_types()
args[2]
@raises(Exception)
def test_argument_types_invalid_type():
"""Ensure that obtaining argument_types on a Type without them raises."""
tu = get_tu('int i;')
i = get_cursor(tu, 'i')
assert i is not None
i.type.argument_types()
def test_is_pod():
"""Ensure Type.is_pod() works."""
tu = get_tu('int i; void f();')
i = get_cursor(tu, 'i')
f = get_cursor(tu, 'f')
assert i is not None
assert f is not None
assert i.type.is_pod()
assert not f.type.is_pod()
def test_function_variadic():
"""Ensure Type.is_function_variadic works."""
source ="""
#include <stdarg.h>
void foo(int a, ...);
void bar(int a, int b);
"""
tu = get_tu(source)
foo = get_cursor(tu, 'foo')
bar = get_cursor(tu, 'bar')
assert foo is not None
assert bar is not None
assert isinstance(foo.type.is_function_variadic(), bool)
assert foo.type.is_function_variadic()
assert not bar.type.is_function_variadic()
def test_element_type():
"""Ensure Type.element_type works."""
tu = get_tu('int i[5];')
i = get_cursor(tu, 'i')
assert i is not None
assert i.type.kind == TypeKind.CONSTANTARRAY
assert i.type.element_type.kind == TypeKind.INT
@raises(Exception)
def test_invalid_element_type():
"""Ensure Type.element_type raises if type doesn't have elements."""
tu = get_tu('int i;')
i = get_cursor(tu, 'i')
assert i is not None
i.element_type
def test_element_count():
"""Ensure Type.element_count works."""
tu = get_tu('int i[5]; int j;')
i = get_cursor(tu, 'i')
j = get_cursor(tu, 'j')
assert i is not None
assert j is not None
assert i.type.element_count == 5
try:
j.type.element_count
assert False
except:
assert True
def test_is_volatile_qualified():
"""Ensure Type.is_volatile_qualified works."""
tu = get_tu('volatile int i = 4; int j = 2;')
i = get_cursor(tu, 'i')
j = get_cursor(tu, 'j')
assert i is not None
assert j is not None
assert isinstance(i.type.is_volatile_qualified(), bool)
assert i.type.is_volatile_qualified()
assert not j.type.is_volatile_qualified()
def test_is_restrict_qualified():
"""Ensure Type.is_restrict_qualified works."""
tu = get_tu('struct s { void * restrict i; void * j; };')
i = get_cursor(tu, 'i')
j = get_cursor(tu, 'j')
assert i is not None
assert j is not None
assert isinstance(i.type.is_restrict_qualified(), bool)
assert i.type.is_restrict_qualified()
assert not j.type.is_restrict_qualified()

View File

@@ -1,93 +0,0 @@
# This file provides common utility functions for the test suite.
from clang.cindex import Cursor
from clang.cindex import TranslationUnit
def get_tu(source, lang='c', all_warnings=False):
"""Obtain a translation unit from source and language.
By default, the translation unit is created from source file "t.<ext>"
where <ext> is the default file extension for the specified language. By
default it is C, so "t.c" is the default file name.
Supported languages are {c, cpp, objc}.
all_warnings is a convenience argument to enable all compiler warnings.
"""
name = 't.c'
args = []
if lang == 'cpp':
name = 't.cpp'
args.append('-std=c++11')
elif lang == 'objc':
name = 't.m'
elif lang != 'c':
raise Exception('Unknown language: %s' % lang)
if all_warnings:
args += ['-Wall', '-Wextra']
return TranslationUnit.from_source(name, args, unsaved_files=[(name,
source)])
def get_cursor(source, spelling):
"""Obtain a cursor from a source object.
This provides a convenient search mechanism to find a cursor with specific
spelling within a source. The first argument can be either a
TranslationUnit or Cursor instance.
If the cursor is not found, None is returned.
"""
children = []
if isinstance(source, Cursor):
children = source.get_children()
else:
# Assume TU
children = source.cursor.get_children()
for cursor in children:
if cursor.spelling == spelling:
return cursor
# Recurse into children.
result = get_cursor(cursor, spelling)
if result is not None:
return result
return None
def get_cursors(source, spelling):
"""Obtain all cursors from a source object with a specific spelling.
This provides a convenient search mechanism to find all cursors with specific
spelling within a source. The first argument can be either a
TranslationUnit or Cursor instance.
If no cursors are found, an empty list is returned.
"""
cursors = []
children = []
if isinstance(source, Cursor):
children = source.get_children()
else:
# Assume TU
children = source.cursor.get_children()
for cursor in children:
if cursor.spelling == spelling:
cursors.append(cursor)
# Recurse into children.
cursors.extend(get_cursors(cursor, spelling))
return cursors
__all__ = [
'get_cursor',
'get_cursors',
'get_tu',
]

View File

@@ -1,524 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
<start>
<choice>
<!-- Everything else not explicitly mentioned below. -->
<ref name="Other" />
<ref name="Function" />
<ref name="Class" />
<ref name="Variable" />
<ref name="Namespace" />
<ref name="Typedef" />
<ref name="Enum" />
</choice>
</start>
<define name="Other">
<element name="Other">
<ref name="attrSourceLocation" />
<ref name="Name" />
<optional>
<ref name="USR" />
</optional>
<optional>
<ref name="Declaration" />
</optional>
<optional>
<ref name="Abstract" />
</optional>
<optional>
<ref name="TemplateParameters" />
</optional>
<optional>
<ref name="Parameters" />
</optional>
<optional>
<ref name="ResultDiscussion" />
</optional>
<optional>
<ref name="Discussion" />
</optional>
</element>
</define>
<define name="Function">
<element name="Function">
<optional>
<attribute name="templateKind">
<choice>
<value>template</value>
<value>specialization</value>
</choice>
</attribute>
</optional>
<ref name="attrSourceLocation" />
<optional>
<attribute name="isInstanceMethod">
<data type="boolean" />
</attribute>
</optional>
<optional>
<attribute name="isClassMethod">
<data type="boolean" />
</attribute>
</optional>
<ref name="Name" />
<optional>
<ref name="USR" />
</optional>
<!-- TODO: Add exception specification. -->
<optional>
<ref name="Declaration" />
</optional>
<optional>
<ref name="Abstract" />
</optional>
<optional>
<ref name="TemplateParameters" />
</optional>
<optional>
<ref name="Parameters" />
</optional>
<zeroOrMore>
<ref name="Availability" />
</zeroOrMore>
<zeroOrMore>
<ref name="Deprecated" />
</zeroOrMore>
<zeroOrMore>
<ref name="Unavailable" />
</zeroOrMore>
<optional>
<ref name="ResultDiscussion" />
</optional>
<optional>
<ref name="Discussion" />
</optional>
</element>
</define>
<define name="Class">
<element name="Class">
<optional>
<attribute name="templateKind">
<choice>
<value>template</value>
<value>specialization</value>
<value>partialSpecialization</value>
</choice>
</attribute>
</optional>
<ref name="attrSourceLocation" />
<ref name="Name" />
<optional>
<ref name="USR" />
</optional>
<optional>
<ref name="Declaration" />
</optional>
<optional>
<ref name="Abstract" />
</optional>
<optional>
<ref name="TemplateParameters" />
</optional>
<!-- Parameters and results don't make sense for classes, but the user
can specify \param or \returns in a comment anyway. -->
<optional>
<ref name="Parameters" />
</optional>
<optional>
<ref name="ResultDiscussion" />
</optional>
<optional>
<ref name="Discussion" />
</optional>
</element>
</define>
<define name="Variable">
<element name="Variable">
<ref name="attrSourceLocation" />
<ref name="Name" />
<optional>
<ref name="USR" />
</optional>
<optional>
<ref name="Declaration" />
</optional>
<optional>
<ref name="Abstract" />
</optional>
<!-- Template parameters, parameters and results don't make sense for
variables, but the user can specify \tparam \param or \returns
in a comment anyway. -->
<optional>
<ref name="TemplateParameters" />
</optional>
<optional>
<ref name="Parameters" />
</optional>
<optional>
<ref name="ResultDiscussion" />
</optional>
<optional>
<ref name="Discussion" />
</optional>
</element>
</define>
<define name="Namespace">
<element name="Namespace">
<ref name="attrSourceLocation" />
<ref name="Name" />
<optional>
<ref name="USR" />
</optional>
<optional>
<ref name="Declaration" />
</optional>
<optional>
<ref name="Abstract" />
</optional>
<!-- Template parameters, parameters and results don't make sense for
namespaces, but the user can specify \tparam, \param or \returns
in a comment anyway. -->
<optional>
<ref name="TemplateParameters" />
</optional>
<optional>
<ref name="Parameters" />
</optional>
<optional>
<ref name="ResultDiscussion" />
</optional>
<optional>
<ref name="Discussion" />
</optional>
</element>
</define>
<define name="Typedef">
<element name="Typedef">
<ref name="attrSourceLocation" />
<ref name="Name" />
<optional>
<ref name="USR" />
</optional>
<optional>
<ref name="Declaration" />
</optional>
<optional>
<ref name="Abstract" />
</optional>
<optional>
<ref name="TemplateParameters" />
</optional>
<!-- Parameters and results might make sense for typedefs if the type is
a function pointer type. -->
<optional>
<ref name="Parameters" />
</optional>
<optional>
<ref name="ResultDiscussion" />
</optional>
<optional>
<ref name="Discussion" />
</optional>
</element>
</define>
<define name="Enum">
<element name="Enum">
<ref name="attrSourceLocation" />
<ref name="Name" />
<optional>
<ref name="USR" />
</optional>
<optional>
<ref name="Declaration" />
</optional>
<optional>
<ref name="Abstract" />
</optional>
<!-- Template parameters, parameters and results don't make sense for
enums, but the user can specify \tparam \param or \returns in a
comment anyway. -->
<optional>
<ref name="TemplateParameters" />
</optional>
<optional>
<ref name="Parameters" />
</optional>
<optional>
<ref name="ResultDiscussion" />
</optional>
<optional>
<ref name="Discussion" />
</optional>
</element>
</define>
<define name="attrSourceLocation">
<optional>
<attribute name="file">
<!-- Non-empty text content. -->
<data type="string">
<param name="pattern">.*\S.*</param>
</data>
</attribute>
</optional>
<optional>
<attribute name="line">
<data type="positiveInteger" />
</attribute>
<attribute name="column">
<data type="positiveInteger" />
</attribute>
</optional>
</define>
<define name="Name">
<element name="Name">
<!-- Non-empty text content. -->
<data type="string">
<param name="pattern">.*\S.*</param>
</data>
</element>
</define>
<define name="USR">
<element name="USR">
<!-- Non-empty text content. -->
<data type="string">
<param name="pattern">.*\S.*</param>
</data>
</element>
</define>
<define name="Abstract">
<element name="Abstract">
<zeroOrMore>
<ref name="TextBlockContent" />
</zeroOrMore>
</element>
</define>
<define name="Declaration">
<element name="Declaration">
<!-- Non-empty text content. -->
<data type="string"/>
</element>
</define>
<define name="Discussion">
<element name="Discussion">
<zeroOrMore>
<ref name="TextBlockContent" />
</zeroOrMore>
</element>
</define>
<define name="TemplateParameters">
<element name="TemplateParameters">
<!-- Parameter elements should be sorted according to position. -->
<oneOrMore>
<element name="Parameter">
<element name="Name">
<!-- Non-empty text content. -->
<data type="string">
<param name="pattern">.*\S.*</param>
</data>
</element>
<optional>
<!-- This is index at depth 0. libclang API can return more
information about position, but we expose only essential
information here, since "Parameter" elements are already
sorted.
"Position" element could be added in future if needed. -->
<element name="Index">
<data type="nonNegativeInteger" />
</element>
</optional>
<!-- In general, template parameters with whitespace discussion
should not be emitted. Schema might be more strict here. -->
<element name="Discussion">
<ref name="TextBlockContent" />
</element>
</element>
</oneOrMore>
</element>
</define>
<define name="Parameters">
<element name="Parameters">
<!-- Parameter elements should be sorted according to index. -->
<oneOrMore>
<element name="Parameter">
<element name="Name">
<!-- Non-empty text content. -->
<data type="string">
<param name="pattern">.*\S.*</param>
</data>
</element>
<optional>
<element name="Index">
<data type="nonNegativeInteger" />
</element>
</optional>
<element name="Direction">
<attribute name="isExplicit">
<data type="boolean" />
</attribute>
<choice>
<value>in</value>
<value>out</value>
<value>in,out</value>
</choice>
</element>
<!-- In general, template parameters with whitespace discussion
should not be emitted, unless direction is explicitly specified.
Schema might be more strict here. -->
<element name="Discussion">
<ref name="TextBlockContent" />
</element>
</element>
</oneOrMore>
</element>
</define>
<define name="Availability">
<element name="Availability">
<attribute name="distribution">
<data type="string" />
</attribute>
<optional>
<element name="IntroducedInVersion">
<data type="string">
<param name="pattern">\d+|\d+\.\d+|\d+\.\d+.\d+</param>
</data>
</element>
</optional>
<optional>
<element name="DeprecatedInVersion">
<data type="string">
<param name="pattern">\d+|\d+\.\d+|\d+\.\d+.\d+</param>
</data>
</element>
</optional>
<optional>
<element name="RemovedAfterVersion">
<data type="string">
<param name="pattern">\d+|\d+\.\d+|\d+\.\d+.\d+</param>
</data>
</element>
</optional>
<optional>
<element name="DeprecationSummary">
<data type="string" />
</element>
</optional>
<optional>
<ref name="Unavailable" />
</optional>
</element>
</define>
<define name="Deprecated">
<element name="Deprecated">
<optional>
<data type="string" />
</optional>
</element>
</define>
<define name="Unavailable">
<element name="Unavailable">
<optional>
<data type="string" />
</optional>
</element>
</define>
<define name="ResultDiscussion">
<element name="ResultDiscussion">
<zeroOrMore>
<ref name="TextBlockContent" />
</zeroOrMore>
</element>
</define>
<define name="TextBlockContent">
<choice>
<element name="Para">
<zeroOrMore>
<ref name="TextInlineContent" />
</zeroOrMore>
</element>
<element name="Verbatim">
<attribute name="xml:space">
<value>preserve</value>
</attribute>
<attribute name="kind">
<!-- TODO: add all Doxygen verbatim kinds -->
<choice>
<value>code</value>
<value>verbatim</value>
</choice>
</attribute>
<text />
</element>
</choice>
</define>
<define name="TextInlineContent">
<choice>
<text />
<element name="bold">
<!-- Non-empty text content. -->
<data type="string">
<param name="pattern">.*\S.*</param>
</data>
</element>
<element name="monospaced">
<!-- Non-empty text content. -->
<data type="string">
<param name="pattern">.*\S.*</param>
</data>
</element>
<element name="emphasized">
<!-- Non-empty text content. -->
<data type="string">
<param name="pattern">.*\S.*</param>
</data>
</element>
<element name="rawHTML">
<!-- Non-empty text content. -->
<data type="string">
<param name="pattern">.*\S.*</param>
</data>
</element>
</choice>
</define>
</grammar>

View File

@@ -1,171 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<!-- Material used from: HTML 4.01 specs: http://www.w3.org/TR/html401/ -->
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>AddressSanitizer, a fast memory error detector</title>
<link type="text/css" rel="stylesheet" href="../menu.css">
<link type="text/css" rel="stylesheet" href="../content.css">
<style type="text/css">
td {
vertical-align: top;
}
</style>
</head>
<body>
<!--#include virtual="../menu.html.incl"-->
<div id="content">
<h1>AddressSanitizer</h1>
<ul>
<li> <a href="#intro">Introduction</a>
<li> <a href="#howtobuild">How to Build</a>
<li> <a href="#usage">Usage</a>
<ul><li> <a href="#has_feature">__has_feature(address_sanitizer)</a></ul>
<ul><li> <a href="#no_address_safety_analysis">
__attribute__((no_address_safety_analysis))</a></ul>
<li> <a href="#platforms">Supported Platforms</a>
<li> <a href="#limitations">Limitations</a>
<li> <a href="#status">Current Status</a>
<li> <a href="#moreinfo">More Information</a>
</ul>
<h2 id="intro">Introduction</h2>
AddressSanitizer is a fast memory error detector.
It consists of a compiler instrumentation module and a run-time library.
The tool can detect the following types of bugs:
<ul> <li> Out-of-bounds accesses to heap, stack and globals
<li> Use-after-free
<li> Use-after-return (to some extent)
<li> Double-free, invalid free
</ul>
Typical slowdown introduced by AddressSanitizer is <b>2x</b>.
<h2 id="howtobuild">How to build</h2>
Follow the <a href="../get_started.html">clang build instructions</a>.
CMake build is supported.<BR>
<h2 id="usage">Usage</h2>
Simply compile and link your program with <tt>-fsanitize=address</tt> flag. <BR>
The AddressSanitizer run-time library should be linked to the final executable,
so make sure to use <tt>clang</tt> (not <tt>ld</tt>) for the final link step.<BR>
When linking shared libraries, the AddressSanitizer run-time is not linked,
so <tt>-Wl,-z,defs</tt> may cause link errors (don't use it with AddressSanitizer). <BR>
To get a reasonable performance add <tt>-O1</tt> or higher. <BR>
To get nicer stack traces in error messages add
<tt>-fno-omit-frame-pointer</tt>. <BR>
To get perfect stack traces you may need to disable inlining (just use <tt>-O1</tt>) and tail call
elimination (<tt>-fno-optimize-sibling-calls</tt>).
<pre>
% cat example_UseAfterFree.cc
int main(int argc, char **argv) {
int *array = new int[100];
delete [] array;
return array[argc]; // BOOM
}
</pre>
<pre>
# Compile and link
% clang -O1 -g -fsanitize=address -fno-omit-frame-pointer example_UseAfterFree.cc
</pre>
OR
<pre>
# Compile
% clang -O1 -g -fsanitize=address -fno-omit-frame-pointer -c example_UseAfterFree.cc
# Link
% clang -g -fsanitize=address example_UseAfterFree.o
</pre>
If a bug is detected, the program will print an error message to stderr and exit with a
non-zero exit code.
Currently, AddressSanitizer does not symbolize its output, so you may need to use a
separate script to symbolize the result offline (this will be fixed in future).
<pre>
% ./a.out 2> log
% projects/compiler-rt/lib/asan/scripts/asan_symbolize.py / < log | c++filt
==9442== ERROR: AddressSanitizer heap-use-after-free on address 0x7f7ddab8c084 at pc 0x403c8c bp 0x7fff87fb82d0 sp 0x7fff87fb82c8
READ of size 4 at 0x7f7ddab8c084 thread T0
#0 0x403c8c in main example_UseAfterFree.cc:4
#1 0x7f7ddabcac4d in __libc_start_main ??:0
0x7f7ddab8c084 is located 4 bytes inside of 400-byte region [0x7f7ddab8c080,0x7f7ddab8c210)
freed by thread T0 here:
#0 0x404704 in operator delete[](void*) ??:0
#1 0x403c53 in main example_UseAfterFree.cc:4
#2 0x7f7ddabcac4d in __libc_start_main ??:0
previously allocated by thread T0 here:
#0 0x404544 in operator new[](unsigned long) ??:0
#1 0x403c43 in main example_UseAfterFree.cc:2
#2 0x7f7ddabcac4d in __libc_start_main ??:0
==9442== ABORTING
</pre>
AddressSanitizer exits on the first detected error. This is by design.
One reason: it makes the generated code smaller and faster (both by ~5%).
Another reason: this makes fixing bugs unavoidable. With Valgrind, it is often
the case that users treat Valgrind warnings as false positives
(which they are not) and don't fix them.
<h3 id="has_feature">__has_feature(address_sanitizer)</h3>
In some cases one may need to execute different code depending on whether
AddressSanitizer is enabled.
<a href="LanguageExtensions.html#__has_feature_extension">__has_feature</a>
can be used for this purpose.
<pre>
#if defined(__has_feature)
# if __has_feature(address_sanitizer)
code that builds only under AddressSanitizer
# endif
#endif
</pre>
<h3 id="no_address_safety_analysis">__attribute__((no_address_safety_analysis))</h3>
Some code should not be instrumented by AddressSanitizer.
One may use the function attribute
<a href="LanguageExtensions.html#address_sanitizer">
<tt>no_address_safety_analysis</tt></a>
to disable instrumentation of a particular function.
This attribute may not be supported by other compilers, so we suggest to
use it together with <tt>__has_feature(address_sanitizer)</tt>.
Note: currently, this attribute will be lost if the function is inlined.
<h2 id="platforms">Supported Platforms</h2>
AddressSanitizer is supported on
<ul><li>Linux i386/x86_64 (tested on Ubuntu 10.04 and 12.04).
<li>MacOS 10.6, 10.7 and 10.8 (i386/x86_64).
</ul>
Support for Linux ARM (and Android ARM) is in progress
(it may work, but is not guaranteed too).
<h2 id="limitations">Limitations</h2>
<ul>
<li> AddressSanitizer uses more real memory than a native run.
Exact overhead depends on the allocations sizes. The smaller the
allocations you make the bigger the overhead is.
<li> AddressSanitizer uses more stack memory. We have seen up to 3x increase.
<li> On 64-bit platforms AddressSanitizer maps (but not reserves)
16+ Terabytes of virtual address space.
This means that tools like <tt>ulimit</tt> may not work as usually expected.
<li> Static linking is not supported.
</ul>
<h2 id="status">Current Status</h2>
AddressSanitizer is fully functional on supported platforms starting from LLVM 3.1.
The test suite is integrated into CMake build and can be run with
<tt>make check-asan</tt> command.
<h2 id="moreinfo">More Information</h2>
<a href="http://code.google.com/p/address-sanitizer/">http://code.google.com/p/address-sanitizer</a>.
</div>
</body>
</html>

View File

@@ -1,260 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Static Analyzer Design Document: Memory Regions</title>
</head>
<body>
<h1>Static Analyzer Design Document: Memory Regions</h1>
<h3>Authors</h3>
<p>Ted Kremenek, <tt>kremenek at apple</tt><br>
Zhongxing Xu, <tt>xuzhongzhing at gmail</tt></p>
<h2 id="intro">Introduction</h2>
<p>The path-sensitive analysis engine in libAnalysis employs an extensible API
for abstractly modeling the memory of an analyzed program. This API employs the
concept of "memory regions" to abstractly model chunks of program memory such as
program variables and dynamically allocated memory such as those returned from
'malloc' and 'alloca'. Regions are hierarchical, with subregions modeling
subtyping relationships, field and array offsets into larger chunks of memory,
and so on.</p>
<p>The region API consists of two components:</p>
<ul> <li>A taxonomy and representation of regions themselves within the analyzer
engine. The primary definitions and interfaces are described in <tt><a
href="http://clang.llvm.org/doxygen/MemRegion_8h-source.html">MemRegion.h</a></tt>.
At the root of the region hierarchy is the class <tt>MemRegion</tt> with
specific subclasses refining the region concept for variables, heap allocated
memory, and so forth.</li> <li>The modeling of binding of values to regions. For
example, modeling the value stored to a local variable <tt>x</tt> consists of
recording the binding between the region for <tt>x</tt> (which represents the
raw memory associated with <tt>x</tt>) and the value stored to <tt>x</tt>. This
binding relationship is captured with the notion of &quot;symbolic
stores.&quot;</li> </ul>
<p>Symbolic stores, which can be thought of as representing the relation
<tt>regions -> values</tt>, are implemented by subclasses of the
<tt>StoreManager</tt> class (<tt><a
href="http://clang.llvm.org/doxygen/Store_8h-source.html">Store.h</a></tt>). A
particular StoreManager implementation has complete flexibility concerning the
following:
<ul>
<li><em>How</em> to model the binding between regions and values</li>
<li><em>What</em> bindings are recorded
</ul>
<p>Together, both points allow different StoreManagers to tradeoff between
different levels of analysis precision and scalability concerning the reasoning
of program memory. Meanwhile, the core path-sensitive engine makes no
assumptions about either points, and queries a StoreManager about the bindings
to a memory region through a generic interface that all StoreManagers share. If
a particular StoreManager cannot reason about the potential bindings of a given
memory region (e.g., '<tt>BasicStoreManager</tt>' does not reason about fields
of structures) then the StoreManager can simply return 'unknown' (represented by
'<tt>UnknownVal</tt>') for a particular region-binding. This separation of
concerns not only isolates the core analysis engine from the details of
reasoning about program memory but also facilities the option of a client of the
path-sensitive engine to easily swap in different StoreManager implementations
that internally reason about program memory in very different ways.</p>
<p>The rest of this document is divided into two parts. We first discuss region
taxonomy and the semantics of regions. We then discuss the StoreManager
interface, and details of how the currently available StoreManager classes
implement region bindings.</p>
<h2 id="regions">Memory Regions and Region Taxonomy</h2>
<h3>Pointers</h3>
<p>Before talking about the memory regions, we would talk about the pointers
since memory regions are essentially used to represent pointer values.</p>
<p>The pointer is a type of values. Pointer values have two semantic aspects.
One is its physical value, which is an address or location. The other is the
type of the memory object residing in the address.</p>
<p>Memory regions are designed to abstract these two properties of the pointer.
The physical value of a pointer is represented by MemRegion pointers. The rvalue
type of the region corresponds to the type of the pointee object.</p>
<p>One complication is that we could have different view regions on the same
memory chunk. They represent the same memory location, but have different
abstract location, i.e., MemRegion pointers. Thus we need to canonicalize the
abstract locations to get a unique abstract location for one physical
location.</p>
<p>Furthermore, these different view regions may or may not represent memory
objects of different types. Some different types are semantically the same,
for example, 'struct s' and 'my_type' are the same type.</p>
<pre>
struct s;
typedef struct s my_type;
</pre>
<p>But <tt>char</tt> and <tt>int</tt> are not the same type in the code below:</p>
<pre>
void *p;
int *q = (int*) p;
char *r = (char*) p;
</pre>
<p>Thus we need to canonicalize the MemRegion which is used in binding and
retrieving.</p>
<h3>Regions</h3>
<p>Region is the entity used to model pointer values. A Region has the following
properties:</p>
<ul>
<li>Kind</li>
<li>ObjectType: the type of the object residing on the region.</li>
<li>LocationType: the type of the pointer value that the region corresponds to.
Usually this is the pointer to the ObjectType. But sometimes we want to cache
this type explicitly, for example, for a CodeTextRegion.</li>
<li>StartLocation</li>
<li>EndLocation</li>
</ul>
<h3>Symbolic Regions</h3>
<p>A symbolic region is a map of the concept of symbolic values into the domain
of regions. It is the way that we represent symbolic pointers. Whenever a
symbolic pointer value is needed, a symbolic region is created to represent
it.</p>
<p>A symbolic region has no type. It wraps a SymbolData. But sometimes we have
type information associated with a symbolic region. For this case, a
TypedViewRegion is created to layer the type information on top of the symbolic
region. The reason we do not carry type information with the symbolic region is
that the symbolic regions can have no type. To be consistent, we don't let them
to carry type information.</p>
<p>Like a symbolic pointer, a symbolic region may be NULL, has unknown extent,
and represents a generic chunk of memory.</p>
<p><em><b>NOTE</b>: We plan not to use loc::SymbolVal in RegionStore and remove it
gradually.</em></p>
<p>Symbolic regions get their rvalue types through the following ways:</p>
<ul>
<li>Through the parameter or global variable that points to it, e.g.:
<pre>
void f(struct s* p) {
...
}
</pre>
<p>The symbolic region pointed to by <tt>p</tt> has type <tt>struct
s</tt>.</p></li>
<li>Through explicit or implicit casts, e.g.:
<pre>
void f(void* p) {
struct s* q = (struct s*) p;
...
}
</pre>
</li>
</ul>
<p>We attach the type information to the symbolic region lazily. For the first
case above, we create the <tt>TypedViewRegion</tt> only when the pointer is
actually used to access the pointee memory object, that is when the element or
field region is created. For the cast case, the <tt>TypedViewRegion</tt> is
created when visiting the <tt>CastExpr</tt>.</p>
<p>The reason for doing lazy typing is that symbolic regions are sometimes only
used to do location comparison.</p>
<h3>Pointer Casts</h3>
<p>Pointer casts allow people to impose different 'views' onto a chunk of
memory.</p>
<p>Usually we have two kinds of casts. One kind of casts cast down with in the
type hierarchy. It imposes more specific views onto more generic memory regions.
The other kind of casts cast up with in the type hierarchy. It strips away more
specific views on top of the more generic memory regions.</p>
<p>We simulate the down casts by layering another <tt>TypedViewRegion</tt> on
top of the original region. We simulate the up casts by striping away the top
<tt>TypedViewRegion</tt>. Down casts is usually simple. For up casts, if the
there is no <tt>TypedViewRegion</tt> to be stripped, we return the original
region. If the underlying region is of the different type than the cast-to type,
we flag an error state.</p>
<p>For toll-free bridging casts, we return the original region.</p>
<p>We can set up a partial order for pointer types, with the most general type
<tt>void*</tt> at the top. The partial order forms a tree with <tt>void*</tt> as
its root node.</p>
<p>Every <tt>MemRegion</tt> has a root position in the type tree. For example,
the pointee region of <tt>void *p</tt> has its root position at the root node of
the tree. <tt>VarRegion</tt> of <tt>int x</tt> has its root position at the 'int
type' node.</p>
<p><tt>TypedViewRegion</tt> is used to move the region down or up in the tree.
Moving down in the tree adds a <tt>TypedViewRegion</tt>. Moving up in the tree
removes a <Tt>TypedViewRegion</tt>.</p>
<p>Do we want to allow moving up beyond the root position? This happens
when:</p> <pre> int x; void *p = &amp;x; </pre>
<p>The region of <tt>x</tt> has its root position at 'int*' node. the cast to
void* moves that region up to the 'void*' node. I propose to not allow such
casts, and assign the region of <tt>x</tt> for <tt>p</tt>.</p>
<p>Another non-ideal case is that people might cast to a non-generic pointer
from another non-generic pointer instead of first casting it back to the generic
pointer. Direct handling of this case would result in multiple layers of
TypedViewRegions. This enforces an incorrect semantic view to the region,
because we can only have one typed view on a region at a time. To avoid this
inconsistency, before casting the region, we strip the TypedViewRegion, then do
the cast. In summary, we only allow one layer of TypedViewRegion.</p>
<h3>Region Bindings</h3>
<p>The following region kinds are boundable: VarRegion, CompoundLiteralRegion,
StringRegion, ElementRegion, FieldRegion, and ObjCIvarRegion.</p>
<p>When binding regions, we perform canonicalization on element regions and field
regions. This is because we can have different views on the same region, some
of which are essentially the same view with different sugar type names.</p>
<p>To canonicalize a region, we get the canonical types for all TypedViewRegions
along the way up to the root region, and make new TypedViewRegions with those
canonical types.</p>
<p>For Objective-C and C++, perhaps another canonicalization rule should be
added: for FieldRegion, the least derived class that has the field is used as
the type of the super region of the FieldRegion.</p>
<p>All bindings and retrievings are done on the canonicalized regions.</p>
<p>Canonicalization is transparent outside the region store manager, and more
specifically, unaware outside the Bind() and Retrieve() method. We don't need to
consider region canonicalization when doing pointer cast.</p>
<h3>Constraint Manager</h3>
<p>The constraint manager reasons about the abstract location of memory objects.
We can have different views on a region, but none of these views changes the
location of that object. Thus we should get the same abstract location for those
regions.</p>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -1,669 +0,0 @@
Block Implementation Specification
Copyright 2008-2010 Apple, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
0. History
2008/7/14 - created
2008/8/21 - revised, C++
2008/9/24 - add NULL isa field to __block storage
2008/10/1 - revise block layout to use a static descriptor structure
2008/10/6 - revise block layout to use an unsigned long int flags
2008/10/28 - specify use of _Block_object_assign/dispose for all "Object" types in helper functions
2008/10/30 - revise new layout to have invoke function in same place
2008/10/30 - add __weak support
2010/3/16 - rev for stret return, signature field
2010/4/6 - improved wording
This document describes the Apple ABI implementation specification of Blocks.
The first shipping version of this ABI is found in Mac OS X 10.6, and shall be referred to as 10.6.ABI. As of 2010/3/16, the following describes the ABI contract with the runtime and the compiler, and, as necessary, will be referred to as ABI.2010.3.16.
Since the Apple ABI references symbols from other elements of the system, any attempt to use this ABI on systems prior to SnowLeopard is undefined.
1. High Level
The ABI of blocks consist of their layout and the runtime functions required by the compiler.
A Block consists of a structure of the following form:
struct Block_literal_1 {
void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor_1 {
unsigned long int reserved; // NULL
unsigned long int size; // sizeof(struct Block_literal_1)
// optional helper functions
void (*copy_helper)(void *dst, void *src); // IFF (1<<25)
void (*dispose_helper)(void *src); // IFF (1<<25)
// required ABI.2010.3.16
const char *signature; // IFF (1<<30)
} *descriptor;
// imported variables
};
The following flags bits are in use thusly for a possible ABI.2010.3.16:
enum {
BLOCK_HAS_COPY_DISPOSE = (1 << 25),
BLOCK_HAS_CTOR = (1 << 26), // helpers have C++ code
BLOCK_IS_GLOBAL = (1 << 28),
BLOCK_HAS_STRET = (1 << 29), // IFF BLOCK_HAS_SIGNATURE
BLOCK_HAS_SIGNATURE = (1 << 30),
};
In 10.6.ABI the (1<<29) was usually set and was always ignored by the runtime - it had been a transitional marker that did not get deleted after the transition. This bit is now paired with (1<<30), and represented as the pair (3<<30), for the following combinations of valid bit settings, and their meanings.
switch (flags & (3<<29)) {
case (0<<29): 10.6.ABI, no signature field available
case (1<<29): 10.6.ABI, no signature field available
case (2<<29): ABI.2010.3.16, regular calling convention, presence of signature field
case (3<<29): ABI.2010.3.16, stret calling convention, presence of signature field,
}
The signature field is not always populated.
The following discussions are presented as 10.6.ABI otherwise.
Block literals may occur within functions where the structure is created in stack local memory. They may also appear as initialization expressions for Block variables of global or static local variables.
When a Block literal expression is evaluated the stack based structure is initialized as follows:
1) static descriptor structure is declared and initialized as follows:
1a) the invoke function pointer is set to a function that takes the Block structure as its first argument and the rest of the arguments (if any) to the Block and executes the Block compound statement.
1b) the size field is set to the size of the following Block literal structure.
1c) the copy_helper and dispose_helper function pointers are set to respective helper functions if they are required by the Block literal
2) a stack (or global) Block literal data structure is created and initialized as follows:
2a) the isa field is set to the address of the external _NSConcreteStackBlock, which is a block of uninitialized memory supplied in libSystem, or _NSConcreteGlobalBlock if this is a static or file level block literal.
2) The flags field is set to zero unless there are variables imported into the block that need helper functions for program level Block_copy() and Block_release() operations, in which case the (1<<25) flags bit is set.
As an example, the Block literal expression
^ { printf("hello world\n"); }
would cause to be created on a 32-bit system:
struct __block_literal_1 {
void *isa;
int flags;
int reserved;
void (*invoke)(struct __block_literal_1 *);
struct __block_descriptor_1 *descriptor;
};
void __block_invoke_1(struct __block_literal_1 *_block) {
printf("hello world\n");
}
static struct __block_descriptor_1 {
unsigned long int reserved;
unsigned long int Block_size;
} __block_descriptor_1 = { 0, sizeof(struct __block_literal_1), __block_invoke_1 };
and where the block literal appeared
struct __block_literal_1 _block_literal = {
&_NSConcreteStackBlock,
(1<<29), <uninitialized>,
__block_invoke_1,
&__block_descriptor_1
};
Blocks import other Block references, const copies of other variables, and variables marked __block. In Objective-C variables may additionally be objects.
When a Block literal expression used as the initial value of a global or static local variable it is initialized as follows:
struct __block_literal_1 __block_literal_1 = {
&_NSConcreteGlobalBlock,
(1<<28)|(1<<29), <uninitialized>,
__block_invoke_1,
&__block_descriptor_1
};
that is, a different address is provided as the first value and a particular (1<<28) bit is set in the flags field, and otherwise it is the same as for stack based Block literals. This is an optimization that can be used for any Block literal that imports no const or __block storage variables.
2. Imported Variables
Variables of "auto" storage class are imported as const copies. Variables of "__block" storage class are imported as a pointer to an enclosing data structure. Global variables are simply referenced and not considered as imported.
2.1 Imported const copy variables
Automatic storage variables not marked with __block are imported as const copies.
The simplest example is that of importing a variable of type int.
int x = 10;
void (^vv)(void) = ^{ printf("x is %d\n", x); }
x = 11;
vv();
would be compiled
struct __block_literal_2 {
void *isa;
int flags;
int reserved;
void (*invoke)(struct __block_literal_2 *);
struct __block_descriptor_2 *descriptor;
const int x;
};
void __block_invoke_2(struct __block_literal_2 *_block) {
printf("x is %d\n", _block->x);
}
static struct __block_descriptor_2 {
unsigned long int reserved;
unsigned long int Block_size;
} __block_descriptor_2 = { 0, sizeof(struct __block_literal_2) };
and
struct __block_literal_2 __block_literal_2 = {
&_NSConcreteStackBlock,
(1<<29), <uninitialized>,
__block_invoke_2,
&__block_descriptor_2,
x
};
In summary, scalars, structures, unions, and function pointers are generally imported as const copies with no need for helper functions.
2.2 Imported const copy of Block reference
The first case where copy and dispose helper functions are required is for the case of when a block itself is imported. In this case both a copy_helper function and a dispose_helper function are needed. The copy_helper function is passed both the existing stack based pointer and the pointer to the new heap version and should call back into the runtime to actually do the copy operation on the imported fields within the block. The runtime functions are all described in Section 5.0 Runtime Helper Functions.
An example:
void (^existingBlock)(void) = ...;
void (^vv)(void) = ^{ existingBlock(); }
vv();
struct __block_literal_3 {
...; // existing block
};
struct __block_literal_4 {
void *isa;
int flags;
int reserved;
void (*invoke)(struct __block_literal_4 *);
struct __block_literal_3 *const existingBlock;
};
void __block_invoke_4(struct __block_literal_2 *_block) {
__block->existingBlock->invoke(__block->existingBlock);
}
void __block_copy_4(struct __block_literal_4 *dst, struct __block_literal_4 *src) {
//_Block_copy_assign(&dst->existingBlock, src->existingBlock, 0);
_Block_object_assign(&dst->existingBlock, src->existingBlock, BLOCK_FIELD_IS_BLOCK);
}
void __block_dispose_4(struct __block_literal_4 *src) {
// was _Block_destroy
_Block_object_dispose(src->existingBlock, BLOCK_FIELD_IS_BLOCK);
}
static struct __block_descriptor_4 {
unsigned long int reserved;
unsigned long int Block_size;
void (*copy_helper)(struct __block_literal_4 *dst, struct __block_literal_4 *src);
void (*dispose_helper)(struct __block_literal_4 *);
} __block_descriptor_4 = {
0,
sizeof(struct __block_literal_4),
__block_copy_4,
__block_dispose_4,
};
and where it is used
struct __block_literal_4 _block_literal = {
&_NSConcreteStackBlock,
(1<<25)|(1<<29), <uninitialized>
__block_invoke_4,
& __block_descriptor_4
existingBlock,
};
2.2.1 Importing __attribute__((NSObject)) variables.
GCC introduces __attribute__((NSObject)) on structure pointers to mean "this is an object". This is useful because many low level data structures are declared as opaque structure pointers, e.g. CFStringRef, CFArrayRef, etc. When used from C, however, these are still really objects and are the second case where that requires copy and dispose helper functions to be generated. The copy helper functions generated by the compiler should use the _Block_object_assign runtime helper function and in the dispose helper the _Block_object_dispose runtime helper function should be called.
For example, block xyzzy in the following
struct Opaque *__attribute__((NSObject)) objectPointer = ...;
...
void (^xyzzy)(void) = ^{ CFPrint(objectPointer); };
would have helper functions
void __block_copy_xyzzy(struct __block_literal_5 *dst, struct __block_literal_5 *src) {
_Block_object_assign(&dst->objectPointer, src-> objectPointer, BLOCK_FIELD_IS_OBJECT);
}
void __block_dispose_xyzzy(struct __block_literal_5 *src) {
_Block_object_dispose(src->objectPointer, BLOCK_FIELD_IS_OBJECT);
}
generated.
2.3 Imported __block marked variables.
2.3.1 Layout of __block marked variables
The compiler must embed variables that are marked __block in a specialized structure of the form:
struct _block_byref_xxxx {
void *isa;
struct Block_byref *forwarding;
int flags; //refcount;
int size;
typeof(marked_variable) marked_variable;
};
Variables of certain types require helper functions for when Block_copy() and Block_release() are performed upon a referencing Block. At the "C" level only variables that are of type Block or ones that have __attribute__((NSObject)) marked require helper functions. In Objective-C objects require helper functions and in C++ stack based objects require helper functions. Variables that require helper functions use the form:
struct _block_byref_xxxx {
void *isa;
struct _block_byref_xxxx *forwarding;
int flags; //refcount;
int size;
// helper functions called via Block_copy() and Block_release()
void (*byref_keep)(void *dst, void *src);
void (*byref_dispose)(void *);
typeof(marked_variable) marked_variable;
};
The structure is initialized such that
a) the forwarding pointer is set to the beginning of its enclosing structure,
b) the size field is initialized to the total size of the enclosing structure,
c) the flags field is set to either 0 if no helper functions are needed or (1<<25) if they are,
d) the helper functions are initialized (if present)
e) the variable itself is set to its initial value.
f) the isa field is set to NULL
2.3.2 Access to __block variables from within its lexical scope.
In order to "move" the variable to the heap upon a copy_helper operation the compiler must rewrite access to such a variable to be indirect through the structures forwarding pointer. For example:
int __block i = 10;
i = 11;
would be rewritten to be:
struct _block_byref_i {
void *isa;
struct _block_byref_i *forwarding;
int flags; //refcount;
int size;
int captured_i;
} i = { NULL, &i, 0, sizeof(struct _block_byref_i), 10 };
i.forwarding->captured_i = 11;
In the case of a Block reference variable being marked __block the helper code generated must use the _Block_object_assign and _Block_object_dispose routines supplied by the runtime to make the copies. For example:
__block void (voidBlock)(void) = blockA;
voidBlock = blockB;
would translate into
struct _block_byref_voidBlock {
void *isa;
struct _block_byref_voidBlock *forwarding;
int flags; //refcount;
int size;
void (*byref_keep)(struct _block_byref_voidBlock *dst, struct _block_byref_voidBlock *src);
void (*byref_dispose)(struct _block_byref_voidBlock *);
void (^captured_voidBlock)(void);
};
void _block_byref_keep_helper(struct _block_byref_voidBlock *dst, struct _block_byref_voidBlock *src) {
//_Block_copy_assign(&dst->captured_voidBlock, src->captured_voidBlock, 0);
_Block_object_assign(&dst->captured_voidBlock, src->captured_voidBlock, BLOCK_FIELD_IS_BLOCK | BLOCK_BYREF_CALLER);
}
void _block_byref_dispose_helper(struct _block_byref_voidBlock *param) {
//_Block_destroy(param->captured_voidBlock, 0);
_Block_object_dispose(param->captured_voidBlock, BLOCK_FIELD_IS_BLOCK | BLOCK_BYREF_CALLER)}
and
struct _block_byref_voidBlock voidBlock = {( .forwarding=&voidBlock, .flags=(1<<25), .size=sizeof(struct _block_byref_voidBlock *),
.byref_keep=_block_byref_keep_helper, .byref_dispose=_block_byref_dispose_helper,
.captured_voidBlock=blockA )};
voidBlock.forwarding->captured_voidBlock = blockB;
2.3.3 Importing __block variables into Blocks
A Block that uses a __block variable in its compound statement body must import the variable and emit copy_helper and dispose_helper helper functions that, in turn, call back into the runtime to actually copy or release the byref data block using the functions _Block_object_assign and _Block_object_dispose.
For example:
int __block i = 2;
functioncall(^{ i = 10; });
would translate to
struct _block_byref_i {
void *isa; // set to NULL
struct _block_byref_voidBlock *forwarding;
int flags; //refcount;
int size;
void (*byref_keep)(struct _block_byref_i *dst, struct _block_byref_i *src);
void (*byref_dispose)(struct _block_byref_i *);
int captured_i;
};
struct __block_literal_5 {
void *isa;
int flags;
int reserved;
void (*invoke)(struct __block_literal_5 *);
struct __block_descriptor_5 *descriptor;
struct _block_byref_i *i_holder;
};
void __block_invoke_5(struct __block_literal_5 *_block) {
_block->forwarding->captured_i = 10;
}
void __block_copy_5(struct __block_literal_5 *dst, struct __block_literal_5 *src) {
//_Block_byref_assign_copy(&dst->captured_i, src->captured_i);
_Block_object_assign(&dst->captured_i, src->captured_i, BLOCK_FIELD_IS_BYREF | BLOCK_BYREF_CALLER);
}
void __block_dispose_5(struct __block_literal_5 *src) {
//_Block_byref_release(src->captured_i);
_Block_object_dispose(src->captured_i, BLOCK_FIELD_IS_BYREF | BLOCK_BYREF_CALLER);
}
static struct __block_descriptor_5 {
unsigned long int reserved;
unsigned long int Block_size;
void (*copy_helper)(struct __block_literal_5 *dst, struct __block_literal_5 *src);
void (*dispose_helper)(struct __block_literal_5 *);
} __block_descriptor_5 = { 0, sizeof(struct __block_literal_5) __block_copy_5, __block_dispose_5 };
and
struct _block_byref_i i = {( .forwarding=&i, .flags=0, .size=sizeof(struct _block_byref_i) )};
struct __block_literal_5 _block_literal = {
&_NSConcreteStackBlock,
(1<<25)|(1<<29), <uninitialized>,
__block_invoke_5,
&__block_descriptor_5,
2,
};
2.3.4 Importing __attribute__((NSObject)) __block variables
A __block variable that is also marked __attribute__((NSObject)) should have byref_keep and byref_dispose helper functions that use _Block_object_assign and _Block_object_dispose.
2.3.5 __block escapes
Because Blocks referencing __block variables may have Block_copy() performed upon them the underlying storage for the variables may move to the heap. In Objective-C Garbage Collection Only compilation environments the heap used is the garbage collected one and no further action is required. Otherwise the compiler must issue a call to potentially release any heap storage for __block variables at all escapes or terminations of their scope. The call should be:
_Block_object_dispose(&_block_byref_xxx, BLOCK_FIELD_IS_BYREF);
2.3.6 Nesting
Blocks may contain Block literal expressions. Any variables used within inner blocks are imported into all enclosing Block scopes even if the variables are not used. This includes const imports as well as __block variables.
3. Objective C Extensions to Blocks
3.1 Importing Objects
Objects should be treated as __attribute__((NSObject)) variables; all copy_helper, dispose_helper, byref_keep, and byref_dispose helper functions should use _Block_object_assign and _Block_object_dispose. There should be no code generated that uses -retain or -release methods.
3.2 Blocks as Objects
The compiler will treat Blocks as objects when synthesizing property setters and getters, will characterize them as objects when generating garbage collection strong and weak layout information in the same manner as objects, and will issue strong and weak write-barrier assignments in the same manner as objects.
3.3 __weak __block Support
Objective-C (and Objective-C++) support the __weak attribute on __block variables. Under normal circumstances the compiler uses the Objective-C runtime helper support functions objc_assign_weak and objc_read_weak. Both should continue to be used for all reads and writes of __weak __block variables:
objc_read_weak(&block->byref_i->forwarding->i)
The __weak variable is stored in a _block_byref_xxxx structure and the Block has copy and dispose helpers for this structure that call:
_Block_object_assign(&dest->_block_byref_i, src-> _block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_BYREF);
and
_Block_object_dispose(src->_block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_BYREF);
In turn, the block_byref copy support helpers distinguish between whether the __block variable is a Block or not and should either call:
_Block_object_assign(&dest->_block_byref_i, src->_block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_OBJECT | BLOCK_BYREF_CALLER);
for something declared as an object or
_Block_object_assign(&dest->_block_byref_i, src->_block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_BLOCK | BLOCK_BYREF_CALLER);
for something declared as a Block.
A full example follows:
__block __weak id obj = <initialization expression>;
functioncall(^{ [obj somemessage]; });
would translate to
struct _block_byref_obj {
void *isa; // uninitialized
struct _block_byref_obj *forwarding;
int flags; //refcount;
int size;
void (*byref_keep)(struct _block_byref_i *dst, struct _block_byref_i *src);
void (*byref_dispose)(struct _block_byref_i *);
id captured_obj;
};
void _block_byref_obj_keep(struct _block_byref_voidBlock *dst, struct _block_byref_voidBlock *src) {
//_Block_copy_assign(&dst->captured_obj, src->captured_obj, 0);
_Block_object_assign(&dst->captured_obj, src->captured_obj, BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK | BLOCK_BYREF_CALLER);
}
void _block_byref_obj_dispose(struct _block_byref_voidBlock *param) {
//_Block_destroy(param->captured_obj, 0);
_Block_object_dispose(param->captured_obj, BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK | BLOCK_BYREF_CALLER);
};
for the block byref part and
struct __block_literal_5 {
void *isa;
int flags;
int reserved;
void (*invoke)(struct __block_literal_5 *);
struct __block_descriptor_5 *descriptor;
struct _block_byref_obj *byref_obj;
};
void __block_invoke_5(struct __block_literal_5 *_block) {
[objc_read_weak(&_block->byref_obj->forwarding->captured_obj) somemessage];
}
void __block_copy_5(struct __block_literal_5 *dst, struct __block_literal_5 *src) {
//_Block_byref_assign_copy(&dst->byref_obj, src->byref_obj);
_Block_object_assign(&dst->byref_obj, src->byref_obj, BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK);
}
void __block_dispose_5(struct __block_literal_5 *src) {
//_Block_byref_release(src->byref_obj);
_Block_object_dispose(src->byref_obj, BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK);
}
static struct __block_descriptor_5 {
unsigned long int reserved;
unsigned long int Block_size;
void (*copy_helper)(struct __block_literal_5 *dst, struct __block_literal_5 *src);
void (*dispose_helper)(struct __block_literal_5 *);
} __block_descriptor_5 = { 0, sizeof(struct __block_literal_5), __block_copy_5, __block_dispose_5 };
and within the compound statement:
struct _block_byref_obj obj = {( .forwarding=&obj, .flags=(1<<25), .size=sizeof(struct _block_byref_obj),
.byref_keep=_block_byref_obj_keep, .byref_dispose=_block_byref_obj_dispose,
.captured_obj = <initialization expression> )};
struct __block_literal_5 _block_literal = {
&_NSConcreteStackBlock,
(1<<25)|(1<<29), <uninitialized>,
__block_invoke_5,
&__block_descriptor_5,
&obj, // a reference to the on-stack structure containing "captured_obj"
};
functioncall(_block_literal->invoke(&_block_literal));
4.0 C++ Support
Within a block stack based C++ objects are copied into const copies using the copy constructor. It is an error if a stack based C++ object is used within a block if it does not have a copy constructor. In addition both copy and destroy helper routines must be synthesized for the block to support the Block_copy() operation, and the flags work marked with the (1<<26) bit in addition to the (1<<25) bit. The copy helper should call the constructor using appropriate offsets of the variable within the supplied stack based block source and heap based destination for all const constructed copies, and similarly should call the destructor in the destroy routine.
As an example, suppose a C++ class FOO existed with a copy constructor. Within a code block a stack version of a FOO object is declared and used within a Block literal expression:
{
FOO foo;
void (^block)(void) = ^{ printf("%d\n", foo.value()); };
}
The compiler would synthesize
struct __block_literal_10 {
void *isa;
int flags;
int reserved;
void (*invoke)(struct __block_literal_10 *);
struct __block_descriptor_10 *descriptor;
const FOO foo;
};
void __block_invoke_10(struct __block_literal_10 *_block) {
printf("%d\n", _block->foo.value());
}
void __block_literal_10(struct __block_literal_10 *dst, struct __block_literal_10 *src) {
FOO_ctor(&dst->foo, &src->foo);
}
void __block_dispose_10(struct __block_literal_10 *src) {
FOO_dtor(&src->foo);
}
static struct __block_descriptor_10 {
unsigned long int reserved;
unsigned long int Block_size;
void (*copy_helper)(struct __block_literal_10 *dst, struct __block_literal_10 *src);
void (*dispose_helper)(struct __block_literal_10 *);
} __block_descriptor_10 = { 0, sizeof(struct __block_literal_10), __block_copy_10, __block_dispose_10 };
and the code would be:
{
FOO foo;
comp_ctor(&foo); // default constructor
struct __block_literal_10 _block_literal = {
&_NSConcreteStackBlock,
(1<<25)|(1<<26)|(1<<29), <uninitialized>,
__block_invoke_10,
&__block_descriptor_10,
};
comp_ctor(&_block_literal->foo, &foo); // const copy into stack version
struct __block_literal_10 &block = &_block_literal; // assign literal to block variable
block->invoke(block); // invoke block
comp_dtor(&_block_literal->foo); // destroy stack version of const block copy
comp_dtor(&foo); // destroy original version
}
C++ objects stored in __block storage start out on the stack in a block_byref data structure as do other variables. Such objects (if not const objects) must support a regular copy constructor. The block_byref data structure will have copy and destroy helper routines synthesized by the compiler. The copy helper will have code created to perform the copy constructor based on the initial stack block_byref data structure, and will also set the (1<<26) bit in addition to the (1<<25) bit. The destroy helper will have code to do the destructor on the object stored within the supplied block_byref heap data structure. For example,
__block FOO blockStorageFoo;
requires the normal constructor for the embedded blockStorageFoo object
FOO_ctor(& _block_byref_blockStorageFoo->blockStorageFoo);
and at scope termination the destructor:
FOO_dtor(& _block_byref_blockStorageFoo->blockStorageFoo);
Note that the forwarding indirection is NOT used.
The compiler would need to generate (if used from a block literal) the following copy/dispose helpers:
void _block_byref_obj_keep(struct _block_byref_blockStorageFoo *dst, struct _block_byref_blockStorageFoo *src) {
FOO_ctor(&dst->blockStorageFoo, &src->blockStorageFoo);
}
void _block_byref_obj_dispose(struct _block_byref_blockStorageFoo *src) {
FOO_dtor(&src->blockStorageFoo);
}
for the appropriately named constructor and destructor for the class/struct FOO.
To support member variable and function access the compiler will synthesize a const pointer to a block version of the "this" pointer.
5.0 Runtime Helper Functions
The runtime helper functions are described in /usr/local/include/Block_private.h. To summarize their use, a block requires copy/dispose helpers if it imports any block variables, __block storage variables, __attribute__((NSObject)) variables, or C++ const copied objects with constructor/destructors. The (1<<26) bit is set and functions are generated.
The block copy helper function should, for each of the variables of the type mentioned above, call
_Block_object_assign(&dst->target, src->target, BLOCK_FIELD_<appropo>);
in the copy helper and
_Block_object_dispose(->target, BLOCK_FIELD_<appropo>);
in the dispose helper where
<appropo> is
enum {
BLOCK_FIELD_IS_OBJECT = 3, // id, NSObject, __attribute__((NSObject)), block, ...
BLOCK_FIELD_IS_BLOCK = 7, // a block variable
BLOCK_FIELD_IS_BYREF = 8, // the on stack structure holding the __block variable
BLOCK_FIELD_IS_WEAK = 16, // declared __weak
BLOCK_BYREF_CALLER = 128, // called from byref copy/dispose helpers
};
and of course the CTORs/DTORs for const copied C++ objects.
The block_byref data structure similarly requires copy/dispose helpers for block variables, __attribute__((NSObject)) variables, or C++ const copied objects with constructor/destructors, and again the (1<<26) bit is set and functions are generated in the same manner.
Under ObjC we allow __weak as an attribute on __block variables, and this causes the addition of BLOCK_FIELD_IS_WEAK orred onto the BLOCK_FIELD_IS_BYREF flag when copying the block_byref structure in the block copy helper, and onto the BLOCK_FIELD_<appropo> field within the block_byref copy/dispose helper calls.
The prototypes, and summary, of the helper functions are
/* Certain field types require runtime assistance when being copied to the heap. The following function is used
to copy fields of types: blocks, pointers to byref structures, and objects (including __attribute__((NSObject)) pointers.
BLOCK_FIELD_IS_WEAK is orthogonal to the other choices which are mutually exclusive.
Only in a Block copy helper will one see BLOCK_FIELD_IS_BYREF.
*/
void _Block_object_assign(void *destAddr, const void *object, const int flags);
/* Similarly a compiler generated dispose helper needs to call back for each field of the byref data structure.
(Currently the implementation only packs one field into the byref structure but in principle there could be more).
The same flags used in the copy helper should be used for each call generated to this function:
*/
void _Block_object_dispose(const void *object, const int flags);

View File

@@ -1,171 +0,0 @@
Language Specification for Blocks
2008/2/25 — created
2008/7/28 — revised, __block syntax
2008/8/13 — revised, Block globals
2008/8/21 — revised, C++ elaboration
2008/11/1 — revised, __weak support
2009/1/12 — revised, explicit return types
2009/2/10 — revised, __block objects need retain
Copyright 2008-2009 Apple, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
The Block Type
A new derived type is introduced to C and, by extension, Objective-C, C++, and Objective-C++. Like function types, the Block type is a pair consisting of a result value type and a list of parameter types very similar to a function type. Blocks are intended to be used much like functions with the key distinction being that in addition to executable code they also contain various variable bindings to automatic (stack) or managed (heap) memory.
The abstract declarator int (^)(char, float) describes a reference to a Block that, when invoked, takes two parameters, the first of type char and the second of type float, and returns a value of type int. The Block referenced is of opaque data that may reside in automatic (stack) memory, global memory, or heap memory.
Block Variable Declarations
A variable with Block type is declared using function pointer style notation substituting ^ for *. The following are valid Block variable declarations:
void (^blockReturningVoidWithVoidArgument)(void);
int (^blockReturningIntWithIntAndCharArguments)(int, char);
void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int);
Variadic ... arguments are supported. [variadic.c] A Block that takes no arguments must specify void in the argument list [voidarg.c]. An empty parameter list does not represent, as K&R provide, an unspecified argument list. Note: both gcc and clang support K&R style as a convenience.
A Block reference may be cast to a pointer of arbitrary type and vice versa. [cast.c] A Block reference may not be dereferenced via the pointer dereference operator *, and thus a Block's size may not be computed at compile time. [sizeof.c]
Block Literal Expressions
A Block literal expression produces a reference to a Block. It is introduced by the use of the ^ token as a unary operator.
Block_literal_expression ::= ^ block_decl compound_statement_body
block_decl ::=
block_decl ::= parameter_list
block_decl ::= type_expression
...where type expression is extended to allow ^ as a Block reference (pointer) where * is allowed as a function reference (pointer).
The following Block literal:
^ void (void) { printf("hello world\n"); }
...produces a reference to a Block with no arguments with no return value.
The return type is optional and is inferred from the return statements. If the return statements return a value, they all must return a value of the same type. If there is no value returned the inferred type of the Block is void; otherwise it is the type of the return statement value.
If the return type is omitted and the argument list is ( void ), the ( void ) argument list may also be omitted.
So:
^ ( void ) { printf("hello world\n"); }
...and:
^ { printf("hello world\n"); }
...are exactly equivalent constructs for the same expression.
The type_expression extends C expression parsing to accommodate Block reference declarations as it accommodates function pointer declarations.
Given:
typedef int (*pointerToFunctionThatReturnsIntWithCharArg)(char);
pointerToFunctionThatReturnsIntWithCharArg functionPointer;
^ pointerToFunctionThatReturnsIntWithCharArg (float x) { return functionPointer; }
...and:
^ int ((*)(float x))(char) { return functionPointer; }
...are equivalent expressions, as is:
^(float x) { return functionPointer; }
[returnfunctionptr.c]
The compound statement body establishes a new lexical scope within that of its parent. Variables used within the scope of the compound statement are bound to the Block in the normal manner with the exception of those in automatic (stack) storage. Thus one may access functions and global variables as one would expect, as well as static local variables. [testme]
Local automatic (stack) variables referenced within the compound statement of a Block are imported and captured by the Block as const copies. The capture (binding) is performed at the time of the Block literal expression evaluation.
The compiler is not required to capture a variable if it can prove that no references to the variable will actually be evaluated. Programmers can force a variable to be captured by referencing it in a statement at the beginning of the Block, like so:
(void) foo;
This matters when capturing the variable has side-effects, as it can in Objective-C or C++.
The lifetime of variables declared in a Block is that of a function; each activation frame contains a new copy of variables declared within the local scope of the Block. Such variable declarations should be allowed anywhere [testme] rather than only when C99 parsing is requested, including for statements. [testme]
Block literal expressions may occur within Block literal expressions (nest) and all variables captured by any nested blocks are implicitly also captured in the scopes of their enclosing Blocks.
A Block literal expression may be used as the initialization value for Block variables at global or local static scope.
The Invoke Operator
Blocks are invoked using function call syntax with a list of expression parameters of types corresponding to the declaration and returning a result type also according to the declaration. Given:
int (^x)(char);
void (^z)(void);
int (^(*y))(char) = &x;
...the following are all legal Block invocations:
x('a');
(*y)('a');
(true ? x : *y)('a')
The Copy and Release Operations
The compiler and runtime provide copy and release operations for Block references that create and, in matched use, release allocated storage for referenced Blocks.
The copy operation Block_copy() is styled as a function that takes an arbitrary Block reference and returns a Block reference of the same type. The release operation, Block_release(), is styled as a function that takes an arbitrary Block reference and, if dynamically matched to a Block copy operation, allows recovery of the referenced allocated memory.
The __block Storage Qualifier
In addition to the new Block type we also introduce a new storage qualifier, __block, for local variables. [testme: a __block declaration within a block literal] The __block storage qualifier is mutually exclusive to the existing local storage qualifiers auto, register, and static.[testme] Variables qualified by __block act as if they were in allocated storage and this storage is automatically recovered after last use of said variable. An implementation may choose an optimization where the storage is initially automatic and only "moved" to allocated (heap) storage upon a Block_copy of a referencing Block. Such variables may be mutated as normal variables are.
In the case where a __block variable is a Block one must assume that the __block variable resides in allocated storage and as such is assumed to reference a Block that is also in allocated storage (that it is the result of a Block_copy operation). Despite this there is no provision to do a Block_copy or a Block_release if an implementation provides initial automatic storage for Blocks. This is due to the inherent race condition of potentially several threads trying to update the shared variable and the need for synchronization around disposing of older values and copying new ones. Such synchronization is beyond the scope of this language specification.
Control Flow
The compound statement of a Block is treated much like a function body with respect to control flow in that goto, break, and continue do not escape the Block. Exceptions are treated "normally" in that when thrown they pop stack frames until a catch clause is found.
Objective-C Extensions
Objective-C extends the definition of a Block reference type to be that also of id. A variable or expression of Block type may be messaged or used as a parameter wherever an id may be. The converse is also true. Block references may thus appear as properties and are subject to the assign, retain, and copy attribute logic that is reserved for objects.
All Blocks are constructed to be Objective-C objects regardless of whether the Objective-C runtime is operational in the program or not. Blocks using automatic (stack) memory are objects and may be messaged, although they may not be assigned into __weak locations if garbage collection is enabled.
Within a Block literal expression within a method definition references to instance variables are also imported into the lexical scope of the compound statement. These variables are implicitly qualified as references from self, and so self is imported as a const copy. The net effect is that instance variables can be mutated.
The Block_copy operator retains all objects held in variables of automatic storage referenced within the Block expression (or form strong references if running under garbage collection). Object variables of __block storage type are assumed to hold normal pointers with no provision for retain and release messages.
Foundation defines (and supplies) -copy and -release methods for Blocks.
In the Objective-C and Objective-C++ languages, we allow the __weak specifier for __block variables of object type. If garbage collection is not enabled, this qualifier causes these variables to be kept without retain messages being sent. This knowingly leads to dangling pointers if the Block (or a copy) outlives the lifetime of this object.
In garbage collected environments, the __weak variable is set to nil when the object it references is collected, as long as the __block variable resides in the heap (either by default or via Block_copy()). The initial Apple implementation does in fact start __block variables on the stack and migrate them to the heap only as a result of a Block_copy() operation.
It is a runtime error to attempt to assign a reference to a stack-based Block into any storage marked __weak, including __weak __block variables.
C++ Extensions
Block literal expressions within functions are extended to allow const use of C++ objects, pointers, or references held in automatic storage.
As usual, within the block, references to captured variables become const-qualified, as if they were references to members of a const object. Note that this does not change the type of a variable of reference type.
For example, given a class Foo:
Foo foo;
Foo &fooRef = foo;
Foo *fooPtr = &foo;
A Block that referenced these variables would import the variables as const variations:
const Foo block_foo = foo;
Foo &block_fooRef = fooRef;
Foo *const block_fooPtr = fooPtr;
Captured variables are copied into the Block at the instant of evaluating the Block literal expression. They are also copied when calling Block_copy() on a Block allocated on the stack. In both cases, they are copied as if the variable were const-qualified, and it's an error if there's no such constructor.
Captured variables in Blocks on the stack are destroyed when control leaves the compound statement that contains the Block literal expression. Captured variables in Blocks on the heap are destroyed when the reference count of the Block drops to zero.
Variables declared as residing in __block storage may be initially allocated in the heap or may first appear on the stack and be copied to the heap as a result of a Block_copy() operation. When copied from the stack, __block variables are copied using their normal qualification (i.e. without adding const). In C++11, __block variables are copied as x-values if that is possible, then as l-values if not; if both fail, it's an error. The destructor for any initial stack-based version is called at the variable's normal end of scope.
References to 'this', as well as references to non-static members of any enclosing class, are evaluated by capturing 'this' just like a normal variable of C pointer type.
Member variables that are Blocks may not be overloaded by the types of their arguments.

View File

@@ -1,170 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Clang Plugins</title>
<link type="text/css" rel="stylesheet" href="../menu.css">
<link type="text/css" rel="stylesheet" href="../content.css">
</head>
<body>
<!--#include virtual="../menu.html.incl"-->
<div id="content">
<h1>Clang Plugins</h1>
<p>Clang Plugins make it possible to run extra user defined actions during
a compilation. This document will provide a basic walkthrough of how to write
and run a Clang Plugin.</p>
<!-- ======================================================================= -->
<h2 id="intro">Introduction</h2>
<!-- ======================================================================= -->
<p>Clang Plugins run FrontendActions over code. See the
<a href="RAVFrontendAction.html">FrontendAction tutorial</a> on how to write a
FrontendAction using the RecursiveASTVisitor. In this tutorial, we'll
demonstrate how to write a simple clang plugin.
</p>
<!-- ======================================================================= -->
<h2 id="pluginactions">Writing a PluginASTAction</h2>
<!-- ======================================================================= -->
<p>The main difference from writing normal FrontendActions is that you can
handle plugin command line options. The
PluginASTAction base class declares a ParseArgs method which you have to
implement in your plugin.
</p>
<pre>
bool ParseArgs(const CompilerInstance &amp;CI,
const std::vector&lt;std::string>&amp; args) {
for (unsigned i = 0, e = args.size(); i != e; ++i) {
if (args[i] == "-some-arg") {
// Handle the command line argument.
}
}
return true;
}
</pre>
<!-- ======================================================================= -->
<h2 id="registerplugin">Registering a plugin</h2>
<!-- ======================================================================= -->
<p>A plugin is loaded from a dynamic library at runtime by the compiler. To register
a plugin in a library, use FrontendPluginRegistry::Add:</p>
<pre>
static FrontendPluginRegistry::Add&lt;MyPlugin> X("my-plugin-name", "my plugin description");
</pre>
<!-- ======================================================================= -->
<h2 id="example">Putting it all together</h2>
<!-- ======================================================================= -->
<p>Let's look at an example plugin that prints top-level function names.
This example is also checked into the clang repository; please also take a look
at the latest <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/examples/PrintFunctionNames/PrintFunctionNames.cpp?view=markup">checked in version of PrintFunctionNames.cpp</a>.</p>
<pre>
#include "clang/Frontend/FrontendPluginRegistry.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/AST.h"
#include "clang/Frontend/CompilerInstance.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
namespace {
class PrintFunctionsConsumer : public ASTConsumer {
public:
virtual bool HandleTopLevelDecl(DeclGroupRef DG) {
for (DeclGroupRef::iterator i = DG.begin(), e = DG.end(); i != e; ++i) {
const Decl *D = *i;
if (const NamedDecl *ND = dyn_cast&lt;NamedDecl>(D))
llvm::errs() &lt;&lt; "top-level-decl: \"" &lt;&lt; ND->getNameAsString() &lt;&lt; "\"\n";
}
return true;
}
};
class PrintFunctionNamesAction : public PluginASTAction {
protected:
ASTConsumer *CreateASTConsumer(CompilerInstance &amp;CI, llvm::StringRef) {
return new PrintFunctionsConsumer();
}
bool ParseArgs(const CompilerInstance &amp;CI,
const std::vector&lt;std::string>&amp; args) {
for (unsigned i = 0, e = args.size(); i != e; ++i) {
llvm::errs() &lt;&lt; "PrintFunctionNames arg = " &lt;&lt; args[i] &lt;&lt; "\n";
// Example error handling.
if (args[i] == "-an-error") {
DiagnosticsEngine &amp;D = CI.getDiagnostics();
unsigned DiagID = D.getCustomDiagID(
DiagnosticsEngine::Error, "invalid argument '" + args[i] + "'");
D.Report(DiagID);
return false;
}
}
if (args.size() &amp;&amp; args[0] == "help")
PrintHelp(llvm::errs());
return true;
}
void PrintHelp(llvm::raw_ostream&amp; ros) {
ros &lt;&lt; "Help for PrintFunctionNames plugin goes here\n";
}
};
}
static FrontendPluginRegistry::Add&lt;PrintFunctionNamesAction>
X("print-fns", "print function names");
</pre>
<!-- ======================================================================= -->
<h2 id="running">Running the plugin</h2>
<!-- ======================================================================= -->
<p>To run a plugin, the dynamic library containing the plugin registry must be
loaded via the -load command line option. This will load all plugins that are
registered, and you can select the plugins to run by specifying the -plugin
option. Additional parameters for the plugins can be passed with -plugin-arg-&lt;plugin-name>.</p>
<p>Note that those options must reach clang's cc1 process. There are two
ways to do so:</p>
<ul>
<li>
Directly call the parsing process by using the -cc1 option; this has the
downside of not configuring the default header search paths, so you'll need to
specify the full system path configuration on the command line.
</li>
<li>
Use clang as usual, but prefix all arguments to the cc1 process with -Xclang.
</li>
</ul>
<p>For example, to run the print-function-names plugin over a source file in clang,
first build the plugin, and then call clang with the plugin from the source tree:</p>
<pre>
$ export BD=/path/to/build/directory
$ (cd $BD &amp;&amp; make PrintFunctionNames )
$ clang++ -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS \
-D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D_GNU_SOURCE \
-I$BD/tools/clang/include -Itools/clang/include -I$BD/include -Iinclude \
tools/clang/tools/clang-check/ClangCheck.cpp -fsyntax-only \
-Xclang -load -Xclang $BD/lib/PrintFunctionNames.so -Xclang \
-plugin -Xclang print-fns
</pre>
<p>Also see the print-function-name plugin example's
<a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/examples/PrintFunctionNames/README.txt?view=markup">README</a></p>
</div>
</body>
</html>

View File

@@ -1,110 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Clang Tools</title>
<link type="text/css" rel="stylesheet" href="../menu.css">
<link type="text/css" rel="stylesheet" href="../content.css">
</head>
<body>
<!--#include virtual="../menu.html.incl"-->
<div id="content">
<h1>Clang Tools</h1>
<p>Clang Tools are standalone command line (and potentially GUI) tools design
for use by C++ developers who are already using and enjoying Clang as their
compiler. These tools provide developer-oriented functionality such as fast
syntax checking, automatic formatting, refactoring, etc.</p>
<p>Only a couple of the most basic and fundamental tools are kept in the primary
Clang Subversion project. The rest of the tools are kept in a side-project so
that developers who don't want or need to build them don't. If you want to get
access to the extra Clang Tools repository, simply check it out into the tools
tree of your Clang checkout and follow the usual process for building and
working with a combined LLVM/Clang checkout:</p>
<ul>
<li>With Subversion:
<ul>
<li><tt>cd llvm/tools/clang/tools</tt></li>
<li><tt>svn co http://llvm.org/svn/llvm-project/clang-tools-extra/trunk
extra</tt></li>
</ul>
</li>
<li>Or with Git:
<ul>
<li><tt>cd llvm/tools/clang/tools</tt></li>
<li><tt>git clone http://llvm.org/git/clang-tools-extra.git extra</tt></li>
</ul>
</li>
</ul>
<p>This document describes a high-level overview of the organization of Clang
Tools within the project as well as giving an introduction to some of the more
important tools. However, it should be noted that this document is currently
focused on Clang and Clang Tool developers, not on end users of these tools.</p>
<!-- ======================================================================= -->
<h2 id="org">Clang Tools Organization</h2>
<!-- ======================================================================= -->
<p>Clang Tools are CLI or GUI programs that are intended to be directly used by
C++ developers. That is they are <em>not</em> primarily for use by Clang
developers, although they are hopefully useful to C++ developers who happen to
work on Clang, and we try to actively dogfood their functionality. They are
developed in three components: the underlying infrastructure for building
a standalone tool based on Clang, core shared logic used by many different tools
in the form of refactoring and rewriting libraries, and the tools
themselves.</p>
<p>The underlying infrastructure for Clang Tools is the
<a href="LibTooling.html">LibTooling</a> platform. See its documentation for
much more detailed information about how this infrastructure works. The common
refactoring and rewriting toolkit-style library is also part of LibTooling
organizationally.</p>
<p>A few Clang Tools are developed along side the core Clang libraries as
examples and test cases of fundamental functionality. However, most of the tools
are developed in a side repository to provide easy separation from the core
libraries. We intentionally do not support public libraries in the side
repository, as we want to carefully review and find good APIs for libraries as
they are lifted out of a few tools and into the core Clang library set.</p>
<p>Regardless of which repository Clang Tools' code resides in, the development
process and practices for all Clang Tools are exactly those of Clang itself.
They are entirely within the Clang <em>project</em>, regardless of the version
control scheme.</p>
<!-- ======================================================================= -->
<h2 id="coretools">Core Clang Tools</h2>
<!-- ======================================================================= -->
<p>The core set of Clang tools that are within the main repository are tools
that very specifically compliment, and allow use and testing of <em>Clang</em>
specific functionality.</p>
<h3 id="clang-check"><tt>clang-check</tt></h3>
<p>This tool combines the LibTooling framework for running a Clang tool with the
basic Clang diagnostics by syntax checking specific files in a fast, command
line interface. It can also accept flags to re-display the diagnostics in
different formats with different flags, suitable for use driving an IDE or
editor. Furthermore, it can be used in fixit-mode to directly apply fixit-hints
offered by clang.</p>
<p>FIXME: Link to user-oriented clang-check documentation.</p>
<!-- ======================================================================= -->
<h2 id="registerplugin">Extra Clang Tools</h2>
<!-- ======================================================================= -->
<p>As various categories of Clang Tools are added to the extra repository,
they'll be tracked here. The focus of this documentation is on the scope and
features of the tools for other tool developers; each tool should provide its
own user-focused documentation.</p>
</div>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More