@@ -115,6 +115,7 @@ def build_module_graph(files: list, modules: list[dict]) -> tuple[list, list]:
115115 module_graph_inverse = {}
116116
117117 module_names = [module [0 ] for module in modules ]
118+ module_names_set = set (module_names ) # Use set for O(1) lookup
118119
119120 # Initialize the direct and inverse graphs with each module
120121 for module_name , _ in modules :
@@ -127,33 +128,65 @@ def build_module_graph(files: list, modules: list[dict]) -> tuple[list, list]:
127128 if not should_exclude_file (file_path ):
128129 filtered_files .append (file_path )
129130
131+ print (f"[GRAPH] Processing { len (filtered_files )} files for { len (module_names )} modules..." )
132+
133+ # Create a more efficient pattern for Verilog/SystemVerilog module instances
134+ # This pattern captures: module_name #(params) instance_name (ports);
135+ instance_pattern = re .compile (
136+ r'^\s*(\w+)\s*(?:#\s*\([^)]*\))?\s+\w+\s*\([^;]*\)\s*;' ,
137+ re .MULTILINE | re .DOTALL
138+ )
139+
140+ processed_files = 0
130141 for file_path in filtered_files :
142+ processed_files += 1
143+ if processed_files % 50 == 0 : # Progress indicator
144+ print (f"[GRAPH] Processed { processed_files } /{ len (filtered_files )} files..." )
145+
131146 try :
132- with open (
133- file_path , 'r' , errors = 'ignore' , encoding = 'utf-8'
134- ) as f : # Ignore decoding errors
147+ with open (file_path , 'r' , errors = 'ignore' , encoding = 'utf-8' ) as f :
135148 content = f .read ()
136149
137150 # Find the current module name (module where instances are being made)
138- current_module_match = re .search (r'module\s+(\w+)' , content )
151+ current_module_match = re .search (r'^\s* module\s+(\w+)' , content , re . MULTILINE )
139152 if not current_module_match :
140153 continue # Skip files without a Verilog module
141154
142155 current_module_name = current_module_match .group (1 )
143156
144- # Find instances within this module
145- module_instances = find_module_instances (content , module_names )
146-
147- # Update the direct (instantiated -> instantiator) and inverse
148- # (instantiator -> instantiated) graphs
149- for instance in module_instances :
150- if instance in module_graph :
157+ # Only process if this module is in our module list
158+ if current_module_name not in module_names_set :
159+ continue
160+
161+ # Find instances within this module using the optimized pattern
162+ instances_found = set () # Use set to avoid duplicates
163+ for match in instance_pattern .finditer (content ):
164+ potential_module = match .group (1 )
165+
166+ # Skip Verilog built-in primitives and keywords
167+ if potential_module in {'input' , 'output' , 'inout' , 'wire' , 'reg' , 'logic' ,
168+ 'parameter' , 'localparam' , 'genvar' , 'generate' ,
169+ 'if' , 'for' , 'case' , 'always' , 'initial' , 'assign' ,
170+ 'and' , 'or' , 'not' , 'nand' , 'nor' , 'xor' , 'xnor' ,
171+ 'buf' , 'bufif0' , 'bufif1' , 'notif0' , 'notif1' }:
172+ continue
173+
174+ # Only add if it's in our module list
175+ if potential_module in module_names_set :
176+ instances_found .add (potential_module )
177+
178+ # Update the direct and inverse graphs
179+ for instance in instances_found :
180+ if instance in module_graph and current_module_name != instance : # Avoid self-reference
151181 module_graph [instance ].append (current_module_name )
152182 module_graph_inverse [current_module_name ].append (instance )
153- except Exception :
154- # Skip files that can't be read
183+
184+ except Exception as e :
185+ # Skip files that can't be read, but log for debugging
186+ print (f"[GRAPH] Warning: Could not process file { file_path } : { e } " )
155187 continue
156188
189+ print (f"[GRAPH] Graph construction completed. Processed { processed_files } files." )
157190 return module_graph , module_graph_inverse
158191
159192
0 commit comments