forked from chipsalliance/verible
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathflow_tree.h
159 lines (126 loc) · 5.65 KB
/
flow_tree.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// 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_