Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/tensorflow/python/autograph/pyct/static_analysis/liveness.py: 24%

113 statements  

« prev     ^ index     » next       coverage.py v7.4.0, created at 2024-01-03 07:57 +0000

1# Copyright 2018 The TensorFlow Authors. All Rights Reserved. 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14# ============================================================================== 

15"""Live variable analysis. 

16 

17See https://en.wikipedia.org/wiki/Live_variable_analysis for a definition of 

18the following idioms: live variable, live in, live out, which are used 

19throughout this file. 

20 

21This analysis attaches the following: 

22 * symbols that are live at the exit of control flow statements 

23 * symbols that are live at the entry of control flow statements 

24 

25Requires activity analysis. 

26""" 

27 

28import gast 

29 

30from tensorflow.python.autograph.pyct import anno 

31from tensorflow.python.autograph.pyct import cfg 

32from tensorflow.python.autograph.pyct import transformer 

33from tensorflow.python.autograph.pyct.static_analysis import annos 

34 

35 

36class Analyzer(cfg.GraphVisitor): 

37 """CFG visitor that performs liveness analysis at statement level.""" 

38 

39 def __init__(self, graph, include_annotations): 

40 super(Analyzer, self).__init__(graph) 

41 self.include_annotations = include_annotations 

42 

43 def init_state(self, _): 

44 return set() 

45 

46 def lamba_check(self, fn_ast_node): 

47 if isinstance(fn_ast_node, gast.Lambda): 

48 # Exception: lambda functions are assumed to be used only in the 

49 # place where they are defined, and not later. 

50 return True 

51 return False 

52 

53 def visit_node(self, node): 

54 prev_live_in = self.in_[node] 

55 

56 if anno.hasanno(node.ast_node, anno.Static.SCOPE): 

57 node_scope = anno.getanno(node.ast_node, anno.Static.SCOPE) 

58 

59 gen = node_scope.read 

60 if not self.include_annotations: 

61 gen -= node_scope.annotations 

62 # TODO(mdan): verify whether composites' parents need to be added. 

63 # E.g. whether x needs to be added if x.y is live. Theoretically the 

64 # activity analysis should have both so that wouldn't be needed. 

65 kill = node_scope.modified | node_scope.deleted 

66 

67 live_out = set() 

68 for n in node.next: 

69 live_out |= self.in_[n] 

70 live_in = gen | (live_out - kill) 

71 

72 reaching_functions = anno.getanno( 

73 node.ast_node, anno.Static.DEFINED_FNS_IN) 

74 for fn_ast_node in reaching_functions: 

75 if self.lamba_check(fn_ast_node): 

76 continue 

77 fn_scope = anno.getanno(fn_ast_node, annos.NodeAnno.ARGS_AND_BODY_SCOPE) 

78 # Any closure of a reaching function definition is conservatively 

79 # considered live. 

80 live_in |= (fn_scope.read - fn_scope.bound) 

81 

82 else: 

83 assert self.can_ignore(node), (node.ast_node, node) 

84 

85 live_out = set() 

86 for n in node.next: 

87 live_out |= self.in_[n] 

88 live_in = live_out 

89 

90 self.in_[node] = live_in 

91 self.out[node] = live_out 

92 

93 # TODO(mdan): Move this to the superclass? 

94 return prev_live_in != live_in 

95 

96 

97class TreeAnnotator(transformer.Base): 

98 """Runs liveness analysis on each of the functions defined in the AST. 

99 

100 If a function defined other local functions, those will have separate CFGs. 

101 However, dataflow analysis needs to tie up these CFGs to properly emulate the 

102 effect of closures. In the case of liveness, the parent function's live 

103 variables must account for the variables that are live at the entry of each 

104 subfunction. For example: 

105 

106 def foo(): 

107 # baz is live from here on 

108 def bar(): 

109 print(baz) 

110 

111 This analyzer runs liveness analysis on each individual function, accounting 

112 for the effect above. 

113 """ 

114 

115 def __init__(self, source_info, graphs, include_annotations): 

116 super(TreeAnnotator, self).__init__(source_info) 

117 self.include_annotations = include_annotations 

118 self.allow_skips = False 

119 self.graphs = graphs 

120 self.current_analyzer = None 

121 

122 def visit(self, node): 

123 node = super(TreeAnnotator, self).visit(node) 

124 if (self.current_analyzer is not None and 

125 isinstance(node, gast.stmt) and 

126 node in self.current_analyzer.graph.index): 

127 cfg_node = self.current_analyzer.graph.index[node] 

128 anno.setanno(node, anno.Static.LIVE_VARS_IN, 

129 frozenset(self.current_analyzer.in_[cfg_node])) 

130 return node 

131 

132 def _analyze_function(self, node, is_lambda): 

133 parent_analyzer = self.current_analyzer 

134 

135 analyzer = Analyzer(self.graphs[node], self.include_annotations) 

136 analyzer.visit_reverse() 

137 self.current_analyzer = analyzer 

138 node = self.generic_visit(node) 

139 

140 self.current_analyzer = parent_analyzer 

141 return node 

142 

143 def visit_Lambda(self, node): 

144 return self._analyze_function(node, is_lambda=True) 

145 

146 def visit_FunctionDef(self, node): 

147 return self._analyze_function(node, is_lambda=False) 

148 

149 def _block_statement_live_out(self, node): 

150 successors = self.current_analyzer.graph.stmt_next[node] 

151 stmt_live_out = set() 

152 for s in successors: 

153 stmt_live_out.update(self.current_analyzer.in_[s]) 

154 anno.setanno(node, anno.Static.LIVE_VARS_OUT, frozenset(stmt_live_out)) 

155 return node 

156 

157 def _block_statement_live_in(self, node, entry_node): 

158 if entry_node in self.current_analyzer.graph.index: 

159 cfg_node = self.current_analyzer.graph.index[entry_node] 

160 stmt_live_in = frozenset(self.current_analyzer.in_[cfg_node]) 

161 else: 

162 assert anno.hasanno(entry_node, anno.Static.LIVE_VARS_IN), ( 

163 'If not matching a CFG node, must be a block statement:' 

164 ' {}'.format(entry_node)) 

165 stmt_live_in = anno.getanno(entry_node, anno.Static.LIVE_VARS_IN) 

166 anno.setanno(node, anno.Static.LIVE_VARS_IN, stmt_live_in) 

167 return node 

168 

169 def visit_If(self, node): 

170 node = self.generic_visit(node) 

171 node = self._block_statement_live_out(node) 

172 return self._block_statement_live_in(node, node.test) 

173 

174 def visit_For(self, node): 

175 node = self.generic_visit(node) 

176 node = self._block_statement_live_out(node) 

177 return self._block_statement_live_in(node, node.iter) 

178 

179 def visit_While(self, node): 

180 node = self.generic_visit(node) 

181 node = self._block_statement_live_out(node) 

182 return self._block_statement_live_in(node, node.test) 

183 

184 def visit_Try(self, node): 

185 node = self.generic_visit(node) 

186 node = self._block_statement_live_out(node) 

187 return self._block_statement_live_in(node, node.body[0]) 

188 

189 def visit_ExceptHandler(self, node): 

190 node = self.generic_visit(node) 

191 node = self._block_statement_live_out(node) 

192 return self._block_statement_live_in(node, node.body[0]) 

193 

194 def visit_With(self, node): 

195 node = self.generic_visit(node) 

196 return self._block_statement_live_in(node, node.items[0]) 

197 

198 def visit_Expr(self, node): 

199 node = self.generic_visit(node) 

200 cfg_node = self.current_analyzer.graph.index[node] 

201 anno.setanno(node, anno.Static.LIVE_VARS_OUT, 

202 frozenset(self.current_analyzer.out[cfg_node])) 

203 return node 

204 

205 

206# TODO(mdan): Investigate the possibility of removing include_annotations. 

207def resolve(node, source_info, graphs, include_annotations=True): 

208 """Resolves the live symbols at the exit of control flow statements. 

209 

210 Args: 

211 node: ast.AST 

212 source_info: transformer.SourceInfo 

213 graphs: Dict[ast.FunctionDef, cfg.Graph] 

214 include_annotations: Bool, whether type annotations should be included in 

215 the analysis. 

216 Returns: 

217 ast.AST 

218 """ 

219 node = TreeAnnotator(source_info, graphs, include_annotations).visit(node) 

220 return node