Commit 2f69ade0 authored by Christian Dietrich's avatar Christian Dietrich

generator: application FSM transformation

The ApplicationFSM pass transforms the ICFG of a task to a finite state
machine. This construction is done by transforming CFG-edges to
FSM-states, and CFG-blocks to FSM-transitions.

In a second step, the CFG-computation blocks are handled as FSM-epsilon
transitions. Then a standard epsilon elimination is done for
subtask-FSM.

Change-Id: I874bc3b17ebaf16b58bb70226e5eef091e5cbbe9
parent a98faa12
from .common import *
from .Analysis import Analysis
from .AtomicBasicBlock import E, S
from .Function import Function
from datastructures.fsm import *
from collections import defaultdict
import logging
class ApplicationFSM(Analysis, GraphObject):
"""The Application FSM Pass transforms the ICFG of every task in the
system to an finite state machine with fewer edges and vertices,
than the ICFG. In this transformation, the possible sequences of
systemcalls coming from an application are preserved.
"""
pass_alias = "app-fsm"
def __init__(self):
Analysis.__init__(self)
GraphObject.__init__(self, "ApplicationFSM", root = True)
def requires(self):
# We require all possible system edges to be contructed
return ["DynamicPriorityAnalysis", "InterruptControlAnalysis"]
def get_edge_filter(self):
return set([E.task_level])
def graph_subobjects(self):
ret = []
for subtask in self.system_graph.subtasks:
wrapper = GraphObjectContainer(str(subtask), color='red')
nodes = {}
for trans in subtask.conf.fsm.transitions:
if not trans.source in nodes:
nodes[trans.source] = Node(None, str(trans.source), color='black')
if not trans.target in nodes:
nodes[trans.target] = Node(None, str(trans.target), color='black')
wrapper.edges.append(Edge(nodes[trans.source],
nodes[trans.target],
label=str(trans.event)))
wrapper.subobjects = nodes.values()
ret.append(wrapper)
return ret
def __to_fsm(self, subtask):
"Transforms the ICFG to an FSM"
ret = FiniteStateMachine()
def icfg_filter(edge):
return edge.isA(self.get_edge_filter())
def block_functor(in_edge, cur_abb):
transitions = []
# ICFG Entry transition
if cur_abb == subtask.entry_abb:
in_edge = None
out_edge = cur_abb.get_outgoing_edges(self.get_edge_filter())[0]
transitions.append(Transition(in_edge, out_edge, cur_abb.definite_after(self.get_edge_filter())))
for in_edge in cur_abb.get_incoming_edges(self.get_edge_filter()):
for out_edge in cur_abb.get_outgoing_edges(self.get_edge_filter()):
# Every Edge becomes a state, every state becomes an edge
transitions.append(Transition(in_edge, out_edge, out_edge.target))
event = Event(cur_abb, transitions)
ret.add_event(event)
# With this depth-first search, we find all reachable
# states. For every block, we add transitions from every
# incoming edge, to every outgoing edge.
dfs(block_functor, icfg_filter, [subtask.entry_abb])
# The initial state is None, since the block_functor is called
# with (None, entry_abb)
ret.initial_state = None
return ret
def __epsilon_elimination(self, fsm):
"""The epsilon elimination pass makes every computation
event/transition to an epsilon transition. With the
standard FSM epslion elimination, we reduce the FSM
size.
"""
epsilon_sets = {}
# Initialize the epsilon sets
for state in fsm.states:
epsilon_sets[state] = set([state])
# Identify the epsilon sets with a fixpoint iteration The
# epsilon set is the set of states, that can be reached from
# one state by using only epsilon transitions. In our case,
# all computation blocks are epsilon transitions.
def is_epsilon_transition(transition):
return trans.event.isA(S.computation)
changed = True
while changed:
changed = False
for trans in fsm.transitions:
if is_epsilon_transition(trans):
epsilon_target = epsilon_sets[trans.target]
if not epsilon_sets[trans.source].issuperset(epsilon_target):
changed = True
epsilon_sets[trans.source] |= epsilon_target
ret = fsm.copy()
# Now that we have the epsilon set, we can construct a new fsm
# from it.
transitions = defaultdict(list)
# Set of reachable states. Initial state is always reachable
reachable = set([fsm.initial_state])
for in_state in fsm.states:
represents_edges = []
for border_state in epsilon_sets[in_state]:
# For every transition from a border state
for trans in fsm.get_outgoing_transitions(border_state):
represents_edges.append(trans)
if is_epsilon_transition(trans):
continue # Epsilon Transitions are ignored
# Copy transition into the new finite state machine
transitions[trans.event] += [Transition(in_state, trans.target, trans.action)]
reachable.add(trans.target)
# Now this state represents more than one CFG edge
ret.state_mapping[in_state] = tuple(represents_edges)
ret.events = []
for event, trans in transitions.items():
# Add only transitions which are reachable, after removing
# the epsilon transitions
trans = [x for x in trans if x.source in reachable]
assert trans, "Transition set for an event should not be of zero-length"
ret.add_event(Event(event, trans))
return ret
def do(self):
for subtask in self.system_graph.subtasks:
app_fsm = self.__to_fsm(subtask)
app_fsm.rename(states=True)
app_fsm = self.__epsilon_elimination(app_fsm)
subtask.conf.fsm = app_fsm
print()
print(app_fsm)
......@@ -24,6 +24,7 @@ from .SystemGraph import SystemGraph
from .SystemSemantic import *
from .SystemStateFlow import *
from .DominanceAnalysis import *
from .ApplicationFSM import *
from .Task import Task
......
from tools import SimpleNamespace
from .sage_finite_state_machine import Transducer, full_group_by, Automaton
class Transition:
def __init__(self, source, target, action):
self._event = None
self.source = source
self.target = target
self.action = action
self.impl = SimpleNamespace()
@property
def event(self):
if self._event:
return self._event.name
def __repr__(self):
return "<Transition(%s) %s -> %s: %s>" %(self.event, self.source, self.target, self.action)
class Event:
def __init__(self, name, transitions):
self.name = name
self.transitions = transitions
for t in transitions:
t._event = self
self.impl = SimpleNamespace()
def add_transition(self, transition):
transition._event = self
self.transitions.append(transition)
def __repr__(self):
return "<Event(%s) %d transitions>" %(self.name, len(self.transitions))
class FiniteStateMachine:
def __init__(self):
self.initial_state = None
# List of Event
self.events = []
# Indirection tables
self.state_mapping = {}
self.event_mapping = {}
self.action_mapping = {}
self.impl = SimpleNamespace()
@property
def states(self):
return self.state_mapping.keys()
@property
def transitions(self):
for ev in self.events:
for t in ev.transitions:
yield t
@property
def actions(self):
return self.action_mapping.keys()
def copy(self):
ret = FiniteStateMachine()
ret.initial_state = self.initial_state
#Copy Mappings
ret.action_mapping = self.action_mapping.copy()
ret.event_mapping = self.event_mapping.copy()
ret.state_mapping = self.state_mapping.copy()
# Generate new transitions and events
for event in self.events:
n_event = Event(event.name, [])
for t in event.transitions:
n_t = Transition(t.source, t.target, t.action)
n_event.add_transition(n_t)
ret.add_event(n_event)
return ret
def add_event(self, event):
self.events.append(event)
if not event.name in self.event_mapping:
self.event_mapping[event.name] = event.name
for transition in event.transitions:
if not transition.source in self.state_mapping:
self.state_mapping[transition.source] = transition.source
if not transition.target in self.state_mapping:
self.state_mapping[transition.target] = transition.target
if not transition.action in self.action_mapping:
self.action_mapping[transition.action] = transition.action
def get_outgoing_transitions(self, state):
for trans in self.transitions:
if trans.source == state:
yield trans
def __rename_events(self, from_to_map):
new_mapping = {}
for event in self.events:
new = from_to_map[event.name]
new_mapping[new] = self.event_mapping[event.name]
event.name = new
self.event_mapping = new_mapping
def __rename_actions(self, from_to_map):
new_mapping = {}
for t in self.transitions:
new = from_to_map[t.action]
new_mapping[new] = self.action_mapping[t.action]
t.action = new
self.action_mapping = new_mapping
def __rename_states(self, from_to_map):
self.initial_state = from_to_map[self.initial_state]
new_mapping = {}
for t in self.transitions:
new = from_to_map[t.source]
new_mapping[new] = self.state_mapping[t.source]
t.source = new
new = from_to_map[t.target]
new_mapping[new] = self.state_mapping[t.target]
t.target = new
self.state_mapping = new_mapping
def rename(self, events = None, states = None, actions = None):
class count_rename:
def __init__(self):
self.__i = 0
def __call__(self, _):
x = self.__i
self.__i += 1
return x
if type(events) == bool and events:
events = count_rename()
if type(states) == bool and states:
states = count_rename()
if type(actions) == bool and actions:
actions = count_rename()
if events:
from_to_map = {}
for event in self.events:
from_to_map[event.name] = events(event.name)
self.__rename_events(from_to_map)
if states:
from_to_map = {}
for t in self.transitions:
if not t.source in from_to_map:
from_to_map[t.source] = states(t.source)
if not t.target in from_to_map:
from_to_map[t.target] = states(t.target)
self.__rename_states(from_to_map)
if actions:
from_to_map = {}
for t in self.transitions:
if not t.action in from_to_map:
from_to_map[t.action] = actions(t.action)
self.__rename_actions(from_to_map)
def to_sage_fsm(self):
actions = []
# What is task at that point
output_map = {}
for t in self.transitions:
actions.append((t.source, t.target, t.event, t.action))
output_map[t.target] = t.action
m = Transducer(actions, initial_states = [self.initial_state])
for state in m.states():
if state.label() in output_map:
state.word_out = [output_map[state.label()]]
return m
@staticmethod
def from_sage_fsm(old_fsm, sage):
events = {}
for transition in sage.transitions():
abb = transition.word_in[0]
if not abb in events:
events[abb] = Event(abb, [])
event = events[abb]
from_state = transition.from_state.label()[0].label()
to_state = transition.to_state.label()[0].label()
action = transition.word_out[0]
event.add_transition(Transition(from_state, to_state, action))
# Assemble a finite state machine
ret = FiniteStateMachine()
for event in events.values():
ret.add_event(event)
ret.initial_state = sage.initial_states()[0].label()[0].label()
ret.action_mapping = old_fsm.action_mapping.copy()
ret.event_mapping = old_fsm.event_mapping.copy()
ret.state_mapping = old_fsm.state_mapping.copy()
return ret
def __str__(self):
ret = []
max_lengths = [0,0,0,0]
table = []
for t in self.transitions:
row = [t.event, t.source, t.target, t.action]
table.append(row)
for i, e in enumerate(row):
max_lengths[i] = max(max_lengths[i], len(str(e)))
for row in table:
# Don't ask
line = "{0!s:<{4}}\t{1!s:<{5}}\t{2!s:<{6}}\t{3!s:<{7}}".format(*(row + max_lengths))
ret.append(line)
return "\n".join(ret)
......@@ -151,6 +151,7 @@ if __name__ == "__main__":
# Task-Level: Dynamic Priority spreading pass
pass_manager.register_analysis(PrioritySpreadingPass())
pass_manager.register_analysis(DynamicPriorityAnalysis())
pass_manager.register_analysis(ApplicationFSM())
# System-Level: Analysis
pass_manager.register_analysis(SystemStateFlow())
......
from generator.analysis.common import *
from generator.analysis import Analysis, SavedStateTransition, S, E
from generator.tools import pairwise
from tools import SimpleNamespace
from collections import namedtuple, defaultdict
from .sage_finite_state_machine import Transducer, full_group_by, Automaton
class Transition:
def __init__(self, source, target, action):
self._event = None
self.source = source
self.target = target
self.action = action
self.impl = SimpleNamespace()
@property
def event(self):
if self._event:
return self._event.name
def __repr__(self):
return "<Transition(%s) %s -> %s: %s>" %(self.event, self.source, self.target, self.action)
class Event:
def __init__(self, name, transitions):
self.name = name
self.transitions = transitions
for t in transitions:
t._event = self
self.impl = SimpleNamespace()
def add_transition(self, transition):
transition._event = self
self.transitions.append(transition)
def __repr__(self):
return "<Event(%s) %d transitions>" %(self.name, len(self.transitions))
class FiniteStateMachine:
def __init__(self):
self.initial_state = None
# List of Event
self.events = []
# Indirection tables
self.state_mapping = {}
self.event_mapping = {}
self.action_mapping = {}
self.impl = SimpleNamespace()
@property
def states(self):
return self.state_mapping.keys()
@property
def transitions(self):
for ev in self.events:
for t in ev.transitions:
yield t
@property
def actions(self):
return self.action_mapping.keys()
def copy(self):
ret = FiniteStateMachine()
ret.initial_state = self.initial_state
#Copy Mappings
ret.action_mapping = self.action_mapping.copy()
ret.event_mapping = self.event_mapping.copy()
ret.state_mapping = self.state_mapping.copy()
# Generate new transitions and events
for event in self.events:
n_event = Event(event.name, [])
for t in event.transitions:
n_t = Transition(t.source, t.target, t.action)
n_event.add_transition(n_t)
ret.add_event(n_event)
return ret
def add_event(self, event):
self.events.append(event)
if not event.name in self.event_mapping:
self.event_mapping[event.name] = event.name
for transition in event.transitions:
if not transition.source in self.state_mapping:
self.state_mapping[transition.source] = transition.source
if not transition.target in self.state_mapping:
self.state_mapping[transition.target] = transition.target
if not transition.action in self.action_mapping:
self.action_mapping[transition.action] = transition.action
def __rename_events(self, from_to_map):
new_mapping = {}
for event in self.events:
new = from_to_map[event.name]
new_mapping[new] = self.event_mapping[event.name]
event.name = new
self.event_mapping = new_mapping
def __rename_actions(self, from_to_map):
new_mapping = {}
for t in self.transitions:
new = from_to_map[t.action]
new_mapping[new] = self.action_mapping[t.action]
t.action = new
self.action_mapping = new_mapping
def __rename_states(self, from_to_map):
self.initial_state = from_to_map[self.initial_state]
new_mapping = {}
for t in self.transitions:
new = from_to_map[t.source]
new_mapping[new] = self.state_mapping[t.source]
t.source = new
new = from_to_map[t.target]
new_mapping[new] = self.state_mapping[t.target]
t.target = new
self.state_mapping = new_mapping
def rename(self, events = None, states = None, actions = None):
class count_rename:
def __init__(self):
self.__i = 0
def __call__(self, _):
x = self.__i
self.__i += 1
return x
if type(events) == bool and events:
events = count_rename()
if type(states) == bool and states:
states = count_rename()
if type(actions) == bool and actions:
actions = count_rename()
if events:
from_to_map = {}
for event in self.events:
from_to_map[event.name] = events(event.name)
self.__rename_events(from_to_map)
if states:
from_to_map = {}
for t in self.transitions:
if not t.source in from_to_map:
from_to_map[t.source] = states(t.source)
if not t.target in from_to_map:
from_to_map[t.target] = states(t.target)
self.__rename_states(from_to_map)
if actions:
from_to_map = {}
for t in self.transitions:
if not t.action in from_to_map:
from_to_map[t.action] = actions(t.action)
self.__rename_actions(from_to_map)
def to_sage_fsm(self):
actions = []
# What is task at that point
output_map = {}
for t in self.transitions:
actions.append((t.source, t.target, t.event, t.action))
output_map[t.target] = t.action
m = Transducer(actions, initial_states = [self.initial_state])
for state in m.states():
if state.label() in output_map:
state.word_out = [output_map[state.label()]]
return m
@staticmethod
def from_sage_fsm(old_fsm, sage):
events = {}
for transition in sage.transitions():
abb = transition.word_in[0]
if not abb in events:
events[abb] = Event(abb, [])
event = events[abb]
from_state = transition.from_state.label()[0].label()
to_state = transition.to_state.label()[0].label()
action = transition.word_out[0]
event.add_transition(Transition(from_state, to_state, action))
# Assemble a finite state machine
ret = FiniteStateMachine()
for event in events.values():
ret.add_event(event)
ret.initial_state = sage.initial_states()[0].label()[0].label()
ret.action_mapping = old_fsm.action_mapping.copy()
ret.event_mapping = old_fsm.event_mapping.copy()
ret.state_mapping = old_fsm.state_mapping.copy()
return ret
def __str__(self):
ret = []
max_lengths = [0,0,0,0]
table = []
for t in self.transitions:
row = [t.event, t.source, t.target, t.action]
table.append(row)
for i, e in enumerate(row):
max_lengths[i] = max(max_lengths[i], len(str(e)))
for row in table:
# Don't ask
line = "{0!s:<{4}}\t{1!s:<{5}}\t{2!s:<{6}}\t{3!s:<{7}}".format(*(row + max_lengths))
ret.append(line)
return "\n".join(ret)
from datastructures.sage_finite_state_machine import Transducer, full_group_by, Automaton
from datastructures.fsm import FiniteStateMachine, Transition, Event