| // Copyright 2017-2022 The Verible Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #ifndef VERIBLE_VERILOG_FLOW_TREE_H_ |
| #define VERIBLE_VERILOG_FLOW_TREE_H_ |
| |
| #include <bitset> |
| #include <map> |
| #include <string> |
| #include <vector> |
| |
| #include "absl/status/status.h" |
| #include "common/text/token_stream_view.h" |
| |
| namespace verilog { |
| |
| // FlowTree class builds the control flow graph of a tokenized System-Verilog |
| // source code. Furthermore, enabling doing the following queries on the graph: |
| // - Generating all the possible variants (provided via a callback function). |
| class FlowTree { |
| private: |
| // 'kMaxDistinctMacros' shows the maximum number of distinct macros that can |
| // be considered in conditonal directives. |
| static constexpr int kMaxDistinctMacros = 128; |
| |
| public: |
| using BitSet = std::bitset<kMaxDistinctMacros>; |
| using TokenSequenceConstIterator = verible::TokenSequence::const_iterator; |
| |
| // "ConditionalBlock" saves locations of conditionals in a "TokenSequence". |
| // All locations should point inside this specific "TokenSequence". |
| // Since it is only used in conjunction with a "TokenSequence", |
| // It should be initialized to last location in this "TokenSequence", |
| // For example in "GenerateControlFlowTree" is initialized to |
| // 'source_sequence_.end()'. |
| |
| struct Variant { |
| // Contains the token sequence of the variant. |
| verible::TokenSequence sequence; |
| |
| // The i-th bit in "macros_mask" is 1 when the macro (with ID = i) is |
| // assumed to be defined, otherwise it is assumed to be undefined. |
| BitSet macros_mask; |
| |
| // The i-th bit in "visited" is 1 when the macro (with ID = i) is visited or |
| // assumed (either defined or not), otherwise it is not visited (its value |
| // doesn't affect this variant). |
| // |
| // e.g.: |
| // `ifdef A |
| // `ifdef B |
| // ... |
| // `endif |
| // `endif |
| // |
| // Consider the variant in which A is undefined, |
| // we notice that B doesn't affect the variant. |
| // Then the bit corresponding to B in "visited" is 0. |
| BitSet visited; |
| }; |
| |
| // Receive a complete token sequence of one variant. |
| // variant_sequence: the generated variant token sequence. |
| using VariantReceiver = std::function<bool(const Variant &variant)>; |
| |
| explicit FlowTree(verible::TokenSequence source_sequence) |
| : source_sequence_(std::move(source_sequence)){}; |
| |
| // Generates all possible variants. |
| absl::Status GenerateVariants(const VariantReceiver &receiver); |
| |
| // Returns all the used macros in conditionals, ordered with the same ID as |
| // used in BitSets. |
| const std::vector<TokenSequenceConstIterator> &GetUsedMacros() { |
| return conditional_macros_; |
| } |
| |
| private: |
| struct ConditionalBlock { |
| ConditionalBlock(TokenSequenceConstIterator if_location, |
| TokenSequenceConstIterator non_location) |
| : if_location(if_location), |
| else_location(non_location), |
| endif_location(non_location) {} |
| |
| // "if_location" points to `ifdef or `ifndef. |
| TokenSequenceConstIterator if_location; |
| std::vector<TokenSequenceConstIterator> elsif_locations; |
| TokenSequenceConstIterator else_location; |
| TokenSequenceConstIterator endif_location; |
| }; |
| |
| // Constructs the control flow tree by adding the tree edges in edges_. |
| absl::Status GenerateControlFlowTree(); |
| |
| // Traveses the tree in a depth first manner. |
| absl::Status DepthFirstSearch(const VariantReceiver &receiver, |
| TokenSequenceConstIterator current_node); |
| |
| // Checks if the iterator points to a conditonal directive (`ifdef/ifndef...). |
| static bool IsConditional(TokenSequenceConstIterator iterator); |
| |
| // Adds all edges withing a conditional block. |
| absl::Status AddBlockEdges(const ConditionalBlock &block); |
| |
| // The tree edges which defines the possible next childs of each token in |
| // source_sequence_. |
| std::map<TokenSequenceConstIterator, std::vector<TokenSequenceConstIterator>> |
| edges_; |
| |
| // Extracts the conditional macro checked. |
| static absl::Status MacroFollows( |
| TokenSequenceConstIterator conditional_iterator); |
| |
| // Adds macro to conditional_macros_ vector, and save its ID in |
| // conditional_macro_id_ map. |
| absl::Status AddMacroOfConditional( |
| TokenSequenceConstIterator conditional_iterator); |
| |
| int GetMacroIDOfConditional(TokenSequenceConstIterator conditional_iterator); |
| |
| // Holds all of the conditional blocks. |
| std::vector<ConditionalBlock> if_blocks_; |
| |
| // The original source code lexed token seqouence. |
| const verible::TokenSequence source_sequence_; |
| |
| // Current variant being generated by DepthFirstSearch. |
| Variant current_variant_; |
| |
| // A flag that determines if the VariantReceiver returned 'false'. |
| // By default: it assumes VariantReceiver wants more variants. |
| bool wants_more_ = 1; |
| |
| // Mapping each conditional macro to an integer ID, |
| // to use it later as a bit offset. |
| std::map<absl::string_view, int> conditional_macro_id_; |
| |
| // A vector containing all the macros used placed by their given ID. |
| std::vector<TokenSequenceConstIterator> conditional_macros_; |
| |
| // Number of macros appeared in `ifdef/`ifndef/`elsif. |
| int conditional_macros_counter_ = 0; |
| }; |
| |
| } // namespace verilog |
| |
| #endif // VERIBLE_VERILOG_FLOW_TREE_H_ |