• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2017 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 
16 // Generally useful utility functions that are common to (not specific to any
17 // given part of) the XLA code base.
18 
19 #ifndef TENSORFLOW_COMPILER_XLA_UTIL_H_
20 #define TENSORFLOW_COMPILER_XLA_UTIL_H_
21 
22 #include <algorithm>
23 #include <string>
24 #include <type_traits>
25 #include <vector>
26 
27 #include "absl/algorithm/container.h"
28 #include "absl/base/thread_annotations.h"
29 #include "absl/container/inlined_vector.h"
30 #include "absl/strings/str_cat.h"
31 #include "absl/strings/str_format.h"
32 #include "absl/strings/string_view.h"
33 #include "absl/types/span.h"
34 #include "tensorflow/compiler/xla/status.h"
35 #include "tensorflow/compiler/xla/status_macros.h"
36 #include "tensorflow/compiler/xla/types.h"
37 #include "tensorflow/compiler/xla/xla_data.pb.h"
38 #include "tensorflow/core/lib/core/errors.h"
39 #include "tensorflow/core/lib/core/status.h"
40 #include "tensorflow/core/lib/math/math_util.h"
41 #include "tensorflow/core/lib/strings/numbers.h"
42 #include "tensorflow/core/platform/logging.h"
43 #include "tensorflow/core/platform/macros.h"
44 #include "tensorflow/core/platform/mutex.h"
45 #include "tensorflow/core/platform/protobuf.h"
46 #include "tensorflow/core/platform/types.h"
47 
48 namespace xla {
49 
50 // Logs the provided status message with a backtrace.
51 //
52 // For use by Status-factories, logs a backtrace at the point where the status
53 // is created, such that we can use --vmodule=util=1 to see all status
54 // creation backtraces.
55 Status WithLogBacktrace(const Status& status);
56 
57 // Ranks greater than 8 are very rare, so use InlinedVector<int64, 8> to store
58 // the bounds and indices. And for the rare cases of ranks greater than 8,
59 // the InlinedVector will just behave like an std::vector<> and allocate the
60 // memory to store its values.
61 static constexpr int kInlineRank = 8;
62 using DimensionVector = absl::InlinedVector<int64, kInlineRank>;
63 
64 // RAII timer that logs with a given label the wall clock time duration in human
65 // readable form. This differs from base's ElapsedTimer primarily in that it
66 // spits out the human-readable duration form.
67 //
68 // Keeps track of global maximum and cumulative times across all invocations.
69 //
70 // By default, the timing traces are only printed at VLOG(1) and above:
71 //
72 //   XLA_SCOPED_LOGGING_TIMER("fooing bar");  // nop if !VLOG_IS_ON(1).
73 //
74 // but you can control this via:
75 //
76 //   XLA_SCOPED_LOGGING_TIMER_LEVEL("fooing bar", 2);  // nop if !VLOG_IS_ON(2)
77 //
78 #define XLA_SCOPED_LOGGING_TIMER(label) \
79   XLA_SCOPED_LOGGING_TIMER_HELPER(label, 1, __COUNTER__)
80 #define XLA_SCOPED_LOGGING_TIMER_LEVEL(label, level) \
81   XLA_SCOPED_LOGGING_TIMER_HELPER(label, level, __COUNTER__)
82 
83 // Helper for implementing macros above.  Do not use directly.
84 //
85 // Forces the evaluation of "counter", which we expect is equal to __COUNTER__.
86 #define XLA_SCOPED_LOGGING_TIMER_HELPER(label, level, counter) \
87   XLA_SCOPED_LOGGING_TIMER_HELPER2(label, level, counter)
88 
89 // Helper for macros above.  Don't use directly.
90 #define XLA_SCOPED_LOGGING_TIMER_HELPER2(label, level, counter)      \
91   static ::xla::TimerStats XLA_TimerStats##counter;                  \
92   ::xla::ScopedLoggingTimer XLA_ScopedLoggingTimerInstance##counter( \
93       label, /*enabled=*/VLOG_IS_ON(level), __FILE__, __LINE__,      \
94       &XLA_TimerStats##counter);
95 
96 struct TimerStats {
97   tensorflow::mutex stats_mutex;
98   double cumulative_secs ABSL_GUARDED_BY(stats_mutex) = 0;
99   double max_secs ABSL_GUARDED_BY(stats_mutex) = 0;
100   uint64 times_called ABSL_GUARDED_BY(stats_mutex) = 0;
101 };
102 
103 // RAII timer for XLA_SCOPED_LOGGING_TIMER and XLA_SCOPED_LOGGING_TIMER_LEVEL
104 // macros above.  Recommended usage is via the macros so you don't have to give
105 // the timer a name or worry about calling VLOG_IS_ON yourself.
106 class ScopedLoggingTimer {
107  public:
108   // label: Label to display for logging.
109   // enabled: Whether this timer should do anything at all.
110   // file: Filename to display in logging.
111   // line: Line number to display in logging.
112   // `timer_stats`: unowned non-null pointer which is used to populate the
113   // global timer statistics.
114   ScopedLoggingTimer(const std::string& label, bool enabled, const char* file,
115                      int line, TimerStats* timer_stats);
116 
117   // Stop the timer and log the tracked time. Timer is disabled after this
118   // function is called.
119   void StopAndLog();
120 
121   ~ScopedLoggingTimer();
122 
123  private:
124   bool enabled_;
125   const char* file_;
126   int line_;
127   string label_;
128   uint64 start_micros_;
129   TimerStats* timer_stats_;
130 };
131 
132 // Given a vector<T>, returns a Span<char> that points at its
133 // internals.
134 //
135 // Warning: if the vector is updated its storage pointer may change, so use this
136 // with caution (ideally in limited scopes with temporary lifetimes).
137 template <typename T>
MutableByteSlice(std::vector<T> * v)138 absl::Span<uint8> MutableByteSlice(std::vector<T>* v) {
139   return absl::Span<uint8>(reinterpret_cast<uint8*>(v->data()),
140                            v->size() * sizeof(T));
141 }
142 
143 // Turns an immutable slice of type T into an immutable slice of bytes with the
144 // same byte size.
145 template <typename T>
CastToByteSlice(absl::Span<const T> slice)146 absl::Span<const uint8> CastToByteSlice(absl::Span<const T> slice) {
147   return absl::Span<const uint8>(reinterpret_cast<const uint8*>(slice.data()),
148                                  slice.size() * sizeof(T));
149 }
150 
151 // Casts a byte slice to a non-byte type T, checking that the original slice
152 // length is a multiple of sizeof(T).
153 template <typename T>
CastByteSlice(absl::Span<const uint8> slice)154 absl::Span<const T> CastByteSlice(absl::Span<const uint8> slice) {
155   CHECK_EQ(0, slice.size() % sizeof(T));
156   return absl::Span<const T>(reinterpret_cast<const T*>(slice.data()),
157                              slice.size() / sizeof(T));
158 }
159 
160 // Convenience function to force a vector to convert to an immutable slice.
161 template <typename T>
AsSlice(const std::vector<T> & v)162 absl::Span<const T> AsSlice(const std::vector<T>& v) {
163   return absl::Span<const T>(v);
164 }
165 
166 // Converts a mutable vector pointer into a Span of the same
167 // type.
168 template <typename T>
AsMutableSlice(std::vector<T> * v)169 absl::Span<T> AsMutableSlice(std::vector<T>* v) {
170   return absl::Span<T>(v->data(), v->size());
171 }
172 
173 // xla::int64 is not the same type as tensorflow::protobuf_int64 in open-source.
174 // Wrapper function that gives an int64 array slice view of a repeated int64
175 // protobuf field.
AsInt64Slice(const tensorflow::protobuf::RepeatedField<tensorflow::protobuf_int64> & v)176 static inline absl::Span<const int64> AsInt64Slice(
177     const tensorflow::protobuf::RepeatedField<tensorflow::protobuf_int64>& v) {
178   absl::Span<const tensorflow::protobuf_int64> slice(v);
179   return absl::Span<const int64>(reinterpret_cast<const int64*>(slice.data()),
180                                  slice.size());
181 }
182 
183 // TODO(b/29771030): This nop overload was added to simplify the migration of
184 // Shape from a proto to a C++ class. Remove after class has been migrated.
AsInt64Slice(absl::Span<const int64> slice)185 static inline absl::Span<const int64> AsInt64Slice(
186     absl::Span<const int64> slice) {
187   return slice;
188 }
189 
190 // As above, but for uint64 types.
AsUInt64Slice(const tensorflow::protobuf::RepeatedField<tensorflow::protobuf_uint64> & v)191 static inline absl::Span<const uint64> AsUInt64Slice(
192     const tensorflow::protobuf::RepeatedField<tensorflow::protobuf_uint64>& v) {
193   absl::Span<const tensorflow::protobuf_uint64> slice(v);
194   return absl::Span<const uint64>(reinterpret_cast<const uint64*>(slice.data()),
195                                   slice.size());
196 }
197 
198 // Compares two containers for equality. Returns true iff the two containers
199 // have the same size and all their elements compare equal using their
200 // operator==. Like std::equal, but forces size equality.
201 template <typename Container1T, typename Container2T>
ContainersEqual(const Container1T & c1,const Container2T & c2)202 bool ContainersEqual(const Container1T& c1, const Container2T& c2) {
203   return ((c1.size() == c2.size()) &&
204           std::equal(std::begin(c1), std::end(c1), std::begin(c2)));
205 }
206 
207 template <typename Container1T,
208           typename ElementType = typename Container1T::value_type>
ContainersEqual(const Container1T & c1,std::initializer_list<ElementType> il)209 bool ContainersEqual(const Container1T& c1,
210                      std::initializer_list<ElementType> il) {
211   absl::Span<const ElementType> c2{il};
212   return ContainersEqual(c1, c2);
213 }
214 
215 // Compares two containers for equality. Returns true iff the two containers
216 // have the same size and all their elements compare equal using the predicate
217 // p. Like std::equal, but forces size equality.
218 template <typename Container1T, typename Container2T, class PredicateT>
ContainersEqual(const Container1T & c1,const Container2T & c2,PredicateT p)219 bool ContainersEqual(const Container1T& c1, const Container2T& c2,
220                      PredicateT p) {
221   return ((c1.size() == c2.size()) &&
222           std::equal(std::begin(c1), std::end(c1), std::begin(c2), p));
223 }
224 
225 // Performs a copy of count values from src to dest, using different strides for
226 // source and destination. The source starting index is src_base, while the
227 // destination one is dest_base.
228 template <typename D, typename S>
StridedCopy(absl::Span<D> dest,int64 dest_base,int64 dest_stride,absl::Span<const S> src,int64 src_base,int64 src_stride,int64 count)229 void StridedCopy(absl::Span<D> dest, int64 dest_base, int64 dest_stride,
230                  absl::Span<const S> src, int64 src_base, int64 src_stride,
231                  int64 count) {
232   for (; count > 0; --count, dest_base += dest_stride, src_base += src_stride) {
233     dest[dest_base] = static_cast<D>(src[src_base]);
234   }
235 }
236 
237 // Adds some context information to the error message in a
238 // Status.  This is useful as Statuses are
239 // propagated upwards.
240 Status AddStatus(Status prior, absl::string_view context);
241 Status AppendStatus(Status prior, absl::string_view context);
242 
243 // Status error shorthands -- StrFormat's the arguments to be used as an error
244 // message and returns a status in the canonical error space.
245 template <typename... Args>
InvalidArgument(const absl::FormatSpec<Args...> & format,const Args &...args)246 Status InvalidArgument(const absl::FormatSpec<Args...>& format,
247                        const Args&... args) {
248   return WithLogBacktrace(
249       tensorflow::errors::InvalidArgument(absl::StrFormat(format, args...)));
250 }
251 template <typename... Args>
Unimplemented(const absl::FormatSpec<Args...> & format,const Args &...args)252 Status Unimplemented(const absl::FormatSpec<Args...>& format,
253                      const Args&... args) {
254   return WithLogBacktrace(
255       tensorflow::errors::Unimplemented(absl::StrFormat(format, args...)));
256 }
257 template <typename... Args>
InternalError(const absl::FormatSpec<Args...> & format,const Args &...args)258 Status InternalError(const absl::FormatSpec<Args...>& format,
259                      const Args&... args) {
260   return WithLogBacktrace(
261       tensorflow::errors::Internal(absl::StrFormat(format, args...)));
262 }
263 template <typename... Args>
FailedPrecondition(const absl::FormatSpec<Args...> & format,const Args &...args)264 Status FailedPrecondition(const absl::FormatSpec<Args...>& format,
265                           const Args&... args) {
266   return WithLogBacktrace(
267       tensorflow::errors::FailedPrecondition(absl::StrFormat(format, args...)));
268 }
269 template <typename... Args>
Cancelled(const absl::FormatSpec<Args...> & format,const Args &...args)270 Status Cancelled(const absl::FormatSpec<Args...>& format, const Args&... args) {
271   return WithLogBacktrace(
272       tensorflow::errors::Cancelled(absl::StrFormat(format, args...)));
273 }
274 template <typename... Args>
ResourceExhausted(const absl::FormatSpec<Args...> & format,const Args &...args)275 Status ResourceExhausted(const absl::FormatSpec<Args...>& format,
276                          const Args&... args) {
277   return WithLogBacktrace(
278       tensorflow::errors::ResourceExhausted(absl::StrFormat(format, args...)));
279 }
280 template <typename... Args>
NotFound(const absl::FormatSpec<Args...> & format,const Args &...args)281 Status NotFound(const absl::FormatSpec<Args...>& format, const Args&... args) {
282   return WithLogBacktrace(
283       tensorflow::errors::NotFound(absl::StrFormat(format, args...)));
284 }
285 template <typename... Args>
Unavailable(const absl::FormatSpec<Args...> & format,const Args &...args)286 Status Unavailable(const absl::FormatSpec<Args...>& format,
287                    const Args&... args) {
288   return WithLogBacktrace(
289       tensorflow::errors::Unavailable(absl::StrFormat(format, args...)));
290 }
291 template <typename... Args>
Unknown(const absl::FormatSpec<Args...> & format,const Args &...args)292 Status Unknown(const absl::FormatSpec<Args...>& format, const Args&... args) {
293   return WithLogBacktrace(
294       tensorflow::errors::Unknown(absl::StrFormat(format, args...)));
295 }
296 template <typename... Args>
Internal(const absl::FormatSpec<Args...> & format,const Args &...args)297 Status Internal(const absl::FormatSpec<Args...>& format, const Args&... args) {
298   return WithLogBacktrace(
299       tensorflow::errors::Internal(absl::StrFormat(format, args...)));
300 }
301 
302 template <typename... Args>
InvalidArgumentStrCat(Args &&...concat)303 Status InvalidArgumentStrCat(Args&&... concat) {
304   return InvalidArgument("%s", absl::StrCat(std::forward<Args>(concat)...));
305 }
306 
307 template <typename... Args>
UnimplementedStrCat(Args &&...concat)308 Status UnimplementedStrCat(Args&&... concat) {
309   return Unimplemented("%s", absl::StrCat(std::forward<Args>(concat)...));
310 }
311 
312 template <typename... Args>
InternalErrorStrCat(Args &&...concat)313 Status InternalErrorStrCat(Args&&... concat) {
314   return InternalError("%s", absl::StrCat(std::forward<Args>(concat)...));
315 }
316 
317 template <typename... Args>
ResourceExhaustedStrCat(Args &&...concat)318 Status ResourceExhaustedStrCat(Args&&... concat) {
319   return ResourceExhausted("%s", absl::StrCat(std::forward<Args>(concat)...));
320 }
321 
322 // Splits the lines of the original, replaces leading whitespace with the prefix
323 // given by "indentation", and returns the string joined by newlines again. As a
324 // side effect, any additional trailing whitespace is removed.
325 //
326 // Note: even different amounts of leading whitespace on different lines will be
327 // uniformly replaced with "indentation".
328 string Reindent(absl::string_view original, absl::string_view indentation);
329 
330 // Checks whether permutation is a permutation of the [0, rank) integer range.
331 bool IsPermutation(absl::Span<const int64> permutation, int64 rank);
332 
333 // Applies `permutation` on `input` and returns the permuted array.
334 // For each i, output[permutation[i]] = input[i].
335 //
336 // Precondition:
337 // 1. `permutation` is a permutation of 0..permutation.size()-1.
338 // 2. permutation.size() == input.size().
339 template <typename Container>
Permute(absl::Span<const int64> permutation,const Container & input)340 std::vector<typename Container::value_type> Permute(
341     absl::Span<const int64> permutation, const Container& input) {
342   using T = typename Container::value_type;
343   absl::Span<const T> data(input);
344   CHECK(IsPermutation(permutation, data.size()));
345   std::vector<T> output(data.size());
346   for (size_t i = 0; i < permutation.size(); ++i) {
347     output[permutation[i]] = data[i];
348   }
349   return output;
350 }
351 
352 // Inverts a permutation, i.e., output_permutation[input_permutation[i]] = i.
353 std::vector<int64> InversePermutation(
354     absl::Span<const int64> input_permutation);
355 
356 // Composes two permutations: output[i] = p1[p2[i]].
357 std::vector<int64> ComposePermutations(absl::Span<const int64> p1,
358                                        absl::Span<const int64> p2);
359 
360 // Returns true iff permutation == {0, 1, 2, ...}.
361 bool IsIdentityPermutation(absl::Span<const int64> permutation);
362 
363 template <typename Container>
PositionInContainer(const Container & container,int64 value)364 int64 PositionInContainer(const Container& container, int64 value) {
365   return std::distance(container.begin(), absl::c_find(container, value));
366 }
367 
368 // Formats the container as a comma-separated string. StrAppend must support
369 // appending the elements of the container. Prefix is prepended and suffix is
370 // appended to the returned string.
371 template <typename Container>
372 string CommaSeparatedString(const Container& c, const char* prefix = "",
373                             const char* suffix = "") {
374   // Not using Join() since the implementation here is simple anyway and this
375   // avoids copying the string to append prefix.
376   string comma_separated = prefix;
377   const char* separator = "";
378   for (const auto& entry : c) {
379     absl::StrAppend(&comma_separated, separator, entry);
380     separator = ", ";
381   }
382   comma_separated += suffix;
383   return comma_separated;
384 }
385 
386 // Overload needed to allow the container to be an initializer list. The default
387 // type for T makes an empty initializer list work as well.
388 template <typename T = int>
389 string CommaSeparatedString(const std::initializer_list<T>& c,
390                             const char* prefix = "", const char* suffix = "") {
391   return CommaSeparatedString<std::initializer_list<T>>(c, prefix, suffix);
392 }
393 
394 // Formats the container in the mathematical notation for a vector, e.g. (1, 3,
395 // 7). StrAppend must support appending the elements of c.
396 template <typename Container>
VectorString(const Container & c)397 string VectorString(const Container& c) {
398   return CommaSeparatedString(c, "(", ")");
399 }
400 
401 // Overload needed to allow the container to be an initializer list. The default
402 // type for T makes an empty initializer list work as well.
403 template <typename T = int>
VectorString(const std::initializer_list<T> & c)404 string VectorString(const std::initializer_list<T>& c) {
405   return VectorString<std::initializer_list<T>>(c);
406 }
407 
408 // Returns a string which can losslessly round trip to a bfloat.
409 string RoundTripFpToString(tensorflow::bfloat16 value);
410 
411 // Returns a string which can losslessly round trip to a fp16.
412 string RoundTripFpToString(Eigen::half value);
413 
414 // Returns a string which can losslessly round trip to a float.
415 string RoundTripFpToString(float value);
416 
417 // Returns a string which can losslessly round trip to a double.
418 string RoundTripFpToString(double value);
419 
420 // Returns a PaddingConfig object that represents no padding for the given rank.
421 PaddingConfig MakeNoPaddingConfig(int64 rank);
422 
423 // Returns a PaddingConfig object where 'padding' contains
424 // (low edge padding, high edge padding) pairs for each dimension.
425 PaddingConfig MakeEdgePaddingConfig(
426     absl::Span<const std::pair<int64, int64>> padding);
427 
428 // Returns true if the padding configuration has at least one dimension with
429 // non-zero interior padding.
430 bool HasInteriorPadding(const PaddingConfig& config);
431 
432 // Imports the templated FloorOfRatio math function from the TensorFlow
433 // namespace, as it is very commonly used.
434 template <typename T>
FloorOfRatio(T dividend,T divisor)435 T FloorOfRatio(T dividend, T divisor) {
436   return tensorflow::MathUtil::FloorOfRatio<T>(dividend, divisor);
437 }
438 
439 // Imports the templated CeilOfRatio math function from the TensorFlow
440 // namespace, as it is very commonly used.
441 template <typename T>
CeilOfRatio(T dividend,T divisor)442 T CeilOfRatio(T dividend, T divisor) {
443   return tensorflow::MathUtil::CeilOfRatio<T>(dividend, divisor);
444 }
445 
446 // Rounds the value up to a multiple of the divisor by first calling CeilOfRatio
447 // then multiplying by the divisor. For example: RoundUpToNearest(13, 8) => 16
448 template <typename T>
RoundUpToNearest(T value,T divisor)449 T RoundUpToNearest(T value, T divisor) {
450   return CeilOfRatio(value, divisor) * divisor;
451 }
452 
453 // Rounds the value down to a multiple of the divisor by first calling
454 // FloorOfRatio then multiplying by the divisor. For example:
455 // RoundDownToNearest(13, 8) => 8
456 template <typename T>
RoundDownToNearest(T value,T divisor)457 T RoundDownToNearest(T value, T divisor) {
458   return FloorOfRatio(value, divisor) * divisor;
459 }
460 
461 // Given a number of flops executed in an amount of time, produces a string that
462 // represents the throughput;
463 // e.g. HumanReadableNumFlops(1e9, 1e9) => 1.00GFLOP/s.
464 string HumanReadableNumFlops(double flops, double nanoseconds);
465 
466 // Given a number of transcendental ops executed in an amount of time, produces
467 // a string that represents the throughput;
468 // e.g. HumanReadableNumTranscendentalOps(1e9, 1e9) => 1.00GTROP/s.
469 string HumanReadableNumTranscendentalOps(double trops, double nanoseconds);
470 
471 // Split the text into multiple lines and log each line with the given
472 // severity, filename, and line number.
473 void LogLines(int sev, absl::string_view text, const char* fname, int lineno);
474 
475 template <typename T>
IsPowerOfTwo(T x)476 inline bool IsPowerOfTwo(T x) {
477   static_assert(!std::numeric_limits<T>::is_signed, "unsigned types only");
478   return x != 0 && (x & (x - 1)) == 0;
479 }
480 
481 // Returns a mask with "bits" number of least significant bits set.
LsbMaskU32(int bits)482 inline uint32 LsbMaskU32(int bits) {
483   CHECK_GE(bits, 0);
484   return (1U << bits) - 1;
485 }
486 
487 // Utility for performing a static_cast<> on a std::unique_ptr<>.
488 template <typename Derived, typename Base>
unique_ptr_static_cast(std::unique_ptr<Base> ptr)489 std::unique_ptr<Derived> unique_ptr_static_cast(std::unique_ptr<Base> ptr) {
490   return std::unique_ptr<Derived>(static_cast<Derived*>(ptr.release()));
491 }
492 
493 int64 Product(absl::Span<const int64> xs);
494 
495 // Returns the start indices of consecutive non-overlapping subsequences of `a`
496 // and `b` with the same product, i.e. `(i, j)` so
497 // • a = {a[0 = i_0], ..., a[i_1 - 1], a[i_1], ... , a[i_2 - 1], ...}
498 // • b = {b[0 = j_0], ..., b[j_1 - 1], b[j_1], ... , b[j_2 - 1], ...}
499 // • ∀ k . 0 <= k < CommonFactors(a, b).size - 1 =>
500 //         a[i_k] × a[i_k + 1] × ... × a[i_(k+1) - 1] =
501 //         b[j_k] × b[j_k + 1] × ... × b[j_(k+1) - 1]
502 // where `CommonFactors(a, b)[CommonFactors(a, b).size - 1] = (a.size, b.size)`
503 //
504 // If the given shapes have non-zero size, returns the bounds of the shortest
505 // possible such subsequences; else, returns `{(0, 0), (a.size, b.size)}`.
506 absl::InlinedVector<std::pair<int64, int64>, 8> CommonFactors(
507     absl::Span<const int64> a, absl::Span<const int64> b);
508 
509 // Removes illegal characters from filenames.
510 string SanitizeFileName(string file_name);
511 
512 template <typename C, typename Value>
FindIndex(const C & c,Value && value)513 int64 FindIndex(const C& c, Value&& value) {
514   auto it = absl::c_find(c, std::forward<Value>(value));
515   return std::distance(c.begin(), it);
516 }
517 
518 template <typename C, typename Value>
InsertAt(C * c,int64 index,Value && value)519 void InsertAt(C* c, int64 index, Value&& value) {
520   c->insert(c->begin() + index, std::forward<Value>(value));
521 }
522 
523 template <typename C>
EraseAt(C * c,int64 index)524 void EraseAt(C* c, int64 index) {
525   c->erase(c->begin() + index);
526 }
527 
528 template <typename T>
SpanToVector(absl::Span<const T> slice)529 std::vector<T> SpanToVector(absl::Span<const T> slice) {
530   return std::vector<T>(slice.begin(), slice.end());
531 }
532 
533 template <typename T, size_t N>
InlinedVectorToVector(const absl::InlinedVector<T,N> & inlined_vector)534 std::vector<T> InlinedVectorToVector(
535     const absl::InlinedVector<T, N>& inlined_vector) {
536   return std::vector<T>(inlined_vector.begin(), inlined_vector.end());
537 }
538 
539 // Returns true if `x` fits in 32-bits.
540 template <typename T>
IsInt32(T x)541 bool IsInt32(T x) {
542   // Following conversion rules: "the value is unchanged if it can be
543   // represented in the destination type (and bit-field width); otherwise, the
544   // value is implementation-defined."
545   return static_cast<int32>(x) == x;
546 }
547 
548 template <typename T>
EraseElementFromVector(std::vector<T> * container,const T & value)549 Status EraseElementFromVector(std::vector<T>* container, const T& value) {
550   // absl::c_find returns a const_iterator which does not seem to work on
551   // gcc 4.8.4, and this breaks the ubuntu/xla_gpu build bot.
552   auto it = std::find(container->begin(), container->end(), value);
553   TF_RET_CHECK(it != container->end());
554   container->erase(it);
555   return Status::OK();
556 }
557 
558 // Utility function which splits a double-precision float (F64) into a pair of
559 // single-precision floating point numbers. The most significant 49 bits (out of
560 // the total 53 available) in the mantissa of the F64 is represented as the
561 // unevaluated sum of two non-overlapping single-precision F32s; the 'high' part
562 // contains 24 bits in its mantissa, and the 'low' part contains 25 bits in its
563 // sign bit and its mantissa.
564 // Note: The resulting representation can still only represent 8-bit exponent
565 // range that is available in F32s (out of a total of 11 exponent bits in F64s).
566 std::pair<float, float> SplitF64ToF32(double x);
567 
568 // MakeCleanup(f) returns an RAII cleanup object that calls 'f' in its
569 // destructor. The easiest way to use MakeCleanup is with a lambda argument,
570 // capturing the return value in an 'auto' local variable. Most users will not
571 // need more sophisticated syntax than that.
572 //
573 // Example:
574 //   void func() {
575 //     auto resource = acquire_resource();
576 //     auto cleanup = MakeCleanup([&] { release_resource(resource); });
577 //     TF_RETURN_IF_ERROR(...);  // phew, calls release_resource!
578 //   }
579 //
580 // You can use Cleanup<F> directly, instead of using MakeCleanup and auto,
581 // but there's rarely a reason to do that.
582 //
583 // You can call 'release()' on a Cleanup object to cancel the cleanup
584 //
585 // You probably do not want to capture by reference in the cleanup lambda a
586 // variable that is returned by the function.  This can lead to disabling of RVO
587 // at best, and undefined behavior at worst.
588 template <typename F>
589 class Cleanup {
590  public:
Cleanup()591   Cleanup() : released_(true), f_() {}
592 
593   template <typename G>
Cleanup(G && f)594   explicit Cleanup(G&& f) : f_(std::forward<G>(f)) {}
595 
Cleanup(Cleanup && src)596   Cleanup(Cleanup&& src) : released_(src.is_released()), f_(src.release()) {}
597 
598   // Implicitly move-constructible from any compatible Cleanup<G>. The source
599   // will be released as if src.release() were called. A moved-from Cleanup can
600   // be safely destroyed or reassigned.
601   template <typename G>
Cleanup(Cleanup<G> && src)602   Cleanup(Cleanup<G>&& src) : released_(src.is_released()), f_(src.release()) {}
603 
604   // Assignment to a Cleanup object behaves like destroying it and making a new
605   // one in its place, analogous to unique_ptr semantics.
606   Cleanup& operator=(Cleanup&& src) {
607     if (!released_) std::move(f_)();
608     released_ = src.released_;
609     f_ = src.release();
610     return *this;
611   }
612 
~Cleanup()613   ~Cleanup() {
614     if (!released_) std::move(f_)();
615   }
616 
617   // Releases the cleanup function instead of running it. Hint: use
618   // c.release()() to run early.
release()619   F release() {
620     released_ = true;
621     return std::move(f_);
622   }
623 
is_released()624   bool is_released() const { return released_; }
625 
626  private:
627   static_assert(!std::is_reference<F>::value, "F must not be a reference");
628 
629   bool released_ = false;
630   F f_;
631 };
632 
633 template <int&... ExplicitParameterBarrier, typename F,
634           typename DecayF = typename std::decay<F>::type>
MakeCleanup(F && f)635 ABSL_MUST_USE_RESULT Cleanup<DecayF> MakeCleanup(F&& f) {
636   return Cleanup<DecayF>(std::forward<F>(f));
637 }
638 
639 }  // namespace xla
640 
641 #define XLA_LOG_LINES(SEV, STRING) \
642   ::xla::LogLines(SEV, STRING, __FILE__, __LINE__)
643 
644 #define XLA_VLOG_LINES(LEVEL, STRING)                                 \
645   do {                                                                \
646     if (VLOG_IS_ON(LEVEL)) XLA_LOG_LINES(::tensorflow::INFO, STRING); \
647   } while (false);
648 
649 // Utility macro that performs the equivalent of what one would expect
650 // LOG_LINES(FATAL, X) to do but can be used at the end of a function that
651 // returns a value without getting a compiler warning that no value is returned.
652 #define XLA_FATAL_LOG(X)                 \
653   XLA_LOG_LINES(::tensorflow::ERROR, X); \
654   LOG(FATAL) << "Aborting in " << __FUNCTION__ << " due to previous errors.";
655 
656 #endif  // TENSORFLOW_COMPILER_XLA_UTIL_H_
657