From 87a2f70092364e2fd9f087277cc4daf568ce836e Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Mon, 21 Oct 2019 14:03:35 +0100 Subject: [PATCH] First cut at constructing a call graph. --- bin/sixtypical | 12 ++++++++++++ src/sixtypical/analyzer.py | 2 ++ src/sixtypical/context.py | 6 ++++++ 3 files changed, 20 insertions(+) diff --git a/bin/sixtypical b/bin/sixtypical index c847389..bb9db27 100755 --- a/bin/sixtypical +++ b/bin/sixtypical @@ -50,6 +50,13 @@ def process_input_files(filenames, options): sys.stdout.write(json.dumps(analyzer.exit_contexts_map, indent=4, sort_keys=True, separators=(',', ':'))) sys.stdout.write("\n") + if options.dump_callgraph: + for routine in program.routines: + sys.stdout.write('-----------\n') + sys.stdout.write('{}\n'.format(routine.name)) + for called_routine in routine.called_routines: + sys.stdout.write(' {}\n'.format(called_routine.name)) + compilation_roster = None if options.optimize_fallthru: from sixtypical.fallthru import FallthruAnalyzer @@ -161,6 +168,11 @@ if __name__ == '__main__': action="store_true", help="Dump the ordered fallthru map, in JSON, to stdout after analyzing the program." ) + argparser.add_argument( + "--dump-callgraph", + action="store_true", + help="Dump the call graph, in JSON, to stdout after analyzing the program." + ) argparser.add_argument( "--parse-only", action="store_true", diff --git a/src/sixtypical/analyzer.py b/src/sixtypical/analyzer.py index 1e3991b..9e1b840 100644 --- a/src/sixtypical/analyzer.py +++ b/src/sixtypical/analyzer.py @@ -143,6 +143,7 @@ class Analyzer(object): for routine in program.routines: context = self.analyze_routine(routine) routine.encountered_gotos = list(context.encountered_gotos()) if context else [] + routine.called_routines = list(context.called_routines) if context else [] def analyze_routine(self, routine): assert isinstance(routine, Routine) @@ -515,6 +516,7 @@ class Analyzer(object): type = self.get_type(instr.location) if not isinstance(type, (RoutineType, VectorType)): raise TypeMismatchError(instr, instr.location.name) + context.mark_as_called(instr.location) if isinstance(type, VectorType): type = type.of_type for ref in type.inputs: diff --git a/src/sixtypical/context.py b/src/sixtypical/context.py index a92c513..c66f204 100644 --- a/src/sixtypical/context.py +++ b/src/sixtypical/context.py @@ -35,6 +35,7 @@ class AnalysisContext(object): self._terminated = False self._gotos_encountered = set() self._pointer_assoc = dict() + self.called_routines = set() for ref in inputs: if self.is_constant(ref): @@ -79,6 +80,7 @@ class AnalysisContext(object): c._writeable = set(self._writeable) c._pointer_assoc = dict(self._pointer_assoc) c._gotos_encountered = set(self._gotos_encountered) + c.called_routines = set(self.called_routines) return c def update_from(self, other): @@ -94,6 +96,7 @@ class AnalysisContext(object): self._writeable = set(other._writeable) self._terminated = other._terminated self._pointer_assoc = dict(other._pointer_assoc) + self.called_routines = set(other.called_routines) def each_meaningful(self): for ref in self._range.keys(): @@ -326,3 +329,6 @@ class AnalysisContext(object): return self.symtab.fetch_local_type(self.routine.name, ref.name).max_range else: return self.symtab.fetch_global_type(ref.name).max_range + + def mark_as_called(self, routine): + self.called_routines.add(routine)