mirror of
https://github.com/catseye/SixtyPical.git
synced 2024-11-24 15:32:27 +00:00
Callgraph uses reachability.
This commit is contained in:
parent
1098347fa5
commit
1df6941b01
@ -5,9 +5,9 @@ History of SixtyPical
|
|||||||
----
|
----
|
||||||
|
|
||||||
* The reference implementation constructs a callgraph and
|
* The reference implementation constructs a callgraph and
|
||||||
approximates the set of routines which are not called
|
determines the set of routines which are not reachable
|
||||||
by any other routine, with an eye to omitting them from
|
(directly or indirectly) from `main`, with an eye to
|
||||||
the final executable.
|
omitting them from the final executable.
|
||||||
* A routine can be declared `preserved`, which prevents a
|
* A routine can be declared `preserved`, which prevents a
|
||||||
compiler from omitting it from the final executable, even
|
compiler from omitting it from the final executable, even
|
||||||
if it determines it is not called by any other routine.
|
if it determines it is not called by any other routine.
|
||||||
|
3
TODO.md
3
TODO.md
@ -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
|
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.
|
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
|
Implementation
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
@ -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_)]
|
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):
|
def construct_callgraph(program):
|
||||||
graph = {}
|
graph = {}
|
||||||
|
|
||||||
@ -33,8 +42,19 @@ def construct_callgraph(program):
|
|||||||
potential_calls = node['potentially-calls']
|
potential_calls = node['potentially-calls']
|
||||||
if routine.name in potential_calls:
|
if routine.name in potential_calls:
|
||||||
potentially_called_by.append(name)
|
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
|
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
|
return graph
|
||||||
|
@ -11,22 +11,23 @@ called.
|
|||||||
-> Tests for functionality "Dump callgraph info for SixtyPical program"
|
-> Tests for functionality "Dump callgraph info for SixtyPical program"
|
||||||
|
|
||||||
The `main` routine is always called. The thing that it will
|
The `main` routine is always called. The thing that it will
|
||||||
be called by is the system, but the callgraph analyzer will
|
be called by is the system, but the callgraph analyzer simply
|
||||||
simply consider it to be "marked as preserved".
|
considers it to be "reachable".
|
||||||
|
|
||||||
| define main routine
|
| define main routine
|
||||||
| {
|
| {
|
||||||
| }
|
| }
|
||||||
= {
|
= {
|
||||||
= "main": {
|
= "main": {
|
||||||
= "potentially-called-by": [
|
= "potentially-called-by": [],
|
||||||
= "*preserved*"
|
= "potentially-calls": [],
|
||||||
= ],
|
= "reachable": true
|
||||||
= "potentially-calls": []
|
|
||||||
= }
|
= }
|
||||||
= }
|
= }
|
||||||
|
|
||||||
If a routine is called by another routine, this fact will be noted.
|
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
|
| define main routine
|
||||||
| {
|
| {
|
||||||
@ -38,25 +39,25 @@ If a routine is called by another routine, this fact will be noted.
|
|||||||
| }
|
| }
|
||||||
= {
|
= {
|
||||||
= "main": {
|
= "main": {
|
||||||
= "potentially-called-by": [
|
= "potentially-called-by": [],
|
||||||
= "*preserved*"
|
|
||||||
= ],
|
|
||||||
= "potentially-calls": [
|
= "potentially-calls": [
|
||||||
= "other"
|
= "other"
|
||||||
= ]
|
= ],
|
||||||
|
= "reachable": true
|
||||||
= },
|
= },
|
||||||
= "other": {
|
= "other": {
|
||||||
= "potentially-called-by": [
|
= "potentially-called-by": [
|
||||||
= "main"
|
= "main"
|
||||||
= ],
|
= ],
|
||||||
= "potentially-calls": []
|
= "potentially-calls": [],
|
||||||
|
= "reachable": true
|
||||||
= }
|
= }
|
||||||
= }
|
= }
|
||||||
|
|
||||||
If a routine is not called by another routine, and it is not `main`
|
If a routine is not potentially called by any other routine that is
|
||||||
and it is not explicitly marked as preserved, this absence will be
|
ultimately potentially called by `main`, this absence will be noted
|
||||||
noted, and a compiler or linker will be permitted to omit it from
|
— the routine will not be considered reachable — and a compiler or
|
||||||
the final executable.
|
linker will be permitted to omit it from the final executable.
|
||||||
|
|
||||||
| define main routine
|
| define main routine
|
||||||
| {
|
| {
|
||||||
@ -67,10 +68,9 @@ the final executable.
|
|||||||
| }
|
| }
|
||||||
= {
|
= {
|
||||||
= "main": {
|
= "main": {
|
||||||
= "potentially-called-by": [
|
= "potentially-called-by": [],
|
||||||
= "*preserved*"
|
= "potentially-calls": [],
|
||||||
= ],
|
= "reachable": true
|
||||||
= "potentially-calls": []
|
|
||||||
= },
|
= },
|
||||||
= "other": {
|
= "other": {
|
||||||
= "potentially-called-by": [],
|
= "potentially-called-by": [],
|
||||||
@ -79,11 +79,11 @@ the final executable.
|
|||||||
= }
|
= }
|
||||||
|
|
||||||
If a routine is not called by another routine, but it is declared
|
If a routine is not called by another routine, but it is declared
|
||||||
explicitly as `preserved`, then it will not be considered unused,
|
explicitly as `preserved`, then it will still be considered
|
||||||
and a compiler or linker will not be permitted to omit it from
|
reachable, and a compiler or linker will not be permitted to omit it
|
||||||
the final executable. This is useful for interrupt routines and
|
from the final executable. This is useful for interrupt routines
|
||||||
such that really are used by some part of the system, even if not
|
and such that really are used by some part of the system, even if
|
||||||
directly by another SixtyPical routine.
|
not directly by another SixtyPical routine.
|
||||||
|
|
||||||
| define main routine
|
| define main routine
|
||||||
| {
|
| {
|
||||||
@ -94,22 +94,58 @@ directly by another SixtyPical routine.
|
|||||||
| }
|
| }
|
||||||
= {
|
= {
|
||||||
= "main": {
|
= "main": {
|
||||||
= "potentially-called-by": [
|
= "potentially-called-by": [],
|
||||||
= "*preserved*"
|
= "potentially-calls": [],
|
||||||
= ],
|
= "reachable": true
|
||||||
= "potentially-calls": []
|
|
||||||
= },
|
= },
|
||||||
= "other": {
|
= "other": {
|
||||||
= "potentially-called-by": [
|
= "potentially-called-by": [],
|
||||||
= "*preserved*"
|
= "potentially-calls": [],
|
||||||
= ],
|
= "reachable": true
|
||||||
= "potentially-calls": []
|
|
||||||
= }
|
= }
|
||||||
= }
|
= }
|
||||||
|
|
||||||
If two routines potentially call each other, this will be noted,
|
If a routine is called from a preserved routine, that routine is
|
||||||
even if nothing else potentially calls either of those routines.
|
reachable.
|
||||||
This may change in the future.
|
|
||||||
|
| 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
|
| define main routine
|
||||||
| {
|
| {
|
||||||
@ -126,10 +162,9 @@ This may change in the future.
|
|||||||
| }
|
| }
|
||||||
= {
|
= {
|
||||||
= "main": {
|
= "main": {
|
||||||
= "potentially-called-by": [
|
= "potentially-called-by": [],
|
||||||
= "*preserved*"
|
= "potentially-calls": [],
|
||||||
= ],
|
= "reachable": true
|
||||||
= "potentially-calls": []
|
|
||||||
= },
|
= },
|
||||||
= "other1": {
|
= "other1": {
|
||||||
= "potentially-called-by": [
|
= "potentially-called-by": [
|
||||||
|
Loading…
Reference in New Issue
Block a user