1*5f39d1b3SJooyung Han // Copyright 2015 The Gemmlowp Authors. All Rights Reserved.
2*5f39d1b3SJooyung Han //
3*5f39d1b3SJooyung Han // Licensed under the Apache License, Version 2.0 (the "License");
4*5f39d1b3SJooyung Han // you may not use this file except in compliance with the License.
5*5f39d1b3SJooyung Han // You may obtain a copy of the License at
6*5f39d1b3SJooyung Han //
7*5f39d1b3SJooyung Han // http://www.apache.org/licenses/LICENSE-2.0
8*5f39d1b3SJooyung Han //
9*5f39d1b3SJooyung Han // Unless required by applicable law or agreed to in writing, software
10*5f39d1b3SJooyung Han // distributed under the License is distributed on an "AS IS" BASIS,
11*5f39d1b3SJooyung Han // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*5f39d1b3SJooyung Han // See the License for the specific language governing permissions and
13*5f39d1b3SJooyung Han // limitations under the License.
14*5f39d1b3SJooyung Han
15*5f39d1b3SJooyung Han // pack.h: packing blocks of the LHS and RHS into the data layout
16*5f39d1b3SJooyung Han // that is expected by compute.h and eventually by kernels.
17*5f39d1b3SJooyung Han // Because this data layout depends on the kernel format, code here
18*5f39d1b3SJooyung Han // is templated in KernelLhsFormat/KernelRhsFormat.
19*5f39d1b3SJooyung Han //
20*5f39d1b3SJooyung Han // Readers note: an important theme around here is that we try hard
21*5f39d1b3SJooyung Han // to handle both Lhs and Rhs with a single piece of code. We indifferently
22*5f39d1b3SJooyung Han // refer to the Lhs and Rhs as a 'Side'. Instead of addressing matrices
23*5f39d1b3SJooyung Han // by (row, column) indices, we address them by (width, depth), as explained
24*5f39d1b3SJooyung Han // in kernel.h. This allows us to handle both Lhs and Rhs on an equal footing,
25*5f39d1b3SJooyung Han // at once.
26*5f39d1b3SJooyung Han
27*5f39d1b3SJooyung Han #ifndef GEMMLOWP_INTERNAL_PACK_H_
28*5f39d1b3SJooyung Han #define GEMMLOWP_INTERNAL_PACK_H_
29*5f39d1b3SJooyung Han
30*5f39d1b3SJooyung Han #include <cstring>
31*5f39d1b3SJooyung Han
32*5f39d1b3SJooyung Han #include "allocator.h"
33*5f39d1b3SJooyung Han #include "block_params.h"
34*5f39d1b3SJooyung Han #include "common.h"
35*5f39d1b3SJooyung Han #include "kernel.h"
36*5f39d1b3SJooyung Han
37*5f39d1b3SJooyung Han namespace gemmlowp {
38*5f39d1b3SJooyung Han
39*5f39d1b3SJooyung Han // A PackedSideBlock instance is a packed block of either the LHS or RHS
40*5f39d1b3SJooyung Han // (whence the generic 'Side' name).
41*5f39d1b3SJooyung Han //
42*5f39d1b3SJooyung Han // 'Packed' means that it is laid out in the storage order that
43*5f39d1b3SJooyung Han // is expected by the specified kernel format. From a block of the input
44*5f39d1b3SJooyung Han // LHS or RHS matrix, one obtains a PackedSideBlock by calling PackLhs()
45*5f39d1b3SJooyung Han // or PackRhs().
46*5f39d1b3SJooyung Han template <typename tKernelSideFormat>
47*5f39d1b3SJooyung Han class PackedSideBlock {
48*5f39d1b3SJooyung Han public:
49*5f39d1b3SJooyung Han typedef tKernelSideFormat KernelSideFormat;
50*5f39d1b3SJooyung Han
PackedSideBlock(Side side,Allocator * allocator,const BlockParams & block_params)51*5f39d1b3SJooyung Han PackedSideBlock(Side side, Allocator* allocator,
52*5f39d1b3SJooyung Han const BlockParams& block_params)
53*5f39d1b3SJooyung Han : allocator_(allocator), pos_(0) {
54*5f39d1b3SJooyung Han GetSideBlockParams(side, ¶ms_, block_params);
55*5f39d1b3SJooyung Han data_handle_ =
56*5f39d1b3SJooyung Han allocator_->Reserve<std::uint8_t>(params_.l2_width * params_.l2_depth);
57*5f39d1b3SJooyung Han sums_of_each_slice_handle_ =
58*5f39d1b3SJooyung Han allocator_->Reserve<std::int32_t>(params_.l2_width);
59*5f39d1b3SJooyung Han }
60*5f39d1b3SJooyung Han
~PackedSideBlock()61*5f39d1b3SJooyung Han ~PackedSideBlock() {}
62*5f39d1b3SJooyung Han
seek_run(int start_width,int start_depth)63*5f39d1b3SJooyung Han void seek_run(int start_width, int start_depth) const {
64*5f39d1b3SJooyung Han int kernel_run_depth =
65*5f39d1b3SJooyung Han std::min<int>(params_.l1_depth, params_.l2_depth - start_depth);
66*5f39d1b3SJooyung Han pos_ = params_.l2_width * start_depth + start_width * kernel_run_depth;
67*5f39d1b3SJooyung Han }
68*5f39d1b3SJooyung Han
seek_next_cell()69*5f39d1b3SJooyung Han void seek_next_cell() const { pos_ += KernelSideFormat::Cell::kSize; }
70*5f39d1b3SJooyung Han
seek_forward_n_cells(int n)71*5f39d1b3SJooyung Han void seek_forward_n_cells(int n) const {
72*5f39d1b3SJooyung Han pos_ += n * KernelSideFormat::Cell::kSize;
73*5f39d1b3SJooyung Han }
74*5f39d1b3SJooyung Han
75*5f39d1b3SJooyung Han // TODO(suharshs): The datatype can now be int8 as well. We could introduce a
76*5f39d1b3SJooyung Han // new int8 current_data impl as well. This change would propagate to all pack
77*5f39d1b3SJooyung Han // impls and the Kernel::Run API, which all assume uint8. For now we leave
78*5f39d1b3SJooyung Han // this as-is pending future refactor.
current_data()79*5f39d1b3SJooyung Han const std::uint8_t* current_data() const {
80*5f39d1b3SJooyung Han return allocator_->GetPointer<std::uint8_t>(data_handle_) + pos_;
81*5f39d1b3SJooyung Han }
82*5f39d1b3SJooyung Han
current_data()83*5f39d1b3SJooyung Han std::uint8_t* current_data() {
84*5f39d1b3SJooyung Han return allocator_->GetPointer<std::uint8_t>(data_handle_) + pos_;
85*5f39d1b3SJooyung Han }
86*5f39d1b3SJooyung Han
sums_of_each_slice()87*5f39d1b3SJooyung Han std::int32_t* sums_of_each_slice() {
88*5f39d1b3SJooyung Han return allocator_->GetPointer<std::int32_t>(sums_of_each_slice_handle_);
89*5f39d1b3SJooyung Han }
90*5f39d1b3SJooyung Han
sums_of_each_slice()91*5f39d1b3SJooyung Han const std::int32_t* sums_of_each_slice() const {
92*5f39d1b3SJooyung Han return allocator_->GetPointer<const std::int32_t>(
93*5f39d1b3SJooyung Han sums_of_each_slice_handle_);
94*5f39d1b3SJooyung Han }
95*5f39d1b3SJooyung Han
params()96*5f39d1b3SJooyung Han const SideBlockParams& params() const { return params_; }
97*5f39d1b3SJooyung Han
98*5f39d1b3SJooyung Han private:
99*5f39d1b3SJooyung Han // The block size parameters that this PackedSizeBlock follows.
100*5f39d1b3SJooyung Han // The L2 parameters determine its overall size, while the L1 parameters,
101*5f39d1b3SJooyung Han // together with the kernel format template parameter, determine
102*5f39d1b3SJooyung Han // the fine details of the storage/traversal order.
103*5f39d1b3SJooyung Han SideBlockParams params_;
104*5f39d1b3SJooyung Han
105*5f39d1b3SJooyung Han // Pointer to the allocator provided by the caller. Not owned.
106*5f39d1b3SJooyung Han // The Allocator is assumed to outlive the PackedSideBlock.
107*5f39d1b3SJooyung Han Allocator* const allocator_;
108*5f39d1b3SJooyung Han
109*5f39d1b3SJooyung Han // Handle on the buffer backing this packed block. Owned.
110*5f39d1b3SJooyung Han Allocator::Handle data_handle_;
111*5f39d1b3SJooyung Han
112*5f39d1b3SJooyung Han // Handle on the additional buffer backing the vector of sums of slices
113*5f39d1b3SJooyung Han // associated with this block. Owned.
114*5f39d1b3SJooyung Han Allocator::Handle sums_of_each_slice_handle_;
115*5f39d1b3SJooyung Han
116*5f39d1b3SJooyung Han // pos_ is the current position in the buffer, which we access
117*5f39d1b3SJooyung Han // sequentially, like a file.
118*5f39d1b3SJooyung Han // The idea is that we pack data in the same order as it is
119*5f39d1b3SJooyung Han // going to be traversed during the computation, which for
120*5f39d1b3SJooyung Han // cache-friendliness reasons is complicated to random-access,
121*5f39d1b3SJooyung Han // as the offsets calculations would be intricate. So we
122*5f39d1b3SJooyung Han // give up random-access addressing, and instead content ourselves
123*5f39d1b3SJooyung Han // with sequential access.
124*5f39d1b3SJooyung Han //
125*5f39d1b3SJooyung Han // pos_ is mutable because during the computation we will want to
126*5f39d1b3SJooyung Han // be able to iterate on the data in a const PackedSideBlock.
127*5f39d1b3SJooyung Han mutable int pos_;
128*5f39d1b3SJooyung Han };
129*5f39d1b3SJooyung Han
130*5f39d1b3SJooyung Han // WidthMajor and DepthMajor are custom phrases modelled after the
131*5f39d1b3SJooyung Han // standard terminology 'row-major' and 'column-major'. Their meaning
132*5f39d1b3SJooyung Han // should be transparent once one has read the explanation in kernel.h:
133*5f39d1b3SJooyung Han // for example, in the Lhs, the 'width' dimension is the rows dimension,
134*5f39d1b3SJooyung Han // so there WidthMajor means RowMajor, while in the Rhs it is the opposite.
135*5f39d1b3SJooyung Han // Another way to put it: WidthMajor means that contiguous storage is used
136*5f39d1b3SJooyung Han // for entries having the same 'width' index.
137*5f39d1b3SJooyung Han enum class SideMapOrder { WidthMajor, DepthMajor };
138*5f39d1b3SJooyung Han
139*5f39d1b3SJooyung Han // Similar to MatrixMap from map.h, but in terms of width/depth instead of
140*5f39d1b3SJooyung Han // rows/columns. Used to address blocks of the input LHS/RHS matrices when
141*5f39d1b3SJooyung Han // packing them.
142*5f39d1b3SJooyung Han template <typename tScalar, SideMapOrder tOrder>
143*5f39d1b3SJooyung Han class SideMap {
144*5f39d1b3SJooyung Han public:
145*5f39d1b3SJooyung Han typedef tScalar Scalar;
146*5f39d1b3SJooyung Han static constexpr SideMapOrder kOrder = tOrder;
147*5f39d1b3SJooyung Han
SideMap(Scalar * data,int width,int depth,int stride)148*5f39d1b3SJooyung Han SideMap(Scalar* data, int width, int depth, int stride)
149*5f39d1b3SJooyung Han : data_(data), width_(width), depth_(depth), stride_(stride) {}
150*5f39d1b3SJooyung Han
SideMap(Scalar * data,int width,int depth)151*5f39d1b3SJooyung Han SideMap(Scalar* data, int width, int depth)
152*5f39d1b3SJooyung Han : data_(data), width_(width), depth_(depth) {
153*5f39d1b3SJooyung Han stride_ = kOrder == SideMapOrder::WidthMajor ? depth_ : width_;
154*5f39d1b3SJooyung Han }
155*5f39d1b3SJooyung Han
SideMap(const SideMap & other)156*5f39d1b3SJooyung Han SideMap(const SideMap& other)
157*5f39d1b3SJooyung Han : data_(other.data_),
158*5f39d1b3SJooyung Han width_(other.width_),
159*5f39d1b3SJooyung Han depth_(other.depth_),
160*5f39d1b3SJooyung Han stride_(other.stride_) {}
161*5f39d1b3SJooyung Han
width()162*5f39d1b3SJooyung Han int width() const { return width_; }
depth()163*5f39d1b3SJooyung Han int depth() const { return depth_; }
stride()164*5f39d1b3SJooyung Han int stride() const { return stride_; }
width_stride()165*5f39d1b3SJooyung Han int width_stride() const {
166*5f39d1b3SJooyung Han return kOrder == SideMapOrder::DepthMajor ? 1 : stride_;
167*5f39d1b3SJooyung Han }
depth_stride()168*5f39d1b3SJooyung Han int depth_stride() const {
169*5f39d1b3SJooyung Han return kOrder == SideMapOrder::WidthMajor ? 1 : stride_;
170*5f39d1b3SJooyung Han }
data()171*5f39d1b3SJooyung Han Scalar* data() const { return data_; }
data(int w,int d)172*5f39d1b3SJooyung Han Scalar* data(int w, int d) const {
173*5f39d1b3SJooyung Han return data_ + w * width_stride() + d * depth_stride();
174*5f39d1b3SJooyung Han }
operator()175*5f39d1b3SJooyung Han Scalar operator()(int w, int d) const { return *data(w, d); }
operator()176*5f39d1b3SJooyung Han Scalar& operator()(int w, int d) { return *data(w, d); }
177*5f39d1b3SJooyung Han
block(int start_width,int start_depth,int block_width,int block_depth)178*5f39d1b3SJooyung Han SideMap block(int start_width, int start_depth, int block_width,
179*5f39d1b3SJooyung Han int block_depth) const {
180*5f39d1b3SJooyung Han assert(start_width >= 0);
181*5f39d1b3SJooyung Han assert(start_width + block_width <= width_);
182*5f39d1b3SJooyung Han assert(start_depth >= 0);
183*5f39d1b3SJooyung Han assert(start_depth + block_depth <= depth_);
184*5f39d1b3SJooyung Han
185*5f39d1b3SJooyung Han return SideMap(data(start_width, start_depth), block_width, block_depth,
186*5f39d1b3SJooyung Han stride_);
187*5f39d1b3SJooyung Han }
188*5f39d1b3SJooyung Han
189*5f39d1b3SJooyung Han private:
190*5f39d1b3SJooyung Han Scalar* data_; // not owned.
191*5f39d1b3SJooyung Han int width_, depth_, stride_;
192*5f39d1b3SJooyung Han };
193*5f39d1b3SJooyung Han
194*5f39d1b3SJooyung Han // A PackingRegisterBlock is a small fixed-size block of a matrix being
195*5f39d1b3SJooyung Han // packed. This class is the generic non-optimized implementation,
196*5f39d1b3SJooyung Han // it is inherited by the generic implementation of PackingRegisterBlock,
197*5f39d1b3SJooyung Han // which may be overriden by template specialization. Overriding it is how
198*5f39d1b3SJooyung Han // one may provide optimized packing code paths.
199*5f39d1b3SJooyung Han //
200*5f39d1b3SJooyung Han // The packing of a block proceeds in two steps:
201*5f39d1b3SJooyung Han // 1. Ensuring that we have a complete block of source data, i.e. a block of
202*5f39d1b3SJooyung Han // the compile-time prescribed size. This is where we handle unaligned
203*5f39d1b3SJooyung Han // boundaries: if we don't have a complete block of source data, then
204*5f39d1b3SJooyung Han // we copy and zero-extend it into a local temporary (complete_src_),
205*5f39d1b3SJooyung Han // see MakeCompleteSrc. In the generic case, we do have a complete block,
206*5f39d1b3SJooyung Han // so we just use it in-place, see UseCompleteSrcInPlace.
207*5f39d1b3SJooyung Han // 2. Packing a complete block into the destination, see Pack. This is the
208*5f39d1b3SJooyung Han // most critical part, so it's convenient that unaligned boundaries have
209*5f39d1b3SJooyung Han // already been handled in step 1.
210*5f39d1b3SJooyung Han template <typename SrcMapType, typename PackedSideBlock>
211*5f39d1b3SJooyung Han class PackingRegisterBlockBase {
212*5f39d1b3SJooyung Han public:
213*5f39d1b3SJooyung Han typedef typename PackedSideBlock::KernelSideFormat KernelSideFormat;
214*5f39d1b3SJooyung Han typedef typename KernelSideFormat::Cell CellFormat;
215*5f39d1b3SJooyung Han typedef typename KernelSideFormat::InputScalar KernelInputScalar;
216*5f39d1b3SJooyung Han typedef typename KernelSideFormat::Scalar KernelScalar;
217*5f39d1b3SJooyung Han static constexpr int kCells = KernelSideFormat::kCells;
218*5f39d1b3SJooyung Han static constexpr int kCellWidth = CellFormat::kWidth;
219*5f39d1b3SJooyung Han static constexpr int kKernelWidth = CellFormat::kWidth * kCells;
220*5f39d1b3SJooyung Han static constexpr int kCellDepth = CellFormat::kDepth;
221*5f39d1b3SJooyung Han static constexpr int kCellSize = CellFormat::kSize;
222*5f39d1b3SJooyung Han static constexpr SideMapOrder kSrcOrder = SrcMapType::kOrder;
223*5f39d1b3SJooyung Han static constexpr int kZeroPointInputValue =
224*5f39d1b3SJooyung Han ZeroPointInputValue<KernelInputScalar, KernelScalar>::kValue;
225*5f39d1b3SJooyung Han
PackingRegisterBlockBase()226*5f39d1b3SJooyung Han PackingRegisterBlockBase() : complete_src_(nullptr, 0, 0, 0) {}
227*5f39d1b3SJooyung Han
228*5f39d1b3SJooyung Han protected:
229*5f39d1b3SJooyung Han // The source data that's ready for packing. May point to
230*5f39d1b3SJooyung Han // in-place actual source data if it's already a complete block,
231*5f39d1b3SJooyung Han // (see UseCompleteSrcInPlace)
232*5f39d1b3SJooyung Han // or to the local buf_ below into which we copy incomplete blocks
233*5f39d1b3SJooyung Han // (see MakeCompleteSrc)
234*5f39d1b3SJooyung Han SrcMapType complete_src_;
235*5f39d1b3SJooyung Han
236*5f39d1b3SJooyung Han // Temporary buffer for loading incomplete blocks to,
237*5f39d1b3SJooyung Han // in the source storage order
238*5f39d1b3SJooyung Han std::uint8_t buf_[kKernelWidth * kRegisterSize];
239*5f39d1b3SJooyung Han
240*5f39d1b3SJooyung Han public:
241*5f39d1b3SJooyung Han // Selects a block if in-place source data that's already a complete block.
UseCompleteSrcInPlace(const SrcMapType & src)242*5f39d1b3SJooyung Han void UseCompleteSrcInPlace(const SrcMapType& src) { complete_src_ = src; }
243*5f39d1b3SJooyung Han // Copies an incomplete block of source data into a local temporary
244*5f39d1b3SJooyung Han // complete block by zero-extending it.
MakeCompleteSrc(const SrcMapType & src)245*5f39d1b3SJooyung Han void MakeCompleteSrc(const SrcMapType& src) {
246*5f39d1b3SJooyung Han memset(buf_, kZeroPointInputValue, kKernelWidth * kRegisterSize);
247*5f39d1b3SJooyung Han if (kSrcOrder == SideMapOrder::WidthMajor) {
248*5f39d1b3SJooyung Han for (int w = 0; w < src.width(); w++) {
249*5f39d1b3SJooyung Han memcpy(buf_ + w * kRegisterSize, src.data(w, 0), src.depth());
250*5f39d1b3SJooyung Han }
251*5f39d1b3SJooyung Han } else {
252*5f39d1b3SJooyung Han assert(kSrcOrder == SideMapOrder::DepthMajor);
253*5f39d1b3SJooyung Han for (int d = 0; d < src.depth(); d++) {
254*5f39d1b3SJooyung Han memcpy(buf_ + d * kKernelWidth, src.data(0, d), src.width());
255*5f39d1b3SJooyung Han }
256*5f39d1b3SJooyung Han }
257*5f39d1b3SJooyung Han
258*5f39d1b3SJooyung Han // Since the KernelInputScalar type may not be uint8, we need to cast buf_.
259*5f39d1b3SJooyung Han complete_src_ = SrcMapType(reinterpret_cast<KernelInputScalar*>(buf_),
260*5f39d1b3SJooyung Han kKernelWidth, kRegisterSize);
261*5f39d1b3SJooyung Han }
262*5f39d1b3SJooyung Han // Packs a complete block into the destination. This is the most
263*5f39d1b3SJooyung Han // critical part and the part that we most typically want to
264*5f39d1b3SJooyung Han // override in architecture-specific optimized specializations.
Pack(PackedSideBlock * dst,int start_width)265*5f39d1b3SJooyung Han void Pack(PackedSideBlock* dst, int start_width) {
266*5f39d1b3SJooyung Han std::uint8_t* dst_ptr = dst->current_data();
267*5f39d1b3SJooyung Han for (int cell_start_depth = 0; cell_start_depth < kRegisterSize;
268*5f39d1b3SJooyung Han cell_start_depth += kCellDepth) {
269*5f39d1b3SJooyung Han for (int cell_start_width = 0; cell_start_width < kKernelWidth;
270*5f39d1b3SJooyung Han cell_start_width += kCellWidth) {
271*5f39d1b3SJooyung Han std::int32_t* cell_sums_of_each_slice_ptr =
272*5f39d1b3SJooyung Han dst->sums_of_each_slice() + start_width + cell_start_width;
273*5f39d1b3SJooyung Han const SideMap<const std::uint8_t, kSrcOrder> src_cell_map(
274*5f39d1b3SJooyung Han complete_src_.block(cell_start_width, cell_start_depth, kCellWidth,
275*5f39d1b3SJooyung Han kCellDepth));
276*5f39d1b3SJooyung Han for (int w = 0; w < kCellWidth; w++) {
277*5f39d1b3SJooyung Han std::int32_t sum = 0;
278*5f39d1b3SJooyung Han for (int d = 0; d < kCellDepth; d++) {
279*5f39d1b3SJooyung Han const std::uint8_t src_val = src_cell_map(w, d);
280*5f39d1b3SJooyung Han const std::int16_t kernel_val_unwrapped =
281*5f39d1b3SJooyung Han src_val - kZeroPointInputValue;
282*5f39d1b3SJooyung Han const std::uint8_t kernel_val_uint8 = kernel_val_unwrapped;
283*5f39d1b3SJooyung Han dst_ptr[OffsetIntoCell<CellFormat>(w, d)] = kernel_val_uint8;
284*5f39d1b3SJooyung Han sum += kernel_val_unwrapped;
285*5f39d1b3SJooyung Han }
286*5f39d1b3SJooyung Han cell_sums_of_each_slice_ptr[w] += sum;
287*5f39d1b3SJooyung Han }
288*5f39d1b3SJooyung Han dst_ptr += kCellSize;
289*5f39d1b3SJooyung Han }
290*5f39d1b3SJooyung Han }
291*5f39d1b3SJooyung Han dst->seek_forward_n_cells(kCells * kRegisterSize / kCellDepth);
292*5f39d1b3SJooyung Han }
293*5f39d1b3SJooyung Han };
294*5f39d1b3SJooyung Han
295*5f39d1b3SJooyung Han template <typename SrcMapType, typename PackedSideBlock>
296*5f39d1b3SJooyung Han class PackingRegisterBlock
297*5f39d1b3SJooyung Han : public PackingRegisterBlockBase<SrcMapType, PackedSideBlock> {};
298*5f39d1b3SJooyung Han
299*5f39d1b3SJooyung Han // Large-scale implementation of packing.
300*5f39d1b3SJooyung Han template <typename SrcMapType, typename PackedSideBlock>
301*5f39d1b3SJooyung Han class PackSideBlockImpl {
302*5f39d1b3SJooyung Han public:
303*5f39d1b3SJooyung Han typedef typename PackedSideBlock::KernelSideFormat KernelSideFormat;
304*5f39d1b3SJooyung Han typedef typename KernelSideFormat::Cell CellFormat;
305*5f39d1b3SJooyung Han static constexpr int kCells = KernelSideFormat::kCells;
306*5f39d1b3SJooyung Han static constexpr int kCellWidth = CellFormat::kWidth;
307*5f39d1b3SJooyung Han static constexpr int kKernelWidth = CellFormat::kWidth * kCells;
308*5f39d1b3SJooyung Han static constexpr int kCellDepth = CellFormat::kDepth;
309*5f39d1b3SJooyung Han
310*5f39d1b3SJooyung Han typedef PackingRegisterBlock<SrcMapType, PackedSideBlock>
311*5f39d1b3SJooyung Han PackingRegisterBlockType;
312*5f39d1b3SJooyung Han
PackSideBlockImpl(PackedSideBlock * packed_side_block,const SrcMapType & src_map)313*5f39d1b3SJooyung Han PackSideBlockImpl(PackedSideBlock* packed_side_block,
314*5f39d1b3SJooyung Han const SrcMapType& src_map)
315*5f39d1b3SJooyung Han : packed_side_block_(packed_side_block), src_map_(src_map) {}
316*5f39d1b3SJooyung Han
packed_side_block()317*5f39d1b3SJooyung Han PackedSideBlock* packed_side_block() const { return packed_side_block_; }
318*5f39d1b3SJooyung Han
src_map()319*5f39d1b3SJooyung Han const SrcMapType& src_map() const { return src_map_; }
320*5f39d1b3SJooyung Han
321*5f39d1b3SJooyung Han // The public entry point to pack a block.
PackL2()322*5f39d1b3SJooyung Han void PackL2() {
323*5f39d1b3SJooyung Han memset(packed_side_block_->sums_of_each_slice(), 0,
324*5f39d1b3SJooyung Han sizeof(std::int32_t) * packed_side_block_->params().l2_width);
325*5f39d1b3SJooyung Han for (int d = 0; d < src_map_.depth();
326*5f39d1b3SJooyung Han d += packed_side_block_->params().l1_depth) {
327*5f39d1b3SJooyung Han int ds = std::min<int>(packed_side_block_->params().l1_depth,
328*5f39d1b3SJooyung Han src_map_.depth() - d);
329*5f39d1b3SJooyung Han
330*5f39d1b3SJooyung Han for (int w = 0; w < src_map_.width();
331*5f39d1b3SJooyung Han w += packed_side_block_->params().l1_width) {
332*5f39d1b3SJooyung Han int ws = std::min<int>(packed_side_block_->params().l1_width,
333*5f39d1b3SJooyung Han src_map_.width() - w);
334*5f39d1b3SJooyung Han
335*5f39d1b3SJooyung Han PrefetchL1(w, ws, d, ds);
336*5f39d1b3SJooyung Han PackL1(w, ws, d, ds);
337*5f39d1b3SJooyung Han }
338*5f39d1b3SJooyung Han }
339*5f39d1b3SJooyung Han }
340*5f39d1b3SJooyung Han
341*5f39d1b3SJooyung Han protected:
342*5f39d1b3SJooyung Han // The intermediate-level loops, between PackL2 and PackRun.
PackL1(int start_width,int width,int start_depth,int depth)343*5f39d1b3SJooyung Han void PackL1(int start_width, int width, int start_depth, int depth) {
344*5f39d1b3SJooyung Han for (int w = 0; w < width; w += kKernelWidth) {
345*5f39d1b3SJooyung Han int ws = std::min(+kKernelWidth, width - w);
346*5f39d1b3SJooyung Han packed_side_block_->seek_run(start_width + w, start_depth);
347*5f39d1b3SJooyung Han PackRun(start_width + w, ws, start_depth, depth);
348*5f39d1b3SJooyung Han }
349*5f39d1b3SJooyung Han }
350*5f39d1b3SJooyung Han
351*5f39d1b3SJooyung Han // Prefetches the data that will be read by PackL1.
PrefetchL1(int start_width,int width,int start_depth,int depth)352*5f39d1b3SJooyung Han void PrefetchL1(int start_width, int width, int start_depth, int depth) {
353*5f39d1b3SJooyung Han if (SrcMapType::kOrder == SideMapOrder::WidthMajor) {
354*5f39d1b3SJooyung Han for (int d = 0; d < depth; d += kDefaultCacheLineSize) {
355*5f39d1b3SJooyung Han for (int w = 0; w < width; w += 1) {
356*5f39d1b3SJooyung Han Prefetch(src_map_.data(start_width + w, start_depth + d));
357*5f39d1b3SJooyung Han }
358*5f39d1b3SJooyung Han }
359*5f39d1b3SJooyung Han } else {
360*5f39d1b3SJooyung Han for (int d = 0; d < depth; d++) {
361*5f39d1b3SJooyung Han for (int w = 0; w < width; w += kDefaultCacheLineSize) {
362*5f39d1b3SJooyung Han Prefetch(src_map_.data(start_width + w, start_depth + d));
363*5f39d1b3SJooyung Han }
364*5f39d1b3SJooyung Han }
365*5f39d1b3SJooyung Han }
366*5f39d1b3SJooyung Han }
367*5f39d1b3SJooyung Han
368*5f39d1b3SJooyung Han // PackRun packs only a run i.e. is the inner loop in the depth dimension.
PackRun(int start_width,int width,int start_depth,int depth)369*5f39d1b3SJooyung Han void PackRun(int start_width, int width, int start_depth, int depth) {
370*5f39d1b3SJooyung Han PackingRegisterBlockType b;
371*5f39d1b3SJooyung Han if (width == kKernelWidth) {
372*5f39d1b3SJooyung Han const int register_aligned_depth = RoundDown<kRegisterSize>(depth);
373*5f39d1b3SJooyung Han if (register_aligned_depth) {
374*5f39d1b3SJooyung Han for (int d = 0; d < register_aligned_depth; d += kRegisterSize) {
375*5f39d1b3SJooyung Han b.UseCompleteSrcInPlace(src_map_.block(start_width, start_depth + d,
376*5f39d1b3SJooyung Han width, kRegisterSize));
377*5f39d1b3SJooyung Han b.Pack(packed_side_block_, start_width);
378*5f39d1b3SJooyung Han }
379*5f39d1b3SJooyung Han }
380*5f39d1b3SJooyung Han if (register_aligned_depth < depth) {
381*5f39d1b3SJooyung Han b.MakeCompleteSrc(
382*5f39d1b3SJooyung Han src_map_.block(start_width, start_depth + register_aligned_depth,
383*5f39d1b3SJooyung Han width, depth - register_aligned_depth));
384*5f39d1b3SJooyung Han b.Pack(packed_side_block_, start_width);
385*5f39d1b3SJooyung Han }
386*5f39d1b3SJooyung Han } else {
387*5f39d1b3SJooyung Han assert(width < kKernelWidth);
388*5f39d1b3SJooyung Han for (int d = 0; d < depth; d += kRegisterSize) {
389*5f39d1b3SJooyung Han const int ds = std::min(+kRegisterSize, depth - d);
390*5f39d1b3SJooyung Han b.MakeCompleteSrc(
391*5f39d1b3SJooyung Han src_map_.block(start_width, start_depth + d, width, ds));
392*5f39d1b3SJooyung Han b.Pack(packed_side_block_, start_width);
393*5f39d1b3SJooyung Han }
394*5f39d1b3SJooyung Han }
395*5f39d1b3SJooyung Han }
396*5f39d1b3SJooyung Han
397*5f39d1b3SJooyung Han // The PackedSideBlock being packed, i.e. the 'destination'.
398*5f39d1b3SJooyung Han PackedSideBlock* const packed_side_block_;
399*5f39d1b3SJooyung Han
400*5f39d1b3SJooyung Han // A map on the block of the original matrix block being packed,
401*5f39d1b3SJooyung Han // i.e. the 'source'.
402*5f39d1b3SJooyung Han const SrcMapType& src_map_;
403*5f39d1b3SJooyung Han };
404*5f39d1b3SJooyung Han
405*5f39d1b3SJooyung Han // Packs a block of the input LHS matrix, into a PackedSideBlock.
406*5f39d1b3SJooyung Han template <typename PackedSideBlock, typename MatrixMapType>
PackLhs(PackedSideBlock * dst,const MatrixMapType & src)407*5f39d1b3SJooyung Han void PackLhs(PackedSideBlock* dst, const MatrixMapType& src) {
408*5f39d1b3SJooyung Han ScopedProfilingLabel label("pack LHS");
409*5f39d1b3SJooyung Han static const SideMapOrder kSideMapOrder =
410*5f39d1b3SJooyung Han MatrixMapType::kOrder == MapOrder::RowMajor ? SideMapOrder::WidthMajor
411*5f39d1b3SJooyung Han : SideMapOrder::DepthMajor;
412*5f39d1b3SJooyung Han typedef typename MatrixMapType::Scalar Scalar;
413*5f39d1b3SJooyung Han typedef SideMap<Scalar, kSideMapOrder> SideMapType;
414*5f39d1b3SJooyung Han SideMapType src_side_map(src.data(), src.rows(), src.cols(), src.stride());
415*5f39d1b3SJooyung Han typedef PackSideBlockImpl<SideMapType, PackedSideBlock> ImplType;
416*5f39d1b3SJooyung Han ImplType impl(dst, src_side_map);
417*5f39d1b3SJooyung Han impl.PackL2();
418*5f39d1b3SJooyung Han }
419*5f39d1b3SJooyung Han
420*5f39d1b3SJooyung Han // Packs a block of the input RHS matrix, into a PackedSideBlock.
421*5f39d1b3SJooyung Han template <typename PackedSideBlock, typename MatrixMapType>
PackRhs(PackedSideBlock * dst,const MatrixMapType & src)422*5f39d1b3SJooyung Han void PackRhs(PackedSideBlock* dst, const MatrixMapType& src) {
423*5f39d1b3SJooyung Han ScopedProfilingLabel label("pack RHS");
424*5f39d1b3SJooyung Han static const SideMapOrder kSideMapOrder =
425*5f39d1b3SJooyung Han MatrixMapType::kOrder == MapOrder::ColMajor ? SideMapOrder::WidthMajor
426*5f39d1b3SJooyung Han : SideMapOrder::DepthMajor;
427*5f39d1b3SJooyung Han typedef typename MatrixMapType::Scalar Scalar;
428*5f39d1b3SJooyung Han typedef SideMap<Scalar, kSideMapOrder> SideMapType;
429*5f39d1b3SJooyung Han SideMapType src_side_map(src.data(), src.cols(), src.rows(), src.stride());
430*5f39d1b3SJooyung Han typedef PackSideBlockImpl<SideMapType, PackedSideBlock> ImplType;
431*5f39d1b3SJooyung Han ImplType impl(dst, src_side_map);
432*5f39d1b3SJooyung Han impl.PackL2();
433*5f39d1b3SJooyung Han }
434*5f39d1b3SJooyung Han
435*5f39d1b3SJooyung Han } // namespace gemmlowp
436*5f39d1b3SJooyung Han
437*5f39d1b3SJooyung Han #ifdef GEMMLOWP_NEON
438*5f39d1b3SJooyung Han #include "pack_neon.h"
439*5f39d1b3SJooyung Han #elif defined(GEMMLOWP_SSE4)
440*5f39d1b3SJooyung Han #include "pack_sse.h"
441*5f39d1b3SJooyung Han #elif defined(GEMMLOWP_AVX2)
442*5f39d1b3SJooyung Han #include "pack_avx.h"
443*5f39d1b3SJooyung Han #elif defined(GEMMLOWP_MSA)
444*5f39d1b3SJooyung Han #include "pack_msa.h"
445*5f39d1b3SJooyung Han #endif
446*5f39d1b3SJooyung Han
447*5f39d1b3SJooyung Han #endif // GEMMLOWP_INTERNAL_PACK_H_
448