diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6bceefa0a..ada17cacf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,7 @@ on: pull_request: branches: - master + - tape-push - coverity_scan concurrency: diff --git a/include/clad/Differentiator/CladUtils.h b/include/clad/Differentiator/CladUtils.h index baf8649eb..2a3b4cdab 100644 --- a/include/clad/Differentiator/CladUtils.h +++ b/include/clad/Differentiator/CladUtils.h @@ -28,7 +28,7 @@ namespace clad { std::string ComputeEffectiveFnName(const clang::FunctionDecl* FD); /// Creates and returns a compound statement having statements as follows: - /// {`S`, all the statement of `initial` in sequence} + /// {`S`, all the statement of `initial` in sequence} clang::CompoundStmt* PrependAndCreateCompoundStmt(clang::ASTContext& C, clang::Stmt* initial, clang::Stmt* S); @@ -38,7 +38,7 @@ namespace clad { clang::CompoundStmt* AppendAndCreateCompoundStmt(clang::ASTContext& C, clang::Stmt* initial, clang::Stmt* S); - + /// Shorthand to issues a warning or error. template void EmitDiag(clang::Sema& semaRef, @@ -126,8 +126,8 @@ namespace clad { /// /// \param S /// \param namespc - /// \param shouldExist If true, then asserts that the specified namespace - /// is found. + /// \param shouldExist If true, then asserts that the specified namespace + /// is found. /// \param DC clang::NamespaceDecl* LookupNSD(clang::Sema& S, llvm::StringRef namespc, bool shouldExist, @@ -234,7 +234,7 @@ namespace clad { bool IsCladValueAndPushforwardType(clang::QualType T); - /// Returns a valid `SourceRange` to be used in places where clang + /// Returns a valid `SourceRange` to be used in places where clang /// requires a valid `SourceRange`. clang::SourceRange GetValidSRange(clang::Sema& semaRef); @@ -313,6 +313,9 @@ namespace clad { bool hasNonDifferentiableAttribute(const clang::Decl* D); bool hasNonDifferentiableAttribute(const clang::Expr* E); + /// Finds all the possible expressions E could return a reference to. + /// For example, for 'x = y' it will return the DeclRefExpr* of x. + std::vector GetInnermostReturnExpr(clang::Expr* E); } // namespace utils } diff --git a/include/clad/Differentiator/Compatibility.h b/include/clad/Differentiator/Compatibility.h index fdc76a9b4..946665f80 100644 --- a/include/clad/Differentiator/Compatibility.h +++ b/include/clad/Differentiator/Compatibility.h @@ -133,6 +133,20 @@ static inline bool Expr_EvaluateAsInt(const Expr *E, #endif } +// Clang 12: bool Expr::EvaluateAsConstantExpr(EvalResult &Result, +// ConstExprUsage Usage, ASTContext &) +// => bool Expr::EvaluateAsConstantExpr(EvalResult &Result, ASTContext &) + +static inline bool Expr_EvaluateAsConstantExpr(const Expr* E, + Expr::EvalResult& res, + const ASTContext& Ctx) { +#if CLANG_VERSION_MAJOR < 12 + return E->EvaluateAsConstantExpr(res, Expr::EvaluateForCodeGen, Ctx); +#else + return E->EvaluateAsConstantExpr(res, Ctx); +#endif +} + // Compatibility helper function for creation IfStmt. // Clang 8 and above use Create. // Clang 12 and above use two extra params. diff --git a/include/clad/Differentiator/ReverseModeVisitor.h b/include/clad/Differentiator/ReverseModeVisitor.h index 3b668ff87..a9f28c569 100644 --- a/include/clad/Differentiator/ReverseModeVisitor.h +++ b/include/clad/Differentiator/ReverseModeVisitor.h @@ -30,7 +30,7 @@ namespace clad { class ReverseModeVisitor : public clang::ConstStmtVisitor, public VisitorBase { - + private: // FIXME: We should remove friend-dependency of the plugin classes here. // For this we will need to separate out AST related functions in @@ -42,6 +42,7 @@ namespace clad { /// the reverse mode we also accumulate Stmts for the reverse pass which /// will be executed on return. std::vector m_Reverse; + std::vector m_EssentialReverse; /// Stack is used to pass the arguments (dfdx) to further nodes /// in the Visit method. std::stack m_Stack; @@ -55,10 +56,6 @@ namespace clad { bool isInsideLoop = false; /// Output variable of vector-valued function std::string outputArrayStr; - /// Stores the pop index values for arrays in reverse mode.This is required - /// to maintain the correct statement order when the current block has - /// delayed emission i.e. assignment LHS. - Stmts m_PopIdxValues; std::vector m_LoopBlock; unsigned outputArrayCursor = 0; unsigned numParams = 0; @@ -137,15 +134,19 @@ namespace clad { Stmts& getCurrentBlock(direction d = direction::forward) { if (d == direction::forward) return m_Blocks.back(); - else + else if (d == direction::reverse) return m_Reverse.back(); + else + return m_EssentialReverse.back(); } /// Create new block. Stmts& beginBlock(direction d = direction::forward) { if (d == direction::forward) m_Blocks.push_back({}); - else + else if (d == direction::reverse) m_Reverse.push_back({}); + else + m_EssentialReverse.push_back({}); return getCurrentBlock(d); } /// Remove the block from the stack, wrap it in CompoundStmt and return it. @@ -154,13 +155,28 @@ namespace clad { auto CS = MakeCompoundStmt(getCurrentBlock(direction::forward)); m_Blocks.pop_back(); return CS; - } else { + } else if (d == direction::reverse) { auto CS = MakeCompoundStmt(getCurrentBlock(direction::reverse)); std::reverse(CS->body_begin(), CS->body_end()); m_Reverse.pop_back(); return CS; + } else { + auto CS = MakeCompoundStmt(getCurrentBlock(d)); + m_EssentialReverse.pop_back(); + return CS; } } + + Stmts EndBlockWithoutCreatingCS(direction d = direction::forward) { + auto blk = getCurrentBlock(d); + if (d == direction::forward) + m_Blocks.pop_back(); + else if (d == direction::reverse) + m_Reverse.pop_back(); + else + m_EssentialReverse.pop_back(); + return blk; + } /// Output a statement to the current block. If Stmt is null or is an unused /// expression, it is not output and false is returned. bool addToCurrentBlock(clang::Stmt* S, direction d = direction::forward) { @@ -200,7 +216,15 @@ namespace clad { // Name reverse temporaries as "_r" instead of "_t". if ((d == direction::reverse) && (prefix == "_t")) prefix = "_r"; - return VisitorBase::StoreAndRef(E, Type, getCurrentBlock(d), prefix, + Stmts* blk = nullptr; + if (d == direction::essential_reverse) { + if (!m_EssentialReverse.empty()) + blk = &getCurrentBlock(direction::essential_reverse); + else + blk = &getCurrentBlock(direction::reverse); + } else + blk = &getCurrentBlock(d); + return VisitorBase::StoreAndRef(E, Type, *blk, prefix, forceDeclCreation, IS); } @@ -251,6 +275,12 @@ namespace clad { StmtDiff Result; bool isConstant; bool isInsideLoop; + bool needsUpdate; + DelayedStoreResult(ReverseModeVisitor& pV, StmtDiff pResult, + bool pIsConstant, bool pIsInsideLoop, + bool pNeedsUpdate = false) + : V(pV), Result(pResult), isConstant(pIsConstant), + isInsideLoop(pIsInsideLoop), needsUpdate(pNeedsUpdate) {} void Finalize(clang::Expr* New); }; @@ -394,7 +424,7 @@ namespace clad { clang::QualType xType); /// Allows to easily create and manage a counter for counting the number of - /// executed iterations of a loop. + /// executed iterations of a loop. /// /// It is required to save the number of executed iterations to use the /// same number of iterations in the reverse pass. @@ -413,11 +443,11 @@ namespace clad { /// for counter; otherwise, returns nullptr. clang::Expr* getPush() const { return m_Push; } - /// Returns `clad::pop(_t)` expression if clad tape is used for + /// Returns `clad::pop(_t)` expression if clad tape is used for /// for counter; otherwise, returns nullptr. clang::Expr* getPop() const { return m_Pop; } - /// Returns reference to the last object of the clad tape if clad tape + /// Returns reference to the last object of the clad tape if clad tape /// is used as the counter; otherwise returns reference to the counter /// variable. clang::Expr* getRef() const { return m_Ref; } @@ -459,11 +489,11 @@ namespace clad { /// This class modifies forward and reverse blocks of the loop /// body so that `break` and `continue` statements are correctly - /// handled. `break` and `continue` statements are handled by + /// handled. `break` and `continue` statements are handled by /// enclosing entire reverse block loop body in a switch statement /// and only executing the statements, with the help of case labels, - /// that were executed in the associated forward iteration. This is - /// determined by keeping track of which `break`/`continue` statement + /// that were executed in the associated forward iteration. This is + /// determined by keeping track of which `break`/`continue` statement /// was hit in which iteration and that in turn helps to determine which /// case label should be selected. /// @@ -491,7 +521,7 @@ namespace clad { /// \note `m_ControlFlowTape` is only initialized if the body contains /// `continue` or `break` statement. std::unique_ptr m_ControlFlowTape; - + /// Each `break` and `continue` statement is assigned a unique number, /// starting from 1, that is used as the case label corresponding to that `break`/`continue` /// statement. `m_CaseCounter` stores the value that was used for last @@ -530,7 +560,7 @@ namespace clad { /// control flow switch statement. clang::CaseStmt* GetNextCFCaseStmt(); - /// Builds and returns `clad::push(TapeRef, m_CurrentCounter)` + /// Builds and returns `clad::push(TapeRef, m_CurrentCounter)` /// expression, where `TapeRef` and `m_CurrentCounter` are replaced /// by their actual values respectively. clang::Stmt* CreateCFTapePushExprToCurrentCase(); @@ -553,7 +583,9 @@ namespace clad { void PopBreakContStmtHandler() { m_BreakContStmtHandlers.pop_back(); } - + + std::map m_ToBeRecorded; + /// Registers an external RMV source. /// /// Multiple external RMV source can be registered by calling this function @@ -581,6 +613,8 @@ namespace clad { clang::QualType ComputeAdjointType(clang::QualType T); clang::QualType ComputeParamType(clang::QualType T); + + std::vector GetInnermostReturnExpr(clang::Expr* E); }; } // end namespace clad diff --git a/include/clad/Differentiator/ReverseModeVisitorDirectionKinds.h b/include/clad/Differentiator/ReverseModeVisitorDirectionKinds.h index 449a996a7..d4cbc8d95 100644 --- a/include/clad/Differentiator/ReverseModeVisitorDirectionKinds.h +++ b/include/clad/Differentiator/ReverseModeVisitorDirectionKinds.h @@ -4,7 +4,7 @@ namespace clad { namespace rmv { /// An enum to operate between forward and reverse passes. - enum direction : int { forward, reverse }; + enum direction : int { forward, reverse, essential_reverse }; } // namespace rmv } // namespace clad diff --git a/include/clad/Differentiator/TBRAnalyzer.h b/include/clad/Differentiator/TBRAnalyzer.h new file mode 100644 index 000000000..e05ee645c --- /dev/null +++ b/include/clad/Differentiator/TBRAnalyzer.h @@ -0,0 +1,304 @@ +#ifndef CLAD_TBR_ANALYZER_H +#define CLAD_TBR_ANALYZER_H + +#include "clang/AST/StmtVisitor.h" +#include "clad/Differentiator/CladUtils.h" + +#include +#include + +using namespace clang; + +namespace clad { + +class TBRAnalyzer : public clang::ConstStmtVisitor { +private: + /// Used to provide a hash function for an unordered_map with llvm::APInt + /// type keys. + struct APIntHash { + size_t operator()(const llvm::APInt& apint) const { + return std::hash{}(apint.toString(10, true)); + } + }; + + /// Just a helper struct serving as a wrapper for IdxOrMemberValue union. + /// Used to unwrap expressions like a[6].x.t[3]. Only used in + /// TBRAnalyzer::overlay(). + struct IdxOrMember { + enum IdxOrMemberType { FIELD, INDEX }; + union IdxOrMemberValue { + const clang::FieldDecl* field; + llvm::APInt index; + IdxOrMemberValue() {} + ~IdxOrMemberValue() {} + }; + IdxOrMemberType type; + IdxOrMemberValue val; + IdxOrMember(const clang::FieldDecl* field) : type(IdxOrMemberType::FIELD) { + val.field = field; + } + IdxOrMember(llvm::APInt index) : type(IdxOrMemberType::INDEX) { + new (&val.index) llvm::APInt(index); + } + IdxOrMember(const IdxOrMember& other) : type(other.type) { + if (type == IdxOrMemberType::FIELD) + val.field = other.val.field; + else + new (&val.index) llvm::APInt(other.val.index); + } + }; + + /// Stores all the necessary information about one variable. Fundamental type + /// variables need only one bool. An object/array needs a separate VarData for + /// every its field/element. Reference type variables have their own type for + /// convenience reasons and just point to the corresponding VarData. + /// UNDEFINED is used whenever the type of a node cannot be determined. + + /// FIXME: Pointers to objects are considered OBJ_TYPE for simplicity. This + /// approach might cause problems when the support for pointers is added. + + /// FIXME: Only References to concrete variables are supported. Using + /// 'double& x = (cond ? a : b);' or 'double& x = arr[n*k]' (non-const index) + /// will lead to unpredictable behavior. + + /// FIXME: Different array elements are considered different variables + /// which makes the analysis way more complicated (both in terms of + /// readability and performance) when non-constant indices are used. Moreover, + /// in such cases we start assuming 'arr[k]' could be any element of arr. + /// The only scenario where analysing elements separately would make sense is + /// when an element with the same constant index is changed multiple times in + /// a row, which seems uncommon. It's worth considering analysing arrays as + /// whole structures instead (just one VarData for the whole array). + + struct VarData { + enum VarDataType { UNDEFINED, FUND_TYPE, OBJ_TYPE, ARR_TYPE, REF_TYPE }; + union VarDataValue { + bool fundData; + std::unordered_map objData; + std::unordered_map arrData; + VarData* refData; + VarDataValue() {} + ~VarDataValue() {} + }; + VarDataType type; + VarDataValue val; + bool isReferenced = false; + + /// For non-fundamental type variables, all the child nodes have to be + /// deleted. + ~VarData() { + if (type == OBJ_TYPE) { + for (auto pair : val.objData) { + delete pair.second; + } + } else if (type == ARR_TYPE) { + for (auto pair : val.arrData) { + delete pair.second; + } + } + } + + /// Recursively sets all the leaves' bools to isReq. + void setIsRequired(bool isReq = true); + /// Returns true if there is at least one required to store node among + /// child nodes. + bool findReq(); + /// Whenever an array element with a non-constant index is set to required + /// this function is used to set to required all the array elements that + /// could match that element (e.g. set 'a[1].y' and 'a[6].y' to required + /// when 'a[k].y' is set to required). Takes unwrapped sequence of + /// indices/members of the expression being overlaid and the index of of the + /// current index/member. + void overlay(llvm::SmallVector& IdxAndMemberSequence, + size_t i); + /// Used to merge together VarData for one variable from two branches + /// (e.g. after an if-else statements). Look at the Control Flow section for + /// more information. + void merge(VarData* mergeData); + /// Used to recursively copy VarData when separating into different branches + /// (e.g. when entering an if-else statements). Look at the Control Flow + /// section for more information. refVars stores copied nodes for + /// corresponding original nodes in case those are referenced (a referenced + /// node is a child to multiple nodes, therefore, we need to make sure we + /// don't make multiple copies of it). + VarData* copy(std::unordered_map& refVars); + }; + + /// Given a MemberExpr*/ArraySubscriptExpr* return a pointer to its + /// corresponding VarData. If the given element of an array does not have a + /// VarData* yet it will be added automatically. If addNonConstIdx==false this + /// will return the last VarData* before the non-constant index + /// (e.g. for 'x.arr[k+1].y' the return value will be the VarData* of x.arr). + /// Otherwise, non-const indices will be represented as index -1. + VarData* getMemberVarData(const clang::MemberExpr* ME, + bool addNonConstIdx = false); + VarData* getArrSubVarData(const clang::ArraySubscriptExpr* ASE, + bool addNonConstIdx = false); + /// Given an Expr* returns its corresponding VarData. + VarData* getExprVarData(const clang::Expr* E, bool addNonConstIdx = false); + /// Adds the field FD to objData. + void addField(std::unordered_map& objData, + const FieldDecl* FD); + /// Whenever an array element with a non-constant index is set to required + /// this function is used to set to required all the array elements that + /// could match that element (e.g. set 'a[1].y' and 'a[6].y' to required when + /// 'a[k].y' is set to required). Unwraps the a given expression into a + /// sequence of indices/members of the expression being overlaid and calls + /// VarData::overlay() recursively. + void overlay(const clang::Expr* E); + + /// Used to store all the necessary information about variables at a + /// particular moment. + /// Note: 'this' pointer does not have a declaration so nullptr is used as + /// its key instead. + using VarsData = std::unordered_map; + /// Used to find DeclRefExpr's that will be used in the backwards pass. + /// In order to be marked as required, a variables has to appear in a place + /// where it would have a differential influence and will appear non-linearly + /// (e.g. for 'x = 2 * y;', y will not appear in the backwards pass). Hence, + /// markingMode and nonLinearMode. + enum Mode { markingMode = 1, nonLinearMode = 2 }; + /// Tells if the variable at a given location is required to store. Basically, + /// is the result of analysis. + std::map TBRLocs; + /// Stores VarsData for every branch in control flow (e.g. if-else statements, + /// loops). + std::vector reqStack; + /// Stores modes in a stack (used to retrieve the old mode after entering + /// a new one). + std::vector modeStack; + /// Stores local variables to delete them after exiting the corresponding + /// scope. + /// Note: This is not used every time a new scope is entered. This is only + /// used when merging an if-else statement to get rid of local variables in + /// the then-branch. + std::vector> localVarsStack; + + ASTContext* m_Context; + + /// The index of the innermost branch corresponding to a loop (used to handle + /// break/continue statements). + size_t innermostLoopBranch = 0; + /// Tells if the current branch should be deleted instead of merged with + /// others. This happens when the branch has a break/continue statement or a + /// return expression in it. + bool deleteCurBranch = false; + /// Loop bodies have to be passed twice. This tells us what pass is currently + /// happening. + bool firstLoopPass = false; + /// Set to true when a non-const index is found while analysing an + /// array subscript expression. + bool nonConstIndexFound = false; + + //// Setters + /// Creates VarData for a new VarDecl*. + void addVar(const clang::VarDecl* VD); + /// Marks the SourceLocation of E if it is required to store. + /// E could be DeclRefExpr*, ArraySubscriptExpr* or MemberExpr*. + void markLocation(const clang::Expr* E); + /// Sets E's corresponding VarData (or all its child nodes) to + /// required/not required. For isReq==true, checks if the current mode is + /// markingMode and nonLinearMode. E could be DeclRefExpr*, + /// ArraySubscriptExpr* or MemberExpr*. + void setIsRequired(const clang::Expr* E, bool isReq = true); + + //// Control Flow + /// Creates a new branch as a copy of the last used branch. + void addBranch(); + /// Merges the last into the one right before it and deletes it. + /// If keepNewVars==false, it removes all the variables that are present + /// in the last branch but not the other. If keepNewVars==true, all the new + /// variables are kept. + /// Note: The branch we are merging into is not supposed to have its own + /// local variables (this doesn't matter to the branch being merged). + void mergeAndDelete(bool keepNewVars = false); + /// Swaps the last two branches in the stack. + void swapLastPairOfBranches(); + /// Merges the current branch to a branch with a given index in the stack. + /// Current branch is NOT deleted. + /// Note: The branch we are merging into is not supposed to have its own + /// local variables (this doesn't matter to the branch being merged). + void mergeCurBranchTo(size_t targetBranchNum); + /// Removes local variables from the current branch (uses localVarsStack). + /// This is necessary when merging if-else branches. + void removeLocalVars(); + + //// Modes Setters + /// Sets the mode manually + void setMode(int mode) { modeStack.push_back(mode); } + /// Sets nonLinearMode but leaves markingMode just as it was. + void startNonLinearMode() { + modeStack.push_back(modeStack.back() | Mode::nonLinearMode); + } + /// Sets markingMode but leaves nonLinearMode just as it was. + void startMarkingMode() { + modeStack.push_back(Mode::markingMode | modeStack.back()); + } + /// Removes the last mode in the stack (retrieves the previous one). + void resetMode() { modeStack.pop_back(); } + +public: + // Constructor + TBRAnalyzer(ASTContext* m_Context) : m_Context(m_Context) { + modeStack.push_back(0); + reqStack.push_back(VarsData()); + } + + // Destructor + ~TBRAnalyzer() { + /// By the end of analysis, reqStack is supposed have just one branch + /// but it's better to iterate through it just to make sure there's no + /// memory leak. + for (auto& branch : reqStack) { + for (auto pair : branch) { + delete pair.second; + } + } + } + + /// Returns the result of the whole analysis + const std::map getResult() { return TBRLocs; } + + /// Visitors + + void Analyze(const clang::FunctionDecl* FD); + + void Visit(const clang::Stmt* stmt) { + clang::ConstStmtVisitor::Visit(stmt); + } + + void VisitArraySubscriptExpr(const clang::ArraySubscriptExpr* ASE); + void VisitBinaryOperator(const clang::BinaryOperator* BinOp); + void VisitBreakStmt(const clang::BreakStmt* BS); + void VisitCallExpr(const clang::CallExpr* CE); + void VisitCompoundStmt(const clang::CompoundStmt* CS); + void VisitConditionalOperator(const clang::ConditionalOperator* CO); + void VisitContinueStmt(const clang::ContinueStmt* CS); + void VisitCXXConstructExpr(const clang::CXXConstructExpr* CE); + void VisitCXXDefaultArgExpr(const clang::CXXDefaultArgExpr* DE); + void VisitCXXStaticCastExpr(const clang::CXXStaticCastExpr* SCE); + void VisitDeclRefExpr(const clang::DeclRefExpr* DRE); + void VisitDeclStmt(const clang::DeclStmt* DS); + void VisitDoStmt(const clang::DoStmt* DS); + void VisitExprWithCleanups(const clang::ExprWithCleanups* EWC); + void VisitForStmt(const clang::ForStmt* FS); + void VisitIfStmt(const clang::IfStmt* If); + void VisitImplicitCastExpr(const clang::ImplicitCastExpr* ICE); + void VisitInitListExpr(const clang::InitListExpr* ILE); + void VisitMemberExpr(const clang::MemberExpr* ME); + void VisitParenExpr(const clang::ParenExpr* PE); + void VisitReturnStmt(const clang::ReturnStmt* RS); + void VisitUnaryOperator(const clang::UnaryOperator* UnOp); + void VisitWhileStmt(const clang::WhileStmt* WS); + + /// FIXME: Make sure these are not necessary + /// Unused Visitors: + // void VisitCXXBoolLiteralExpr(const clang::CXXBoolLiteralExpr* BL); + // void VisitCXXThisExpr(const clang::CXXThisExpr* TE); + // void VisitFloatingLiteral(const clang::FloatingLiteral* FL); + // void VisitIntegerLiteral(const clang::IntegerLiteral* IL); + // void VisitStmt(const clang::Stmt* S); +}; + +} // end namespace clad +#endif // CLAD_TBR_ANALYZER_H diff --git a/include/clad/Differentiator/VisitorBase.h b/include/clad/Differentiator/VisitorBase.h index e9118481f..3a0d9a3f1 100644 --- a/include/clad/Differentiator/VisitorBase.h +++ b/include/clad/Differentiator/VisitorBase.h @@ -33,10 +33,13 @@ namespace clad { private: std::array data; clang::Stmt* m_DerivativeForForwSweep; + clang::Stmt* m_ValueForRevSweep; public: StmtDiff(clang::Stmt* orig = nullptr, clang::Stmt* diff = nullptr, - clang::Stmt* forwSweepDiff = nullptr) - : m_DerivativeForForwSweep(forwSweepDiff) { + clang::Stmt* forwSweepDiff = nullptr, + clang::Stmt* valueForRevSweep = nullptr) + : m_DerivativeForForwSweep(forwSweepDiff), + m_ValueForRevSweep(valueForRevSweep) { data[1] = orig; data[0] = diff; } @@ -57,6 +60,14 @@ namespace clad { clang::Stmt* getForwSweepStmt_dx() { return m_DerivativeForForwSweep; } + clang::Expr* getRevSweepExpr() { + return llvm::cast_or_null(m_ValueForRevSweep); + } + + clang::Stmt* getRevSweepStmt() { + return m_ValueForRevSweep; + } + clang::Expr* getForwSweepExpr_dx() { return llvm::cast_or_null(m_DerivativeForForwSweep); } diff --git a/lib/Differentiator/CMakeLists.txt b/lib/Differentiator/CMakeLists.txt index 61695990e..0c6dd37be 100644 --- a/lib/Differentiator/CMakeLists.txt +++ b/lib/Differentiator/CMakeLists.txt @@ -32,6 +32,7 @@ add_llvm_library(cladDifferentiator JacobianModeVisitor.cpp MultiplexExternalRMVSource.cpp ReverseModeVisitor.cpp + TBRAnalyzer.cpp StmtClone.cpp VectorForwardModeVisitor.cpp Version.cpp diff --git a/lib/Differentiator/CladUtils.cpp b/lib/Differentiator/CladUtils.cpp index bd97b37a6..2c06b87be 100644 --- a/lib/Differentiator/CladUtils.cpp +++ b/lib/Differentiator/CladUtils.cpp @@ -109,7 +109,7 @@ namespace clad { CompoundStmt* CS = dyn_cast(initial); if (CS) block.append(CS->body_begin(), CS->body_end()); - else + else block.push_back(initial); auto stmtsRef = clad_compat::makeArrayRef(block.begin(), block.end()); return clad_compat::CompoundStmt_Create(C, stmtsRef /**/CLAD_COMPAT_CLANG15_CompoundStmt_Create_ExtraParam1(CS), noLoc, noLoc); @@ -182,7 +182,7 @@ namespace clad { if (isa(DC2)) break; if (isa(DC2)) { - DC2 = DC2->getParent(); + DC2 = DC2->getParent(); continue; } // We don't want to 'extend' the DC1 context with class declarations. @@ -253,7 +253,7 @@ namespace clad { } return DC; } - + StringLiteral* CreateStringLiteral(ASTContext& C, llvm::StringRef str) { // Copied and adapted from clang::Sema::ActOnStringLiteral. QualType CharTyConst = C.CharTy.withConst(); @@ -346,10 +346,10 @@ namespace clad { QualType valueType = T; if (T->isPointerType()) valueType = T->getPointeeType(); - else if (T->isReferenceType()) + else if (T->isReferenceType()) valueType = T.getNonReferenceType(); // FIXME: `QualType::getPointeeOrArrayElementType` loses type qualifiers. - else if (T->isArrayType()) + else if (T->isArrayType()) valueType = T->getPointeeOrArrayElementType()->getCanonicalTypeInternal(); return valueType; @@ -427,7 +427,7 @@ namespace clad { else if (S) block.push_back(S); } - + MemberExpr* BuildMemberExpr(clang::Sema& semaRef, clang::Scope* S, clang::Expr* base, llvm::ArrayRef fields) { @@ -533,5 +533,68 @@ namespace clad { // non-differentiable attribute return false; } + + std::vector GetInnermostReturnExpr(clang::Expr* E) { + struct Finder : public ConstStmtVisitor { + std::vector m_return_exprs; + // Sema* m_Sema; + // ASTContext* m_Context; + + public: + Finder(/*Sema* S*/) /* : m_Sema(S), m_Context(S.getASTContext()) */ {} + + std::vector Find(const clang::Expr* E) { + Visit(E); + return m_return_exprs; + } + + void VisitBinaryOperator(const clang::BinaryOperator* BO) { + if (BO->isAssignmentOp() || BO->isCompoundAssignmentOp()) { + Visit(BO->getLHS()); + } else if (BO->isCommaOp()) { + /**/ + } else { + assert("Unexpected binary operator!!"); + } + } + + void VisitConditionalOperator(const clang::ConditionalOperator* CO) { + // FIXME: in cases like (cond ? x : y) = 2; both x and y will be + // stored. + Visit(CO->getTrueExpr()); + Visit(CO->getFalseExpr()); + } + + void VisitUnaryOperator(const clang::UnaryOperator* UnOp) { + auto opCode = UnOp->getOpcode(); + if (opCode == clang::UO_PreInc || opCode == clang::UO_PreDec) + Visit(UnOp->getSubExpr()); + } + + void VisitDeclRefExpr(const clang::DeclRefExpr* DRE) { + m_return_exprs.push_back(const_cast(DRE)); + } + + void VisitExpr(const clang::Expr* E) { + if (auto PE = dyn_cast(E)) { + Visit(PE->getSubExpr()); + } + } + + void VisitMemberExpr(const clang::MemberExpr* ME) { + m_return_exprs.push_back(const_cast(ME)); + } + + void VisitArraySubscriptExpr(const clang::ArraySubscriptExpr* ASE) { + m_return_exprs.push_back(const_cast(ASE)); + } + + void VisitImplicitCastExpr(const clang::ImplicitCastExpr* ICE) { + Visit(ICE->getSubExpr()); + } + }; + Finder finder; + return finder.Find(E); + } } // namespace utils } // namespace clad diff --git a/lib/Differentiator/ReverseModeVisitor.cpp b/lib/Differentiator/ReverseModeVisitor.cpp index 4e9805b64..e074c1545 100644 --- a/lib/Differentiator/ReverseModeVisitor.cpp +++ b/lib/Differentiator/ReverseModeVisitor.cpp @@ -10,9 +10,10 @@ #include "clad/Differentiator/DiffPlanner.h" #include "clad/Differentiator/ErrorEstimator.h" -#include "clad/Differentiator/StmtClone.h" #include "clad/Differentiator/ExternalRMVSource.h" #include "clad/Differentiator/MultiplexExternalRMVSource.h" +#include "clad/Differentiator/StmtClone.h" +#include "clad/Differentiator/TBRAnalyzer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Expr.h" @@ -454,6 +455,19 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, DerivativeAndOverload ReverseModeVisitor::DerivePullback(const clang::FunctionDecl* FD, const DiffRequest& request) { + TBRAnalyzer* analyzer = new TBRAnalyzer(&m_Context); + analyzer->Analyze(FD); + m_ToBeRecorded = analyzer->getResult(); + delete analyzer; + + // for (auto pair : m_ToBeRecorded) { + // auto line = + // m_Context.getSourceManager().getPresumedLoc(pair.first).getLine(); auto + // column = + // m_Context.getSourceManager().getPresumedLoc(pair.first).getColumn(); + // llvm::errs() << line << "|" <Analyze(m_Function); + m_ToBeRecorded = analyzer->getResult(); + delete analyzer; + + // for (auto pair : m_ToBeRecorded) { + // auto line = + // m_Context.getSourceManager().getPresumedLoc(pair.first).getLine(); auto + // column = + // m_Context.getSourceManager().getPresumedLoc(pair.first).getColumn(); + // llvm::errs() << line << "|" < paramsRef = m_Derivative->parameters(); // create derived variables for parameters which are not part of @@ -800,7 +827,8 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, // skip corresponding push. cond = StoreAndRef(condExpr.getExpr(), direction::forward, "_t", /*forceDeclCreation=*/true); - StmtDiff condPushPop = GlobalStoreAndRef(cond.getExpr(), "_cond"); + StmtDiff condPushPop = GlobalStoreAndRef(cond.getExpr(), "_cond", + /*force=*/true); PushCond = condPushPop.getExpr(); PopCond = condPushPop.getExpr_dx(); } else @@ -1245,26 +1273,20 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, llvm::SmallVector forwSweepDerivativeIndices(Indices.size()); for (std::size_t i = 0; i < Indices.size(); i++) { StmtDiff IdxDiff = Visit(Indices[i]); - StmtDiff IdxStored = GlobalStoreAndRef(IdxDiff.getExpr()); + auto idxStored = GlobalStoreAndRef(IdxDiff.getExpr(), "_t", /*force=*/true); + clonedIndices[i] = idxStored.getExpr(); + reverseIndices[i] = idxStored.getExpr_dx(); if (isInsideLoop) { - // Here we make sure that we are popping each time we push. - // Since the max no of pushes = no. of array index expressions in the - // loop. - Expr* popExpr = IdxStored.getExpr_dx(); - VarDecl* popVal = BuildVarDecl(popExpr->getType(), "_t", popExpr, - /*DirectInit=*/true); - if (dfdx()) - addToCurrentBlock(BuildDeclStmt(popVal), direction::reverse); - else - m_PopIdxValues.push_back(BuildDeclStmt(popVal)); - IdxStored = StmtDiff(IdxStored.getExpr(), BuildDeclRef(popVal)); + auto forwIndex = StoreAndRef(idxStored.getExpr(), direction::forward); + auto revIndex = StoreAndRef(idxStored.getExpr_dx(), direction::essential_reverse, "_r"); + clonedIndices[i] = forwIndex; + reverseIndices[i] = revIndex; } - clonedIndices[i] = IdxStored.getExpr(); - reverseIndices[i] = IdxStored.getExpr_dx(); + // reverseIndices[i] = Clone(IdxDiff.getExpr()); forwSweepDerivativeIndices[i] = IdxDiff.getExpr(); } auto cloned = BuildArraySubscript(BaseDiff.getExpr(), clonedIndices); - + auto valueForRevSweep = BuildArraySubscript(BaseDiff.getExpr(), reverseIndices); Expr* target = BaseDiff.getExpr_dx(); if (!target) return cloned; @@ -1296,7 +1318,7 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, // Add it to the body statements. addToCurrentBlock(add_assign, direction::reverse); } - return StmtDiff(cloned, result, forwSweepDerivative); + return StmtDiff(cloned, result, forwSweepDerivative, valueForRevSweep); } StmtDiff ReverseModeVisitor::VisitDeclRefExpr(const DeclRefExpr* DRE) { @@ -1496,7 +1518,13 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, CallArgDx.push_back(argDiff.getExpr_dx()); // Save cloned arg in a "global" variable, so that it is accessible from // the reverse pass. - StmtDiff argDiffStore = GlobalStoreAndRef(argDiff.getExpr()); + // FIXME: At this point, we assume all the variables passed by reference + // may be changed since we have no way to determine otherwise. + // FIXME: We cannot use GlobalStoreAndRef to store a whole array so now + // arrays are not stored. + StmtDiff argDiffStore = GlobalStoreAndRef( + argDiff.getExpr(), "_t", + /*force=*/passByRef && !argDiff.getExpr()->getType()->isArrayType()); // We need to pass the actual argument in the cloned call expression, // instead of a temporary, for arguments passed by reference. This is // because, callee function may modify the argument passed as reference @@ -1520,7 +1548,9 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, // _t1 = a; // modify(a); // ``` - if (passByRef) { + // FIXME: We cannot use GlobalStoreAndRef to store a whole array so now + // arrays are not stored. + if (passByRef && !argDiff.getExpr()->getType()->isArrayType()) { if (isInsideLoop) { // Add tape push expression. We need to explicitly add it here because // we cannot add it as call expression argument -- we need to pass the @@ -1541,11 +1571,17 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, auto& block = getCurrentBlock(direction::reverse); block.insert(block.begin() + insertionPoint, BuildDeclStmt(argDiffLocalVD)); + // Restore agrs + auto op = BuildOp(BinaryOperatorKind::BO_Assign, + argDiff.getExpr(), BuildDeclRef(argDiffLocalVD)); + block.insert(block.begin() + insertionPoint + 1, op); + Expr* argDiffLocalE = BuildDeclRef(argDiffLocalVD); - // We added local variable to store result of `clad::pop(...)`. Thus - // we need to correspondingly adjust the insertion point. - insertionPoint += 1; + // We added local variable to store result of `clad::pop(...)` and + // restoration of the original arg. Thus we need to correspondingly + // adjust the insertion point. + insertionPoint += 2; // We cannot use the already existing `argDiff.getExpr()` here because // it will cause inconsistent pushes and pops to the clad tape. // FIXME: Modify `GlobalStoreAndRef` such that its functioning is @@ -1554,6 +1590,15 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, Expr* newArgE = Visit(arg).getExpr(); argDiffStore = {newArgE, argDiffLocalE}; } else { + // Restore args + auto& block = getCurrentBlock(direction::reverse); + auto op = BuildOp(BinaryOperatorKind::BO_Assign, + argDiff.getExpr(), argDiffStore.getExpr()); + block.insert(block.begin() + insertionPoint, op); + // We added restoration of the original arg. Thus we need to + // correspondingly adjust the insertion point. + insertionPoint += 1; + argDiffStore = {argDiff.getExpr(), argDiffStore.getExpr_dx()}; } } @@ -1871,6 +1916,7 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, StmtDiff ReverseModeVisitor::VisitUnaryOperator(const UnaryOperator* UnOp) { auto opCode = UnOp->getOpcode(); StmtDiff diff{}; + Expr* E = UnOp->getSubExpr(); // If it is a post-increment/decrement operator, its result is a reference // and we should return it. Expr* ResultRef = nullptr; @@ -1878,23 +1924,37 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, // xi = +xj // dxi/dxj = +1.0 // df/dxj += df/dxi * dxi/dxj = df/dxi - diff = Visit(UnOp->getSubExpr(), dfdx()); + diff = Visit(E, dfdx()); else if (opCode == UO_Minus) { // xi = -xj // dxi/dxj = -1.0 // df/dxj += df/dxi * dxi/dxj = -df/dxi auto d = BuildOp(UO_Minus, dfdx()); - diff = Visit(UnOp->getSubExpr(), d); + diff = Visit(E, d); } else if (opCode == UO_PostInc || opCode == UO_PostDec) { - diff = Visit(UnOp->getSubExpr(), dfdx()); + auto EStored = GlobalStoreAndRef(E); + auto assign = + BuildOp(BinaryOperatorKind::BO_Assign, E, EStored.getExpr_dx()); + if (isInsideLoop) + addToCurrentBlock(EStored.getExpr(), direction::forward); + addToCurrentBlock(assign, direction::reverse); + + diff = Visit(E, dfdx()); ResultRef = diff.getExpr_dx(); if (m_ExternalSource) m_ExternalSource->ActBeforeFinalisingPostIncDecOp(diff); } else if (opCode == UO_PreInc || opCode == UO_PreDec) { - diff = Visit(UnOp->getSubExpr(), dfdx()); + auto EStored = GlobalStoreAndRef(E); + auto assign = + BuildOp(BinaryOperatorKind::BO_Assign, E, EStored.getExpr_dx()); + if (isInsideLoop) + addToCurrentBlock(EStored.getExpr(), direction::forward); + addToCurrentBlock(assign, direction::reverse); + + diff = Visit(E, dfdx()); } else if (opCode == UnaryOperatorKind::UO_Real || opCode == UnaryOperatorKind::UO_Imag) { - diff = VisitWithExplicitNoDfDx(UnOp->getSubExpr()); + diff = VisitWithExplicitNoDfDx(E); ResultRef = BuildOp(opCode, diff.getExpr_dx()); /// Create and add `__real r += dfdx()` expression. if (dfdx()) { @@ -1902,8 +1962,7 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, // Add it to the body statements. addToCurrentBlock(add_assign, direction::reverse); } - } - else { + } else { // FIXME: This is not adding 'address-of' operator support. // This is just making this special case differentiable that is required // for computing hessian: @@ -1918,7 +1977,7 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, if (MD->isInstance()) { auto thisType = clad_compat::CXXMethodDecl_getThisType(m_Sema, MD); if (utils::SameCanonicalType(thisType, UnOp->getType())) { - diff = Visit(UnOp->getSubExpr()); + diff = Visit(E); Expr* cloneE = BuildOp(UnaryOperatorKind::UO_AddrOf, diff.getExpr()); Expr* derivedE = @@ -1934,11 +1993,10 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, if (opCode != UO_LNot) unsupportedOpWarn(UnOp->getEndLoc()); - Expr* subExpr = UnOp->getSubExpr(); - if (isa(subExpr)) - diff = Visit(subExpr); + if (isa(E)) + diff = Visit(E); else - diff = StmtDiff(subExpr); + diff = StmtDiff(E); } Expr* op = BuildOp(opCode, diff.getExpr()); return StmtDiff(op, ResultRef); @@ -1949,6 +2007,7 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, auto opCode = BinOp->getOpcode(); StmtDiff Ldiff{}; StmtDiff Rdiff{}; + StmtDiff Lstored{}; auto L = BinOp->getLHS(); auto R = BinOp->getRHS(); // If it is an assignment operator, its result is a reference to LHS and @@ -1998,7 +2057,11 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, if (!RDelayed.isConstant) { Expr* dr = nullptr; if (dfdx()) { - StmtDiff LResult = GlobalStoreAndRef(LStored); + StmtDiff LResult; + if (isa(LStored->IgnoreImpCasts())) + LResult = {LStored, LStored}; + else + LResult = GlobalStoreAndRef(LStored, "_t", /*force=*/true); LStored = LResult.getExpr(); dr = BuildOp(BO_Mul, LResult.getExpr_dx(), dfdx()); dr = StoreAndRef(dr, direction::reverse); @@ -2028,8 +2091,12 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, Expr* LStored = Ldiff.getExpr(); if (!RDelayed.isConstant) { Expr* dr = nullptr; + StmtDiff LResult; if (dfdx()) { - StmtDiff LResult = GlobalStoreAndRef(LStored); + if (isa(LStored->IgnoreParenImpCasts())) + LResult = {LStored, LStored}; + else + LResult = GlobalStoreAndRef(LStored, "_t", /*force=*/true); LStored = LResult.getExpr(); Expr* RxR = BuildParens(BuildOp(BO_Mul, RStored, RStored)); dr = BuildOp(BO_Mul, @@ -2115,8 +2182,26 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, // Visit LHS, but delay emission of its derivative statements, save them // in Lblock beginBlock(direction::reverse); + beginBlock(direction::essential_reverse); Ldiff = Visit(L, dfdx()); + Stmts essentialRevBlock = EndBlockWithoutCreatingCS(direction::essential_reverse); auto Lblock = endBlock(direction::reverse); + auto return_exprs = utils::GetInnermostReturnExpr(Ldiff.getExpr()); + if (L->HasSideEffects(m_Context)) { + Expr* E = Ldiff.getExpr(); + auto storeE = StoreAndRef(E, m_Context.getLValueReferenceType(E->getType())); + Ldiff.updateStmt(storeE); + } + + // if (/*!L->HasSideEffects(m_Context)*/true) { + // Lstored = GlobalStoreAndRef(Ldiff.getExpr(), "_t", /*force*/true); + // auto assign = BuildOp(BO_Assign, Ldiff.getExpr(), Lstored.getExpr_dx()); + // if (isInsideLoop) { + // addToCurrentBlock(Lstored.getExpr(), direction::forward); + // } + // addToCurrentBlock(assign, direction::reverse); + // } + Expr* LCloned = Ldiff.getExpr(); // For x, AssignedDiff is _d_x, for x[i] its _d_x[i], for reference exprs // like (x = y) it propagates recursively, so _d_x is also returned. @@ -2139,12 +2224,43 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, // If assigned expr is dependent, first update its derivative; auto Lblock_begin = Lblock->body_rbegin(); auto Lblock_end = Lblock->body_rend(); - if (dfdx() && Lblock->size()) { + // if (Lblock->size()) { + // addToCurrentBlock(*Lblock_begin, direction::reverse); + // Lblock_begin = std::next(Lblock_begin); + // } + + for (auto S : essentialRevBlock) + addToCurrentBlock(S, direction::reverse); + + if (dfdx() && Lblock_begin != Lblock_end) { addToCurrentBlock(*Lblock_begin, direction::reverse); Lblock_begin = std::next(Lblock_begin); } - while(!m_PopIdxValues.empty()) - addToCurrentBlock(m_PopIdxValues.pop_back_val(), direction::reverse); + + // llvm::errs()<<"Dumping return_exprs:\n"; + for (auto E : return_exprs) { + // E->dumpColor(); + // llvm::errs()<<"\n"; + Lstored = GlobalStoreAndRef(E); + if (Lstored.getExpr() != E) { + auto assign = + BuildOp(BinaryOperatorKind::BO_Assign, E, Lstored.getExpr_dx()); + if (isInsideLoop) + addToCurrentBlock(Lstored.getExpr(), direction::forward); + addToCurrentBlock(assign, direction::reverse); + } + } + // Lstored = GlobalStoreAndRef(L, "_t", /*force=*/true); + // // auto line = + // m_Context.getSourceManager().getPresumedLoc(L->getBeginLoc()).getLine(); + // // auto column = + // m_Context.getSourceManager().getPresumedLoc(L->getBeginLoc()).getColumn(); + // // llvm::errs() << line << "|" <ActAfterCloningLHSOfAssignOp(LCloned, R, opCode); @@ -2153,6 +2269,7 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, // like x = x. auto oldValue = StoreAndRef(AssignedDiff, direction::reverse, "_r_d", /*forceDeclCreation=*/true); + if (opCode == BO_Assign) { Rdiff = Visit(R, oldValue); } else if (opCode == BO_AddAssign) { @@ -2186,12 +2303,16 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, // double & _ref0 = (x *= y); // _t0 = _ref0; // double r = _ref0 *= z; + StmtDiff LResult; if (LCloned->HasSideEffects(m_Context)) { auto RefType = getNonConstType(L->getType(), m_Context, m_Sema); + // RefType = m_Context.getLValueReferenceType(RefType); LRef = StoreAndRef(LCloned, RefType, direction::forward, "_ref", /*forceDeclCreation=*/true); - } - StmtDiff LResult = GlobalStoreAndRef(LRef); + LResult = GlobalStoreAndRef(LRef, "_t", /*force=*/true); + } else + LResult = {LRef, LRef}; + if (isInsideLoop) addToCurrentBlock(LResult.getExpr(), direction::forward); Expr* dr = BuildOp(BO_Mul, LResult.getExpr_dx(), oldValue); @@ -2210,13 +2331,15 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, direction::reverse); Expr* LRef = LCloned; if (!RDelayed.isConstant) { + StmtDiff LResult; if (LCloned->HasSideEffects(m_Context)) { QualType RefType = m_Context.getLValueReferenceType( getNonConstType(L->getType(), m_Context, m_Sema)); LRef = StoreAndRef(LCloned, RefType, direction::forward, "_ref", /*forceDeclCreation=*/true); - } - StmtDiff LResult = GlobalStoreAndRef(LRef); + LResult = GlobalStoreAndRef(LRef, "_t", /*force=*/true); + } else + LResult = {LRef, LRef}; if (isInsideLoop) addToCurrentBlock(LResult.getExpr(), direction::forward); Expr* RxR = BuildParens(BuildOp(BO_Mul, RStored, RStored)); @@ -2553,16 +2676,14 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, } bool ReverseModeVisitor::UsefulToStoreGlobal(Expr* E) { - if (isInsideLoop) - return !E->isEvaluatable(m_Context, Expr::SE_NoSideEffects); if (!E) return false; // Use stricter policy when inside loops: IsEvaluatable is also true for // arithmetical expressions consisting of constants, e.g. (1 + 2)*3. This // chech is more expensive, but it doesn't make sense to push such constants // into stack. - if (isInsideLoop) - return !E->isEvaluatable(m_Context, Expr::SE_NoSideEffects); + if (isInsideLoop && E->isEvaluatable(m_Context, Expr::SE_NoSideEffects)) + return false; Expr* B = E->IgnoreParenImpCasts(); // FIXME: find a more general way to determine that or add more options. if (isa(B) || isa(B)) @@ -2574,6 +2695,32 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, return UsefulToStoreGlobal(UO->getSubExpr()); return true; } + if (isa(B)) { + auto ASE = cast(B); + return UsefulToStoreGlobal(ASE->getBase()) || UsefulToStoreGlobal(ASE->getIdx()); + } + // We lack context to decide if this is useful to store or not. In the + // current system that should have been decided by the parent expression. + // FIXME: Here will be the entry point of the advanced activity analysis. + if (isa(B)) { + // auto line = + // m_Context.getSourceManager().getPresumedLoc(B->getBeginLoc()).getLine(); + // auto column = + // m_Context.getSourceManager().getPresumedLoc(B->getBeginLoc()).getColumn(); + // llvm::errs() << line << "|" <getBeginLoc()); + if (it == m_ToBeRecorded.end()) { + return true; + } else { + return it->second; + } + // return true; + } + + // FIXME: Attach checkpointing. + if (isa(B)) + return false; + return true; } @@ -2640,7 +2787,7 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, } void ReverseModeVisitor::DelayedStoreResult::Finalize(Expr* New) { - if (isConstant) + if (isConstant || !needsUpdate) return; if (isInsideLoop) { auto Push = cast(Result.getExpr()); @@ -2656,12 +2803,15 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, ReverseModeVisitor::DelayedGlobalStoreAndRef(Expr* E, llvm::StringRef prefix) { assert(E && "must be provided"); - if (!UsefulToStoreGlobal(E)) { + if (isa(E) /*!UsefulToStoreGlobal(E)*/) { Expr* Cloned = Clone(E); - return DelayedStoreResult{*this, - StmtDiff{Cloned, Cloned}, - /*isConstant*/ true, - /*isInsideLoop*/ false}; + Expr::EvalResult evalRes; + bool isConst = + clad_compat::Expr_EvaluateAsConstantExpr(E, evalRes, m_Context); + return DelayedStoreResult{*this, StmtDiff{Cloned, Cloned}, + /*isConstant*/ isConst, + /*isInsideLoop*/ false, + /*needsUpdate=*/ false}; } if (isInsideLoop) { Expr* dummy = E; @@ -2671,7 +2821,7 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, return DelayedStoreResult{*this, StmtDiff{Push, Pop}, /*isConstant*/ false, - /*isInsideLoop*/ true}; + /*isInsideLoop*/ true, /*needsUpdate=*/ true}; } else { Expr* Ref = BuildDeclRef(GlobalStoreImpl( getNonConstType(E->getType(), m_Context, m_Sema), prefix)); @@ -2679,7 +2829,7 @@ Expr* getArraySizeExpr(const ArrayType* AT, ASTContext& context, return DelayedStoreResult{*this, StmtDiff{Ref, Ref}, /*isConstant*/ false, - /*isInsideLoop*/ false}; + /*isInsideLoop*/ false, /*needsUpdate=*/ true}; } } diff --git a/lib/Differentiator/TBRAnalyzer.cpp b/lib/Differentiator/TBRAnalyzer.cpp new file mode 100644 index 000000000..d35b38d72 --- /dev/null +++ b/lib/Differentiator/TBRAnalyzer.cpp @@ -0,0 +1,1006 @@ +#include "clad/Differentiator/TBRAnalyzer.h" + +using namespace clang; + +namespace clad { + +void TBRAnalyzer::VarData::setIsRequired(bool isReq) { + if (type == FUND_TYPE) { + val.fundData = isReq; + } else if (type == OBJ_TYPE) { + for (auto pair : val.objData) { + pair.second->setIsRequired(isReq); + } + } else if (type == ARR_TYPE) { + for (auto pair : val.arrData) { + pair.second->setIsRequired(isReq); + } + } else if (type == REF_TYPE && val.refData) { + val.refData->setIsRequired(isReq); + } +} + +void TBRAnalyzer::VarData::merge(VarData* mergeData) { + if (this->type == FUND_TYPE) { + this->val.fundData = this->val.fundData || mergeData->val.fundData; + } else if (this->type == OBJ_TYPE) { + for (auto pair : this->val.objData) { + pair.second->merge(mergeData->val.objData[pair.first]); + } + } else if (this->type == ARR_TYPE) { + for (auto pair : this->val.arrData) { + pair.second->merge(mergeData->val.arrData[pair.first]); + } + } else if (this->type == REF_TYPE && this->val.refData) { + this->val.refData->merge(mergeData->val.refData); + } +} + +TBRAnalyzer::VarData* +TBRAnalyzer::VarData::copy(std::unordered_map& refVars) { + VarData* res; + + /// The child node of a reference node should be copied only once. Hence, + /// we use refVars to match original referenced nodes to corresponding copies. + if (isReferenced) { + auto it = refVars.find(this); + if (it != refVars.end()) { + return it->second; + } else { + res = new VarData(); + refVars[this] = res; + } + } else { + res = new VarData(); + } + + res->type = this->type; + + if (this->type == FUND_TYPE) { + res->val.fundData = this->val.fundData; + } else if (this->type == OBJ_TYPE) { + for (auto pair : this->val.objData) + res->val.objData[pair.first] = pair.second->copy(refVars); + } else if (this->type == ARR_TYPE) { + for (auto pair : this->val.arrData) { + res->val.arrData[pair.first] = pair.second->copy(refVars); + } + } else if (this->type == REF_TYPE && this->val.refData) { + res->val.refData = this->val.refData->copy(refVars); + res->val.refData->isReferenced = true; + } + + return res; +} + +bool TBRAnalyzer::VarData::findReq() { + if (type == FUND_TYPE) { + return val.fundData; + } else if (type == OBJ_TYPE) { + for (auto pair : val.objData) { + if (pair.second->findReq()) + return true; + } + } else if (type == ARR_TYPE) { + for (auto pair : val.arrData) { + if (pair.second->findReq()) + return true; + } + } else if (type == REF_TYPE && val.refData) { + if (val.refData->findReq()) + return true; + } + return false; +} + +void TBRAnalyzer::VarData::overlay( + llvm::SmallVector& IdxAndMemberSequence, size_t i) { + if (i == 0) { + setIsRequired(); + return; + } + --i; + IdxOrMember& curIdxOrMember = IdxAndMemberSequence[i]; + if (curIdxOrMember.type == IdxOrMember::IdxOrMemberType::FIELD) { + val.objData[curIdxOrMember.val.field]->overlay(IdxAndMemberSequence, i); + } else if (curIdxOrMember.type == IdxOrMember::IdxOrMemberType::INDEX) { + auto idx = curIdxOrMember.val.index; + if (idx == llvm::APInt(32, -1, true)) { + for (auto pair : val.arrData) { + pair.second->overlay(IdxAndMemberSequence, i); + } + } else { + val.arrData[idx]->overlay(IdxAndMemberSequence, i); + } + } +} + +TBRAnalyzer::VarData* TBRAnalyzer::getMemberVarData(const clang::MemberExpr* ME, + bool addNonConstIdx) { + if (auto FD = dyn_cast(ME->getMemberDecl())) { + auto base = ME->getBase(); + VarData* baseData = getExprVarData(base); + /// If the VarData is ref type just go to the VarData being referenced. + if (baseData && baseData->type == VarData::VarDataType::REF_TYPE) { + baseData = baseData->val.refData; + } + + if (!baseData) + return nullptr; + /// FUND_TYPE might be set by default earlier. + if (baseData->type == VarData::VarDataType::FUND_TYPE) { + baseData->type = VarData::VarDataType::OBJ_TYPE; + baseData->val.objData = + std::unordered_map(); + } + /// if non-const index was found and it is not supposed to be added just + /// return the current VarData*. + if (nonConstIndexFound && !addNonConstIdx) + return baseData; + + auto& baseObjData = baseData->val.objData; + /// Add the current field if it was not added previously + if (baseObjData.find(FD) == baseObjData.end()) { + VarData* FDData = new VarData(); + baseObjData[FD] = FDData; + FDData->type = VarData::VarDataType::UNDEFINED; + return FDData; + } + + return baseObjData[FD]; + } + return nullptr; +} + +TBRAnalyzer::VarData* +TBRAnalyzer::getArrSubVarData(const clang::ArraySubscriptExpr* ASE, + bool addNonConstIdx) { + auto idxExpr = ASE->getIdx(); + llvm::APInt idx; + if (auto IL = dyn_cast(idxExpr)) { + idx = IL->getValue(); + } else { + nonConstIndexFound = true; + /// Non-const indices are represented with -1. + idx = llvm::APInt(32, -1, true); + } + + auto base = ASE->getBase()->IgnoreImpCasts(); + VarData* baseData = getExprVarData(base); + /// If the VarData is ref type just go to the VarData being referenced. + if (baseData && baseData->type == VarData::VarDataType::REF_TYPE) { + baseData = baseData->val.refData; + } + + if (!baseData) + return nullptr; + /// FUND_TYPE might be set by default earlier. + if (baseData->type == VarData::VarDataType::FUND_TYPE) { + baseData->type = VarData::VarDataType::ARR_TYPE; + baseData->val.arrData = + std::unordered_map(); + } + + /// if non-const index was found and it is not supposed to be added just + /// return the current VarData*. + if (nonConstIndexFound && !addNonConstIdx) + return baseData; + + auto& baseArrData = baseData->val.arrData; + auto itEnd = baseArrData.end(); + + /// Add the current index if it was not added previously + if (baseArrData.find(idx) == itEnd) { + VarData* idxData = new VarData(); + baseArrData[idx] = idxData; + /// Since -1 represents non-const indices, whenever we add a new index we + /// have to copy the VarData of -1's element (if an element with undefined + /// index was used this might be our current element). + auto it = baseArrData.find(llvm::APInt(32, -1, true)); + if (it != itEnd) { + std::unordered_map dummy; + idxData = it->second->copy(dummy); + } else { + idxData->type = VarData::VarDataType::UNDEFINED; + } + return idxData; + } + + return baseArrData[idx]; +} + +TBRAnalyzer::VarData* TBRAnalyzer::getExprVarData(const clang::Expr* E, + bool addNonConstIdx) { + /// This line is necessary for pointer member expressions (in 'x->y' + /// x would be implicitly casted with the * operator). + E = E->IgnoreImpCasts(); + VarData* EData; + if (auto DRE = dyn_cast(E)) { + if (auto VD = dyn_cast(DRE->getDecl())) { + EData = reqStack.back()[VD]; + } + } + if (auto ME = dyn_cast(E)) { + EData = getMemberVarData(ME, addNonConstIdx); + } + if (auto ASE = dyn_cast(E)) { + EData = getArrSubVarData(ASE, addNonConstIdx); + } + /// 'this' pointer is represented as a nullptr. + if (auto TE = dyn_cast(E)) { + EData = reqStack.back()[nullptr]; + } + + /// If the type of this VarData was not defined previously set it to + /// FUND_TYPE. + /// FIXME: this assumes that we only assign fundamental values and not + /// objects or pointers. + if (EData && EData->type == VarData::VarDataType::UNDEFINED) { + EData->type = VarData::VarDataType::FUND_TYPE; + EData->val.fundData = false; + } + return EData; +} + +void TBRAnalyzer::addField( + std::unordered_map& objData, + const FieldDecl* FD) { + auto varType = FD->getType(); + VarData* data = new VarData(); + objData[FD] = data; + + if (varType->isReferenceType()) { + data->type = VarData::VarDataType::REF_TYPE; + data->val.refData = nullptr; + } else if (utils::isArrayOrPointerType(varType)) { + data->type = VarData::VarDataType::ARR_TYPE; + data->val.arrData = + std::unordered_map(); + } else if (varType->isBuiltinType()) { + data->type = VarData::VarDataType::FUND_TYPE; + data->val.fundData = false; + } else if (varType->isRecordType()) { + data->type = VarData::VarDataType::OBJ_TYPE; + auto recordDecl = varType->getAs()->getDecl(); + auto& newObjData = data->val.objData; + for (auto field : recordDecl->fields()) { + addField(newObjData, field); + } + } +} + +void TBRAnalyzer::overlay(const clang::Expr* E) { + nonConstIndexFound = false; + llvm::SmallVector IdxAndMemberSequence; + const clang::DeclRefExpr* innermostDRE; + bool cond = true; + /// Unwrap the given expression to a vector of indices and fields. + while (cond) { + E = E->IgnoreImplicit(); + if (auto ASE = dyn_cast(E)) { + if (auto IL = dyn_cast(ASE->getIdx())) { + IdxAndMemberSequence.push_back(IdxOrMember(IL->getValue())); + } else { + IdxAndMemberSequence.push_back(IdxOrMember(llvm::APInt(32, -1, true))); + } + E = ASE->getBase(); + } else if (auto ME = dyn_cast(E)) { + if (auto FD = dyn_cast(ME->getMemberDecl())) { + IdxAndMemberSequence.push_back(IdxOrMember(FD)); + } + E = ME->getBase(); + } else if ((innermostDRE = dyn_cast(E))) { + cond = false; + } else + return; + } + /// Overlay on all the VarData's recursively. + if (auto VD = dyn_cast(innermostDRE->getDecl())) + reqStack.back()[VD]->overlay(IdxAndMemberSequence, + IdxAndMemberSequence.size()); +} + +void TBRAnalyzer::addVar(const clang::VarDecl* VD) { + // FIXME: this marks the SourceLocation of DeclStmt which doesn't work for + // declarations with multiple VarDecls. + auto& curBranch = reqStack.back(); + if (curBranch.find(VD) != curBranch.end()) { + auto& VDData = curBranch[VD]; + if (VDData->type == VarData::VarDataType::FUND_TYPE) { + TBRLocs[VD->getBeginLoc()] = + (deleteCurBranch ? false : VDData->findReq()); + } + } + + if (!localVarsStack.empty()) { + localVarsStack.back().push_back(VD); + } + + auto varType = VD->getType(); + VarData* data = new VarData(); + curBranch[VD] = data; + + if (varType->isReferenceType()) { + data->type = VarData::VarDataType::REF_TYPE; + data->val.refData = nullptr; + } else if (utils::isArrayOrPointerType(varType)) { + if (auto pointerType = llvm::dyn_cast(varType)) { + /// FIXME: If the pointer points to an object we represent it with a + /// OBJ_TYPE VarData*. + auto pointeeType = pointerType->getPointeeType().getTypePtrOrNull(); + if (pointeeType && pointeeType->isRecordType()) { + data->type = VarData::VarDataType::OBJ_TYPE; + auto recordDecl = pointeeType->getAs()->getDecl(); + auto& objData = data->val.objData; + objData = std::unordered_map(); + for (auto field : recordDecl->fields()) { + addField(objData, field); + } + return; + } + } + data->type = VarData::VarDataType::ARR_TYPE; + data->val.arrData = + std::unordered_map(); + } else if (varType->isBuiltinType()) { + data->type = VarData::VarDataType::FUND_TYPE; + data->val.fundData = false; + } else if (varType->isRecordType()) { + data->type = VarData::VarDataType::OBJ_TYPE; + auto recordDecl = varType->getAs()->getDecl(); + auto& objData = data->val.objData; + objData = std::unordered_map(); + for (auto field : recordDecl->fields()) { + addField(objData, field); + } + } +} + +void TBRAnalyzer::markLocation(const clang::Expr* E) { + VarData* data = getExprVarData(E); + if (data) { + /// FIXME: If any of the data's child nodes are required to store then data + /// itselt is stored. We might add an option to store separate fields. + TBRLocs[E->getBeginLoc()] = (deleteCurBranch ? false : data->findReq()); + } else + /// If the current branch is going to be deleted then there is not point in + /// storing anything in it. + TBRLocs[E->getBeginLoc()] = !deleteCurBranch; +} + +void TBRAnalyzer::addBranch() { + VarsData& curBranch = reqStack.back(); + VarsData newBranch; + std::unordered_map refVars; + for (auto pair : curBranch) { + newBranch[pair.first] = pair.second->copy(refVars); + } + reqStack.push_back(newBranch); +} + +void TBRAnalyzer::mergeAndDelete(bool keepNewVars) { + auto removedBranch = reqStack.back(); + reqStack.pop_back(); + auto& curBranch = reqStack.back(); + + if (keepNewVars) { + for (auto& pair : curBranch) { + removedBranch[pair.first]->merge(pair.second); + delete pair.second; + pair.second = removedBranch[pair.first]; + } + } else { + for (auto pair : curBranch) { + pair.second->merge(removedBranch[pair.first]); + delete removedBranch[pair.first]; + } + } +} + +void TBRAnalyzer::swapLastPairOfBranches() { + size_t s = reqStack.size(); + std::swap(reqStack[s - 1], reqStack[s - 2]); +} + +void TBRAnalyzer::mergeCurBranchTo(size_t targetBranchNum) { + auto& targetBranch = reqStack[targetBranchNum]; + auto& curBranch = reqStack.back(); + + for (auto& pair : targetBranch) { + pair.second->merge(curBranch[pair.first]); + } +} + +void TBRAnalyzer::setIsRequired(const clang::Expr* E, bool isReq) { + if (!isReq || + (modeStack.back() == (Mode::markingMode | Mode::nonLinearMode))) { + VarData* data = getExprVarData(E, /*addNonConstIdx=*/isReq); + if (isReq || !nonConstIndexFound) { + data->setIsRequired(isReq); + } + /// If an array element with a non-const element is set to required + /// all the elements of that array should be set to required. + if (isReq && nonConstIndexFound) { + overlay(E); + } + nonConstIndexFound = false; + } +} + +void TBRAnalyzer::Analyze(const FunctionDecl* FD) { + /// If we are analysing a method add a VarData for 'this' pointer (it is + /// represented with nullptr). + if (isa(FD)) { + VarData* data = new VarData(); + reqStack.back()[nullptr] = data; + data->type = VarData::VarDataType::OBJ_TYPE; + auto recordDecl = dyn_cast(FD->getParent()); + auto& objData = data->val.objData; + objData = std::unordered_map(); + for (auto field : recordDecl->fields()) { + addField(objData, field); + } + } + auto paramsRef = FD->parameters(); + + for (std::size_t i = 0; i < FD->getNumParams(); ++i) + addVar(paramsRef[i]); + Visit(FD->getBody()); +} + +void TBRAnalyzer::VisitCompoundStmt(const CompoundStmt* CS) { + for (Stmt* S : CS->body()) + Visit(S); +} + +void TBRAnalyzer::VisitDeclRefExpr(const DeclRefExpr* DRE) { + if (auto VD = dyn_cast(DRE->getDecl())) { + auto& curBranch = reqStack.back(); + // FIXME: this is only necessary to ensure global variables are added. + // It doesn't make any sense to first add variables when visiting DeclStmt + // and then checking if they were added while visiting DeclRefExpr. + if (curBranch.find(VD) == curBranch.end()) + addVar(VD); + } + + if (auto E = dyn_cast(DRE)) { + setIsRequired(E); + } +} + +void TBRAnalyzer::VisitImplicitCastExpr(const clang::ImplicitCastExpr* ICE) { + Visit(ICE->getSubExpr()); +} + +void TBRAnalyzer::VisitCXXDefaultArgExpr(const clang::CXXDefaultArgExpr* DE) { + Visit(DE->getExpr()); +} + +void TBRAnalyzer::VisitParenExpr(const clang::ParenExpr* PE) { + Visit(PE->getSubExpr()); +} + +void TBRAnalyzer::VisitReturnStmt(const clang::ReturnStmt* RS) { + Visit(RS->getRetValue()); + deleteCurBranch = true; +} + +void TBRAnalyzer::VisitExprWithCleanups(const clang::ExprWithCleanups* EWC) { + Visit(EWC->getSubExpr()); +} + +void TBRAnalyzer::VisitCXXStaticCastExpr(const clang::CXXStaticCastExpr* SCE) { + Visit(SCE->getSubExpr()); +} + +void TBRAnalyzer::VisitDeclStmt(const DeclStmt* DS) { + for (auto D : DS->decls()) { + if (auto VD = dyn_cast(D)) { + addVar(VD); + if (clang::Expr* init = VD->getInit()) { + setMode(Mode::markingMode); + Visit(init); + resetMode(); + auto VDExpr = reqStack.back()[VD]; + /// if the declared variable is ref type attach its VarData* to the + /// VarData* of the RHS variable. + if (VDExpr->type == VarData::VarDataType::REF_TYPE) { + auto RHSExpr = getExprVarData(utils::GetInnermostReturnExpr(init)[0]); + VDExpr->val.refData = RHSExpr; + RHSExpr->isReferenced = VDExpr; + } + } + } + } +} + +void TBRAnalyzer::VisitConditionalOperator( + const clang::ConditionalOperator* CO) { + setMode(0); + Visit(CO->getCond()); + resetMode(); + + addBranch(); + Visit(CO->getTrueExpr()); + swapLastPairOfBranches(); + Visit(CO->getFalseExpr()); + mergeAndDelete(); +} + +void TBRAnalyzer::VisitBinaryOperator(const BinaryOperator* BinOp) { + auto opCode = BinOp->getOpcode(); + auto L = BinOp->getLHS(); + auto R = BinOp->getRHS(); + /// Addition is not able to create any differential influence by itself so + /// markingMode should be left as it is. Similarly, addition does not affect + /// linearity so nonLinearMode shouldn't be changed as well. The same applies + /// to subtraction. + if (opCode == BO_Add) { + Visit(L); + Visit(R); + } else if (opCode == BO_Sub) { + Visit(L); + Visit(R); + } else if (opCode == BO_Mul) { + /// Multiplication results in a linear expression if and only if one of the + /// factors is constant. + Expr::EvalResult dummy; + bool nonLinear = !R->EvaluateAsConstantExpr(dummy, *m_Context) && + !L->EvaluateAsConstantExpr(dummy, *m_Context); + if (nonLinear) + startNonLinearMode(); + + Visit(L); + Visit(R); + + if (nonLinear) + resetMode(); + } else if (opCode == BO_Div) { + /// Division normally only results in a linear expression when the + /// denominator is constant. + Expr::EvalResult dummy; + bool nonLinear = !R->EvaluateAsConstantExpr(dummy, *m_Context); + if (nonLinear) + startNonLinearMode(); + + Visit(L); + Visit(R); + + if (nonLinear) + resetMode(); + } else if (BinOp->isAssignmentOp()) { + + if (opCode == BO_Assign || opCode == BO_AddAssign || + opCode == BO_SubAssign) { + /// Since we only care about non-linear usages of variables, there is + /// no difference between operators =, -=, += in terms of TBR analysis. + Visit(L); + + startMarkingMode(); + Visit(R); + resetMode(); + } else if (opCode == BO_MulAssign || opCode == BO_DivAssign) { + /// *= (/=) normally only performs a linear operation if and only if + /// the RHS is constant. If RHS is not constant, 'x *= y' ('x /= y') + /// represents the same operation as 'x = x * y' ('x = x / y') and, + /// therefore, LHS has to be visited in markingMode|nonLinearMode. + Expr::EvalResult dummy; + bool RisNotConst = R->EvaluateAsConstantExpr(dummy, *m_Context); + + if (RisNotConst) + setMode(Mode::markingMode | Mode::nonLinearMode); + Visit(L); + if (RisNotConst) + resetMode(); + + setMode(Mode::markingMode | Mode::nonLinearMode); + Visit(R); + resetMode(); + } + auto return_exprs = utils::GetInnermostReturnExpr(L); + for (auto innerExpr : return_exprs) { + /// Mark corresponding SourceLocation as required/not required to be + /// stored for all expressions that could be used changed. + markLocation(innerExpr); + /// Set them to not required to store because the values were changed. + /// (if some value was not changed, this could only happen if it was + /// already not required to store). + setIsRequired(innerExpr, /*isReq=*/false); + } + + } else if (opCode == BO_Comma) { + setMode(0); + Visit(L); + resetMode(); + + Visit(R); + } + // else { + // FIXME: add logic/bitwise/comparison operators + // } +} + +void TBRAnalyzer::VisitUnaryOperator(const clang::UnaryOperator* UnOp) { + auto opCode = UnOp->getOpcode(); + Expr* E = UnOp->getSubExpr(); + Visit(E); + if (opCode == UO_PostInc || opCode == UO_PostDec || opCode == UO_PreInc || + opCode == UO_PreDec) { + // FIXME: this doesn't support all the possible references + /// Mark corresponding SourceLocation as required/not required to be + /// stored for all expressions that could be used in this operation. + auto innerExprs = utils::GetInnermostReturnExpr(E); + for (auto innerExpr : innerExprs) { + /// Mark corresponding SourceLocation as required/not required to be + /// stored for all expressions that could be changed. + markLocation(innerExpr); + /// Set them to not required to store because the values were changed. + /// (if some value was not changed, this could only happen if it was + /// already not required to store). + setIsRequired(innerExpr, /*isReq=*/false); + } + } +} + +void TBRAnalyzer::VisitIfStmt(const clang::IfStmt* If) { + auto cond = If->getCond(); + auto condVarDecl = If->getConditionVariable(); + auto condInit = If->getInit(); + + /// We have to separated analyse then-block and else-block and then merge + /// them together. First, we make a copy of the current branch and analyse + /// then-block on it. Then swap last two branches and analyse the else-block + /// on the last branch. Finally, we merge them together. This diagram explains + /// the transformations performed on the reqStack: + /// ... - + /// ... - - + /// ... - - + /// ... - - + /// ... - - + /// ... - + + addBranch(); + + bool thenBranchNotDeleted = true; + bool elseBranchNotDeleted = true; + auto thenBranch = If->getThen(); + auto elseBranch = If->getElse(); + if (thenBranch) { + localVarsStack.push_back(std::vector()); + Visit(cond); + if (condVarDecl) + addVar(condVarDecl); + if (condInit) { + setMode(Mode::markingMode); + Visit(condInit); + resetMode(); + } + Visit(thenBranch); + if (deleteCurBranch) { + /// This section is performed if this branch had break/continue/return + /// and, therefore, shouldn't be merged. + reqStack.pop_back(); + deleteCurBranch = false; + thenBranchNotDeleted = false; + } else { + /// We have to remove local variables from then-branch to later merge the + /// else-branch into it. + removeLocalVars(); + localVarsStack.pop_back(); + } + } + + if (elseBranch) { + if (thenBranchNotDeleted) + swapLastPairOfBranches(); + Visit(cond); + if (condVarDecl) + addVar(condVarDecl); + if (condInit) { + setMode(Mode::markingMode); + Visit(condInit); + resetMode(); + } + Visit(elseBranch); + if (deleteCurBranch && thenBranchNotDeleted) { + /// This section is performed if this branch had break/continue/return + /// and, therefore, shouldn't be merged. + reqStack.pop_back(); + deleteCurBranch = false; + elseBranchNotDeleted = false; + } + } + + if (thenBranchNotDeleted && elseBranchNotDeleted) + mergeAndDelete(); +} + +void TBRAnalyzer::VisitWhileStmt(const clang::WhileStmt* WS) { + auto body = WS->getBody(); + auto cond = WS->getCond(); + size_t backupILB = innermostLoopBranch; + bool backupFLP = firstLoopPass; + bool backupDCB = deleteCurBranch; + /// Let's assume we have a section of code structured like this + /// (A, B, C represent blocks): + /// ``` + /// A + /// while (cond) B + /// C + /// ``` + /// Depending on cond, this could give us 3 types of scenarios: 'AC', 'ABC', + /// 'AB...BC'. We must notice two things: 1) C comes either after B or A, + /// 2) B comes either after A or B itself. So first, we have to merge original + /// state with after-first-iteration state and analyse B a second time on top + /// to get the state that represents arbitrary non-zero number of iterations. + /// Finally, we have to merge it with the original state once again to account + /// for the fact that the loop block may not be executed at all. + /// This diagram explains the transformations performed on the reqStack: + /// ... - + /// ... - - + /// ... - - - + /// ... - - - + /// ... - - + /// ... - - + /// ... - + + Visit(cond); + addBranch(); + addBranch(); + /// First pass + innermostLoopBranch = reqStack.size() - 2; + firstLoopPass = true; + mergeCurBranchTo(innermostLoopBranch - 1); + if (body) + Visit(body); + if (deleteCurBranch) { + reqStack.pop_back(); + deleteCurBranch = backupDCB; + } else { + Visit(cond); + mergeAndDelete(/*keepNewVars=*/true); + } + + /// Second pass + --innermostLoopBranch; + firstLoopPass = false; + if (body) + Visit(body); + if (deleteCurBranch) + reqStack.pop_back(); + else { + Visit(cond); + mergeAndDelete(); + } + + innermostLoopBranch = backupILB; + firstLoopPass = backupFLP; + deleteCurBranch = backupDCB; +} + +void TBRAnalyzer::VisitForStmt(const clang::ForStmt* FS) { + auto body = FS->getBody(); + auto condVar = FS->getConditionVariable(); + auto init = FS->getInit(); + auto cond = FS->getCond(); + auto incr = FS->getInc(); + size_t backupILB = innermostLoopBranch; + bool backupFLP = firstLoopPass; + bool backupDCB = deleteCurBranch; + /// The logic here is virtually the same as with while-loop. Take a look at + /// TBRAnalyzer::VisitWhileStmt for more details. + if (init) { + setMode(Mode::markingMode); + Visit(init); + resetMode(); + } + if (cond) + Visit(cond); + addBranch(); + if (condVar) + addVar(condVar); + addBranch(); + /// First pass + innermostLoopBranch = reqStack.size() - 2; + firstLoopPass = true; + if (body) + Visit(body); + if (deleteCurBranch) { + reqStack.pop_back(); + deleteCurBranch = backupDCB; + } else { + if (incr) + Visit(incr); + if (cond) + Visit(cond); + mergeAndDelete(/*keepNewVars=*/true); + } + + /// Second pass + --innermostLoopBranch; + firstLoopPass = false; + if (body) + Visit(body); + if (incr) + Visit(incr); + if (deleteCurBranch) + reqStack.pop_back(); + else { + if (cond) + Visit(cond); + mergeAndDelete(); + } + + innermostLoopBranch = backupILB; + firstLoopPass = backupFLP; + deleteCurBranch = backupDCB; +} + +void TBRAnalyzer::VisitDoStmt(const clang::DoStmt* DS) { + auto body = DS->getBody(); + auto cond = DS->getCond(); + size_t backupILB = innermostLoopBranch; + bool backupFLP = firstLoopPass; + bool backupDCB = deleteCurBranch; + + /// The logic used here is virtually the same as with while-loop. Take a look + /// at TBRAnalyzer::VisitWhileStmt for more details. + /// FIXME: do-while-block is performed at least once and so we don't have to + /// account for the possibility of it not being performed at all. However, + /// having two loop branches is necessary for handling continue statements + /// so we can't just remove one of them. + + addBranch(); + addBranch(); + /// First pass + innermostLoopBranch = reqStack.size() - 2; + firstLoopPass = true; + if (body) + Visit(body); + if (deleteCurBranch) { + reqStack.pop_back(); + deleteCurBranch = backupDCB; + } else { + Visit(cond); + mergeAndDelete(/*keepNewVars=*/true); + } + + /// Second pass + --innermostLoopBranch; + firstLoopPass = false; + if (body) + Visit(body); + Visit(cond); + if (deleteCurBranch) { + reqStack.pop_back(); + mergeAndDelete(); + } + + innermostLoopBranch = backupILB; + firstLoopPass = backupFLP; + deleteCurBranch = backupDCB; +} + +void TBRAnalyzer::VisitContinueStmt(const clang::ContinueStmt* CS) { + /// If this is the first loop pass, the reqStack will look like this: + /// ... - - - + /// And so continue might be the end of this loop as well as the the end of + /// the first iteration. So we have to merge the current branch into first + /// two branches on the diagram. + /// If this is the second loop pass, the reqStack will look like this: + /// ... - - + /// And so this continue could be the end of this loop. So we have to merge + /// the current branch into the first branch on the diagram. + /// FIXME: If this is the second pass, this continue statement could still be + /// followed by another iteration. We have to either add an additional branch + /// or find a better solution. (However, this bug will matter only in really + /// rare cases) + mergeCurBranchTo(innermostLoopBranch); + /// After the continue statement, this branch cannot be followed by any other + /// code so we can delete it. + if (firstLoopPass) + mergeCurBranchTo(innermostLoopBranch - 1); + deleteCurBranch = true; +} + +void TBRAnalyzer::VisitBreakStmt(const clang::BreakStmt* BS) { + /// If this is the second loop pass, the reqStack will look like this: + /// ... - - + /// And so this break could be the end of this loop. So we have to merge + /// the current branch into the first branch on the diagram. + if (!firstLoopPass) + mergeCurBranchTo(innermostLoopBranch); + /// After the break statement, this branch cannot be followed by any other + /// code so we can delete it. + deleteCurBranch = true; +} + +void TBRAnalyzer::VisitCallExpr(const clang::CallExpr* CE) { + /// FIXME: Currently TBR analysis just stops here and assumes that all the + /// variables passed by value/reference are used/used and changed. Analysis + /// could proceed to the function to analyse data flow inside it. + auto FD = CE->getDirectCallee(); + setMode(Mode::markingMode | Mode::nonLinearMode); + for (std::size_t i = 0, e = CE->getNumArgs(); i != e; ++i) { + clang::Expr* arg = const_cast(CE->getArg(i)); + bool passByRef = FD->getParamDecl(i)->getType()->isReferenceType(); + setMode(Mode::markingMode | Mode::nonLinearMode); + Visit(arg); + resetMode(); + auto B = arg->IgnoreParenImpCasts(); + // FIXME: this supports only DeclRefExpr + auto innerExpr = utils::GetInnermostReturnExpr(arg); + if (passByRef) { + /// Mark SourceLocation as required for ref-type arguments. + if (isa(B) || isa(B)) { + TBRLocs[arg->getBeginLoc()] = true; + setIsRequired(arg, /*isReq=*/false); + } + } else { + /// Mark SourceLocation as not required for non-ref-type arguments. + if (isa(B) || isa(B)) + TBRLocs[arg->getBeginLoc()] = false; + } + } + resetMode(); +} + +void TBRAnalyzer::VisitCXXConstructExpr(const clang::CXXConstructExpr* CE) { + /// FIXME: Currently TBR analysis just stops here and assumes that all the + /// variables passed by value/reference are used/used and changed. Analysis + /// could proceed to the constructor to analyse data flow inside it. + /// FIXME: add support for default values + auto FD = CE->getConstructor(); + setMode(Mode::markingMode | Mode::nonLinearMode); + for (std::size_t i = 0, e = CE->getNumArgs(); i != e; ++i) { + auto arg = CE->getArg(i); + bool passByRef = FD->getParamDecl(i)->getType()->isReferenceType(); + setMode(Mode::markingMode | Mode::nonLinearMode); + Visit(arg); + resetMode(); + auto B = arg->IgnoreParenImpCasts(); + // FIXME: this supports only DeclRefExpr + if (passByRef) { + /// Mark SourceLocation as required for ref-type arguments. + if (isa(B) || isa(B)) { + TBRLocs[arg->getBeginLoc()] = true; + setIsRequired(arg, /*isReq=*/false); + } + } else { + /// Mark SourceLocation as not required for non-ref-type arguments. + if (isa(B) || isa(B)) + TBRLocs[arg->getBeginLoc()] = false; + } + } + resetMode(); +} + +void TBRAnalyzer::VisitMemberExpr(const clang::MemberExpr* ME) { + setIsRequired(dyn_cast(ME)); +} + +void TBRAnalyzer::VisitArraySubscriptExpr( + const clang::ArraySubscriptExpr* ASE) { + setIsRequired(dyn_cast(ASE)); + setMode(Mode::markingMode | Mode::nonLinearMode); + Visit(ASE->getIdx()); + resetMode(); +} + +void TBRAnalyzer::VisitInitListExpr(const clang::InitListExpr* ILE) { + setMode(0); + for (auto init : ILE->inits()) { + Visit(init); + } + resetMode(); +} + +void TBRAnalyzer::removeLocalVars() { + auto& curBranch = reqStack.back(); + for (auto VD : localVarsStack.back()) + curBranch.erase(VD); +} + +} // end namespace clad diff --git a/lib/Differentiator/VisitorBase.cpp b/lib/Differentiator/VisitorBase.cpp index 541b72990..f5432e620 100644 --- a/lib/Differentiator/VisitorBase.cpp +++ b/lib/Differentiator/VisitorBase.cpp @@ -168,9 +168,10 @@ namespace clad { } DeclStmt* VisitorBase::BuildDeclStmt(Decl* D) { - Stmt* DS = - m_Sema.ActOnDeclStmt(m_Sema.ConvertDeclToDeclGroup(D), noLoc, noLoc) - .get(); + Stmt* DS = m_Sema + .ActOnDeclStmt(m_Sema.ConvertDeclToDeclGroup(D), + D->getBeginLoc(), D->getEndLoc()) + .get(); return cast(DS); } @@ -184,7 +185,7 @@ namespace clad { QualType T = D->getType(); T = T.getNonReferenceType(); return cast(clad_compat::GetResult( - m_Sema.BuildDeclRefExpr(D, T, VK_LValue, noLoc, SS))); + m_Sema.BuildDeclRefExpr(D, T, VK_LValue, D->getBeginLoc()))); } IdentifierInfo* diff --git a/test/Arrays/ArrayInputsForwardMode.C b/test/Arrays/ArrayInputsForwardMode.C index 57d9938c6..54897a050 100644 --- a/test/Arrays/ArrayInputsForwardMode.C +++ b/test/Arrays/ArrayInputsForwardMode.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oArrayInputsForwardMode.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oArrayInputsForwardMode.out // RUN: ./ArrayInputsForwardMode.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/Arrays/ArrayInputsReverseMode.C b/test/Arrays/ArrayInputsReverseMode.C index ee2e0e723..99bbe92d9 100644 --- a/test/Arrays/ArrayInputsReverseMode.C +++ b/test/Arrays/ArrayInputsReverseMode.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -Wno-unused-value -oArrayInputsReverseMode.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -Wno-unused-value -oArrayInputsReverseMode.out // RUN: ./ArrayInputsReverseMode.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/Arrays/Arrays.C b/test/Arrays/Arrays.C index 9718498b5..eff1f8021 100644 --- a/test/Arrays/Arrays.C +++ b/test/Arrays/Arrays.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oArrays.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oArrays.out // RUN: ./Arrays.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/CUDA/GradientCuda.cu b/test/CUDA/GradientCuda.cu index b855bc56c..10b946e3d 100644 --- a/test/CUDA/GradientCuda.cu +++ b/test/CUDA/GradientCuda.cu @@ -2,7 +2,7 @@ // the device having all the dependencies also as device functions. // RUN: %cladclang_cuda -I%S/../../include %s -fsyntax-only \ -// RUN: %cudasmlevel --cuda-path=%cudapath -Xclang -verify 2>&1 | FileCheck %s +// RUN: %cudasmlevel --cuda-path=%cudapath -Xclang -verify // RUN: %cladclang_cuda -I%S/../../include %s -xc++ %cudasmlevel \ // RUN: --cuda-path=%cudapath -L/usr/local/cuda/lib64 -lcudart_static \ diff --git a/test/ErrorEstimation/Assignments.C b/test/ErrorEstimation/Assignments.C index 97f3b638f..9a9b48b29 100644 --- a/test/ErrorEstimation/Assignments.C +++ b/test/ErrorEstimation/Assignments.C @@ -1,4 +1,4 @@ -// RUN: %cladclang -I%S/../../include -oAssignments.out %s 2>&1 | FileCheck %s +// RUN: %cladclang -I%S/../../include -oAssignments.out %s // RUN: ./Assignments.out // CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/ErrorEstimation/BasicOps.C b/test/ErrorEstimation/BasicOps.C index 64057c056..18f6c77f3 100644 --- a/test/ErrorEstimation/BasicOps.C +++ b/test/ErrorEstimation/BasicOps.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -fsyntax-only -Xclang -verify 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -fsyntax-only -Xclang -verify //CHECK-NOT: {{.*error|warning|note:.*}} #include "clad/Differentiator/Differentiator.h" diff --git a/test/ErrorEstimation/ConditonalStatements.C b/test/ErrorEstimation/ConditonalStatements.C index 0b3b3392e..60cb10b1d 100644 --- a/test/ErrorEstimation/ConditonalStatements.C +++ b/test/ErrorEstimation/ConditonalStatements.C @@ -1,4 +1,4 @@ -// RUN: %cladclang -I%S/../../include -oCondStmts.out %s 2>&1 | FileCheck %s +// RUN: %cladclang -I%S/../../include -oCondStmts.out %s // CHECK-NOT: {{.*error|warning|note:.*}} #include "clad/Differentiator/Differentiator.h" diff --git a/test/ErrorEstimation/LoopsAndArrays.C b/test/ErrorEstimation/LoopsAndArrays.C index db6d329fd..dce182d45 100644 --- a/test/ErrorEstimation/LoopsAndArrays.C +++ b/test/ErrorEstimation/LoopsAndArrays.C @@ -1,4 +1,4 @@ -// RUN: %cladclang -I%S/../../include -oLoopsAndArrays.out %s 2>&1 | FileCheck %s +// RUN: %cladclang -I%S/../../include -oLoopsAndArrays.out %s // CHECK-NOT: {{.*error|warning|note:.*}} #include "clad/Differentiator/Differentiator.h" diff --git a/test/ErrorEstimation/LoopsAndArraysExec.C b/test/ErrorEstimation/LoopsAndArraysExec.C index 7def734d1..a4ab806dc 100644 --- a/test/ErrorEstimation/LoopsAndArraysExec.C +++ b/test/ErrorEstimation/LoopsAndArraysExec.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oLoopsAndArraysExec.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oLoopsAndArraysExec.out // RUN: ./LoopsAndArraysExec.out | FileCheck -check-prefix=CHECK-EXEC %s // CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/FirstDerivative/Assignments.C b/test/FirstDerivative/Assignments.C index 1b680d0a9..e785d7018 100644 --- a/test/FirstDerivative/Assignments.C +++ b/test/FirstDerivative/Assignments.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oAssignments.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oAssignments.out // RUN: ./Assignments.out //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/FirstDerivative/BasicArithmeticAddSub.C b/test/FirstDerivative/BasicArithmeticAddSub.C index a69483586..6433b82b7 100644 --- a/test/FirstDerivative/BasicArithmeticAddSub.C +++ b/test/FirstDerivative/BasicArithmeticAddSub.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oBasicArithmeticAddSub.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oBasicArithmeticAddSub.out // RUN: ./BasicArithmeticAddSub.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/FirstDerivative/BasicArithmeticAll.C b/test/FirstDerivative/BasicArithmeticAll.C index 0ac24212d..5020b2ae3 100644 --- a/test/FirstDerivative/BasicArithmeticAll.C +++ b/test/FirstDerivative/BasicArithmeticAll.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oBasicArithmeticAll.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oBasicArithmeticAll.out // RUN: ./BasicArithmeticAll.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/FirstDerivative/BuiltinDerivatives.C b/test/FirstDerivative/BuiltinDerivatives.C index 34d7b15ef..791c0ab4b 100644 --- a/test/FirstDerivative/BuiltinDerivatives.C +++ b/test/FirstDerivative/BuiltinDerivatives.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -Xclang -verify -oBuiltinDerivatives.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -Xclang -verify -oBuiltinDerivatives.out // RUN: ./BuiltinDerivatives.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/FirstDerivative/CallArguments.C b/test/FirstDerivative/CallArguments.C index c81155a51..541503a69 100644 --- a/test/FirstDerivative/CallArguments.C +++ b/test/FirstDerivative/CallArguments.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -Xclang -verify -oCallArguments.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -Xclang -verify -oCallArguments.out // RUN: ./CallArguments.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/FirstDerivative/ClassMethodCall.C b/test/FirstDerivative/ClassMethodCall.C index bdf3fd64f..07771497d 100644 --- a/test/FirstDerivative/ClassMethodCall.C +++ b/test/FirstDerivative/ClassMethodCall.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oClassMethods.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oClassMethods.out // RUN: ./ClassMethods.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/FirstDerivative/CodeGenSimple.C b/test/FirstDerivative/CodeGenSimple.C index 0759b15a2..7abbad868 100644 --- a/test/FirstDerivative/CodeGenSimple.C +++ b/test/FirstDerivative/CodeGenSimple.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oCodeGenSimple.out -Xclang -verify 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oCodeGenSimple.out -Xclang -verify // RUN: ./CodeGenSimple.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/FirstDerivative/CompoundAssignments.C b/test/FirstDerivative/CompoundAssignments.C index 490f26349..7e92306a8 100644 --- a/test/FirstDerivative/CompoundAssignments.C +++ b/test/FirstDerivative/CompoundAssignments.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oCompoundAssignments.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oCompoundAssignments.out // RUN: ./CompoundAssignments.out //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/FirstDerivative/DependencyTracking.C b/test/FirstDerivative/DependencyTracking.C index b3304b449..3036792dc 100644 --- a/test/FirstDerivative/DependencyTracking.C +++ b/test/FirstDerivative/DependencyTracking.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include 2>&1 -fsyntax-only | FileCheck %s +// RUN: %cladclang %s -I%S/../../include 2>&1 -fsyntax-only //CHECK-NOT: {{.*error|warning|note:.*}} #include "clad/Differentiator/Differentiator.h" diff --git a/test/FirstDerivative/DiffInterface.C b/test/FirstDerivative/DiffInterface.C index 13bd42394..500236282 100644 --- a/test/FirstDerivative/DiffInterface.C +++ b/test/FirstDerivative/DiffInterface.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -fsyntax-only -Xclang -verify 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -fsyntax-only -Xclang -verify #include "clad/Differentiator/Differentiator.h" diff --git a/test/FirstDerivative/FunctionCalls.C b/test/FirstDerivative/FunctionCalls.C index c070b281c..1ab413bbe 100644 --- a/test/FirstDerivative/FunctionCalls.C +++ b/test/FirstDerivative/FunctionCalls.C @@ -1,4 +1,4 @@ -// RUN: %cladnumdiffclang %s -I%S/../../include -fsyntax-only -Xclang -verify 2>&1 | FileCheck %s +// RUN: %cladnumdiffclang %s -I%S/../../include -fsyntax-only -Xclang -verify //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/FirstDerivative/FunctionCallsWithResults.C b/test/FirstDerivative/FunctionCallsWithResults.C index e7e36615b..073e690b6 100644 --- a/test/FirstDerivative/FunctionCallsWithResults.C +++ b/test/FirstDerivative/FunctionCallsWithResults.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oFunctionCallsWithResults.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oFunctionCallsWithResults.out // RUN: ./FunctionCallsWithResults.out | FileCheck -check-prefix=CHECK-EXEC %s #include "clad/Differentiator/Differentiator.h" diff --git a/test/FirstDerivative/FunctionsInNamespaces.C b/test/FirstDerivative/FunctionsInNamespaces.C index 2e1720fc9..4e824dc86 100644 --- a/test/FirstDerivative/FunctionsInNamespaces.C +++ b/test/FirstDerivative/FunctionsInNamespaces.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oFunctionsInNamespaces.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oFunctionsInNamespaces.out // RUN: ./FunctionsInNamespaces.out | FileCheck -check-prefix=CHECK-EXEC %s // CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/FirstDerivative/FunctionsOneVariable.C b/test/FirstDerivative/FunctionsOneVariable.C index 33783d7a0..82cd9296c 100644 --- a/test/FirstDerivative/FunctionsOneVariable.C +++ b/test/FirstDerivative/FunctionsOneVariable.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -fsyntax-only -Xclang -verify 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -fsyntax-only -Xclang -verify #include "clad/Differentiator/Differentiator.h" diff --git a/test/FirstDerivative/Loops.C b/test/FirstDerivative/Loops.C index 4fef1c66b..0bed4b5bb 100644 --- a/test/FirstDerivative/Loops.C +++ b/test/FirstDerivative/Loops.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -std=c++17 -I%S/../../include -oLoops.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -std=c++17 -I%S/../../include -oLoops.out // RUN: ./Loops.out | FileCheck -check-prefix=CHECK-EXEC %s // CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/FirstDerivative/Namespaces.C b/test/FirstDerivative/Namespaces.C index 2142eabcf..e641ed94a 100644 --- a/test/FirstDerivative/Namespaces.C +++ b/test/FirstDerivative/Namespaces.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oNamespaces.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oNamespaces.out // RUN: ./Namespaces.out //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/FirstDerivative/NonContinuous.C b/test/FirstDerivative/NonContinuous.C index b9359dfe8..765993c4b 100644 --- a/test/FirstDerivative/NonContinuous.C +++ b/test/FirstDerivative/NonContinuous.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oNonContinuous.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oNonContinuous.out // RUN: ./NonContinuous.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/FirstDerivative/Overloads.C b/test/FirstDerivative/Overloads.C index 1e70f8aca..ddf5a7fd7 100644 --- a/test/FirstDerivative/Overloads.C +++ b/test/FirstDerivative/Overloads.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oOverloads.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oOverloads.out // RUN: ./Overloads.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/FirstDerivative/Recursive.C b/test/FirstDerivative/Recursive.C index d4845da10..7a1fd7724 100644 --- a/test/FirstDerivative/Recursive.C +++ b/test/FirstDerivative/Recursive.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oRecursive.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oRecursive.out // RUN: ./Recursive.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/FirstDerivative/StructMethodCall.C b/test/FirstDerivative/StructMethodCall.C index 42601e9b4..f43916082 100644 --- a/test/FirstDerivative/StructMethodCall.C +++ b/test/FirstDerivative/StructMethodCall.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include //CHECK-NOT: {{.*error|warning|note:.*}} #include "clad/Differentiator/Differentiator.h" diff --git a/test/FirstDerivative/Switch.C b/test/FirstDerivative/Switch.C index 7e24133c6..01ad04628 100644 --- a/test/FirstDerivative/Switch.C +++ b/test/FirstDerivative/Switch.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oSwitch.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oSwitch.out // RUN: ./Switch.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} @@ -318,4 +318,4 @@ int main() { TEST_SWITCH_CASES(fn2, 0, 4); // CHECK-EXEC: 2.00 0.92 2.20 7.00 7.00 TEST_SWITCH_CASES(fn3, 0, 2); // CHECK-EXEC: 27.00 27.00 27.00 TEST_SWITCH_CASES(fn4, 0, 3); // CHECK-EXEC: 7.00 7.20 10.00 10.00 -} \ No newline at end of file +} diff --git a/test/FirstDerivative/SwitchInit.C b/test/FirstDerivative/SwitchInit.C index a95c2be27..a279bf268 100644 --- a/test/FirstDerivative/SwitchInit.C +++ b/test/FirstDerivative/SwitchInit.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -std=c++17 -oSwitchInit.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -std=c++17 -oSwitchInit.out // RUN: ./SwitchInit.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} @@ -65,4 +65,4 @@ printf("\n"); int main() { INIT(fn1, "i"); TEST_SWITCH_CASES(fn1, 0, 3); // CHECK-EXEC: 1.00 6.00 27.00 0.00 -} \ No newline at end of file +} diff --git a/test/FirstDerivative/TemplateFunction.C b/test/FirstDerivative/TemplateFunction.C index f808d5da5..00f88ad7a 100644 --- a/test/FirstDerivative/TemplateFunction.C +++ b/test/FirstDerivative/TemplateFunction.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include //CHECK-NOT: {{.*error|warning|note:.*}} #include "clad/Differentiator/Differentiator.h" diff --git a/test/FirstDerivative/UnsupportedOpsWarn.C b/test/FirstDerivative/UnsupportedOpsWarn.C index fba84186c..6bfabf0ca 100644 --- a/test/FirstDerivative/UnsupportedOpsWarn.C +++ b/test/FirstDerivative/UnsupportedOpsWarn.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -fsyntax-only -Xclang -verify 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -fsyntax-only -Xclang -verify #include "clad/Differentiator/Differentiator.h" diff --git a/test/FirstDerivative/Variables.C b/test/FirstDerivative/Variables.C index a9d7c6202..7dac6cff7 100644 --- a/test/FirstDerivative/Variables.C +++ b/test/FirstDerivative/Variables.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oVariables.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oVariables.out // RUN: ./Variables.out //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/FirstDerivative/VirtualMethodsCall.C b/test/FirstDerivative/VirtualMethodsCall.C index 2ef52b667..ee6b0fade 100644 --- a/test/FirstDerivative/VirtualMethodsCall.C +++ b/test/FirstDerivative/VirtualMethodsCall.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oVirtualMethodsCall.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oVirtualMethodsCall.out // RUN: ./VirtualMethodsCall.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/ForwardMode/Casts.C b/test/ForwardMode/Casts.C index 58a66d8da..f2d67738d 100644 --- a/test/ForwardMode/Casts.C +++ b/test/ForwardMode/Casts.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oCasts.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oCasts.out // RUN: ./Casts.out | FileCheck -check-prefix=CHECK-EXEC %s // CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/ForwardMode/Functors.C b/test/ForwardMode/Functors.C index f7e54b054..a5ee7d052 100644 --- a/test/ForwardMode/Functors.C +++ b/test/ForwardMode/Functors.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oFunctors.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oFunctors.out // RUN: ./Functors.out | FileCheck -check-prefix=CHECK-EXEC %s // CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/ForwardMode/MemberFunctions.C b/test/ForwardMode/MemberFunctions.C index 5982da5cd..8ec47c3f4 100644 --- a/test/ForwardMode/MemberFunctions.C +++ b/test/ForwardMode/MemberFunctions.C @@ -1,8 +1,8 @@ -// RUN: %cladclang %s -I%S/../../include -oMemberFunctions.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oMemberFunctions.out // RUN: ./MemberFunctions.out | FileCheck -check-prefix=CHECK-EXEC %s -// RUN: %cladclang -std=c++14 %s -I%S/../../include -oMemberFunctions-cpp14.out 2>&1 | FileCheck %s +// RUN: %cladclang -std=c++14 %s -I%S/../../include -oMemberFunctions-cpp14.out // RUN: ./MemberFunctions-cpp14.out | FileCheck -check-prefix=CHECK-EXEC %s -// RUN: %cladclang -std=c++17 %s -I%S/../../include -oMemberFunctions-cpp17.out 2>&1 | FileCheck %s +// RUN: %cladclang -std=c++17 %s -I%S/../../include -oMemberFunctions-cpp17.out // RUN: ./MemberFunctions-cpp17.out | FileCheck -check-prefix=CHECK-EXEC %s // CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/ForwardMode/Pointer.C b/test/ForwardMode/Pointer.C index de181231d..cef8e50d7 100644 --- a/test/ForwardMode/Pointer.C +++ b/test/ForwardMode/Pointer.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oPointer.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oPointer.out // RUN: ./Pointer.out | FileCheck -check-prefix=CHECK-EXEC %s // CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/ForwardMode/ReferenceArguments.C b/test/ForwardMode/ReferenceArguments.C index 4f28984ed..0e5c83137 100644 --- a/test/ForwardMode/ReferenceArguments.C +++ b/test/ForwardMode/ReferenceArguments.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oReferenceArguments.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oReferenceArguments.out // RUN: ./ReferenceArguments.out | FileCheck -check-prefix=CHECK-EXEC %s // CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/ForwardMode/SourceFnArg.C b/test/ForwardMode/SourceFnArg.C index 85f82b2f5..b86579b1f 100644 --- a/test/ForwardMode/SourceFnArg.C +++ b/test/ForwardMode/SourceFnArg.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oSourceFnArg.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oSourceFnArg.out // RUN: ./SourceFnArg.out | FileCheck -check-prefix=CHECK-EXEC %s // CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/ForwardMode/TemplateFunctors.C b/test/ForwardMode/TemplateFunctors.C index 8e15db065..02b0be5c8 100644 --- a/test/ForwardMode/TemplateFunctors.C +++ b/test/ForwardMode/TemplateFunctors.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oTemplateFunctors.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oTemplateFunctors.out // RUN: ./TemplateFunctors.out | FileCheck -check-prefix=CHECK-EXEC %s // CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/ForwardMode/UserDefinedTypes.C b/test/ForwardMode/UserDefinedTypes.C index 568ca261d..bb093ace3 100644 --- a/test/ForwardMode/UserDefinedTypes.C +++ b/test/ForwardMode/UserDefinedTypes.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oUserDefinedTypes.out | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oUserDefinedTypes.out // RUN: ./UserDefinedTypes.out | FileCheck -check-prefix=CHECK-EXEC %s // CHECK-NOT: {{.*error|warning|note:.*}} @@ -1245,4 +1245,4 @@ int main() { TEST_DIFFERENTIATE(fn15, pairdd(), pairdd()); // CHECK-EXEC: {1.00} TEST_DIFFERENTIATE(fn16, pair_of_pairdd(), pair_of_pairdd()); // CHECK-EXEC: {2.00} TEST_DIFFERENTIATE(fn17, A(3.00), B(5.00)); // CHECK-EXEC: {3.00} -} \ No newline at end of file +} diff --git a/test/Functors/Simple.C b/test/Functors/Simple.C index 9358113fa..6fed5eaa4 100644 --- a/test/Functors/Simple.C +++ b/test/Functors/Simple.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oSimpleFunctor.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oSimpleFunctor.out // RUN: ./SimpleFunctor.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/Gradient/Assignments.C b/test/Gradient/Assignments.C index bf0514afd..843d1be73 100644 --- a/test/Gradient/Assignments.C +++ b/test/Gradient/Assignments.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oReverseAssignments.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oReverseAssignments.out // RUN: ./ReverseAssignments.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/Gradient/DiffInterface.C b/test/Gradient/DiffInterface.C index 6c245b710..6ca17d544 100644 --- a/test/Gradient/DiffInterface.C +++ b/test/Gradient/DiffInterface.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oGradientDiffInterface.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oGradientDiffInterface.out // RUN: ./GradientDiffInterface.out | FileCheck -check-prefix=CHECK-EXEC %s #include "clad/Differentiator/Differentiator.h" diff --git a/test/Gradient/FunctionCalls.C b/test/Gradient/FunctionCalls.C index 725731bc4..1a43b5253 100644 --- a/test/Gradient/FunctionCalls.C +++ b/test/Gradient/FunctionCalls.C @@ -1,4 +1,4 @@ -// RUN: %cladnumdiffclang %s -I%S/../../include -oFunctionCalls.out 2>&1 | FileCheck %s +// RUN: %cladnumdiffclang %s -I%S/../../include -oFunctionCalls.out // RUN: ./FunctionCalls.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/Gradient/Functors.C b/test/Gradient/Functors.C index 7a989836a..b4ee264d8 100644 --- a/test/Gradient/Functors.C +++ b/test/Gradient/Functors.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oFunctors.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oFunctors.out // RUN: ./Functors.out | FileCheck -check-prefix=CHECK-EXEC %s // CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/Gradient/Gradients.C b/test/Gradient/Gradients.C index 8ac9fed7d..a3f4f8f40 100644 --- a/test/Gradient/Gradients.C +++ b/test/Gradient/Gradients.C @@ -1,4 +1,4 @@ -// RUN: %cladnumdiffclang %s -I%S/../../include -oGradients.out 2>&1 | FileCheck %s +// RUN: %cladnumdiffclang %s -I%S/../../include -oGradients.out // RUN: ./Gradients.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/Gradient/Loops.C b/test/Gradient/Loops.C index 0b4be619c..3e287c1bf 100644 --- a/test/Gradient/Loops.C +++ b/test/Gradient/Loops.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oReverseLoops.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oReverseLoops.out // RUN: ./ReverseLoops.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/Gradient/MemberFunctions.C b/test/Gradient/MemberFunctions.C index 2350b1d0f..afc84b214 100644 --- a/test/Gradient/MemberFunctions.C +++ b/test/Gradient/MemberFunctions.C @@ -1,8 +1,8 @@ -// RUN: %cladclang %s -fno-exceptions -I%S/../../include -oMemberFunctions.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -fno-exceptions -I%S/../../include -oMemberFunctions.out // RUN: ./MemberFunctions.out | FileCheck -check-prefix=CHECK-EXEC %s -// RUN: %cladclang -std=c++14 %s -fno-exceptions -I%S/../../include -oMemberFunctions-cpp14.out 2>&1 | FileCheck %s +// RUN: %cladclang -std=c++14 %s -fno-exceptions -I%S/../../include -oMemberFunctions-cpp14.out // RUN: ./MemberFunctions-cpp14.out | FileCheck -check-prefix=CHECK-EXEC %s -// RUN: %cladclang -std=c++17 %s -fno-exceptions -I%S/../../include -oMemberFunctions-cpp17.out 2>&1 | FileCheck %s +// RUN: %cladclang -std=c++17 %s -fno-exceptions -I%S/../../include -oMemberFunctions-cpp17.out // RUN: ./MemberFunctions-cpp17.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/Gradient/Pointers.C b/test/Gradient/Pointers.C index 50701f42e..0f6ef5168 100644 --- a/test/Gradient/Pointers.C +++ b/test/Gradient/Pointers.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oPointers.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oPointers.out // RUN: ./Pointers.out | FileCheck -check-prefix=CHECK-EXEC %s // CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/Gradient/TemplateFunctors.C b/test/Gradient/TemplateFunctors.C index a7ede0364..c14439967 100644 --- a/test/Gradient/TemplateFunctors.C +++ b/test/Gradient/TemplateFunctors.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oTemplateFunctors.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oTemplateFunctors.out // RUN: ./TemplateFunctors.out | FileCheck -check-prefix=CHECK-EXEC %s // CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/Gradient/TestAgainstDiff.C b/test/Gradient/TestAgainstDiff.C index 4c6445725..0a9aa6056 100644 --- a/test/Gradient/TestAgainstDiff.C +++ b/test/Gradient/TestAgainstDiff.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oTestAgainstDiff.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oTestAgainstDiff.out // RUN: ./TestAgainstDiff.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/Gradient/UserDefinedTypes.C b/test/Gradient/UserDefinedTypes.C index d10b41857..46b25a4bb 100644 --- a/test/Gradient/UserDefinedTypes.C +++ b/test/Gradient/UserDefinedTypes.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oUserDefinedTypes.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oUserDefinedTypes.out // RUN: ./UserDefinedTypes.out | FileCheck -check-prefix=CHECK-EXEC %s // CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/Hessian/Arrays.C b/test/Hessian/Arrays.C index 592917daf..ff7d87903 100644 --- a/test/Hessian/Arrays.C +++ b/test/Hessian/Arrays.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oArrays.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oArrays.out // RUN: ./Arrays.out | FileCheck -check-prefix=CHECK-EXEC %s // CHECK-NOT: {{.*error|warning|note:.*}} @@ -44,4 +44,4 @@ int main() { TEST(h1, 2, x); // CHECK-EXEC: Result = {0.00 4.00 3.00 4.00 0.00 2.00 3.00 2.00 0.00} auto h2 = clad::hessian(g, "i, j[0:1]"); TEST(h2, 2, x); // CHECK-EXEC: Result = {0.00 1.00 1.00 1.00 0.00 0.00 1.00 0.00 0.00} -} \ No newline at end of file +} diff --git a/test/Hessian/BuiltinDerivatives.C b/test/Hessian/BuiltinDerivatives.C index d6a401e94..57ace536a 100644 --- a/test/Hessian/BuiltinDerivatives.C +++ b/test/Hessian/BuiltinDerivatives.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oHessianBuiltinDerivatives.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oHessianBuiltinDerivatives.out // RUN: ./HessianBuiltinDerivatives.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/Hessian/Functors.C b/test/Hessian/Functors.C index eb1fbe054..2d6d5ef1b 100644 --- a/test/Hessian/Functors.C +++ b/test/Hessian/Functors.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oFunctors.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oFunctors.out // RUN: ./Functors.out | FileCheck -check-prefix=CHECK-EXEC %s // CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/Hessian/Hessians.C b/test/Hessian/Hessians.C index f7249a867..55276ab84 100644 --- a/test/Hessian/Hessians.C +++ b/test/Hessian/Hessians.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oHessians.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oHessians.out // RUN: ./Hessians.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/Hessian/Pointers.C b/test/Hessian/Pointers.C index 89fd3dcbf..263901e31 100644 --- a/test/Hessian/Pointers.C +++ b/test/Hessian/Pointers.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oPointers.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oPointers.out // RUN: ./Pointers.out | FileCheck -check-prefix=CHECK-EXEC %s // CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/Hessian/TemplateFunctors.C b/test/Hessian/TemplateFunctors.C index e37732ba8..f3236fe6d 100644 --- a/test/Hessian/TemplateFunctors.C +++ b/test/Hessian/TemplateFunctors.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oTemplateFunctors.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oTemplateFunctors.out // RUN: ./TemplateFunctors.out | FileCheck -check-prefix=CHECK-EXEC %s // CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/Jacobian/FunctionCalls.C b/test/Jacobian/FunctionCalls.C index 4e6a216e5..f8b3b42ad 100644 --- a/test/Jacobian/FunctionCalls.C +++ b/test/Jacobian/FunctionCalls.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oFunctionCalls.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oFunctionCalls.out // RUN: ./FunctionCalls.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} @@ -70,4 +70,4 @@ int main() { INIT(fn1); test<2>(DERIVED_FN(fn1), 3, 5); // CHECK-EXEC: {405.00, 266.96, 201.18, 75.00} -} \ No newline at end of file +} diff --git a/test/Jacobian/Functors.C b/test/Jacobian/Functors.C index da7b664c4..05f314281 100644 --- a/test/Jacobian/Functors.C +++ b/test/Jacobian/Functors.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oFunctors.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oFunctors.out // RUN: ./Functors.out | FileCheck -check-prefix=CHECK-EXEC %s // CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/Jacobian/Jacobian.C b/test/Jacobian/Jacobian.C index 0af0ada5a..29c5d887a 100644 --- a/test/Jacobian/Jacobian.C +++ b/test/Jacobian/Jacobian.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oJacobian.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oJacobian.out // RUN: ./Jacobian.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/Jacobian/Pointers.C b/test/Jacobian/Pointers.C index 9629d7975..b839153f4 100644 --- a/test/Jacobian/Pointers.C +++ b/test/Jacobian/Pointers.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oPointers.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oPointers.out // RUN: ./Pointers.out | FileCheck -check-prefix=CHECK-EXEC %s // CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/Jacobian/TemplateFunctors.C b/test/Jacobian/TemplateFunctors.C index 2c206bc34..d4a4e799a 100644 --- a/test/Jacobian/TemplateFunctors.C +++ b/test/Jacobian/TemplateFunctors.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oTemplateFunctors.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oTemplateFunctors.out // RUN: ./TemplateFunctors.out | FileCheck -check-prefix=CHECK-EXEC %s // CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/MixedDerivatives/Simple.C b/test/MixedDerivatives/Simple.C index 1fc9c5726..b372fead5 100644 --- a/test/MixedDerivatives/Simple.C +++ b/test/MixedDerivatives/Simple.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oSimple.out -Xclang -verify 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oSimple.out -Xclang -verify // RUN: ./Simple.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/NestedCalls/NestedCalls.C b/test/NestedCalls/NestedCalls.C index bf2725a2d..dc287ac6b 100644 --- a/test/NestedCalls/NestedCalls.C +++ b/test/NestedCalls/NestedCalls.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oNestedCalls.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oNestedCalls.out // RUN: ./NestedCalls.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/NthDerivative/BasicArithmeticMul.C b/test/NthDerivative/BasicArithmeticMul.C index 51310198a..310ed3004 100644 --- a/test/NthDerivative/BasicArithmeticMul.C +++ b/test/NthDerivative/BasicArithmeticMul.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oBasicArithmeticMul2.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oBasicArithmeticMul2.out // RUN: ./BasicArithmeticMul2.out | FileCheck -check-prefix=CHECK-EXEC %s #include "clad/Differentiator/Differentiator.h" #include "clad/Differentiator/BuiltinDerivatives.h" diff --git a/test/NumericalDiff/GradientMultiArg.C b/test/NumericalDiff/GradientMultiArg.C index 3a3b19c78..8a7b68620 100644 --- a/test/NumericalDiff/GradientMultiArg.C +++ b/test/NumericalDiff/GradientMultiArg.C @@ -1,4 +1,4 @@ -// RUN: %cladnumdiffclang %s -I%S/../../include -oGradientMultiArg.out 2>&1 | FileCheck -check-prefix=CHECK %s +// RUN: %cladnumdiffclang %s -I%S/../../include -oGradientMultiArg.out // RUN: ./GradientMultiArg.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/NumericalDiff/NoNumDiff.C b/test/NumericalDiff/NoNumDiff.C index b7a8dc091..c690b65ca 100644 --- a/test/NumericalDiff/NoNumDiff.C +++ b/test/NumericalDiff/NoNumDiff.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oNoNumDiff.out 2>&1 | FileCheck -check-prefix=CHECK %s +// RUN: %cladclang %s -I%S/../../include -oNoNumDiff.out //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/NumericalDiff/NumDiff.C b/test/NumericalDiff/NumDiff.C index d05a05080..2166cca99 100644 --- a/test/NumericalDiff/NumDiff.C +++ b/test/NumericalDiff/NumDiff.C @@ -1,4 +1,4 @@ -// RUN: %cladnumdiffclang %s -I%S/../../include -oNumDiff.out 2>&1 | FileCheck -check-prefix=CHECK %s +// RUN: %cladnumdiffclang %s -I%S/../../include -oNumDiff.out //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/NumericalDiff/PrintErrorNumDiff.C b/test/NumericalDiff/PrintErrorNumDiff.C index a1f93531f..cc12cdad5 100644 --- a/test/NumericalDiff/PrintErrorNumDiff.C +++ b/test/NumericalDiff/PrintErrorNumDiff.C @@ -1,5 +1,5 @@ -// RUN: %cladnumdiffclang -Xclang -plugin-arg-clad -Xclang -fprint-num-diff-errors %s -I%S/../../include -oPrintErrorNumDiff.out 2>&1 | FileCheck -check-prefix=CHECK %s -// -Xclang -verify 2>&1 RUN: ./PrintErrorNumDiff.out | FileCheck -check-prefix=CHECK-EXEC %s +// RUN: %cladnumdiffclang -Xclang -plugin-arg-clad -Xclang -fprint-num-diff-errors %s -I%S/../../include -oPrintErrorNumDiff.out 2>&1 +// RUN: ./PrintErrorNumDiff.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/Performance/Perf1 b/test/Performance/Perf1 index 47bbe6626..581c4210b 100644 --- a/test/Performance/Perf1 +++ b/test/Performance/Perf1 @@ -1,4 +1,4 @@ -// RUN: time LIBCLAD_TIMING=1 %cladclang %s -O3 -I%S/../../include -std=c++11 -oPerf1.out 2>&1 | FileCheck %s +// RUN: time LIBCLAD_TIMING=1 %cladclang %s -O3 -I%S/../../include -std=c++11 -oPerf1.out // RUN: ./Perf1.out | FileCheck -check-prefix=CHECK-EXEC %s //#define COMPILER_TEST_ONLY diff --git a/test/Performance/Perf2 b/test/Performance/Perf2 index fced9c919..bb1229945 100644 --- a/test/Performance/Perf2 +++ b/test/Performance/Perf2 @@ -1,4 +1,4 @@ -// RUN: time LIBCLAD_TIMING=1 %cladclang %s -O3 -I%S/../../include -std=c++11 -oPerf1.out 2>&1 | FileCheck %s +// RUN: time LIBCLAD_TIMING=1 %cladclang %s -O3 -I%S/../../include -std=c++11 -oPerf1.out // RUN: ./Perf1.out | FileCheck -check-prefix=CHECK-EXEC %s //#define COMPILER_TEST_ONLY diff --git a/test/ROOT/Hessian.C b/test/ROOT/Hessian.C index c7ca79325..de74c78f4 100644 --- a/test/ROOT/Hessian.C +++ b/test/ROOT/Hessian.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oHessian.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oHessian.out // RUN: ./Hessian.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} @@ -32,4 +32,4 @@ int main() { matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], matrix[6], matrix[7], matrix[8]); // CHECK-EXEC: Result is = {2.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00} -} \ No newline at end of file +} diff --git a/test/ROOT/Interface.C b/test/ROOT/Interface.C index 31261c07c..4df6dc694 100644 --- a/test/ROOT/Interface.C +++ b/test/ROOT/Interface.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oInterface.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oInterface.out // RUN: ./Interface.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/ROOT/TFormula.C b/test/ROOT/TFormula.C index ba53c4d3d..d64525f69 100644 --- a/test/ROOT/TFormula.C +++ b/test/ROOT/TFormula.C @@ -1,4 +1,4 @@ -// RUN: %cladclang %s -I%S/../../include -oTFormula.out 2>&1 | FileCheck %s +// RUN: %cladclang %s -I%S/../../include -oTFormula.out 2>&1 // RUN: ./TFormula.out | FileCheck -check-prefix=CHECK-EXEC %s //CHECK-NOT: {{.*error|warning|note:.*}} diff --git a/test/lit.cfg b/test/lit.cfg index 62ba8de32..2d06ae85e 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -253,8 +253,8 @@ lit.util.usePlatformSdkOnDarwin(config, lit_config) #\ -plugin-arg-ad -Xclang -fdump-derived-fn -Xclang -load -Xclang../../Debug+Asserts/lib/libclad.so #FIXME: we need to introduce a better way to check compatible version of clang, propagating #-fvalidate-clang-version flag is not enough. -flags = ' -std=c++11 -Xclang -add-plugin -Xclang clad -Xclang \ - -plugin-arg-clad -Xclang -fdump-derived-fn -Xclang \ +flags = ' -std=c++11 -Xclang -add-plugin -Xclang clad\ + -Xclang \ -load -Xclang ' + config.cladlib config.substitutions.append( ('%cladclang_cuda', config.clang + flags) )