/// \file ASTNode.cpp /// \brief // // This file is distributed under the MIT License. See LICENSE.md for details. // // Standard includes #include #include #include // LLVM includes #include #include // Local libraries includes #include "revng-c/RestructureCFGPass/ASTNode.h" using namespace llvm; void IfNode::updateCondExprPtr(ExprNodeMap &Map) { revng_assert(ConditionExpression != nullptr); ConditionExpression = Map[ConditionExpression]; } void ContinueNode::addComputationIfNode(IfNode *ComputationIfNode) { revng_assert(ComputationIf == nullptr); ComputationIf = ComputationIfNode; } IfNode *ContinueNode::getComputationIfNode() const { revng_assert(ComputationIf != nullptr); return ComputationIf; } // #### updateASTNodesPointers methods #### void IfNode::updateASTNodesPointers(ASTNodeMap &SubstitutionMap) { // Update the pointers to the `then` and `else` branches. if (hasThen()) { revng_assert(SubstitutionMap.count(Then) != 0); Then = SubstitutionMap[Then]; } if (hasElse()) { revng_assert(SubstitutionMap.count(Else) != 0); Else = SubstitutionMap[Else]; } } void SequenceNode::updateASTNodesPointers(ASTNodeMap &SubstitutionMap) { // Update all the pointers of the sequence node. for (auto NodeIt = NodeList.begin(); NodeIt != NodeList.end(); NodeIt++) { ASTNode *Node = *NodeIt; revng_assert(SubstitutionMap.count(Node) != 0); ASTNode *NewNode = SubstitutionMap[Node]; *NodeIt = NewNode; } } void SwitchNode::updateASTNodesPointers(ASTNodeMap &SubstitutionMap) { for (auto &Case : CaseVec) Case = SubstitutionMap.at(Case); } // #### isEqual methods #### template typename SwitchNodeType::case_value getCaseValueN(const SwitchNodeType *S, typename SwitchNodeType::case_container::size_type N) { return cast(S)->getCaseValueN(N); } bool RegularSwitchNode::nodeIsEqual(const ASTNode *Node) const { auto *OtherSwitch = dyn_cast_or_null(Node); if (OtherSwitch == nullptr) return false; ASTNode *OtherDefault = OtherSwitch->getDefault(); ASTNode *ThisDefault = this->getDefault(); if ((OtherDefault == nullptr) != (ThisDefault == nullptr)) return false; if (ThisDefault and not ThisDefault->isEqual(OtherDefault)) return false; // Continue the comparison only if the sequence node size are the same if (CaseSize() != OtherSwitch->CaseSize()) return false; for (const auto &PairOfPairs : llvm::zip_first(labeled_cases(), OtherSwitch->labeled_cases())) { const auto &[ThisCase, OtherCase] = PairOfPairs; const auto &[ThisCaseChild, ThisCaseLabel] = ThisCase; const auto &[OtherCaseChild, OtherCaseLabel] = OtherCase; if (ThisCaseLabel != OtherCaseLabel) return false; if (not ThisCaseChild->isEqual(OtherCaseChild)) return false; } return true; } bool SwitchDispatcherNode::nodeIsEqual(const ASTNode *Node) const { auto *OtherSwitch = dyn_cast_or_null(Node); if (OtherSwitch == nullptr) return false; ASTNode *OtherDefault = OtherSwitch->getDefault(); ASTNode *ThisDefault = this->getDefault(); if ((OtherDefault == nullptr) != (ThisDefault == nullptr)) return false; if (ThisDefault and not ThisDefault->isEqual(OtherDefault)) return false; // Continue the comparison only if the sequence node size are the same if (CaseSize() != OtherSwitch->CaseSize()) return false; for (const auto &PairOfPairs : llvm::zip_first(labeled_cases(), OtherSwitch->labeled_cases())) { const auto &[ThisCase, OtherCase] = PairOfPairs; const auto &[ThisCaseChild, ThisCaseLabel] = ThisCase; const auto &[OtherCaseChild, OtherCaseLabel] = OtherCase; if (ThisCaseLabel != OtherCaseLabel) return false; if (not ThisCaseChild->isEqual(OtherCaseChild)) return false; } return true; } bool CodeNode::nodeIsEqual(const ASTNode *Node) const { auto *OtherCode = dyn_cast_or_null(Node); if (OtherCode == nullptr) return false; return (getOriginalBB() != nullptr) and (getOriginalBB() == OtherCode->getOriginalBB()); } bool IfNode::nodeIsEqual(const ASTNode *Node) const { auto *OtherIf = dyn_cast_or_null(Node); if (OtherIf == nullptr) return false; if ((getOriginalBB() != nullptr) and (getOriginalBB() == OtherIf->getOriginalBB())) { // TODO: this is necessary since we may not have one between `then` or // `else` branches, refactor in a more elegant way bool ComparisonState = true; if (hasThen()) ComparisonState = Then->isEqual(OtherIf->getThen()); if (hasElse()) ComparisonState = Else->isEqual(OtherIf->getElse()); return ComparisonState; } return false; } bool ScsNode::nodeIsEqual(const ASTNode *Node) const { auto *OtherScs = dyn_cast_or_null(Node); if (OtherScs == nullptr) return false; return Body->isEqual(OtherScs->getBody()); } bool SetNode::nodeIsEqual(const ASTNode *Node) const { auto *OtherSet = dyn_cast_or_null(Node); if (OtherSet == nullptr) return false; return StateVariableValue == OtherSet->getStateVariableValue(); } bool SequenceNode::nodeIsEqual(const ASTNode *Node) const { auto *OtherSequence = dyn_cast_or_null(Node); if (OtherSequence == nullptr) return false; links_container::size_type FirstDimension = NodeList.size(); links_container::size_type SecondDimension = OtherSequence->listSize(); // Continue the comparison only if the sequence node size are the same if (FirstDimension != SecondDimension) return false; revng_assert(FirstDimension == SecondDimension); for (links_container::size_type I = 0; I < FirstDimension; I++) { ASTNode *FirstNode = getNodeN(I); ASTNode *SecondNode = OtherSequence->getNodeN(I); // As soon as two nodes does not match, exit and make the comparison fail if (!FirstNode->isEqual(SecondNode)) return false; } return true; } // #### Dump methods #### void CodeNode::dump(std::ofstream &ASTFile) { ASTFile << "\"" << this->getName() << "\" ["; ASTFile << "label=\"" << this->getName(); ASTFile << "\""; ASTFile << ",shape=\"box\",color=\"red\"];\n"; } void IfNode::dump(std::ofstream &ASTFile) { ASTFile << "\"" << this->getName() << "\" ["; // TODO: Implement the printing of the conditional expression for the if. // ASTFile << "label=\"" << ConditionalNames; ASTFile << "label=\"" << this->getName(); ASTFile << "\""; ASTFile << ",shape=\"invhouse\",color=\"blue\"];\n"; if (this->getThen() != nullptr) { ASTFile << "\"" << this->getName() << "\"" << " -> \"" << this->getThen()->getName() << "\"" << " [color=green,label=\"then\"];\n"; this->getThen()->dump(ASTFile); } if (this->getElse() != nullptr) { ASTFile << "\"" << this->getName() << "\"" << " -> \"" << this->getElse()->getName() << "\"" << " [color=green,label=\"else\"];\n"; this->getElse()->dump(ASTFile); } } void ScsNode::dump(std::ofstream &ASTFile) { ASTFile << "\"" << this->getName() << "\" ["; ASTFile << "label=\"" << this->getName(); ASTFile << "\""; ASTFile << ",shape=\"circle\",color=\"black\"];\n"; // After do-while and while match loop nodes could be empty // revng_assert(this->getBody() != nullptr); if (this->getBody() != nullptr) { ASTFile << "\"" << this->getName() << "\"" << " -> \"" << this->getBody()->getName() << "\"" << " [color=green,label=\"body\"];\n"; this->getBody()->dump(ASTFile); } } void SequenceNode::dump(std::ofstream &ASTFile) { ASTFile << "\"" << this->getName() << "\" ["; ASTFile << "label=\"" << this->getName(); ASTFile << "\""; ASTFile << ",shape=\"box\",color=\"black\"];\n"; int SuccessorIndex = 0; for (ASTNode *Successor : this->nodes()) { ASTFile << "\"" << this->getName() << "\"" << " -> \"" << Successor->getName() << "\"" << " [color=green,label=\"elem " << SuccessorIndex << "\"];\n"; Successor->dump(ASTFile); SuccessorIndex += 1; } } void RegularSwitchNode::dump(std::ofstream &ASTFile) { ASTFile << "\"" << this->getName() << "\" ["; ASTFile << "label=\"" << this->getName(); ASTFile << "\""; ASTFile << ",shape=\"hexagon\",color=\"black\"];\n"; case_container::size_type CaseIndex = 0; for (ASTNode *Case : this->unordered_cases()) { ASTFile << "\"" << this->getName() << "\"" << " -> \"" << Case->getName() << "\"" << " [color=green,label=\"case "; // Cases can now be sets of cases, we need to print all of them on a edge. for (auto *CaseConstantInt : CaseValueVec[CaseIndex]) { uint64_t CaseVal = CaseConstantInt->getZExtValue(); ASTFile << CaseVal << ","; } // Close the line. ASTFile << "\"];\n"; // Continue dumping the children of the switch node. Case->dump(ASTFile); ++CaseIndex; } if (ASTNode *Default = this->getDefault()) { ASTFile << "\"" << this->getName() << "\"" << " -> \"" << Default->getName() << "\"" << " [color=green,label=\"default\"];\n"; Default->dump(ASTFile); } } void BreakNode::dump(std::ofstream &ASTFile) { ASTFile << "\"" << this->getName() << "\" ["; ASTFile << "label=\"loop break\""; ASTFile << ",shape=\"box\",color=\"red\"];\n"; } void SwitchBreakNode::dump(std::ofstream &ASTFile) { ASTFile << "\"" << this->getName() << "\" ["; ASTFile << "label=\"switch break\""; ASTFile << ",shape=\"box\",color=\"red\"];\n"; } void ContinueNode::dump(std::ofstream &ASTFile) { ASTFile << "\"" << this->getName() << "\" ["; ASTFile << "label=\"continue\""; ASTFile << ",shape=\"box\",color=\"red\"];\n"; } void SetNode::dump(std::ofstream &ASTFile) { ASTFile << "\"" << this->getName() << "\" ["; ASTFile << "label=\"" << this->getName(); ASTFile << "\""; ASTFile << ",shape=\"box\",color=\"red\"];\n"; } void SwitchDispatcherNode::dump(std::ofstream &ASTFile) { ASTFile << "\"" << this->getName() << "\" ["; ASTFile << "label=\"" << this->getName(); ASTFile << "\""; ASTFile << ",shape=\"hexagon\",color=\"black\"];\n"; case_container::size_type CaseIndex = 0; for (ASTNode *Case : this->unordered_cases()) { uint64_t CaseVal = CaseValueVec[CaseIndex]; ASTFile << "\"" << this->getName() << "\"" << " -> \"" << Case->getName() << "\"" << " [color=green,label=\"case " << CaseVal << "\"];\n"; Case->dump(ASTFile); ++CaseIndex; } if (ASTNode *Default = this->getDefault()) { ASTFile << "\"" << this->getName() << "\"" << " -> \"" << Default->getName() << "\"" << " [color=green,label=\"default\"];\n"; Default->dump(ASTFile); } } void ASTNode::deleteASTNode(ASTNode *A) { switch (A->getKind()) { case NodeKind::NK_Code: delete static_cast(A); break; case NodeKind::NK_Break: delete static_cast(A); break; case NodeKind::NK_Continue: delete static_cast(A); break; // ---- IfNode kinds case NodeKind::NK_If: delete static_cast(A); break; // ---- end IfNode kinds case NodeKind::NK_Scs: delete static_cast(A); break; case NodeKind::NK_List: delete static_cast(A); break; // ---- SwitchNode kinds case NodeKind::NK_SwitchRegular: delete static_cast(A); break; case NodeKind::NK_SwitchDispatcher: delete static_cast(A); break; // ---- end SwitchNode kinds case NodeKind::NK_SwitchBreak: delete static_cast(A); break; case NodeKind::NK_Set: delete static_cast(A); break; } } void SwitchNode::removeCaseN(case_container::size_type N) { revng_assert(N < CaseSize()); CaseVec.erase(CaseVec.begin() + N); // Remove also the counterpart of the N-th case node even in the // `CaseValueVec` field of the subclass. switch (this->getKind()) { case NodeKind::NK_SwitchRegular: { RegularSwitchNode *SwitchR = static_cast(this); auto &CaseValueVec = SwitchR->CaseValueVec; CaseValueVec.erase(CaseValueVec.begin() + N); } break; case NodeKind::NK_SwitchDispatcher: { SwitchDispatcherNode *SwitchD = static_cast(this); auto &CaseValueVec = SwitchD->CaseValueVec; CaseValueVec.erase(CaseValueVec.begin() + N); } break; default: revng_unreachable(); } }