tf_1.8_xla_doc
tuple_points_to_analysis.h
Go to the documentation of this file.
1 
3 /* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
4 
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8 
9  http://www.apache.org/licenses/LICENSE-2.0
10 
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16 ==============================================================================*/
17 
18 #ifndef TENSORFLOW_COMPILER_XLA_SERVICE_TUPLE_POINTS_TO_ANALYSIS_H_
19 #define TENSORFLOW_COMPILER_XLA_SERVICE_TUPLE_POINTS_TO_ANALYSIS_H_
20 
21 #include <stddef.h>
22 #include <iosfwd>
23 #include <memory>
24 #include <set>
25 #include <string>
26 #include <vector>
27 
28 #include "tensorflow/compiler/xla/service/dfs_hlo_visitor_with_default.h"
31 #include "tensorflow/compiler/xla/service/logical_buffer.h"
32 #include "tensorflow/compiler/xla/service/logical_buffer_analysis.h"
33 #include "tensorflow/compiler/xla/shape_tree.h"
34 #include "tensorflow/compiler/xla/statusor.h"
35 #include "tensorflow/compiler/xla/types.h"
36 #include "tensorflow/compiler/xla/xla_data.pb.h"
37 #include "tensorflow/core/lib/core/status.h"
38 #include "tensorflow/core/lib/gtl/array_slice.h"
39 #include "tensorflow/core/lib/gtl/compactptrset.h"
40 #include "tensorflow/core/lib/gtl/flatmap.h"
41 #include "tensorflow/core/lib/gtl/flatset.h"
42 #include "tensorflow/core/platform/macros.h"
43 #include "tensorflow/core/platform/types.h"
44 
45 namespace xla {
46 
47 // A class describing the source(s) of the Buffer(s) contained in the output of
48 // a particular HLO instruction. The structure of PointsToSet mirrors the
49 // structure of the instruction's shape, which may be an arbitrary tree (eg, a
50 // nested tuple). Each node in this tree corresponds to a single buffer in the
51 // instruction's output and contains the set of Buffers which might define
52 // the corresponding buffer.
53 class PointsToSet {
54  public:
55  // Construct our ShapeTree with a pointer rather than a reference to a Shape
56  // because this is very hot code, and copying (and then destroying) all these
57  // Shapes is slow.
58  explicit PointsToSet(const Shape* shape) : tree_(shape) {}
59 
60  // Returns true if any points-to sets for any subshape element is not a
61  // singleton.
62  bool IsAmbiguous() const;
63 
64  // Returns true if no LogicalBuffer appears in more than one points-to set of
65  // the shape nodes.
66  bool IsDistinct() const;
67 
68  // Returns the total number of different LogicalBuffers contained in this
69  // object. This is equal to CreateFlattenedSet().size().
70  size_t size() const;
71 
72  // Creates a set containing the union of all LogicalBuffers contained in the
73  // PointsToSet.
74  using BufferSet = tensorflow::gtl::CompactPointerSet<const LogicalBuffer*>;
75  BufferSet CreateFlattenedSet() const;
76 
77  // Returns true if the given buffer is in the points-to set at the given
78  // index.
79  bool ContainsBufferAtIndex(const LogicalBuffer& buffer,
80  const ShapeIndex& index) const;
81 
82  // Returns true if the given buffer is in the points-to set at any index.
83  bool ContainsBuffer(const LogicalBuffer& buffer) const;
84 
85  // Adds the given buffer to the points-to set at the given index. This is a
86  // nop if the buffer already is in the set at that index.
87  void AddPointedToBuffer(const LogicalBuffer& buffer, const ShapeIndex& index);
88 
89  // For the subshape at the given index (where index is defined as in
90  // ShapeUtil::GetSubshape) this method returns the set of HLO instructions
91  // which may produce the tuple subshape at that index. For example, given:
92  //
93  // %tuple1 = tuple(...)
94  // %tuple2 = tuple(...)
95  // %select = select(%tuple1, %tuple2)
96  // %nested_tuple = tuple(%select, %tuple1)
97  //
98  // These are the values for tuple_sources() for the PointsToSet of
99  // %nested_tuple:
100  //
101  // tuple_sources({}) = {%nested_tuple}
102  // tuple_sources({0}) = {%tuple1, %tuple2}
103  // tuple_sources({1}) = {%tuple1}
104  //
105  // tuple_sources() at the index of an array shape (not a tuple) returns the
106  // empty set. The instructions in the set returned by tuple_sources
107  // necessarily are either Tuple instructions, constants, or parameters.
108  using SourceSet = tensorflow::gtl::CompactPointerSet<HloInstruction*>;
109  const SourceSet& tuple_sources(const ShapeIndex& index) const;
110 
111  // Add a tuple source instruction for the given index.
112  void add_tuple_source(const ShapeIndex& index, HloInstruction* tuple);
113 
114  using BufferList = tensorflow::gtl::InlinedVector<const LogicalBuffer*, 1>;
115 
116  // Return the list of logical buffers for the subshape at index.
117  const BufferList& element(const ShapeIndex& index) const {
118  return tree_.element(index).buffers;
119  }
120  BufferList* mutable_element(const ShapeIndex& index) {
121  return &tree_.mutable_element(index)->buffers;
122  }
123 
124  // Call fn(index, buflist) for every subshape index.
125  template <typename Fn>
126  void ForEachElement(const Fn& fn) const {
127  tree_.ForEachElement([&fn](const ShapeIndex& index, const Elem& elem) {
128  fn(index, elem.buffers);
129  });
130  }
131  template <typename Fn>
132  void ForEachMutableElement(const Fn& fn) {
133  tree_.ForEachMutableElement([&fn](const ShapeIndex& index, Elem* elem) {
134  fn(index, &elem->buffers);
135  });
136  }
137  template <typename Fn>
138  Status ForEachElementWithStatus(const Fn& fn) const {
139  return tree_.ForEachElementWithStatus(
140  [&fn](const ShapeIndex& index, const Elem& elem) {
141  return fn(index, elem.buffers);
142  });
143  }
144 
145  private:
146  struct Elem {
147  BufferList buffers;
148  SourceSet tuple_sources;
149  };
150  ShapeTree<Elem> tree_;
151 
152  // PointsToSet contains references (const LogicalBuffer*) to elements within
153  // TuplePointsToAnalysis, so disable copying.
154  TF_DISALLOW_COPY_AND_ASSIGN(PointsToSet);
155 };
156 
157 // This class describes a particular subshape in a computation (instruction and
158 // shape index) and the logical buffer which may be a source of the subshape
159 // value.
160 class BufferAlias {
161  public:
162  BufferAlias(HloInstruction* instruction, const ShapeIndex& index)
163  : instruction_(instruction), index_(index) {}
164 
165  // Return the instruction/index of the subshape.
166  HloInstruction* instruction() const { return instruction_; }
167  const ShapeIndex& index() const { return index_; }
168 
169  bool operator==(const BufferAlias& other) const {
170  return instruction_ == other.instruction_ && index_ == other.index_;
171  }
172  bool operator!=(const BufferAlias& other) const { return !(*this == other); }
173 
174  string ToString() const;
175 
176  private:
177  HloInstruction* instruction_;
178  ShapeIndex index_;
179 };
180 
181 std::ostream& operator<<(std::ostream& out, const BufferAlias& buffer_alias);
182 
188 class TuplePointsToAnalysis : public DfsHloVisitorWithDefault {
189  public:
190  // Runs points-to analysis on 'module'.
191  static StatusOr<std::unique_ptr<TuplePointsToAnalysis>> Run(
192  const HloModule* module);
193 
194  // Return the points-to set of an instruction. This describes the potential
195  // sources of each buffer in the instruction's output.
196  const PointsToSet& GetPointsToSet(
197  const HloInstruction* hlo_instruction) const;
198 
199  // Returns the logical buffer with the given ID.
200  const LogicalBuffer& GetBuffer(LogicalBuffer::Id id) const;
201 
202  // Returns the buffer defined at the given instruction and index. An error is
203  // returned if no buffer is defined at that point.
204  StatusOr<const LogicalBuffer*> GetBufferDefinedAt(
205  const HloInstruction* instruction, const ShapeIndex& index) const;
206 
207  // Return a (possibly empty) vector containing all BufferAliases of the given
208  // logical buffer The buffer alias set is the inverse of the points-to set.
209  // That is, LogicalBuffer B is in the points-to set of instruction I at index
210  // N iff instruction I, index N is a BufferAlias of B.
211  using BufferAliasVector = tensorflow::gtl::InlinedVector<BufferAlias, 1>;
212  const BufferAliasVector& GetBufferAliases(const LogicalBuffer& buffer) const;
213 
214  // Returns the number of logical buffers in the module
215  LogicalBuffer::Id num_logical_buffers() const {
216  return logical_buffer_analysis_->num_logical_buffers();
217  }
218 
219  // Return a the logical buffer with id "id" in the module. Iteration
220  // over all logical buffers is usually done with something like:
221  //
222  // for (LogicalBuffer:Id id = 0; id < points_to.num_logical_buffers(); id++){
223  // const auto& buffer = points_to.logical_buffer(id);
224  // ... do something with buffer ...
225  // }
226  LogicalBuffer& logical_buffer(LogicalBuffer::Id id) const {
227  return logical_buffer_analysis_->GetBuffer(id);
228  }
229 
230  // Returns a vector of buffers that the instruction produces. Most
231  // instructions produce a single buffer (the top-level buffer), some produce
232  // no buffers (eg bitcast), and some produce more than one buffer (eg,
233  // tuple-shaped parameters).
234  using BufferDefinitionVector =
235  tensorflow::gtl::InlinedVector<const LogicalBuffer*, 1>;
236  const BufferDefinitionVector& GetBuffersDefinedByInstruction(
237  const HloInstruction* instruction) const;
238 
239  // Returns true if the given instruction defines a buffer at the given index.
240  bool InstructionDefinesBufferAtIndex(const HloInstruction* instruction,
241  const ShapeIndex& index) const;
242 
243  // Returns an OK status if the given buffer is defined by instruction
244  // 'buffer.instruction()' at index 'buffer.index()' and if the given buffer
245  // matches the TuplePointsToAnalysis' LogicalBuffer with 'buffer.id'. Returns
246  // an FailedPrecondition error status otherwise. An example of a LogicalBuffer
247  // which is not defined is a tuple element in a Tuple instruction. In this
248  // case, the Tuple instruction does not define the LogicalBuffer, rather that
249  // index aliases one of its operands.
250  Status VerifyBuffer(const LogicalBuffer& buffer) const;
251 
252  Status DefaultAction(HloInstruction* hlo_instruction) override;
253  Status HandleTuple(HloInstruction* tuple) override;
254  Status HandleGetTupleElement(HloInstruction* get_tuple_element) override;
255  Status HandleBitcast(HloInstruction* bitcast) override;
256  Status HandleSlice(HloInstruction* slice) override;
257  Status HandleCopy(HloInstruction* copy) override;
258  Status HandleRecvDone(HloInstruction* recv_done) override;
259  Status HandleSend(HloInstruction* send) override;
260  Status HandleSelect(HloInstruction* select) override;
261 
262  string ToString() const;
263 
264  private:
265  explicit TuplePointsToAnalysis(
266  const HloModule* module,
267  std::unique_ptr<LogicalBufferAnalysis> logical_buffer_analysis)
268  : module_(module),
269  logical_buffer_analysis_(std::move(logical_buffer_analysis)) {}
270 
271  // Perform the analysis. Should be called immediately after constructing the
272  // object and before calling GetPointsToSet.
273  Status Analyze();
274 
275  // Populates instruction-defined buffers and aliases for each instruction
276  // in 'instructions'.
277  Status PopulateDefinedBuffersAndAliases(const decltype(
278  std::declval<HloComputation>().instructions())& instructions);
279 
280  // Creates an empty PointsToSet in the points_to_ map for the given
281  // instruction.
282  PointsToSet& CreateEmptyPointsToSet(const HloInstruction* instruction);
283 
284  // Creates a PointsToSet in the points_to_ map for 'instruction' which is a
285  // copy of the existing PointsToSet for 'src'.
286  PointsToSet& CreateCopiedPointsToSet(const HloInstruction* instruction,
287  const HloInstruction* src);
288 
289  // Adds the buffers defined by the given instruction to the given vector.
290  Status GatherBuffersDefinedByInstruction(const HloInstruction* instruction,
291  BufferDefinitionVector* buffers);
292 
293  // Print points-to set for 'instruction' to 'output'.
294  void InstructionToString(const HloInstruction* instruction,
295  string* output) const;
296 
297  // Information kept per instruction
298  struct PerInstruction {
299  std::unique_ptr<PointsToSet> points_to_set;
300  // Empircally, ~92% of instructions have 1
301  // instruction_defined_buffer, and 99% have 0 or 1
302  BufferDefinitionVector instruction_defined_buffers;
303  };
304 
305  const PerInstruction* PerInst(const HloInstruction* inst) const {
306  int id = inst->unique_id();
307  DCHECK_GE(id, 0);
308  DCHECK_LT(id, per_instruction_.size());
309  return &per_instruction_[id];
310  }
311  PerInstruction* PerInst(const HloInstruction* inst) {
312  int id = inst->unique_id();
313  DCHECK_GE(id, 0);
314  DCHECK_LT(id, per_instruction_.size());
315  return &per_instruction_[id];
316  }
317 
318  // The module this analysis is performed on.
319  const HloModule* module_;
320 
321  // The logical buffers for this module.
322  const std::unique_ptr<LogicalBufferAnalysis> logical_buffer_analysis_;
323 
324  // A map from instruction->unique_id() to
325  std::vector<PerInstruction> per_instruction_;
326 
327  // A map from LogicalBuffer->id() to alias information about that logical
328  // buffer
329  std::vector<BufferAliasVector> logical_buffer_aliases_;
330 
331  TF_DISALLOW_COPY_AND_ASSIGN(TuplePointsToAnalysis);
332 };
333 
334 } // namespace xla
335 
336 #endif // TENSORFLOW_COMPILER_XLA_SERVICE_TUPLE_POINTS_TO_ANALYSIS_H_
static StatusOr< std::unique_ptr< TuplePointsToAnalysis > > Run(const HloModule *module)
Definition: tuple_points_to_analysis.cc:149
Definition: tuple_points_to_analysis.h:188
Definition: hlo_instruction.h:165
namespace for xla
Definition: client_library.cc:26
Definition: hlo_module.h:52