xref: /aosp_15_r20/external/tensorflow/tensorflow/core/framework/tensor.h (revision b6fb3261f9314811a0f4371741dbb8839866f948)
1 /* Copyright 2015 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 #ifndef TENSORFLOW_CORE_FRAMEWORK_TENSOR_H_
17 #define TENSORFLOW_CORE_FRAMEWORK_TENSOR_H_
18 
19 #include <cstdint>
20 #include <type_traits>
21 
22 #include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor"
23 #include "tensorflow/core/framework/allocator.h"
24 #include "tensorflow/core/framework/tensor_shape.h"
25 #include "tensorflow/core/framework/tensor_types.h"
26 #include "tensorflow/core/framework/types.h"
27 #include "tensorflow/core/framework/types.pb.h"
28 #include "tensorflow/core/lib/core/refcount.h"
29 #include "tensorflow/core/lib/core/status.h"
30 #include "tensorflow/core/lib/core/stringpiece.h"
31 #include "tensorflow/core/lib/gtl/inlined_vector.h"
32 #include "tensorflow/core/platform/logging.h"
33 #include "tensorflow/core/platform/macros.h"
34 #include "tensorflow/core/platform/mem.h"
35 #include "tensorflow/core/platform/types.h"
36 
37 namespace tensorflow {
38 
39 // Forward declarations.  In particular, we forward declare protos so that their
40 // symbols can be removed from .so exports.
41 class AllocationDescription;
42 class Allocator;
43 class OpKernelContext;
44 class Tensor;
45 class TensorBuffer;
46 class TensorCApi;
47 class TensorInterface;
48 class TensorCord;
49 class TensorDescription;
50 class TensorProto;
51 class Var;
52 
53 namespace batch_util {
54 Status CopyElementToSlice(Tensor element, Tensor* parent, int64_t index);
55 Status CopySliceToElement(const Tensor& parent, Tensor* element, int64_t index);
56 Status MaybeMoveSliceToElement(Tensor* parent, Tensor* element, int64_t index);
57 Status CopyContiguousSlices(const Tensor& src, int64_t src_offset,
58                             int64_t dst_offset, int64_t num_slices,
59                             Tensor* dst);
60 }  // namespace batch_util
61 
62 /// @ingroup core
63 
64 /// Interface to access the raw ref-counted data buffer.
65 class TensorBuffer : public core::RefCounted {
66  public:
TensorBuffer(void * data_ptr)67   explicit TensorBuffer(void* data_ptr) : data_(data_ptr) {}
~TensorBuffer()68   ~TensorBuffer() override {}
69 
70   /// \brief data() points to a memory region of size() bytes.
71   ///
72   /// NOTE(mrry): The `data()` method is not virtual for performance reasons.
73   /// It can be called multiple times when the contents of a `Tensor` are
74   /// accessed, and so making it non-virtual allows the body to be inlined.
data()75   void* data() const { return data_; }
76 
77   /// \brief Size (in bytes) of the buffer.
78   virtual size_t size() const = 0;
79 
80   /// \brief If this TensorBuffer is sub-buffer of another TensorBuffer,
81   /// returns that TensorBuffer. Otherwise, returns this.
82   virtual TensorBuffer* root_buffer() = 0;
83 
84   /// \brief Fills metadata about the allocation into the proto.
85   virtual void FillAllocationDescription(
86       AllocationDescription* proto) const = 0;
87 
88   virtual bool GetAllocatedBytes(size_t* out_bytes) const;
89 
90   /// \brief Helper method to reinterpret the buffer as an array of `T`.
91   template <typename T>
base()92   T* base() const {
93     return reinterpret_cast<T*>(data());
94   }
95 
96   /// \brief Whether this TensorBuffer owns the underlying memory.
OwnsMemory()97   virtual bool OwnsMemory() const { return true; }
98 
99   /// \brief The type of the underlying memory.
GetMemoryType()100   virtual AllocatorMemoryType GetMemoryType() const {
101     return AllocatorMemoryType::kUnknown;
102   }
103 
104  private:
105   void* const data_;
106 };
107 
108 /// Represents an n-dimensional array of values.
109 class Tensor {
110  public:
111   /// \brief Creates a 1-dimensional, 0-element float tensor.
112   ///
113   /// The returned Tensor is not a scalar (shape {}), but is instead
114   /// an empty one-dimensional Tensor (shape {0}, NumElements() ==
115   /// 0). Since it has no elements, it does not need to be assigned a
116   /// value and is initialized by default (IsInitialized() is
117   /// true). If this is undesirable, consider creating a one-element
118   /// scalar which does require initialization:
119   ///
120   /// ```c++
121   ///
122   ///     Tensor(DT_FLOAT, TensorShape({}))
123   ///
124   /// ```
125   Tensor();
126 
127   /// \brief Creates a Tensor of the given `type` and `shape`.  If
128   /// LogMemory::IsEnabled() the allocation is logged as coming from
129   /// an unknown kernel and step. Calling the Tensor constructor
130   /// directly from within an Op is deprecated: use the
131   /// OpKernelConstruction/OpKernelContext allocate_* methods to
132   /// allocate a new tensor, which record the kernel and step.
133   ///
134   /// The underlying buffer is allocated using a `CPUAllocator`.
135   Tensor(DataType type, const TensorShape& shape);
136 
137   /// \brief Creates a tensor with the input `type` and `shape`, using
138   /// the allocator `a` to allocate the underlying buffer. If
139   /// LogMemory::IsEnabled() the allocation is logged as coming from
140   /// an unknown kernel and step. Calling the Tensor constructor
141   /// directly from within an Op is deprecated: use the
142   /// OpKernelConstruction/OpKernelContext allocate_* methods to
143   /// allocate a new tensor, which record the kernel and step.
144   ///
145   /// `a` must outlive the lifetime of this Tensor.
146   Tensor(Allocator* a, DataType type, const TensorShape& shape);
147 
148   /// \brief Creates a tensor with the input `type` and `shape`, using
149   /// the allocator `a` and the specified "allocation_attr" to
150   /// allocate the underlying buffer. If the kernel and step are known
151   /// allocation_attr.allocation_will_be_logged should be set to true
152   /// and LogMemory::RecordTensorAllocation should be called after the
153   /// tensor is constructed. Calling the Tensor constructor directly
154   /// from within an Op is deprecated: use the
155   /// OpKernelConstruction/OpKernelContext allocate_* methods to
156   /// allocate a new tensor, which record the kernel and step.
157   ///
158   /// `a` must outlive the lifetime of this Tensor.
159   Tensor(Allocator* a, DataType type, const TensorShape& shape,
160          const AllocationAttributes& allocation_attr);
161 
162   /// \brief Creates a tensor with the input datatype, shape and buf.
163   ///
164   /// Acquires a ref on buf that belongs to this Tensor.
165   Tensor(DataType type, const TensorShape& shape, TensorBuffer* buf);
166 
167   /// \brief Creates a tensor with the input datatype, shape and buf.
168   ///
169   /// Takes an ownership of the bufffer from the reference counted pointer.
170   Tensor(DataType type, TensorShape shape, core::RefCountPtr<TensorBuffer> buf);
171 
172   /// \brief Creates an empty Tensor of the given data type.
173   ///
174   /// Like Tensor(), returns a 1-dimensional, 0-element Tensor with
175   /// IsInitialized() returning True. See the Tensor() documentation
176   /// for details.
177   explicit Tensor(DataType type);
178 
179   /// \brief Initializes a tensor with the input `type` and `shape`, or returns
180   /// an error and leaves `out_tensor` unmodified. This factory method should be
181   /// used instead of the corresponding constructor if calling code cannot
182   /// validate that the `DataType` is valid and supported.
183   ///
184   /// The underlying buffer is allocated using a `CPUAllocator`.
185   static Status BuildTensor(DataType type, const TensorShape& shape,
186                             Tensor* out_tensor);
187 
188  private:
189   // A tag type for selecting the `Tensor` constructor overload that creates a
190   // scalar tensor in host memory.
191   struct host_scalar_tag {};
192 
193   class HostScalarTensorBufferBase;
194   template <typename T>
195   struct ValueAndTensorBuffer;
196 
197   // Creates a tensor with the given scalar `value` in CPU memory.
198   template <typename T>
199   Tensor(T value, host_scalar_tag tag);
200 
201  public:
202   // A series of specialized constructors for scalar tensors in host memory.
203   //
204   // NOTE: The `Variant` host-scalar constructor is not defined, because Variant
205   // is implicitly constructible from many different types, and this causes
206   // ambiguities with some compilers.
Tensor(float scalar_value)207   explicit Tensor(float scalar_value)
208       : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(double scalar_value)209   explicit Tensor(double scalar_value)
210       : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(int32_t scalar_value)211   explicit Tensor(int32_t scalar_value)
212       : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(uint32 scalar_value)213   explicit Tensor(uint32 scalar_value)
214       : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(uint16 scalar_value)215   explicit Tensor(uint16 scalar_value)
216       : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(uint8 scalar_value)217   explicit Tensor(uint8 scalar_value)
218       : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(int16_t scalar_value)219   explicit Tensor(int16_t scalar_value)
220       : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(int8_t scalar_value)221   explicit Tensor(int8_t scalar_value)
222       : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(tstring scalar_value)223   explicit Tensor(tstring scalar_value)
224       : Tensor(std::move(scalar_value), host_scalar_tag{}) {}
Tensor(complex64 scalar_value)225   explicit Tensor(complex64 scalar_value)
226       : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(complex128 scalar_value)227   explicit Tensor(complex128 scalar_value)
228       : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(int64_t scalar_value)229   explicit Tensor(int64_t scalar_value)
230       : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(uint64 scalar_value)231   explicit Tensor(uint64 scalar_value)
232       : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(bool scalar_value)233   explicit Tensor(bool scalar_value)
234       : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(qint8 scalar_value)235   explicit Tensor(qint8 scalar_value)
236       : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(quint8 scalar_value)237   explicit Tensor(quint8 scalar_value)
238       : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(qint16 scalar_value)239   explicit Tensor(qint16 scalar_value)
240       : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(quint16 scalar_value)241   explicit Tensor(quint16 scalar_value)
242       : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(qint32 scalar_value)243   explicit Tensor(qint32 scalar_value)
244       : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(bfloat16 scalar_value)245   explicit Tensor(bfloat16 scalar_value)
246       : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(Eigen::half scalar_value)247   explicit Tensor(Eigen::half scalar_value)
248       : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(ResourceHandle scalar_value)249   explicit Tensor(ResourceHandle scalar_value)
250       : Tensor(std::move(scalar_value), host_scalar_tag{}) {}
251 
252   // NOTE: The `const char*` host-scalar constructor is provided as a
253   // convenience because otherwise passing a string literal would surprisingly
254   // construct a DT_BOOL tensor.
Tensor(const char * scalar_value)255   explicit Tensor(const char* scalar_value)
256       : Tensor(tstring(scalar_value), host_scalar_tag{}) {}
257 
258   /// Copy constructor.
259   Tensor(const Tensor& other);
260 
261   /// \brief Move constructor. After this call, <other> is safely destructible
262   /// can be assigned to, and IsInitialized() can be called and will return
263   /// false. Other calls on <other> (e.g. shape manipulation) are not valid.
264   Tensor(Tensor&& other);
265 
266   // Explicitly delete constructor that take a pointer (except char*)
267   // so that the pointer doesn't get implicitly cast to bool.
268   template <typename T, typename std::enable_if<!std::is_same<T, char>::value,
269                                                 T>::type* = nullptr>
270   explicit Tensor(T* t) = delete;
271 
272   ~Tensor();
273 
274   /// Returns the data type.
dtype()275   DataType dtype() const { return shape_.data_type(); }
276 
277   /// Returns the shape of the tensor.
shape()278   const TensorShape& shape() const { return shape_; }
279 
280   /// \brief Convenience accessor for the tensor shape.
281   ///
282   /// For all shape accessors, see comments for relevant methods of
283   /// `TensorShape` in `tensor_shape.h`.
dims()284   int dims() const { return shape().dims(); }
285 
286   /// Convenience accessor for the tensor shape.
dim_size(int d)287   int64_t dim_size(int d) const { return shape().dim_size(d); }
288 
289   /// Convenience accessor for the tensor shape.
NumElements()290   int64_t NumElements() const { return shape().num_elements(); }
291 
IsSameSize(const Tensor & b)292   bool IsSameSize(const Tensor& b) const {
293     return shape().IsSameSize(b.shape());
294   }
295 
296   // True iff the two tensors use the same underlying refcounted storage
297   bool SharesBufferWith(const Tensor& b) const;
298 
299   /// \brief If necessary, has this Tensor been initialized?
300   ///
301   /// Zero-element Tensors are always considered initialized, even if they
302   /// have never been assigned to and do not have any memory allocated.
303   bool IsInitialized() const;
304 
305   /// Returns the estimated memory usage of this tensor.
306   size_t TotalBytes() const;
307 
308   // Returns the size of allocated memory for this tensor.
309   size_t AllocatedBytes() const;
310 
311   /// Returns true iff this tensor is aligned.
IsAligned()312   bool IsAligned() const {
313 #if EIGEN_MAX_ALIGN_BYTES == 0
314     return true;
315 #else
316     void* ptr = base<void>();
317     return dtype() == DT_STRING || NumElements() == 0 ||
318            (reinterpret_cast<intptr_t>(ptr) % EIGEN_MAX_ALIGN_BYTES == 0);
319 #endif
320   }
321 
322   /// Assign operator. This tensor shares other's underlying storage.
323   Tensor& operator=(const Tensor& other) {
324     CopyFromInternal(other, other.shape());
325     return *this;
326   }
327 
328   /// Move operator.  See move constructor for details.
329   Tensor& operator=(Tensor&& other);
330 
331   /// \brief Copy the other tensor into this tensor and reshape it.
332   ///
333   /// This tensor shares other's underlying storage. Returns `true`
334   /// iff `other.shape()` has the same number of elements of the given
335   /// `shape`.
CopyFrom(const Tensor & other,const TensorShape & shape)336   bool CopyFrom(const Tensor& other,
337                 const TensorShape& shape) TF_MUST_USE_RESULT {
338     if (other.NumElements() != shape.num_elements()) return false;
339     CopyFromInternal(other, shape);
340     return true;
341   }
342 
343   /// \brief Slice this tensor along the 1st dimension.
344 
345   /// I.e., the returned tensor satisfies
346   ///     returned[i, ...] == this[dim0_start + i, ...].
347   /// The returned tensor shares the underlying tensor buffer with this
348   /// tensor.
349   ///
350   /// NOTE: The returned tensor may not satisfy the same alignment
351   /// requirement as this tensor depending on the shape. The caller
352   /// must check the returned tensor's alignment before calling certain
353   /// methods that have alignment requirement (e.g., `flat()`, `tensor()`).
354   ///
355   /// NOTE: When fed with an N-dimensional tensor, this method returns a tensor
356   /// also with N dimensions. If you want to select a sub tensor, see SubSlice.
357   ///
358   /// REQUIRES: `dims()` >= 1
359   /// REQUIRES: `0 <= dim0_start <= dim0_limit <= dim_size(0)`
360   Tensor Slice(int64_t dim0_start, int64_t dim0_limit) const;
361 
362   /// \brief Select a subslice from this tensor along the 1st dimension.
363   ///
364   /// When fed with an N-dimensional tensor, this method returns a tensor with
365   /// N-1 dimensions, where the returned tensor is a subslice of the input
366   /// tensor along the first dimension. The N-1 dimensions of the returned
367   /// tensor are the last N-1 dimensions of the input tensor.
368   ///
369   /// NOTE: The returned tensor may not satisfy the same alignment
370   /// requirement as this tensor depending on the shape. The caller
371   /// must check the returned tensor's alignment before calling certain
372   /// methods that have alignment requirement (e.g., `flat()`, `tensor()`).
373   ///
374   /// REQUIRES: `dims()` >= 1
375   /// REQUIRES: `0 <= index < dim_size(0)`
376   Tensor SubSlice(int64_t index) const;
377 
378   /// \brief Parse `other` and construct the tensor.
379 
380   /// Returns `true` iff the parsing succeeds. If the parsing fails,
381   /// the state of `*this` is unchanged.
382   bool FromProto(const TensorProto& other) TF_MUST_USE_RESULT;
383   bool FromProto(Allocator* a, const TensorProto& other) TF_MUST_USE_RESULT;
384 
385   /// \brief Fills in `proto` with `*this` tensor's content.
386   ///
387   /// `AsProtoField()` fills in the repeated field for `proto.dtype()`, while
388   /// `AsProtoTensorContent()` encodes the content in `proto.tensor_content()`
389   /// in a compact form.
390   void AsProtoField(TensorProto* proto) const;
391   void AsProtoTensorContent(TensorProto* proto) const;
392 
393   /// \brief Return the tensor data as an `Eigen::Tensor` with the type and
394   /// sizes of this `Tensor`.
395   ///
396   /// Use these methods when you know the data type and the number of
397   /// dimensions of the Tensor and you want an `Eigen::Tensor`
398   /// automatically sized to the `Tensor` sizes. The implementation check
399   /// fails if either type or sizes mismatch.
400   ///
401   /// Example:
402   ///
403   /// ```c++
404   ///
405   ///     typedef float T;
406   ///     Tensor my_mat(...built with Shape{rows: 3, cols: 5}...);
407   ///     auto mat = my_mat.matrix<T>();    // 2D Eigen::Tensor, 3 x 5.
408   ///     auto mat = my_mat.tensor<T, 2>(); // 2D Eigen::Tensor, 3 x 5.
409   ///     auto vec = my_mat.vec<T>();       // CHECK fails as my_mat is 2D.
410   ///     auto vec = my_mat.tensor<T, 3>(); // CHECK fails as my_mat is 2D.
411   ///     auto mat = my_mat.matrix<int32>();// CHECK fails as type mismatch.
412   ///
413   /// ```
414   template <typename T>
vec()415   typename TTypes<T>::Vec vec() {
416     return tensor<T, 1>();
417   }
418 
419   template <typename T>
matrix()420   typename TTypes<T>::Matrix matrix() {
421     return tensor<T, 2>();
422   }
423 
424   template <typename T, size_t NDIMS>
425   typename TTypes<T, NDIMS>::Tensor tensor() TF_ATTRIBUTE_NOINLINE;
426 
427   /// \brief Return the tensor data to an `Eigen::Tensor` with the
428   /// same size but a bitwise cast to the specified dtype `T`.
429   ///
430   /// Using a bitcast is useful for move and copy operations.
431   /// NOTE: this is the same as `tensor()` except a bitcast is allowed.
432   template <typename T, size_t NDIMS>
433   typename TTypes<T, NDIMS>::Tensor bit_casted_tensor();
434 
435   /// \brief Return the tensor data to an `Eigen::Tensor` with the
436   /// last dimension elements converted into single elements of a larger type.
437   ///
438   /// For example, this is useful for kernels that can treat NCHW_VECT_C int8
439   /// tensors as NCHW int32 tensors. The sizeof(T) should equal the size of
440   /// the original element type * num elements in the original last dimension.
441   /// NDIMS should be 1 less than the original number of dimensions.
442   template <typename T, size_t NDIMS>
443   typename TTypes<T, NDIMS>::Tensor reinterpret_last_dimension();
444 
445   /// \brief Return the tensor data as an `Eigen::Tensor` of the data type and a
446   /// specified shape.
447   ///
448   /// These methods allow you to access the data with the dimensions
449   /// and sizes of your choice.  You do not need to know the number of
450   /// dimensions of the Tensor to call them.  However, they `CHECK` that
451   /// the type matches and the dimensions requested creates an
452   /// `Eigen::Tensor` with the same number of elements as the tensor.
453   ///
454   /// Example:
455   ///
456   /// ```c++
457   ///
458   ///     typedef float T;
459   ///     Tensor my_ten(...built with Shape{planes: 4, rows: 3, cols: 5}...);
460   ///     // 1D Eigen::Tensor, size 60:
461   ///     auto flat = my_ten.flat<T>();
462   ///     // 2D Eigen::Tensor 12 x 5:
463   ///     auto inner = my_ten.flat_inner_dims<T>();
464   ///     // 2D Eigen::Tensor 4 x 15:
465   ///     auto outer = my_ten.shaped<T, 2>({4, 15});
466   ///     // CHECK fails, bad num elements:
467   ///     auto outer = my_ten.shaped<T, 2>({4, 8});
468   ///     // 3D Eigen::Tensor 6 x 5 x 2:
469   ///     auto weird = my_ten.shaped<T, 3>({6, 5, 2});
470   ///     // CHECK fails, type mismatch:
471   ///     auto bad   = my_ten.flat<int32>();
472   ///
473   /// ```
474   template <typename T>
475   typename TTypes<T>::Flat flat();
476 
477   template <typename T>
unaligned_flat()478   typename TTypes<T>::UnalignedFlat unaligned_flat() {
479     return unaligned_shaped<T, 1>({NumElements()});
480   }
481 
482   /// Returns the data as an Eigen::Tensor with NDIMS dimensions, collapsing all
483   /// Tensor dimensions but the last NDIMS-1 into the first dimension of the
484   /// result. If NDIMS > dims() then leading dimensions of size 1 will be
485   /// added to make the output rank NDIMS.
486   template <typename T, size_t NDIMS = 2>
487   typename TTypes<T, NDIMS>::Tensor flat_inner_dims();
488 
489   /// Returns the data as an Eigen::Tensor with NDIMS dimensions, collapsing all
490   /// Tensor dimensions but the first NDIMS-1 into the last dimension of the
491   /// result. If NDIMS > dims() then trailing dimensions of size 1 will be
492   /// added to make the output rank NDIMS.
493   template <typename T, size_t NDIMS = 2>
494   typename TTypes<T, NDIMS>::Tensor flat_outer_dims();
495 
496   /// Returns the data as an Eigen::Tensor with NDIMS dimensions, collapsing the
497   /// first 'begin' Tensor dimensions into the first dimension of the result and
498   /// the Tensor dimensions of the last dims() - 'begin' - NDIMS into the last
499   /// dimension of the result. If 'begin' < 0 then the |'begin'| leading
500   /// dimensions of size 1 will be added. If 'begin' + NDIMS > dims() then
501   /// 'begin' + NDIMS - dims() trailing dimensions of size 1 will be added.
502   template <typename T, size_t NDIMS = 3>
503   typename TTypes<T, NDIMS>::Tensor flat_inner_outer_dims(int64_t begin);
504 
505   template <typename T, size_t NDIMS>
506   typename TTypes<T, NDIMS>::Tensor shaped(gtl::ArraySlice<int64_t> new_sizes);
507 
508   /// \brief Return the tensor data to an `Eigen::Tensor` with the new
509   /// shape specified in `new_sizes` and cast to a new dtype `T`.
510   ///
511   /// Using a bitcast is useful for move and copy operations.
512   /// The allowed bitcast is the only difference from `shaped()`.
513   template <typename T, size_t NDIMS>
514   typename TTypes<T, NDIMS>::Tensor bit_casted_shaped(
515       gtl::ArraySlice<int64_t> new_sizes);
516 
517   template <typename T, size_t NDIMS>
518   typename TTypes<T, NDIMS>::UnalignedTensor unaligned_shaped(
519       gtl::ArraySlice<int64_t> new_sizes);
520 
521   /// \brief Return the Tensor data as a `TensorMap` of fixed size 1:
522   /// `TensorMap<TensorFixedSize<T, 1>>`.
523 
524   /// Using `scalar()` allows the compiler to perform optimizations as
525   /// the size of the tensor is known at compile time.
526   template <typename T>
527   typename TTypes<T>::Scalar scalar();
528 
529   /// Const versions of all the methods above.
530   template <typename T>
vec()531   typename TTypes<T>::ConstVec vec() const {
532     return tensor<T, 1>();
533   }
534 
535   template <typename T>
matrix()536   typename TTypes<T>::ConstMatrix matrix() const {
537     return tensor<T, 2>();
538   }
539 
540   template <typename T, size_t NDIMS>
541   typename TTypes<T, NDIMS>::ConstTensor tensor() const TF_ATTRIBUTE_NOINLINE;
542 
543   /// \brief Return the tensor data to an `Eigen::Tensor` with the
544   /// same size but a bitwise cast to the specified dtype `T`.
545   ///
546   /// Using a bitcast is useful for move and copy operations.
547   /// NOTE: this is the same as `tensor()` except a bitcast is allowed.
548   template <typename T, size_t NDIMS>
549   typename TTypes<T, NDIMS>::ConstTensor bit_casted_tensor() const;
550 
551   /// \brief Return the tensor data to an `Eigen::Tensor` with the
552   /// last dimension elements converted into single elements of a larger type.
553   ///
554   /// For example, this is useful for kernels that can treat NCHW_VECT_C int8
555   /// tensors as NCHW int32 tensors. The sizeof(T) should equal the size of
556   /// the original element type * num elements in the original last dimension.
557   /// NDIMS should be 1 less than the original number of dimensions.
558   template <typename T, size_t NDIMS>
559   typename TTypes<T, NDIMS>::ConstTensor reinterpret_last_dimension() const;
560 
561   template <typename T>
562   typename TTypes<T>::ConstFlat flat() const;
563 
564   template <typename T>
unaligned_flat()565   typename TTypes<T>::UnalignedConstFlat unaligned_flat() const {
566     return unaligned_shaped<T, 1>({NumElements()});
567   }
568 
569   template <typename T, size_t NDIMS>
570   typename TTypes<T, NDIMS>::ConstTensor shaped(
571       gtl::ArraySlice<int64_t> new_sizes) const;
572 
573   /// \brief Return the tensor data to an `Eigen::Tensor` with the new
574   /// shape specified in `new_sizes` and cast to a new dtype `T`.
575   ///
576   /// Using a bitcast is useful for move and copy operations.
577   /// The allowed bitcast is the only difference from `shaped()`.
578   template <typename T, size_t NDIMS>
579   typename TTypes<T, NDIMS>::ConstTensor bit_casted_shaped(
580       gtl::ArraySlice<int64_t> new_sizes) const;
581 
582   template <typename T, size_t NDIMS>
583   typename TTypes<T, NDIMS>::UnalignedConstTensor unaligned_shaped(
584       gtl::ArraySlice<int64_t> new_sizes) const;
585 
586   template <typename T>
587   typename TTypes<T>::ConstScalar scalar() const;
588 
589   template <typename T, size_t NDIMS = 2>
590   typename TTypes<T, NDIMS>::ConstTensor flat_inner_dims() const;
591 
592   template <typename T, size_t NDIMS = 2>
593   typename TTypes<T, NDIMS>::ConstTensor flat_outer_dims() const;
594 
595   template <typename T, size_t NDIMS = 3>
596   typename TTypes<T, NDIMS>::ConstTensor flat_inner_outer_dims(
597       int64_t begin) const;
598 
599   /// Render the first `max_entries` values in `*this` into a string.
600   std::string SummarizeValue(int64_t max_entries, bool print_v2 = false) const;
601 
602   /// A human-readable summary of the tensor suitable for debugging.
603   // `num_values` is the number of actual data values in the tensor
604   // included in the message. If the tensor might be resident in
605   // GPU/TPU memory use DeviceSafeDebugString instead.
606   std::string DebugString(int num_values) const;
DebugString()607   std::string DebugString() const { return DebugString(3); }
608 
609   // Variant of DebugString() that should be used for possibly non-CPU tensors.
610   // If the tensor is not resident on CPU, we can't read its values as
611   // DebugString() does.
612   std::string DeviceSafeDebugString() const;
613 
614   /// Fill in the `TensorDescription` proto with metadata about the
615   /// tensor that is useful for monitoring and debugging.
616   void FillDescription(TensorDescription* description) const;
617 
618   /// \brief Returns a `StringPiece` mapping the current tensor's buffer.
619   ///
620   /// The returned `StringPiece` may point to memory location on devices
621   /// that the CPU cannot address directly.
622   ///
623   /// NOTE: The underlying tensor buffer is refcounted, so the lifetime
624   /// of the contents mapped by the `StringPiece` matches the lifetime of
625   /// the buffer; callers should arrange to make sure the buffer does
626   /// not get destroyed while the `StringPiece` is still used.
627   ///
628   /// REQUIRES: `DataTypeCanUseMemcpy(dtype())`.
629   StringPiece tensor_data() const;
630   void* data() const;
631 
632   /// Copy the other tensor into this tensor, reshape it and reinterpret the
633   /// buffer's datatype. If an ok Status is returned, the two tensors now share
634   /// the same underlying storage.
635   ///
636   /// This call requires that the `other` tensor and the given type and shape
637   /// are "compatible" (i.e. they occupy the same number of bytes).
638   ///
639   /// Specifically:
640   ///
641   /// shape.num_elements() * DataTypeSize(type)
642   ///
643   /// must equal
644   ///
645   /// other.num_elements() * DataTypeSize(other.dtype())
646   ///
647   /// In addition, this function requires:
648   ///   * DataTypeSize(other.dtype()) != 0
649   ///   * DataTypeSize(type) != 0
650   ///
651   /// If any of the requirements are not met, errors::InvalidArgument is
652   /// returned.
653   Status BitcastFrom(const Tensor& other, DataType dtype,
654                      const TensorShape& shape);
655 
656   /// Like BitcastFrom, but CHECK fails if any preconditions are not met.
657   ///
658   /// Deprecated. Use BitcastFrom instead and check the returned Status.
UnsafeCopyFromInternal(const Tensor & other,DataType dtype,const TensorShape & shape)659   void UnsafeCopyFromInternal(const Tensor& other, DataType dtype,
660                               const TensorShape& shape) {
661     TF_CHECK_OK(BitcastFrom(other, dtype, shape));
662   }
663 
664   // Returns true if the refcount on buf_ and any possible underlying root
665   // buffer is one.
666   bool RefCountIsOne() const;
667 
668   // Returns the type of the underlying memory.
GetMemoryType()669   AllocatorMemoryType GetMemoryType() const { return buf_->GetMemoryType(); }
670 
671  private:
672   void CheckType(DataType expected_dtype) const;
673   void CheckTypeAndIsAligned(DataType expected_dtype) const;
674   void CheckIsAlignedAndSingleElement() const;
set_dtype(DataType t)675   void set_dtype(DataType t) { shape_.set_data_type(t); }
676 
677   // TensorShape's InlineVector.
678   static gtl::InlinedVector<int64_t, 4> ComputeFlatInnerDims(
679       gtl::ArraySlice<int64_t> orig, int64_t num_out_dims);
680   static gtl::InlinedVector<int64_t, 4> ComputeFlatOuterDims(
681       gtl::ArraySlice<int64_t> orig, int64_t num_out_dims);
682 
683   TensorShape shape_;
684   TensorBuffer* buf_;
685 
686   friend class DMAHelper;             // For access to buf_.
687   friend class TensorCApi;            // For access to buf_.
688   friend class TensorCord;            // For access to buf_.
689   friend class TensorReference;       // For access to buf_.
690   friend class VariableOp;            // For access to set_shape.
691   friend class AutoReloadVariableOp;  // For access to set_shape.
692   friend class TensorTestHelper;      // For access to set_shape.
693   friend class TensorInterface;       // For access to set_shape.
694   friend class CastOpBase;            // For access to set_dtype.
695   friend class ScopedAllocator;       // For access to buf_.
696   friend Status batch_util::CopyElementToSlice(
697       Tensor element, Tensor* parent,
698       int64_t index);  // For access to base<T>().
699   friend Status batch_util::CopySliceToElement(
700       const Tensor& parent, Tensor* element,
701       int64_t index);  // For access to base<T>().
702   friend Status batch_util::MaybeMoveSliceToElement(
703       Tensor* parent, Tensor* element,
704       int64_t index);  // For access to base<T>().
705   friend Status batch_util::CopyContiguousSlices(
706       const Tensor& src, int64_t src_offset, int64_t dst_offset,
707       int64_t num_slices,
708       Tensor* dst);  // For access to base<T>().
709 
710   bool CanUseDMA() const;
711 
712   // Only needed by variable op to set the shape of an uninitialized
713   // Tensor.
714   // TODO: Remove this when we have a better story for detecting
715   // uninitialized tensors.
set_shape(const TensorShape & shape)716   void set_shape(const TensorShape& shape) {
717     DataType dt = dtype();
718     shape_ = shape;
719     set_dtype(dt);
720   }
721 
CopyFromInternal(const Tensor & other,const TensorShape & shape)722   inline void CopyFromInternal(const Tensor& other, const TensorShape& shape) {
723     DCHECK_EQ(shape.num_elements(), other.NumElements());
724     // Data type will be overwritten if this == &other, since dtype is part of
725     // shape.
726     DataType other_dtype = other.dtype();
727     shape_ = shape;
728     set_dtype(other_dtype);
729     if (buf_ != other.buf_) {
730       if (buf_) buf_->Unref();
731       buf_ = other.buf_;
732       if (buf_) buf_->Ref();
733     }
734   }
735 
736   template <typename T>
737   T* base() const;
738 
739   template <size_t NDIMS>
740   void FillDimsAndValidateCompatibleShape(
741       gtl::ArraySlice<int64_t> new_sizes,
742       Eigen::array<Eigen::DenseIndex, NDIMS>* dims) const;
743 
744   template <typename T, size_t NDIMS>
745   void FillDimsAndValidateCompatibleShape(
746       gtl::ArraySlice<int64_t> new_sizes,
747       Eigen::array<Eigen::DenseIndex, NDIMS>* dims) const;
748 };
749 
750 // Implementation details
751 
752 // START_SKIP_DOXYGEN
753 
754 template <typename T>
base()755 T* Tensor::base() const {
756   return buf_ == nullptr ? nullptr : buf_->base<T>();
757 }
758 
759 // This routine is defined out of line for code-space savings
760 template <typename T, size_t NDIMS>
tensor()761 typename TTypes<T, NDIMS>::Tensor Tensor::tensor() {
762   CheckTypeAndIsAligned(DataTypeToEnum<T>::v());
763   return typename TTypes<T, NDIMS>::Tensor(base<T>(),
764                                            shape().AsEigenDSizes<NDIMS>());
765 }
766 
767 // This routine is defined out of line for code-space savings
768 template <typename T, size_t NDIMS>
tensor()769 typename TTypes<T, NDIMS>::ConstTensor Tensor::tensor() const {
770   CheckTypeAndIsAligned(DataTypeToEnum<T>::v());
771   return typename TTypes<T, NDIMS>::ConstTensor(base<const T>(),
772                                                 shape().AsEigenDSizes<NDIMS>());
773 }
774 
775 template <typename T, size_t NDIMS>
bit_casted_tensor()776 typename TTypes<T, NDIMS>::Tensor Tensor::bit_casted_tensor() {
777   CHECK(IsAligned());
778   return typename TTypes<T, NDIMS>::Tensor(base<T>(),
779                                            shape().AsEigenDSizes<NDIMS>());
780 }
781 
782 template <typename T, size_t NDIMS>
bit_casted_tensor()783 typename TTypes<T, NDIMS>::ConstTensor Tensor::bit_casted_tensor() const {
784   CHECK(IsAligned());
785   return typename TTypes<T, NDIMS>::ConstTensor(base<const T>(),
786                                                 shape().AsEigenDSizes<NDIMS>());
787 }
788 
789 template <typename T, size_t NDIMS>
reinterpret_last_dimension()790 typename TTypes<T, NDIMS>::Tensor Tensor::reinterpret_last_dimension() {
791   if (NDIMS == dims()) {
792     return tensor<T, NDIMS>();
793   }
794   CHECK(IsAligned());
795   CHECK_EQ(static_cast<int>(NDIMS), dims() - 1);
796   CHECK_EQ(static_cast<int64_t>(sizeof(T)),
797            shape_.dim_sizes()[NDIMS] * DataTypeSize(dtype()));
798   Eigen::array<Eigen::DenseIndex, NDIMS> dims;
799   for (int d = 0; d < NDIMS; ++d) {
800     dims[d] = shape_.dim_sizes()[d];
801   }
802   return typename TTypes<T, NDIMS>::Tensor(base<T>(), dims);
803 }
804 
805 template <typename T, size_t NDIMS>
reinterpret_last_dimension()806 typename TTypes<T, NDIMS>::ConstTensor Tensor::reinterpret_last_dimension()
807     const {
808   if (NDIMS == dims()) {
809     return tensor<T, NDIMS>();
810   }
811   CHECK(IsAligned());
812   CHECK_EQ(static_cast<int>(NDIMS), dims() - 1);
813   CHECK_EQ(static_cast<int64_t>(sizeof(T)),
814            shape_.dim_sizes()[NDIMS] * DataTypeSize(dtype()));
815   Eigen::array<Eigen::DenseIndex, NDIMS> dims;
816   for (int d = 0; d < NDIMS; ++d) {
817     dims[d] = shape_.dim_sizes()[d];
818   }
819   return typename TTypes<T, NDIMS>::ConstTensor(base<const T>(), dims);
820 }
821 
822 template <size_t NDIMS>
FillDimsAndValidateCompatibleShape(gtl::ArraySlice<int64_t> new_sizes,Eigen::array<Eigen::DenseIndex,NDIMS> * dims)823 void Tensor::FillDimsAndValidateCompatibleShape(
824     gtl::ArraySlice<int64_t> new_sizes,
825     Eigen::array<Eigen::DenseIndex, NDIMS>* dims) const {
826   CHECK_EQ(NDIMS, new_sizes.size());
827   int64_t new_num_elements = 1;
828   for (size_t d = 0; d < NDIMS; d++) {
829     new_num_elements *= new_sizes[d];
830     (*dims)[d] = new_sizes[d];
831   }
832   CHECK_EQ(new_num_elements, NumElements());
833 }
834 
835 template <typename T, size_t NDIMS>
FillDimsAndValidateCompatibleShape(gtl::ArraySlice<int64_t> new_sizes,Eigen::array<Eigen::DenseIndex,NDIMS> * dims)836 void Tensor::FillDimsAndValidateCompatibleShape(
837     gtl::ArraySlice<int64_t> new_sizes,
838     Eigen::array<Eigen::DenseIndex, NDIMS>* dims) const {
839   CHECK_EQ(NDIMS, new_sizes.size());
840   int64_t new_num_elements = 1;
841   for (size_t d = 0; d < NDIMS; d++) {
842     new_num_elements *= new_sizes[d];
843     (*dims)[d] = new_sizes[d];
844   }
845   const int element_size = DataTypeSize(BaseType(dtype()));
846   if (element_size > 0) {
847     CHECK_EQ(new_num_elements * static_cast<int64_t>(sizeof(T)),
848              NumElements() * element_size);
849   } else {
850     // DataTypeSize() returns 0 for some data types. In this case, assume that T
851     // has the same size as the buffer type.
852     // NOTE: If we can be sure that DataTypeSize() does not return 0 for all POD
853     // types, then we should check DataTypeToEnum<T>::v() == dtype(). Or simply
854     // check if `element_size > 0` to err when bit cast is attempted on Tensor
855     // of unknown data type size.
856     CHECK_EQ(new_num_elements, NumElements());
857   }
858 }
859 
860 template <typename T>
flat()861 typename TTypes<T>::Flat Tensor::flat() {
862   // Equivalent to 'return shaped<T, 1>({NumElements()});'
863   CheckTypeAndIsAligned(DataTypeToEnum<T>::v());
864   Eigen::array<Eigen::DenseIndex, 1> dims;
865   dims[0] = NumElements();
866   return typename TTypes<T, 1>::Tensor(base<T>(), dims);
867 }
868 
869 template <typename T>
flat()870 typename TTypes<T>::ConstFlat Tensor::flat() const {
871   // Equuivalent to 'return shaped<T, 1>({NumElements()});'
872   CheckTypeAndIsAligned(DataTypeToEnum<T>::v());
873   Eigen::array<Eigen::DenseIndex, 1> dims;
874   dims[0] = NumElements();
875   return typename TTypes<T, 1>::ConstTensor(base<T>(), dims);
876 }
877 
878 template <typename T, size_t NDIMS>
shaped(gtl::ArraySlice<int64_t> new_sizes)879 typename TTypes<T, NDIMS>::Tensor Tensor::shaped(
880     gtl::ArraySlice<int64_t> new_sizes) {
881   CheckTypeAndIsAligned(DataTypeToEnum<T>::v());
882   Eigen::array<Eigen::DenseIndex, NDIMS> dims;
883   FillDimsAndValidateCompatibleShape(new_sizes, &dims);
884   return typename TTypes<T, NDIMS>::Tensor(base<T>(), dims);
885 }
886 
887 template <typename T, size_t NDIMS>
bit_casted_shaped(gtl::ArraySlice<int64_t> new_sizes)888 typename TTypes<T, NDIMS>::Tensor Tensor::bit_casted_shaped(
889     gtl::ArraySlice<int64_t> new_sizes) {
890   CHECK(IsAligned());
891   Eigen::array<Eigen::DenseIndex, NDIMS> dims;
892   FillDimsAndValidateCompatibleShape<T>(new_sizes, &dims);
893   return typename TTypes<T, NDIMS>::Tensor(base<T>(), dims);
894 }
895 
896 template <typename T, size_t NDIMS>
unaligned_shaped(gtl::ArraySlice<int64_t> new_sizes)897 typename TTypes<T, NDIMS>::UnalignedTensor Tensor::unaligned_shaped(
898     gtl::ArraySlice<int64_t> new_sizes) {
899   CheckType(DataTypeToEnum<T>::v());
900   Eigen::array<Eigen::DenseIndex, NDIMS> dims;
901   FillDimsAndValidateCompatibleShape(new_sizes, &dims);
902   return typename TTypes<T, NDIMS>::UnalignedTensor(base<T>(), dims);
903 }
904 
905 template <typename T, size_t NDIMS>
shaped(gtl::ArraySlice<int64_t> new_sizes)906 typename TTypes<T, NDIMS>::ConstTensor Tensor::shaped(
907     gtl::ArraySlice<int64_t> new_sizes) const {
908   CheckType(DataTypeToEnum<T>::v());
909   CHECK(IsAligned()) << "ptr = " << base<void>();
910   Eigen::array<Eigen::DenseIndex, NDIMS> dims;
911   FillDimsAndValidateCompatibleShape(new_sizes, &dims);
912   return typename TTypes<T, NDIMS>::ConstTensor(base<T>(), dims);
913 }
914 
915 template <typename T, size_t NDIMS>
bit_casted_shaped(gtl::ArraySlice<int64_t> new_sizes)916 typename TTypes<T, NDIMS>::ConstTensor Tensor::bit_casted_shaped(
917     gtl::ArraySlice<int64_t> new_sizes) const {
918   CHECK(IsAligned());
919   Eigen::array<Eigen::DenseIndex, NDIMS> dims;
920   FillDimsAndValidateCompatibleShape<T>(new_sizes, &dims);
921   return typename TTypes<T, NDIMS>::ConstTensor(base<T>(), dims);
922 }
923 
924 template <typename T, size_t NDIMS>
unaligned_shaped(gtl::ArraySlice<int64_t> new_sizes)925 typename TTypes<T, NDIMS>::UnalignedConstTensor Tensor::unaligned_shaped(
926     gtl::ArraySlice<int64_t> new_sizes) const {
927   CheckType(DataTypeToEnum<T>::v());
928   Eigen::array<Eigen::DenseIndex, NDIMS> dims;
929   FillDimsAndValidateCompatibleShape(new_sizes, &dims);
930   return typename TTypes<T, NDIMS>::UnalignedConstTensor(base<T>(), dims);
931 }
932 
933 template <typename T>
scalar()934 typename TTypes<T>::Scalar Tensor::scalar() {
935   static_assert(
936       !std::is_same<T, std::string>::value,
937       "std::string is no longer a scalar type, use tensorflow::tstring");
938   CheckIsAlignedAndSingleElement();
939   return typename TTypes<T>::Scalar(base<T>());
940 }
941 
942 template <typename T>
scalar()943 typename TTypes<T>::ConstScalar Tensor::scalar() const {
944   static_assert(
945       !std::is_same<T, std::string>::value,
946       "std::string is no longer a scalar type, use tensorflow::tstring");
947   CheckIsAlignedAndSingleElement();
948   return typename TTypes<T>::ConstScalar(base<T>());
949 }
950 
951 template <typename T, size_t NDIMS>
flat_inner_dims()952 typename TTypes<T, NDIMS>::Tensor Tensor::flat_inner_dims() {
953   return shaped<T, NDIMS>(ComputeFlatInnerDims(shape_.dim_sizes(), NDIMS));
954 }
955 
956 template <typename T, size_t NDIMS>
flat_outer_dims()957 typename TTypes<T, NDIMS>::Tensor Tensor::flat_outer_dims() {
958   return shaped<T, NDIMS>(ComputeFlatOuterDims(shape_.dim_sizes(), NDIMS));
959 }
960 
961 template <typename T, size_t NDIMS>
flat_inner_outer_dims(int64_t begin)962 typename TTypes<T, NDIMS>::Tensor Tensor::flat_inner_outer_dims(int64_t begin) {
963   gtl::InlinedVector<int64_t, 4> flat_outer =
964       ComputeFlatOuterDims(shape_.dim_sizes(), begin + NDIMS);
965   return shaped<T, NDIMS>(ComputeFlatInnerDims(flat_outer, NDIMS));
966 }
967 
968 template <typename T, size_t NDIMS>
flat_inner_dims()969 typename TTypes<T, NDIMS>::ConstTensor Tensor::flat_inner_dims() const {
970   return shaped<T, NDIMS>(ComputeFlatInnerDims(shape_.dim_sizes(), NDIMS));
971 }
972 
973 template <typename T, size_t NDIMS>
flat_outer_dims()974 typename TTypes<T, NDIMS>::ConstTensor Tensor::flat_outer_dims() const {
975   return shaped<T, NDIMS>(ComputeFlatOuterDims(shape_.dim_sizes(), NDIMS));
976 }
977 
978 template <typename T, size_t NDIMS>
flat_inner_outer_dims(int64_t begin)979 typename TTypes<T, NDIMS>::ConstTensor Tensor::flat_inner_outer_dims(
980     int64_t begin) const {
981   gtl::InlinedVector<int64_t, 4> flat_outer =
982       ComputeFlatOuterDims(shape_.dim_sizes(), begin + NDIMS);
983   return shaped<T, NDIMS>(ComputeFlatInnerDims(flat_outer, NDIMS));
984 }
985 
Tensor(const Tensor & other)986 inline Tensor::Tensor(const Tensor& other)
987     : shape_(other.shape()), buf_(other.buf_) {
988   if (buf_) buf_->Ref();
989 }
990 
Tensor(Tensor && other)991 inline Tensor::Tensor(Tensor&& other)
992     : shape_(std::move(other.shape_)), buf_(other.buf_) {
993   other.buf_ = nullptr;
994 }
995 
996 class Tensor::HostScalarTensorBufferBase : public TensorBuffer {
997  public:
998   using TensorBuffer::TensorBuffer;
999   bool GetAllocatedBytes(size_t* out_bytes) const final;
1000   void FillAllocationDescription(AllocationDescription* proto) const final;
1001 };
1002 
1003 // A packed representation for a single scalar value of type `T`, and a
1004 // `TensorBuffer` implementation that describes (and manages the lifetime of)
1005 // that value.
1006 template <typename T>
1007 struct Tensor::ValueAndTensorBuffer {
1008   class HostScalarTensorBuffer : public Tensor::HostScalarTensorBufferBase {
1009    public:
HostScalarTensorBufferValueAndTensorBuffer1010     explicit HostScalarTensorBuffer(void* data)
1011         : HostScalarTensorBufferBase(data) {}
sizeValueAndTensorBuffer1012     size_t size() const final { return sizeof(T); }
root_bufferValueAndTensorBuffer1013     TensorBuffer* root_buffer() final { return this; }
1014 
1015     // Override `operator delete` so that calling `delete this` in
1016     // `core::Refcounted::Unref()` for an object of this type will free
1017     // the enclosing `ValueAndTensorBuffer` for the tensor buffer.
1018     //
1019     // NOTE(mrry): The definition of this method must be outside the class
1020     // definition in order to satisfy some compilers.
1021     static void operator delete(void* ptr);
1022 
deleteValueAndTensorBuffer1023     static void operator delete(void*, void*) {
1024       // Some compilers require an overridden class-specific deallocation
1025       // function, which will be called if placement `new` throws an
1026       // exception.
1027     }
1028 
1029    private:
~HostScalarTensorBufferValueAndTensorBuffer1030     ~HostScalarTensorBuffer() override { static_cast<T*>(data())->~T(); }
1031   };
1032 
1033   T value;
1034   HostScalarTensorBuffer tensor_buffer;
1035 };
1036 
1037 /* static */
1038 template <typename T>
delete(void * ptr)1039 void Tensor::ValueAndTensorBuffer<T>::HostScalarTensorBuffer::operator delete(
1040     void* ptr) {
1041   // Use a dummy object to compute to offset of
1042   // `ValueAndTensorBuffer::tensor_buffer`, because `offsetof()` is not
1043   // necessarily defined on this non-POD type (until C++17).
1044   //
1045   // NOTE(mrry): Using `sizeof(Tensor::ValueAndTensorBuffer<T>)` here requires
1046   // us to define this method outside the class definition, so that it is not
1047   // considered an incomplete type.
1048   typename std::aligned_storage<sizeof(Tensor::ValueAndTensorBuffer<T>),
1049                                 alignof(Tensor::ValueAndTensorBuffer<T>)>::type
1050       dummy_storage_;
1051   Tensor::ValueAndTensorBuffer<T>* dummy_object =
1052       reinterpret_cast<Tensor::ValueAndTensorBuffer<T>*>(&dummy_storage_);
1053   intptr_t offset = reinterpret_cast<intptr_t>(&dummy_object->tensor_buffer) -
1054                     reinterpret_cast<intptr_t>(dummy_object);
1055 
1056   port::AlignedFree(static_cast<char*>(ptr) - offset);
1057 }
1058 
1059 template <typename T>
Tensor(T value,host_scalar_tag tag)1060 Tensor::Tensor(T value, host_scalar_tag tag) {
1061   auto* value_and_buf = static_cast<Tensor::ValueAndTensorBuffer<T>*>(
1062       port::AlignedMalloc(sizeof(typename Tensor::ValueAndTensorBuffer<T>),
1063                           EIGEN_MAX_ALIGN_BYTES));
1064   new (&value_and_buf->value) T(std::move(value));
1065   new (&value_and_buf->tensor_buffer)
1066       typename Tensor::ValueAndTensorBuffer<T>::HostScalarTensorBuffer(
1067           value_and_buf);
1068   buf_ = &value_and_buf->tensor_buffer;
1069   set_dtype(DataTypeToEnum<T>::value);
1070 }
1071 
1072 inline Tensor& Tensor::operator=(Tensor&& other) {
1073   // Avoid self-assignment, since we might destroy our underlying buffer.
1074   if (&other != this) {
1075     shape_ = std::move(other.shape_);
1076     if (buf_) buf_->Unref();
1077     buf_ = other.buf_;
1078     other.buf_ = nullptr;
1079   }
1080   return *this;
1081 }
1082 
1083 // END_SKIP_DOXYGEN
1084 
1085 }  // namespace tensorflow
1086 
1087 #endif  // TENSORFLOW_CORE_FRAMEWORK_TENSOR_H_
1088