Callgraph uses reachability.

This commit is contained in:
Chris Pressey 2019-10-22 09:07:16 +01:00
parent 1098347fa5
commit 1df6941b01
4 changed files with 99 additions and 47 deletions

View File

@ -5,9 +5,9 @@ History of SixtyPical
----
* The reference implementation constructs a callgraph and
approximates the set of routines which are not called
by any other routine, with an eye to omitting them from
the final executable.
determines the set of routines which are not reachable
(directly or indirectly) from `main`, with an eye to
omitting them from the final executable.
* A routine can be declared `preserved`, which prevents a
compiler from omitting it from the final executable, even
if it determines it is not called by any other routine.

View File

@ -102,9 +102,6 @@ Once we have a call graph we can omit routines that we're sure aren't called.
This would let us use include-files and standard-libraries nicely: any
routines they define, but that you don't use, don't get included.
Analyzing the set of possible routines that a vector can take on would help
this immensely.
Implementation
--------------

View File

@ -8,6 +8,15 @@ def find_routines_matching_type(program, type_):
return [r for r in program.routines if RoutineType.executable_types_compatible(r.routine_type, type_)]
def mark_as_reachable(graph, routine_name):
node = graph[routine_name]
if node.get('reachable', False):
return
node['reachable'] = True
for next_routine_name in node['potentially-calls']:
mark_as_reachable(graph, next_routine_name)
def construct_callgraph(program):
graph = {}
@ -33,8 +42,19 @@ def construct_callgraph(program):
potential_calls = node['potentially-calls']
if routine.name in potential_calls:
potentially_called_by.append(name)
if getattr(routine, 'preserved', None) or routine.name == 'main':
potentially_called_by.append('*preserved*')
graph[routine.name]['potentially-called-by'] = potentially_called_by
# Root set
root_set = set()
for routine in program.routines:
if getattr(routine, 'preserved', False) or routine.name == 'main':
root_set.add(routine)
# Reachability
for routine in root_set:
mark_as_reachable(graph, routine.name)
return graph

View File

@ -11,22 +11,23 @@ called.
-> Tests for functionality "Dump callgraph info for SixtyPical program"
The `main` routine is always called. The thing that it will
be called by is the system, but the callgraph analyzer will
simply consider it to be "marked as preserved".
be called by is the system, but the callgraph analyzer simply
considers it to be "reachable".
| define main routine
| {
| }
= {
= "main": {
= "potentially-called-by": [
= "*preserved*"
= ],
= "potentially-calls": []
= "potentially-called-by": [],
= "potentially-calls": [],
= "reachable": true
= }
= }
If a routine is called by another routine, this fact will be noted.
If it is reachable (directly or indirectly) from `main`, this will
be noted as well.
| define main routine
| {
@ -38,25 +39,25 @@ If a routine is called by another routine, this fact will be noted.
| }
= {
= "main": {
= "potentially-called-by": [
= "*preserved*"
= ],
= "potentially-called-by": [],
= "potentially-calls": [
= "other"
= ]
= ],
= "reachable": true
= },
= "other": {
= "potentially-called-by": [
= "main"
= ],
= "potentially-calls": []
= "potentially-calls": [],
= "reachable": true
= }
= }
If a routine is not called by another routine, and it is not `main`
and it is not explicitly marked as preserved, this absence will be
noted, and a compiler or linker will be permitted to omit it from
the final executable.
If a routine is not potentially called by any other routine that is
ultimately potentially called by `main`, this absence will be noted
— the routine will not be considered reachable — and a compiler or
linker will be permitted to omit it from the final executable.
| define main routine
| {
@ -67,10 +68,9 @@ the final executable.
| }
= {
= "main": {
= "potentially-called-by": [
= "*preserved*"
= ],
= "potentially-calls": []
= "potentially-called-by": [],
= "potentially-calls": [],
= "reachable": true
= },
= "other": {
= "potentially-called-by": [],
@ -79,11 +79,11 @@ the final executable.
= }
If a routine is not called by another routine, but it is declared
explicitly as `preserved`, then it will not be considered unused,
and a compiler or linker will not be permitted to omit it from
the final executable. This is useful for interrupt routines and
such that really are used by some part of the system, even if not
directly by another SixtyPical routine.
explicitly as `preserved`, then it will still be considered
reachable, and a compiler or linker will not be permitted to omit it
from the final executable. This is useful for interrupt routines
and such that really are used by some part of the system, even if
not directly by another SixtyPical routine.
| define main routine
| {
@ -94,22 +94,58 @@ directly by another SixtyPical routine.
| }
= {
= "main": {
= "potentially-called-by": [
= "*preserved*"
= ],
= "potentially-calls": []
= "potentially-called-by": [],
= "potentially-calls": [],
= "reachable": true
= },
= "other": {
= "potentially-called-by": [
= "*preserved*"
= ],
= "potentially-calls": []
= "potentially-called-by": [],
= "potentially-calls": [],
= "reachable": true
= }
= }
If two routines potentially call each other, this will be noted,
even if nothing else potentially calls either of those routines.
This may change in the future.
If a routine is called from a preserved routine, that routine is
reachable.
| define main routine
| {
| }
|
| define other1 preserved routine
| {
| call other2
| }
|
| define other2 preserved routine
| {
| }
= {
= "main": {
= "potentially-called-by": [],
= "potentially-calls": [],
= "reachable": true
= },
= "other1": {
= "potentially-called-by": [],
= "potentially-calls": [
= "other2"
= ],
= "reachable": true
= },
= "other2": {
= "potentially-called-by": [
= "other1"
= ],
= "potentially-calls": [],
= "reachable": true
= }
= }
If a group of routines potentially call each other, but neither is
found to be reachable (directly or indirectly) from `main` or a
`preserved` routine, the routines in the group will not be considered
reachable.
| define main routine
| {
@ -126,10 +162,9 @@ This may change in the future.
| }
= {
= "main": {
= "potentially-called-by": [
= "*preserved*"
= ],
= "potentially-calls": []
= "potentially-called-by": [],
= "potentially-calls": [],
= "reachable": true
= },
= "other1": {
= "potentially-called-by": [