2 * Copyright (c) 2015-2016 ARM Limited
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are
16 * met: redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer;
18 * redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution;
21 * neither the name of the copyright holders nor the names of its
22 * contributors may be used to endorse or promote products derived from
23 * this software without specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 * Authors: Giacomo Gabrielli
38 * Nathanael Premillieu
42 /** \file arch/generic/vec_reg.hh
43 * Vector Registers layout specification.
45 * This register type is to be used to model the SIMD registers.
46 * It takes into account the possibility that different architectural names
47 * may overlap (like for ARMv8 AArch32 for example).
49 * The design is having a basic vector register container that holds the
50 * bytes, unaware of anything else. This is implemented by VecRegContainer.
51 * As the (maximum) length of the physical vector register is a compile-time
52 * constant, it is defined as a template parameter.
54 * This file also describes two views of the container that have semantic
55 * information about the bytes. The first of this views is VecRegT.
56 * A VecRegT is a view of a VecRegContainer (by reference). The VecRegT has
57 * a type (VecElem) to which bytes are casted, and the amount of such
58 * elements that the vector contains (NumElems). The size of a view,
59 * calculated as sizeof(VecElem) * NumElems must match the size of the
60 * underlying container. As VecRegT has some degree of type information it
61 * has vector semantics, and defines the index operator ([]) to get
62 * references to particular bytes understood as a VecElem.
63 * The second view of a container implemented in this file is VecLaneT, which
64 * is a view of a subset of the container.
65 * A VecLaneT is a view of a lane of a vector register, where a lane is
66 * identified by a type (VecElem) and an index (although the view is
67 * unaware of its index). Operations on the lane are directly applied to
68 * the corresponding bytes of the underlying VecRegContainer through a
71 * The intended usage is requesting views to the VecRegContainer via the
72 * member 'as' for VecRegT and the member 'laneView' for VecLaneT. Kindly
73 * find an example of usage in the following.
76 * // We declare 512 bits vectors
77 * using Vec512 = VecRegContainer<64>;
79 * // We implement the physical vector register file
80 * Vec512 physicalVecRegFile[NUM_VREGS];
82 * // Usage example, for a macro op:
83 * VecFloat8Add(ExecContext* xd) {
84 * // Request source vector register to the execution context (const as it
86 * const Vec512& vsrc1raw = xc->readVecRegOperand(this, 0);
87 * // View it as a vector of floats (we could just specify the first
88 * // template parametre, the second has a default value that works, and the
89 * // last one is derived by the constness of vsrc1raw).
90 * VecRegT<float, 8, true>& vsrc1 = vsrc1raw->as<float, 8>();
92 * // Second source and view
93 * const Vec512& vsrc2raw = xc->readVecRegOperand(this, 1);
94 * VecRegT<float, 8, true>& vsrc2 = vsrc2raw->as<float, 8>();
96 * // Destination and view
98 * VecRegT<float, 8, false>& vdst = vdstraw->as<float, 8>();
100 * for (auto i = 0; i < 8; i++) {
101 * // This asignment sets the bits in the underlying Vec512: vdstraw
102 * vdst[i] = vsrc1[i] + vsrc2[i];
104 * xc->setWriteRegOperand(this, 0, vdstraw);
107 * // Usage example, for a micro op that operates over lane number _lidx:
108 * VecFloatLaneAdd(ExecContext* xd) {
109 * // Request source vector register to the execution context (const as it
111 * const Vec512& vsrc1raw = xc->readVecRegOperand(this, 0);
112 * // View it as a lane of a vector of floats (we could just specify the
113 * // first template parametre, the second is derived by the constness of
115 * VecLaneT<float, true>& src1 = vsrc1raw->laneView<float>(this->_lidx);
117 * // Second source and view
118 * const Vec512& vsrc2raw = xc->readVecRegOperand(this, 1);
119 * VecLaneT<float, true>& src2 = vsrc2raw->laneView<float>(this->_lidx);
121 * // (Writable) destination and view
122 * // As this is a partial write, we need the exec context to support that
123 * // through, e.g., 'readVecRegOperandToWrite' returning a writable
124 * // reference to the register
125 * Vec512 vdstraw = xc->readVecRegOperandToWrite(this, 3);
126 * VecLaneT<float, false>& dst = vdstraw->laneView<float>(this->_lidx);
129 * // There is no need to copy the value back into the exec context, as
130 * // the assignment to dst modifies the appropriate bytes in vdstraw which
131 * // is in turn, a reference to the register in the cpu model.
132 * // For operations that do conditional writeback, we can decouple the
134 * // auto tmp = src1 + src2;
136 * // dst = tmp; // do writeback
138 * // // do not do writeback
144 #ifndef __ARCH_GENERIC_VEC_REG_HH__
145 #define __ARCH_GENERIC_VEC_REG_HH__
151 #include <type_traits>
154 #include "base/cprintf.hh"
155 #include "base/logging.hh"
158 class VecRegContainer;
160 /** Vector Register Abstraction
161 * This generic class is a view in a particularization of MVC, to vector
162 * registers. There is a VecRegContainer that implements the model, and
163 * contains the data. To that model we can interpose different instantiations
164 * of VecRegT to view the container as a vector of NumElems elems of type
166 * @tparam VecElem Type of each element of the vector.
167 * @tparam NumElems Amount of components of the vector.
168 * @tparam Const Indicate if the underlying container can be modified through
171 template <typename VecElem, size_t NumElems, bool Const>
174 /** Size of the register in bytes. */
175 static constexpr size_t SIZE = sizeof(VecElem) * NumElems;
177 /** Container type alias. */
178 using Container = typename std::conditional<Const,
179 const VecRegContainer<SIZE>,
180 VecRegContainer<SIZE>>::type;
182 /** My type alias. */
183 using MyClass = VecRegT<VecElem, NumElems, Const>;
184 /** Reference to container. */
185 Container& container;
189 VecRegT(Container& cnt) : container(cnt) {};
191 /** Zero the container. */
192 template<bool Condition = !Const>
193 typename std::enable_if<Condition, void>::type
194 zero() { container.zero(); }
196 template<bool Condition = !Const>
197 typename std::enable_if<Condition, MyClass&>::type
198 operator=(const MyClass& that)
200 container = that.container;
204 /** Index operator. */
205 const VecElem& operator[](size_t idx) const
207 return container.template raw_ptr<VecElem>()[idx];
210 /** Index operator. */
211 template<bool Condition = !Const>
212 typename std::enable_if<Condition, VecElem&>::type
213 operator[](size_t idx)
215 return container.template raw_ptr<VecElem>()[idx];
218 /** Equality operator.
219 * Required to compare thread contexts.
221 template<typename VE2, size_t NE2, bool C2>
223 operator==(const VecRegT<VE2, NE2, C2>& that) const
225 return container == that.container;
227 /** Inequality operator.
228 * Required to compare thread contexts.
230 template<typename VE2, size_t NE2, bool C2>
232 operator!=(const VecRegT<VE2, NE2, C2>& that) const
234 return !operator==(that);
237 /** Output stream operator. */
239 operator<<(std::ostream& os, const MyClass& vr)
241 /* 0-sized is not allowed */
242 os << "[" << std::hex << (uint32_t)vr[0];
243 for (uint32_t e = 1; e < vr.SIZE; e++)
244 os << " " << std::hex << (uint32_t)vr[e];
249 const std::string print() const { return csprintf("%s", *this); }
251 * Cast to VecRegContainer&
252 * It is useful to get the reference to the container for ISA tricks,
253 * because casting to reference prevents unnecessary copies.
255 operator Container&() { return container; }
258 /* Forward declaration. */
259 template <typename VecElem, bool Const>
263 * Vector Register Abstraction
264 * This generic class is the model in a particularization of MVC, to vector
265 * registers. The model has functionality to create views of itself, or a
266 * portion through the method 'as
267 * @tparam Sz Size of the container in bytes.
270 class VecRegContainer
272 static_assert(Sz > 0,
273 "Cannot create Vector Register Container of zero size");
275 static constexpr size_t SIZE = Sz;
276 using Container = std::array<uint8_t,Sz>;
279 using MyClass = VecRegContainer<SIZE>;
283 /* This is required for de-serialisation. */
284 VecRegContainer(const std::vector<uint8_t>& that)
286 assert(that.size() >= SIZE);
287 std::memcpy(container.data(), &that[0], SIZE);
290 /** Zero the container. */
291 void zero() { memset(container.data(), 0, SIZE); }
293 /** Assignment operators. */
295 /** From VecRegContainer */
296 MyClass& operator=(const MyClass& that)
300 memcpy(container.data(), that.container.data(), SIZE);
304 /** From appropriately sized uint8_t[]. */
305 MyClass& operator=(const Container& that)
307 std::memcpy(container.data(), that.data(), SIZE);
311 /** From vector<uint8_t>.
312 * This is required for de-serialisation.
314 MyClass& operator=(const std::vector<uint8_t>& that)
316 assert(that.size() >= SIZE);
317 std::memcpy(container.data(), that.data(), SIZE);
322 /** Copy the contents into the input buffer. */
324 /** To appropriately sized uint8_t[] */
325 void copyTo(Container& dst) const
327 std::memcpy(dst.data(), container.data(), SIZE);
330 /** To vector<uint8_t>
331 * This is required for serialisation.
333 void copyTo(std::vector<uint8_t>& dst) const
336 std::memcpy(dst.data(), container.data(), SIZE);
340 /** Equality operator.
341 * Required to compare thread contexts.
345 operator==(const VecRegContainer<S2>& that) const
348 !memcmp(container.data(), that.container.data(), SIZE);
350 /** Inequality operator.
351 * Required to compare thread contexts.
355 operator!=(const VecRegContainer<S2>& that) const
357 return !operator==(that);
360 const std::string print() const { return csprintf("%s", *this); }
361 /** Get pointer to bytes. */
362 template <typename Ret>
363 const Ret* raw_ptr() const { return (const Ret*)container.data(); }
365 template <typename Ret>
366 Ret* raw_ptr() { return (Ret*)container.data(); }
370 * Create a view of this container as a vector of VecElems with an
371 * optional amount of elements. If the amount of elements is provided,
372 * the size of the container is checked, to test bounds. If it is not
373 * provided, the length is inferred from the container size and the
375 * @tparam VecElem Type of each element of the vector for the view.
376 * @tparam NumElem Amount of elements in the view.
379 template <typename VecElem, size_t NumElems = SIZE/sizeof(VecElem)>
380 VecRegT<VecElem, NumElems, true> as() const
382 static_assert(SIZE % sizeof(VecElem) == 0,
383 "VecElem does not evenly divide the register size");
384 static_assert(sizeof(VecElem) * NumElems <= SIZE,
385 "Viewing VecReg as something bigger than it is");
386 return VecRegT<VecElem, NumElems, true>(*this);
389 template <typename VecElem, size_t NumElems = SIZE/sizeof(VecElem)>
390 VecRegT<VecElem, NumElems, false> as()
392 static_assert(SIZE % sizeof(VecElem) == 0,
393 "VecElem does not evenly divide the register size");
394 static_assert(sizeof(VecElem) * NumElems <= SIZE,
395 "Viewing VecReg as something bigger than it is");
396 return VecRegT<VecElem, NumElems, false>(*this);
399 template <typename VecElem, int LaneIdx>
400 VecLaneT<VecElem, false> laneView();
401 template <typename VecElem, int LaneIdx>
402 VecLaneT<VecElem, true> laneView() const;
403 template <typename VecElem>
404 VecLaneT<VecElem, false> laneView(int laneIdx);
405 template <typename VecElem>
406 VecLaneT<VecElem, true> laneView(int laneIdx) const;
410 * Used for serialization.
412 friend std::ostream& operator<<(std::ostream& os, const MyClass& v)
414 for (auto& b: v.container) {
415 os << csprintf("%02x", b);
421 /** We define an auxiliary abstraction for LaneData. The ISA should care
422 * about the semantics of a, e.g., 32bit element, treating it as a signed or
423 * unsigned int, or a float depending on the semantics of a particular
424 * instruction. On the other hand, the cpu model should only care about it
425 * being a 32-bit value. */
435 /** LaneSize is an abstraction of a LS byte value for the execution and thread
436 * contexts to handle values just depending on its width. That way, the ISA
437 * can request, for example, the second 4 byte lane of register 5 to the model.
438 * The model serves that value, agnostic of the semantics of those bits. Then,
439 * it is up to the ISA to interpret those bits as a float, or as an uint.
440 * To maximize the utility, this class implements the assignment operator and
441 * the casting to equal-size types.
442 * As opposed to a RegLaneT, LaneData is not 'backed' by a VecRegContainer.
444 * When data is passed and is susceptible to being copied, use LaneData, as
445 * copying the primitive type is build on is cheap.
446 * When data is passed as references (const or not), use RegLaneT, as all
447 * operations happen 'in place', avoiding any copies (no copies is always
448 * cheaper than cheap copies), especially when things are inlined, and
449 * references are not explicitly passed.
451 template <LaneSize LS>
455 /** Alias to the native type of the appropriate size. */
456 using UnderlyingType =
457 typename std::conditional<LS == LaneSize::EightByte, uint64_t,
458 typename std::conditional<LS == LaneSize::FourByte, uint32_t,
459 typename std::conditional<LS == LaneSize::TwoByte, uint16_t,
460 typename std::conditional<LS == LaneSize::Byte, uint8_t,
466 static constexpr auto ByteSz = sizeof(UnderlyingType);
468 using MyClass = LaneData<LS>;
471 template <typename T> explicit
472 LaneData(typename std::enable_if<sizeof(T) == ByteSz, const T&>::type t)
475 template <typename T>
476 typename std::enable_if<sizeof(T) == ByteSz, MyClass&>::type
477 operator=(const T& that)
483 typename std::enable_if<sizeof(T) == ByteSz, int>::type I = 0>
485 return *static_cast<const T*>(&_val);
489 /** Output operator overload for LaneData<Size>. */
490 template <LaneSize LS>
492 operator<<(std::ostream& os, const LaneData<LS>& d)
494 return os << static_cast<typename LaneData<LS>::UnderlyingType>(d);
497 /** Vector Lane abstraction
498 * Another view of a container. This time only a partial part of it is exposed.
499 * @tparam VecElem Type of each element of the vector.
500 * @tparam Const Indicate if the underlying container can be modified through
505 template <typename VecElem, bool Const>
509 /** VecRegContainer friendship to access private VecLaneT constructors.
510 * Only VecRegContainers can build VecLanes.
513 friend VecLaneT<VecElem, !Const>;
515 /*template <size_t Sz>
516 friend class VecRegContainer;*/
517 friend class VecRegContainer<8>;
518 friend class VecRegContainer<16>;
519 friend class VecRegContainer<32>;
520 friend class VecRegContainer<64>;
521 friend class VecRegContainer<128>;
523 /** My type alias. */
524 using MyClass = VecLaneT<VecElem, Const>;
527 using Cont = typename std::conditional<Const,
530 static_assert(!std::is_const<VecElem>::value || Const,
531 "Asked for non-const lane of const type!");
532 static_assert(std::is_integral<VecElem>::value,
533 "VecElem type is not integral!");
534 /** Reference to data. */
538 VecLaneT(Cont& cont) : container(cont) { }
541 /** Assignment operators.
542 * Assignment operators are only enabled if the underlying container is
546 template <bool Assignable = !Const>
547 typename std::enable_if<Assignable, MyClass&>::type
548 operator=(const VecElem& that) {
554 * Generic bitwise assignment. Narrowing and widening assignemnts are
555 * not allowed, pre-treatment of the rhs is required to conform.
557 template <bool Assignable = !Const, typename T>
558 typename std::enable_if<Assignable, MyClass&>::type
559 operator=(const T& that) {
560 static_assert(sizeof(T) >= sizeof(VecElem),
561 "Attempt to perform widening bitwise copy.");
562 static_assert(sizeof(T) <= sizeof(VecElem),
563 "Attempt to perform narrowing bitwise copy.");
564 container = static_cast<VecElem>(that);
568 /** Cast to vecElem. */
569 operator VecElem() const { return container; }
571 /** Constification. */
572 template <bool Cond = !Const, typename std::enable_if<Cond, int>::type = 0>
573 operator VecLaneT<typename std::enable_if<Cond, VecElem>::type, true>()
575 return VecLaneT<VecElem, true>(container);
580 template<typename T, bool Const>
581 struct add_const<VecLaneT<T, Const>> { typedef VecLaneT<T, true> type; };
584 /** View as the Nth lane of type VecElem. */
586 template <typename VecElem, int LaneIdx>
587 VecLaneT<VecElem, false>
588 VecRegContainer<Sz>::laneView()
590 return VecLaneT<VecElem, false>(as<VecElem>()[LaneIdx]);
593 /** View as the const Nth lane of type VecElem. */
595 template <typename VecElem, int LaneIdx>
596 VecLaneT<VecElem, true>
597 VecRegContainer<Sz>::laneView() const
599 return VecLaneT<VecElem, true>(as<VecElem>()[LaneIdx]);
602 /** View as the Nth lane of type VecElem. */
604 template <typename VecElem>
605 VecLaneT<VecElem, false>
606 VecRegContainer<Sz>::laneView(int laneIdx)
608 return VecLaneT<VecElem, false>(as<VecElem>()[laneIdx]);
611 /** View as the const Nth lane of type VecElem. */
613 template <typename VecElem>
614 VecLaneT<VecElem, true>
615 VecRegContainer<Sz>::laneView(int laneIdx) const
617 return VecLaneT<VecElem, true>(as<VecElem>()[laneIdx]);
620 using VecLane8 = VecLaneT<uint8_t, false>;
621 using VecLane16 = VecLaneT<uint16_t, false>;
622 using VecLane32 = VecLaneT<uint32_t, false>;
623 using VecLane64 = VecLaneT<uint64_t, false>;
625 using ConstVecLane8 = VecLaneT<uint8_t, true>;
626 using ConstVecLane16 = VecLaneT<uint16_t, true>;
627 using ConstVecLane32 = VecLaneT<uint32_t, true>;
628 using ConstVecLane64 = VecLaneT<uint64_t, true>;
631 * Calls required for serialization/deserialization
636 to_number(const std::string& value, VecRegContainer<Sz>& v)
640 std::string byte = value.substr(i<<1, 2);
641 v.template raw_ptr<uint8_t>()[i] = stoul(byte, 0, 16);
648 #endif /* __ARCH_GENERIC_VEC_REG_HH__ */