diff --git a/include/clad/Differentiator/TBRAnalyzer.h b/include/clad/Differentiator/TBRAnalyzer.h index 69743d7e9..5e74e9b3b 100644 --- a/include/clad/Differentiator/TBRAnalyzer.h +++ b/include/clad/Differentiator/TBRAnalyzer.h @@ -14,6 +14,14 @@ using namespace clang; namespace clad { +/// Gradient computation requres reversal of the control flow of the original +/// program becomes necessary. To guarantee correctness, certain values that are +/// computed and overwritten in the original program must be made available in +/// the adjoint program. They can be determined by performing a static data flow +/// analysis, the so-called To-Be-Recorded (TBR) analysis. Overestimation of +/// this set must be kept minimal to get efficient adjoint codes. +/// +/// This class implements this to-be-recorded analysis. class TBRAnalyzer : public clang::RecursiveASTVisitor { /// ProfileID is the key type for ArrMap used to represent array indices /// and object fields. @@ -37,22 +45,23 @@ class TBRAnalyzer : public clang::RecursiveASTVisitor { } }; + + struct VarData; + using ArrMap = + std::unordered_map; + + // NOLINTBEGIN(cppcoreguidelines-pro-type-union-access) + /// Stores all the necessary information about one variable. Fundamental type /// variables need only one bit. An object/array needs a separate VarData for /// each field/element. Reference type variables store the clang::Expr* they /// refer to. 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: Add support for references to call expression results. /// 'double& x = f(b);' is not supported. - - struct VarData; - using ArrMap = - std::unordered_map; - - // NOLINTBEGIN(cppcoreguidelines-pro-type-union-access) struct VarData { enum VarDataType { UNDEFINED, FUND_TYPE, OBJ_TYPE, ARR_TYPE, REF_TYPE }; union VarDataValue { diff --git a/lib/Differentiator/TBRAnalyzer.cpp b/lib/Differentiator/TBRAnalyzer.cpp index 4bcaa85c7..247a60dfe 100644 --- a/lib/Differentiator/TBRAnalyzer.cpp +++ b/lib/Differentiator/TBRAnalyzer.cpp @@ -25,7 +25,7 @@ void TBRAnalyzer::merge(VarData& targetData, VarData& mergeData) { for (auto& pair : *targetData.val.m_ArrData) merge(pair.second, (*mergeData.val.m_ArrData)[pair.first]); } else if (targetData.type == VarData::ARR_TYPE) { - /// FIXME: Currently non-constant indices are not supported in merging. + // FIXME: Currently non-constant indices are not supported in merging. for (auto& pair : *targetData.val.m_ArrData) { auto it = mergeData.val.m_ArrData->find(pair.first); if (it != mergeData.val.m_ArrData->end()) @@ -37,8 +37,8 @@ void TBRAnalyzer::merge(VarData& targetData, VarData& mergeData) { (*targetData.val.m_ArrData)[pair.first] = copy(pair.second); } } - /// This might be useful in future if used to analyse pointers. However, for - /// now it's only used for references for which merging doesn't make sense. + // This might be useful in future if used to analyse pointers. However, for + // now it's only used for references for which merging doesn't make sense. // else if (this.type == VarData::REF_TYPE) {} } @@ -102,8 +102,8 @@ TBRAnalyzer::VarData* TBRAnalyzer::getMemberVarData(const clang::MemberExpr* ME, if (!baseData) return nullptr; - /// if non-const index was found and it is not supposed to be added just - /// return the current VarData*. + // if non-const index was found and it is not supposed to be added just + // return the current VarData*. if (m_NonConstIndexFound && !addNonConstIdx) return baseData; @@ -121,7 +121,7 @@ TBRAnalyzer::getArrSubVarData(const clang::ArraySubscriptExpr* ASE, idxID = getProfileID(IL); } else { m_NonConstIndexFound = true; - /// Non-const indices are represented with default FoldingSetNodeID. + // Non-const indices are represented with default FoldingSetNodeID. } const auto* base = ASE->getBase()->IgnoreImpCasts(); @@ -130,20 +130,20 @@ TBRAnalyzer::getArrSubVarData(const clang::ArraySubscriptExpr* ASE, if (!baseData) return nullptr; - /// if non-const index was found and it is not supposed to be added just - /// return the current VarData*. + // if non-const index was found and it is not supposed to be added just + // return the current VarData*. if (m_NonConstIndexFound && !addNonConstIdx) return baseData; auto* baseArrMap = baseData->val.m_ArrData.get(); auto it = baseArrMap->find(idxID); - /// Add the current index if it was not added previously + // Add the current index if it was not added previously if (it == baseArrMap->end()) { auto& idxData = (*baseArrMap)[idxID]; - /// Since default ID represents non-const indices, whenever we add a new - /// index we have to copy the VarData of default ID's element (if an element - /// with undefined index was used this might be our current element). + // Since default ID represents non-const indices, whenever we add a new + // index we have to copy the VarData of default ID's element (if an element + // with undefined index was used this might be our current element). ProfileID nonConstIdxID; idxData = copy((*baseArrMap)[nonConstIdxID]); return &idxData; @@ -154,13 +154,13 @@ TBRAnalyzer::getArrSubVarData(const clang::ArraySubscriptExpr* ASE, 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). + // This line is necessary for pointer member expressions (in 'x->y' x would be + // implicitly casted with the * operator). E = E->IgnoreImpCasts(); VarData* EData = nullptr; if (isa(E) || isa(E)) { const VarDecl* VD = nullptr; - /// ``this`` does not have a declaration so it is represented with nullptr. + // ``this`` does not have a declaration so it is represented with nullptr. if (const auto* DRE = dyn_cast(E)) VD = dyn_cast(DRE->getDecl()); auto* branch = &getCurBlockVarsData(); @@ -219,7 +219,7 @@ void TBRAnalyzer::overlay(const clang::Expr* E) { llvm::SmallVector IDSequence; const clang::DeclRefExpr* innermostDRE = nullptr; bool cond = true; - /// Unwrap the given expression to a vector of indices and fields. + // Unwrap the given expression to a vector of indices and fields. while (cond) { E = E->IgnoreImplicit(); if (const auto* ASE = dyn_cast(E)) { @@ -239,7 +239,7 @@ void TBRAnalyzer::overlay(const clang::Expr* E) { return; } - /// Overlay on all the VarData's recursively. + // Overlay on all the VarData's recursively. if (const auto* VD = dyn_cast(innermostDRE->getDecl())) { overlay(getCurBlockVarsData()[VD], IDSequence, IDSequence.size()); } @@ -247,8 +247,7 @@ void TBRAnalyzer::overlay(const clang::Expr* E) { // NOLINTEND(cppcoreguidelines-pro-type-union-access) void TBRAnalyzer::copyVarToCurBlock(const clang::VarDecl* VD) { - /// Visit all predecessors one by one - /// until the variable VD is found. + // Visit all predecessors one by one until the variable VD is found. auto& curBranch = getCurBlockVarsData(); auto* pred = curBranch.prev; while (pred) { @@ -259,8 +258,7 @@ void TBRAnalyzer::copyVarToCurBlock(const clang::VarDecl* VD) { } pred = pred->prev; } - /// If this variable was not found in - /// predecessors, add it. + // If this variable was not found in predecessors, add it. addVar(VD); } @@ -272,14 +270,14 @@ void TBRAnalyzer::addVar(const clang::VarDecl* VD) { varType = arrayParam->getOriginalType(); else varType = VD->getType(); - /// If varType represents auto or auto*, get the type of init. + // If varType represents auto or auto*, get the type of init. if (utils::IsAutoOrAutoPtrType(varType)) varType = VD->getInit()->getType(); - /// FIXME: If the pointer points to an object we represent it with a - /// OBJ_TYPE VarData. This is done for '_d_this' pointer to be processed - /// correctly in hessian mode. This should be removed once full support for - /// pointers in analysis is introduced. + // FIXME: If the pointer points to an object we represent it with a OBJ_TYPE + // VarData. This is done for '_d_this' pointer to be processed correctly in + // hessian mode. This should be removed once full support for pointers in + // analysis is introduced. if (const auto* const pointerType = dyn_cast(varType)) { const auto* elemType = pointerType->getPointeeType().getTypePtrOrNull(); if (elemType && elemType->isRecordType()) { @@ -293,13 +291,13 @@ void TBRAnalyzer::addVar(const clang::VarDecl* VD) { void TBRAnalyzer::markLocation(const clang::Expr* E) { VarData* data = getExprVarData(E); if (!data || findReq(*data)) { - /// FIXME: If any of the data's child nodes are required to store then data - /// itself is stored. We might add an option to store separate fields. - /// FIXME: Sometimes one location might correspond to multiple stores. - /// For example, in ``(x*=y)=u`` x's location will first be marked as - /// required to be stored (when passing *= operator) but then marked as not - /// required to be stored (when passing = operator). Current method of - /// marking locations does not allow to differentiate between these two. + // FIXME: If any of the data's child nodes are required to store then data + // itself is stored. We might add an option to store separate fields. + // FIXME: Sometimes one location might correspond to multiple stores. For + // example, in ``(x*=y)=u`` x's location will first be marked as required to + // be stored (when passing *= operator) but then marked as not required to + // be stored (when passing = operator). Current method of marking locations + // does not allow to differentiate between these two. m_TBRLocs.insert(E->getBeginLoc()); } } @@ -310,8 +308,8 @@ void TBRAnalyzer::setIsRequired(const clang::Expr* E, bool isReq) { VarData* data = getExprVarData(E, /*addNonConstIdx=*/isReq); if (data && (isReq || !m_NonConstIndexFound)) setIsRequired(*data, 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 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 && m_NonConstIndexFound) overlay(E); m_NonConstIndexFound = false; @@ -319,20 +317,20 @@ void TBRAnalyzer::setIsRequired(const clang::Expr* E, bool isReq) { } void TBRAnalyzer::Analyze(const FunctionDecl* FD) { - /// Build the CFG (control-flow graph) of FD. + // Build the CFG (control-flow graph) of FD. clang::CFG::BuildOptions Options; m_CFG = clang::CFG::buildCFG(FD, FD->getBody(), &m_Context, Options); m_BlockData.resize(m_CFG->size()); m_BlockPassCounter.resize(m_CFG->size(), 0); - /// Set current block ID to the ID of entry the block. + // Set current block ID to the ID of entry the block. auto* entry = &m_CFG->getEntry(); m_CurBlockID = entry->getBlockID(); m_BlockData[m_CurBlockID] = std::unique_ptr(new VarsData()); - /// If we are analysing a non-static method, add a VarData for 'this' pointer - /// (it is represented with nullptr). + // If we are analysing a non-static method, add a VarData for 'this' pointer + // (it is represented with nullptr). const auto* MD = dyn_cast(FD); if (MD && !MD->isStatic()) { const Type* recordType = @@ -343,10 +341,10 @@ void TBRAnalyzer::Analyze(const FunctionDecl* FD) { auto paramsRef = FD->parameters(); for (std::size_t i = 0; i < FD->getNumParams(); ++i) addVar(paramsRef[i]); - /// Add the entry block to the queue. + // Add the entry block to the queue. m_CFGQueue.insert(m_CurBlockID); - /// Visit CFG blocks in the queue until it's empty. + // Visit CFG blocks in the queue until it's empty. while (!m_CFGQueue.empty()) { auto IDIter = std::prev(m_CFGQueue.end()); m_CurBlockID = *IDIter; @@ -366,13 +364,13 @@ void TBRAnalyzer::Analyze(const FunctionDecl* FD) { void TBRAnalyzer::VisitCFGBlock(const CFGBlock& block) { // llvm::errs() << "\n-----BLOCK" << block->getBlockID() << "-----\n"; - /// Visiting loop blocks just once is not enough since the end of one - /// loop iteration may have an effect on the next one. However, two - /// iterations is always enough. Allow a third visit without going to - /// successors to correctly analyse loop conditions. + // Visiting loop blocks just once is not enough since the end of one loop + // iteration may have an effect on the next one. However, two iterations is + // always enough. Allow a third visit without going to successors to correctly + // analyse loop conditions. bool notLastPass = ++m_BlockPassCounter[block.getBlockID()] <= 2; - /// Visit all the statements inside the block. + // Visit all the statements inside the block. for (const clang::CFGElement& Element : block) { if (Element.getKind() == clang::CFGElement::Statement) { const clang::Stmt* S = Element.castAs().getStmt(); @@ -380,43 +378,42 @@ void TBRAnalyzer::VisitCFGBlock(const CFGBlock& block) { } } - /// Traverse successor CFG blocks. + // Traverse successor CFG blocks. for (const auto succ : block.succs()) { - /// Sometimes clang CFG does not create blocks for parts of code that - /// are never executed (e.g. 'if (0) {...'). Add this check for safety. + // Sometimes clang CFG does not create blocks for parts of code that + // are never executed (e.g. 'if (0) {...'). Add this check for safety. if (!succ) continue; auto& varsData = m_BlockData[succ->getBlockID()]; - /// Create VarsData for the succ branch if it hasn't been done previously. - /// If the successor doesn't have a VarsData, assign it and attach the - /// current block as previous. + // Create VarsData for the succ branch if it hasn't been done previously. + // If the successor doesn't have a VarsData, assign it and attach the + // current block as previous. if (!varsData) { varsData = std::unique_ptr(new VarsData()); varsData->prev = m_BlockData[block.getBlockID()].get(); } - /// If this is the third (last) pass of block, it means block represents - /// a loop condition and the loop body has already been visited 2 times. - /// This means we should not visit the loop body anymore. + // If this is the third (last) pass of block, it means block represents a + // loop condition and the loop body has already been visited 2 times. This + // means we should not visit the loop body anymore. if (notLastPass) { - /// Add the successor to the queue. + // Add the successor to the queue. m_CFGQueue.insert(succ->getBlockID()); - /// This part is necessary for loops. For other cases, this is not - /// supposed to do anything. + // This part is necessary for loops. For other cases, this is not supposed + // to do anything. if (succ->getBlockID() < block.getBlockID()) { - /// If there is another loop condition present inside a loop, - /// We have to set it's loop pass counter to 0 (it might be 3 - /// from the previous outer loop pass). + // If there is another loop condition present inside a loop, + // We have to set it's loop pass counter to 0 (it might be 3 + // from the previous outer loop pass). m_BlockPassCounter[succ->getBlockID()] = 0; - /// Remove VarsData left after the previous pass. + // Remove VarsData left after the previous pass. varsData->clear(); } } - /// If the successor's previous block is not this one, - /// perform a merge. + // If the successor's previous block is not this one, perform a merge. if (varsData->prev != m_BlockData[block.getBlockID()].get()) merge(varsData.get(), m_BlockData[block.getBlockID()].get()); } @@ -452,25 +449,25 @@ TBRAnalyzer::findLowestCommonAncestor(VarsData* varsData1, if (pred1->prev) { pred1 = pred1->prev; - /// This ensures we don't get an infinite loop because of VarsData being - /// connected in a loop themselves. + // This ensures we don't get an infinite loop because of VarsData being + // connected in a loop themselves. if (pred1 == varsData1) return nullptr; } else { - /// pred1 not having a predecessor means it is corresponds to the entry - /// block and, therefore it is the lowest common ancestor. + // pred1 not having a predecessor means it is corresponds to the entry + // block and, therefore it is the lowest common ancestor. return pred1; } if (pred2->prev) { pred2 = pred2->prev; - /// This ensures we don't get an infinite loop because of VarsData being - /// connected in a loop themselves. + // This ensures we don't get an infinite loop because of VarsData being + // connected in a loop themselves. if (pred2 == varsData2) return nullptr; } else { - /// pred2 not having a predecessor means it is corresponds to the entry - /// block and, therefore it is the lowest common ancestor. + // pred2 not having a predecessor means it is corresponds to the entry + // block and, therefore it is the lowest common ancestor. return pred2; } } @@ -481,10 +478,10 @@ TBRAnalyzer::collectDataFromPredecessors(VarsData* varsData, TBRAnalyzer::VarsData* limit) { std::unordered_map result; if (varsData != limit) { - /// Copy data from every predecessor. + // Copy data from every predecessor. for (auto* pred = varsData->prev; pred != limit; pred = pred->prev) { - /// If a variable from 'pred' is not present - /// in 'result', place it in there. + // If a variable from 'pred' is not present + // in 'result', place it in there. for (auto& pair : *pred) if (result.find(pair.first) == result.end()) { result[pair.first] = &pair.second; @@ -500,9 +497,9 @@ void TBRAnalyzer::merge(VarsData* targetData, VarsData* mergeData) { auto collectedMergeData = collectDataFromPredecessors(mergeData, /*limit=*/LCA); - /// For every variable in 'collectedMergeData', search it in targetData - /// and all its predecessors (if found in a predecessor, make a copy to - /// targetData). + // For every variable in 'collectedMergeData', search it in targetData + // and all its predecessors (if found in a predecessor, make a copy to + // targetData). for (auto& pair : collectedMergeData) { VarData* found = nullptr; auto elemSearch = targetData->find(pair.first); @@ -521,21 +518,20 @@ void TBRAnalyzer::merge(VarsData* targetData, VarsData* mergeData) { found = &elemSearch->second; } - /// If the variable was found, perform a merge. - /// Else, just copy it from collectedMergeData. + // If the variable was found, perform a merge. Else, just copy it from + // collectedMergeData. if (found) { merge(*found, *pair.second); } else (*targetData)[pair.first] = copy(*pair.second); } - /// For every variable in collected targetData predecessors, - /// search it inside collectedMergeData. If it's not found, - /// that means it was not used anywhere between LCA and mergeData. - /// To correctly merge, we have to take it from LCA's - /// predecessors and merge it to targetData. - /// If targetData is LCA, LCA will come after targetData->prev and - /// collectDataFromPredecessors will not reach the limit. + // For every variable in collected targetData predecessors, search it inside + // collectedMergeData. If it's not found, that means it was not used anywhere + // between LCA and mergeData. To correctly merge, we have to take it from + // LCA's predecessors and merge it to targetData. If targetData is LCA, LCA + // will come after targetData->prev and collectDataFromPredecessors will not + // reach the limit. if (targetData != LCA) { for (auto& pair : collectDataFromPredecessors(targetData->prev, /*limit=*/LCA)) { auto elemSearch = collectedMergeData.find(pair.first); @@ -553,11 +549,10 @@ void TBRAnalyzer::merge(VarsData* targetData, VarsData* mergeData) { } } } - /// For every variable in targetData, search it inside - /// collectedMergeData. If it's not found, that means it - /// was not used anywhere between LCA and mergeData. - /// To correctly merge, we have to take it from LCA's - /// predecessors and merge it to targetData. + // For every variable in targetData, search it inside collectedMergeData. If + // it's not found, that means it was not used anywhere between LCA and + // mergeData. To correctly merge, we have to take it from LCA's predecessors + // and merge it to targetData. for (auto& pair : *targetData) { auto elemSearch = collectedMergeData.find(pair.first); if (elemSearch == collectedMergeData.end()) { @@ -595,8 +590,8 @@ bool TBRAnalyzer::VisitDeclStmt(DeclStmt* DS) { TraverseStmt(init); resetMode(); auto& VDExpr = getCurBlockVarsData()[VD]; - /// if the declared variable is ref type attach its VarData to the - /// VarData of the RHS variable. + // if the declared variable is ref type attach its VarData to the + // VarData of the RHS variable. llvm::SmallVector ExprsToStore; utils::GetInnermostReturnExpr(init, ExprsToStore); if (VDExpr.type == VarData::REF_TYPE && !ExprsToStore.empty()) @@ -631,16 +626,16 @@ bool TBRAnalyzer::VisitBinaryOperator(BinaryOperator* BinOp) { const auto opCode = BinOp->getOpcode(); Expr* L = BinOp->getLHS(); Expr* 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 kNonLinearMode shouldn't be changed as well. The same applies - /// to subtraction. + // 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 kNonLinearMode shouldn't be changed as well. The same applies + // to subtraction. if (opCode == BO_Add || opCode == BO_Sub) { TraverseStmt(L); TraverseStmt(R); } else if (opCode == BO_Mul) { - /// Multiplication results in a linear expression if and only if one of the - /// factors is constant. + // Multiplication results in a linear expression if and only if one of the + // factors is constant. Expr::EvalResult dummy; bool nonLinear = !clad_compat::Expr_EvaluateAsConstantExpr(R, dummy, m_Context) && @@ -654,8 +649,8 @@ bool TBRAnalyzer::VisitBinaryOperator(BinaryOperator* BinOp) { if (nonLinear) resetMode(); } else if (opCode == BO_Div) { - /// Division normally only results in a linear expression when the - /// denominator is constant. + // Division normally only results in a linear expression when the + // denominator is constant. Expr::EvalResult dummy; bool nonLinear = !clad_compat::Expr_EvaluateAsConstantExpr(R, dummy, m_Context); @@ -670,18 +665,18 @@ bool TBRAnalyzer::VisitBinaryOperator(BinaryOperator* BinOp) { } 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. + // Since we only care about non-linear usages of variables, there is + // no difference between operators =, -=, += in terms of TBR analysis. TraverseStmt(L); startMarkingMode(); TraverseStmt(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 kMarkingMode|kNonLinearMode. + // *= (/=) 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 kMarkingMode|kNonLinearMode. Expr::EvalResult dummy; bool RisNotConst = !clad_compat::Expr_EvaluateAsConstantExpr(R, dummy, m_Context); @@ -698,12 +693,12 @@ bool TBRAnalyzer::VisitBinaryOperator(BinaryOperator* BinOp) { llvm::SmallVector ExprsToStore; utils::GetInnermostReturnExpr(L, ExprsToStore); for (const auto* innerExpr : ExprsToStore) { - /// Mark corresponding SourceLocation as required/not required to be - /// stored for all expressions that could be used changed. + // 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). + // 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) { @@ -726,28 +721,27 @@ bool TBRAnalyzer::VisitUnaryOperator(clang::UnaryOperator* UnOp) { 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. + // Mark corresponding SourceLocation as required/not required to be + // stored for all expressions that could be used in this operation. llvm::SmallVector ExprsToStore; utils::GetInnermostReturnExpr(E, ExprsToStore); for (const auto* innerExpr : ExprsToStore) { - /// Mark corresponding SourceLocation as required/not required to be - /// stored for all expressions that could be changed. + // Mark corresponding SourceLocation as required/not required to be + // stored for all expressions that could be changed. markLocation(innerExpr); } } - /// FIXME: Ideally, `__real` and `__imag` operators should be - /// treated as member expressions. However, it is not clear - /// where the FieldDecls of real and imaginary parts should be - /// deduced from (their names might be compiler-specific). - /// So for now we visit the whole subexpression. + // FIXME: Ideally, `__real` and `__imag` operators should be treated as member + // expressions. However, it is not clear where the FieldDecls of real and + // imaginary parts should be deduced from (their names might be + // compiler-specific). So for now we visit the whole subexpression. return true; } bool TBRAnalyzer::VisitCallExpr(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. + // 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. FunctionDecl* FD = CE->getDirectCallee(); bool noHiddenParam = (CE->getNumArgs() == FD->getNumParams()); setMode(Mode::kMarkingMode | Mode::kNonLinearMode); @@ -764,7 +758,7 @@ bool TBRAnalyzer::VisitCallExpr(clang::CallExpr* CE) { const auto* B = arg->IgnoreParenImpCasts(); // FIXME: this supports only DeclRefExpr if (passByRef) { - /// Mark SourceLocation as required to store for ref-type arguments. + // Mark SourceLocation as required to store for ref-type arguments. if (isa(B) || isa(B)) { m_TBRLocs.insert(arg->getBeginLoc()); setIsRequired(arg, /*isReq=*/false); @@ -776,10 +770,10 @@ bool TBRAnalyzer::VisitCallExpr(clang::CallExpr* CE) { } bool TBRAnalyzer::VisitCXXConstructExpr(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 + // 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 FunctionDecl* FD = CE->getConstructor(); setMode(Mode::kMarkingMode | Mode::kNonLinearMode); for (std::size_t i = 0, e = CE->getNumArgs(); i != e; ++i) { @@ -791,7 +785,7 @@ bool TBRAnalyzer::VisitCXXConstructExpr(clang::CXXConstructExpr* CE) { const auto* B = arg->IgnoreParenImpCasts(); // FIXME: this supports only DeclRefExpr if (passByRef) { - /// Mark SourceLocation as required for ref-type arguments. + // Mark SourceLocation as required for ref-type arguments. if (isa(B) || isa(B)) { m_TBRLocs.insert(arg->getBeginLoc()); setIsRequired(arg, /*isReq=*/false);