Commit 7f0d8b94 authored by Yuki Shiino's avatar Yuki Shiino Committed by Commit Bot

bind-gen: Improve SymbolScopeNode._insert_symbol_definition

Re-implements SymbolScopeNode._insert_symbol_definition to
cover more general combinations of SequenceNode and
SymbolScopeNode.

Bug: 839389
Change-Id: I3a9163e2a47795e5e5035edf7021ef1d1d715432
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1948685Reviewed-by: default avatarHitoshi Yoshida <peria@chromium.org>
Commit-Queue: Yuki Shiino <yukishiino@chromium.org>
Cr-Commit-Position: refs/heads/master@{#721794}
parent 29001e74
...@@ -64,6 +64,33 @@ class CodeNode(object): ...@@ -64,6 +64,33 @@ class CodeNode(object):
# SymbolDefinitionNodes at SymbolScopeNode. # SymbolDefinitionNodes at SymbolScopeNode.
self.undefined_code_symbols = [] self.undefined_code_symbols = []
# Dict from a SymbolNode to a set of tuples of SymbolScopeNodes
# where the symbol was used.
#
# For example, given a code symbol |x|, the following code
# structure:
# { // Scope1
# { // Scope2A
# { // Scope3
# x; // [1]
# }
# x; // [2]
# }
# x; // [3]
# { // Scope2B
# x; // [4]
# }
# x; // [5]
# }
# is translated into an entry of the dict below:
# set([
# (Scope1), # [3], [5]
# (Scope1, Scope2A), # [2]
# (Scope1, Scope2A, Scope3), # [1]
# (Scope1, Scope2B), # [4]
# ])
self.undefined_code_symbols_scope_chains = {}
_gensym_seq_id = 0 _gensym_seq_id = 0
@classmethod @classmethod
...@@ -309,25 +336,17 @@ class CodeNode(object): ...@@ -309,25 +336,17 @@ class CodeNode(object):
return self.outer.is_code_symbol_registered(symbol_node) return self.outer.is_code_symbol_registered(symbol_node)
return False return False
def on_undefined_code_symbol_found(self, symbol_node): def on_undefined_code_symbol_found(self, symbol_node, symbol_scope_chain):
"""Receives a report of use of an undefined symbol node.""" """Receives a report of use of an undefined symbol node."""
assert isinstance(symbol_node, SymbolNode) assert isinstance(symbol_node, SymbolNode)
assert isinstance(symbol_scope_chain, tuple)
assert all(
isinstance(scope, SymbolScopeNode) for scope in symbol_scope_chain)
state = self.current_render_state state = self.current_render_state
if symbol_node not in state.undefined_code_symbols: if symbol_node not in state.undefined_code_symbols:
state.undefined_code_symbols.append(symbol_node) state.undefined_code_symbols.append(symbol_node)
state.undefined_code_symbols_scope_chains.setdefault(
def likeliness_of_undefined_code_symbol_usage(self, symbol_node): symbol_node, set()).add(symbol_scope_chain)
"""
Returns how much likely the given |symbol_node| will be used inside this
code node. The likeliness is relative to the closest outer
SymbolScopeNode.
"""
assert isinstance(symbol_node, SymbolNode)
state = self.last_render_state
if symbol_node in state.undefined_code_symbols:
return Likeliness.ALWAYS
else:
return Likeliness.NEVER
class LiteralNode(CodeNode): class LiteralNode(CodeNode):
...@@ -500,77 +519,73 @@ class SymbolScopeNode(SequenceNode): ...@@ -500,77 +519,73 @@ class SymbolScopeNode(SequenceNode):
separator=separator, separator=separator,
separator_last=separator_last) separator_last=separator_last)
self._likeliness = Likeliness.ALWAYS
self._registered_code_symbols = set() self._registered_code_symbols = set()
def _render(self, renderer, last_render_state): def _render(self, renderer, last_render_state):
for symbol_node in last_render_state.undefined_code_symbols: for symbol_node in last_render_state.undefined_code_symbols:
if (self.is_code_symbol_registered(symbol_node) if (self.is_code_symbol_registered(symbol_node)
and not self.is_code_symbol_defined(symbol_node)): and not self.is_code_symbol_defined(symbol_node)):
self._insert_symbol_definition(symbol_node) self._insert_symbol_definition(symbol_node, last_render_state)
return super(SymbolScopeNode, self)._render( return super(SymbolScopeNode, self)._render(
renderer=renderer, last_render_state=last_render_state) renderer=renderer, last_render_state=last_render_state)
def _insert_symbol_definition(self, symbol_node): def _insert_symbol_definition(self, symbol_node, last_render_state):
def likeliness_at(code_node): def count_by_likeliness(render_state):
return code_node.likeliness_of_undefined_code_symbol_usage( counts = {
symbol_node) Likeliness.UNLIKELY: 0,
Likeliness.LIKELY: 0,
def count_by_likeliness(symbol_scope_node, likeliness_cap, counts): Likeliness.ALWAYS: 0,
for node in symbol_scope_node: }
if isinstance(node, SymbolScopeNode):
likeliness_cap = count_by_likeliness( scope_chains = (render_state.undefined_code_symbols_scope_chains.
node, likeliness_cap, counts) get(symbol_node))
continue if not scope_chains:
return counts
likeliness = min(likeliness_cap, likeliness_at(node))
counts.setdefault(likeliness, 0) self_index = iter(scope_chains).next().index(self)
counts[likeliness] += 1 scope_chains = map(
lambda scope_chain: scope_chain[self_index + 1:], scope_chains)
# Count conditional use in exit branches. We'll delegate the for scope_chain in scope_chains:
# symbol definition to the exit branches if appropriate. if not scope_chain:
if (isinstance(node, ConditionalExitNode) counts[Likeliness.ALWAYS] += 1
and Likeliness.NEVER < likeliness_at(node) else:
and likeliness_at(node) < Likeliness.ALWAYS): likeliness = min(
counts.setdefault(EXIT_BRANCH_COUNT_KEY, 0) map(lambda scope: scope.likeliness, scope_chain))
counts[EXIT_BRANCH_COUNT_KEY] += 1 counts[likeliness] += 1
return counts
if isinstance(node, LikelyExitNode):
likeliness_cap = min(likeliness_cap, Likeliness.UNLIKELY) def likeliness_at(render_state):
if isinstance(node, UnlikelyExitNode): counts = count_by_likeliness(render_state)
likeliness_cap = min(likeliness_cap, Likeliness.LIKELY) for likeliness in (Likeliness.ALWAYS, Likeliness.LIKELY,
Likeliness.UNLIKELY):
return likeliness_cap if counts[likeliness] > 0:
return likeliness
def insert_right_before_first_use(symbol_scope_node): return Likeliness.NEVER
for index, node in enumerate(symbol_scope_node):
if isinstance(node, SequenceNode): def insert_before_threshold(sequence_node, threshold):
did_insert = insert_right_before_first_use(node) for index, node in enumerate(sequence_node):
if (isinstance(node, SequenceNode)
and not isinstance(node, SymbolScopeNode)):
did_insert = insert_before_threshold(node, threshold)
if did_insert: if did_insert:
return True return True
elif Likeliness.NEVER < likeliness_at(node): elif likeliness_at(node.last_render_state) >= threshold:
symbol_scope_node.insert( sequence_node.insert(index,
index, symbol_node.create_definition_node()) symbol_node.create_definition_node())
return True return True
return False return False
counts = {} counts = count_by_likeliness(last_render_state)
EXIT_BRANCH_COUNT_KEY = "exit_branch" if counts[Likeliness.ALWAYS] >= 1:
count_by_likeliness(self, Likeliness.ALWAYS, counts) did_insert = insert_before_threshold(self, Likeliness.UNLIKELY)
non_exit_uses = sum([ assert did_insert
counts.get(Likeliness.UNLIKELY, 0), elif counts[Likeliness.LIKELY] >= 2:
counts.get(Likeliness.LIKELY, 0), did_insert = insert_before_threshold(self, Likeliness.LIKELY)
counts.get(Likeliness.ALWAYS, 0), assert did_insert
-counts.get(EXIT_BRANCH_COUNT_KEY, 0) else:
]) pass # Do nothing and let descendant SymbolScopeNodes do the work.
assert non_exit_uses >= 0
if non_exit_uses == 0:
# Do nothing and let descendant SymbolScopeNodes do the work.
return
did_insert = insert_right_before_first_use(self)
assert did_insert
def is_code_symbol_registered(self, symbol_node): def is_code_symbol_registered(self, symbol_node):
if symbol_node in self._registered_code_symbols: if symbol_node in self._registered_code_symbols:
...@@ -588,6 +603,19 @@ class SymbolScopeNode(SequenceNode): ...@@ -588,6 +603,19 @@ class SymbolScopeNode(SequenceNode):
for symbol_node in symbol_nodes: for symbol_node in symbol_nodes:
self.register_code_symbol(symbol_node) self.register_code_symbol(symbol_node)
@property
def likeliness(self):
"""
Returns how much likely that this SymbolScopeNode will be executed in
runtime. The likeliness is relative to the closest outer
SymbolScopeNode.
"""
return self._likeliness
def set_likeliness(self, likeliness):
assert isinstance(likeliness, Likeliness.Level)
self._likeliness = likeliness
class SymbolNode(CodeNode): class SymbolNode(CodeNode):
""" """
...@@ -633,11 +661,14 @@ class SymbolNode(CodeNode): ...@@ -633,11 +661,14 @@ class SymbolNode(CodeNode):
self._definition_constructor = definition_constructor self._definition_constructor = definition_constructor
def _render(self, renderer, last_render_state): def _render(self, renderer, last_render_state):
for caller in renderer.callers: symbol_scope_chain = tuple(
assert isinstance(caller, CodeNode) filter(lambda node: isinstance(node, SymbolScopeNode),
renderer.callers_from_first_to_last))
for caller in renderer.callers_from_last_to_first:
if caller.is_code_symbol_defined(self): if caller.is_code_symbol_defined(self):
break break
caller.on_undefined_code_symbol_found(self) caller.on_undefined_code_symbol_found(self, symbol_scope_chain)
return self.name return self.name
...@@ -745,18 +776,7 @@ if (${{{conditional}}}) {{ ...@@ -745,18 +776,7 @@ if (${{{conditional}}}) {{
self._cond_node = cond self._cond_node = cond
self._body_node = body self._body_node = body
self._body_likeliness = body_likeliness self._body_node.set_likeliness(body_likeliness)
def likeliness_of_undefined_code_symbol_usage(self, symbol_node):
assert isinstance(symbol_node, SymbolNode)
def likeliness_at(code_node):
return code_node.likeliness_of_undefined_code_symbol_usage(
symbol_node)
return max(
likeliness_at(self._cond_node),
min(self._body_likeliness, likeliness_at(self._body_node)))
class LikelyExitNode(ConditionalExitNode): class LikelyExitNode(ConditionalExitNode):
......
...@@ -85,7 +85,15 @@ class MakoRenderer(object): ...@@ -85,7 +85,15 @@ class MakoRenderer(object):
return text return text
@property @property
def callers(self): def callers_from_first_to_last(self):
"""
Returns the callers of this renderer in the order from the first caller
to the last caller.
"""
return iter(self._caller_stack)
@property
def callers_from_last_to_first(self):
""" """
Returns the callers of this renderer in the order from the last caller Returns the callers of this renderer in the order from the last caller
to the first caller. to the first caller.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment