mirror of
https://github.com/catseye/SixtyPical.git
synced 2024-11-27 20:52:43 +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
|
||||
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.
|
||||
|
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
|
||||
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
|
||||
--------------
|
||||
|
||||
|
@ -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
|
||||
|
@ -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": [
|
||||
|
Loading…
Reference in New Issue
Block a user