vec_reg.hh revision 13610
1/* 2 * Copyright (c) 2015-2016, 2018 ARM Limited 3 * All rights reserved 4 * 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. 13 * 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. 24 * 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. 36 * 37 * Authors: Giacomo Gabrielli 38 * Nathanael Premillieu 39 * Rekai Gonzalez 40 */ 41 42/** \file arch/generic/vec_reg.hh 43 * Vector Registers layout specification. 44 * 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). 48 * 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. 53 * 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 69 * reference. 70 * 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. 74 * 75 * 76 * // We declare 512 bits vectors 77 * using Vec512 = VecRegContainer<64>; 78 * ... 79 * // We implement the physical vector register file 80 * Vec512 physicalVecRegFile[NUM_VREGS]; 81 * ... 82 * // Usage example, for a macro op: 83 * VecFloat8Add(ExecContext* xd) { 84 * // Request source vector register to the execution context (const as it 85 * // is read only). 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>(); 91 * 92 * // Second source and view 93 * const Vec512& vsrc2raw = xc->readVecRegOperand(this, 1); 94 * VecRegT<float, 8, true>& vsrc2 = vsrc2raw->as<float, 8>(); 95 * 96 * // Destination and view 97 * Vec512 vdstraw; 98 * VecRegT<float, 8, false>& vdst = vdstraw->as<float, 8>(); 99 * 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]; 103 * } 104 * xc->setWriteRegOperand(this, 0, vdstraw); 105 * } 106 * 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 110 * // is read only). 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 114 * // vsrc1raw). 115 * VecLaneT<float, true>& src1 = vsrc1raw->laneView<float>(this->_lidx); 116 * 117 * // Second source and view 118 * const Vec512& vsrc2raw = xc->readVecRegOperand(this, 1); 119 * VecLaneT<float, true>& src2 = vsrc2raw->laneView<float>(this->_lidx); 120 * 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); 127 * 128 * dst = src1 + src2; 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 133 * // write by doing: 134 * // auto tmp = src1 + src2; 135 * // if (test) { 136 * // dst = tmp; // do writeback 137 * // } else { 138 * // // do not do writeback 139 * // } 140 * } 141 * 142 */ 143 144#ifndef __ARCH_GENERIC_VEC_REG_HH__ 145#define __ARCH_GENERIC_VEC_REG_HH__ 146 147#include <array> 148#include <cassert> 149#include <iostream> 150#include <string> 151#include <type_traits> 152#include <vector> 153 154#include "base/cprintf.hh" 155#include "base/logging.hh" 156 157template <size_t Sz> 158class VecRegContainer; 159 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 165 * VecElem. 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 169 * the view. 170 */ 171template <typename VecElem, size_t NumElems, bool Const> 172class VecRegT 173{ 174 /** Size of the register in bytes. */ 175 static constexpr size_t SIZE = sizeof(VecElem) * NumElems; 176 public: 177 /** Container type alias. */ 178 using Container = typename std::conditional<Const, 179 const VecRegContainer<SIZE>, 180 VecRegContainer<SIZE>>::type; 181 private: 182 /** My type alias. */ 183 using MyClass = VecRegT<VecElem, NumElems, Const>; 184 /** Reference to container. */ 185 Container& container; 186 187 public: 188 /** Constructor. */ 189 VecRegT(Container& cnt) : container(cnt) {}; 190 191 /** Zero the container. */ 192 template<bool Condition = !Const> 193 typename std::enable_if<Condition, void>::type 194 zero() { container.zero(); } 195 196 template<bool Condition = !Const> 197 typename std::enable_if<Condition, MyClass&>::type 198 operator=(const MyClass& that) 199 { 200 container = that.container; 201 return *this; 202 } 203 204 /** Index operator. */ 205 const VecElem& operator[](size_t idx) const 206 { 207 return container.template raw_ptr<VecElem>()[idx]; 208 } 209 210 /** Index operator. */ 211 template<bool Condition = !Const> 212 typename std::enable_if<Condition, VecElem&>::type 213 operator[](size_t idx) 214 { 215 return container.template raw_ptr<VecElem>()[idx]; 216 } 217 218 /** Equality operator. 219 * Required to compare thread contexts. 220 */ 221 template<typename VE2, size_t NE2, bool C2> 222 bool 223 operator==(const VecRegT<VE2, NE2, C2>& that) const 224 { 225 return container == that.container; 226 } 227 /** Inequality operator. 228 * Required to compare thread contexts. 229 */ 230 template<typename VE2, size_t NE2, bool C2> 231 bool 232 operator!=(const VecRegT<VE2, NE2, C2>& that) const 233 { 234 return !operator==(that); 235 } 236 237 /** Output stream operator. */ 238 friend std::ostream& 239 operator<<(std::ostream& os, const MyClass& vr) 240 { 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]; 245 os << ']'; 246 return os; 247 } 248 249 const std::string print() const { return csprintf("%s", *this); } 250 /** 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. 254 */ 255 operator Container&() { return container; } 256}; 257 258/* Forward declaration. */ 259template <typename VecElem, bool Const> 260class VecLaneT; 261 262/** 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. 268 */ 269template <size_t Sz> 270class VecRegContainer 271{ 272 static_assert(Sz > 0, 273 "Cannot create Vector Register Container of zero size"); 274 public: 275 static constexpr size_t SIZE = Sz; 276 using Container = std::array<uint8_t,Sz>; 277 private: 278 Container container; 279 using MyClass = VecRegContainer<SIZE>; 280 281 public: 282 VecRegContainer() {} 283 /* This is required for de-serialisation. */ 284 VecRegContainer(const std::vector<uint8_t>& that) 285 { 286 assert(that.size() >= SIZE); 287 std::memcpy(container.data(), &that[0], SIZE); 288 } 289 290 /** Zero the container. */ 291 void zero() { memset(container.data(), 0, SIZE); } 292 293 /** Assignment operators. */ 294 /** @{ */ 295 /** From VecRegContainer */ 296 MyClass& operator=(const MyClass& that) 297 { 298 if (&that == this) 299 return *this; 300 memcpy(container.data(), that.container.data(), SIZE); 301 return *this; 302 } 303 304 /** From appropriately sized uint8_t[]. */ 305 MyClass& operator=(const Container& that) 306 { 307 std::memcpy(container.data(), that.data(), SIZE); 308 return *this; 309 } 310 311 /** From vector<uint8_t>. 312 * This is required for de-serialisation. 313 * */ 314 MyClass& operator=(const std::vector<uint8_t>& that) 315 { 316 assert(that.size() >= SIZE); 317 std::memcpy(container.data(), that.data(), SIZE); 318 return *this; 319 } 320 /** @} */ 321 322 /** Copy the contents into the input buffer. */ 323 /** @{ */ 324 /** To appropriately sized uint8_t[] */ 325 void copyTo(Container& dst) const 326 { 327 std::memcpy(dst.data(), container.data(), SIZE); 328 } 329 330 /** To vector<uint8_t> 331 * This is required for serialisation. 332 * */ 333 void copyTo(std::vector<uint8_t>& dst) const 334 { 335 dst.resize(SIZE); 336 std::memcpy(dst.data(), container.data(), SIZE); 337 } 338 /** @} */ 339 340 /** Equality operator. 341 * Required to compare thread contexts. 342 */ 343 template<size_t S2> 344 inline bool 345 operator==(const VecRegContainer<S2>& that) const 346 { 347 return SIZE == S2 && 348 !memcmp(container.data(), that.container.data(), SIZE); 349 } 350 /** Inequality operator. 351 * Required to compare thread contexts. 352 */ 353 template<size_t S2> 354 bool 355 operator!=(const VecRegContainer<S2>& that) const 356 { 357 return !operator==(that); 358 } 359 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(); } 364 365 template <typename Ret> 366 Ret* raw_ptr() { return (Ret*)container.data(); } 367 368 /** 369 * View interposers. 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 374 * element size. 375 * @tparam VecElem Type of each element of the vector for the view. 376 * @tparam NumElem Amount of elements in the view. 377 */ 378 /** @{ */ 379 template <typename VecElem, size_t NumElems = SIZE/sizeof(VecElem)> 380 VecRegT<VecElem, NumElems, true> as() const 381 { 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); 387 } 388 389 template <typename VecElem, size_t NumElems = SIZE/sizeof(VecElem)> 390 VecRegT<VecElem, NumElems, false> as() 391 { 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); 397 } 398 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; 407 /** @} */ 408 /** 409 * Output operator. 410 * Used for serialization. 411 */ 412 friend std::ostream& operator<<(std::ostream& os, const MyClass& v) 413 { 414 for (auto& b: v.container) { 415 os << csprintf("%02x", b); 416 } 417 return os; 418 } 419}; 420 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. */ 426enum class LaneSize 427{ 428 Empty = 0, 429 Byte, 430 TwoByte, 431 FourByte, 432 EightByte, 433}; 434 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. 443 * The idea is: 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. 450 */ 451template <LaneSize LS> 452class LaneData 453{ 454 public: 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, 461 void>::type 462 >::type 463 >::type 464 >::type; 465 private: 466 static constexpr auto ByteSz = sizeof(UnderlyingType); 467 UnderlyingType _val; 468 using MyClass = LaneData<LS>; 469 470 public: 471 template <typename T> explicit 472 LaneData(typename std::enable_if<sizeof(T) == ByteSz, const T&>::type t) 473 : _val(t) {} 474 475 template <typename T> 476 typename std::enable_if<sizeof(T) == ByteSz, MyClass&>::type 477 operator=(const T& that) 478 { 479 _val = that; 480 return *this; 481 } 482 template<typename T, 483 typename std::enable_if<sizeof(T) == ByteSz, int>::type I = 0> 484 operator T() const { 485 return *static_cast<const T*>(&_val); 486 } 487}; 488 489/** Output operator overload for LaneData<Size>. */ 490template <LaneSize LS> 491inline std::ostream& 492operator<<(std::ostream& os, const LaneData<LS>& d) 493{ 494 return os << static_cast<typename LaneData<LS>::UnderlyingType>(d); 495} 496 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 501 * the view. 502 */ 503/** @{ */ 504/* General */ 505template <typename VecElem, bool Const> 506class VecLaneT 507{ 508 public: 509 /** VecRegContainer friendship to access private VecLaneT constructors. 510 * Only VecRegContainers can build VecLanes. 511 */ 512 /** @{ */ 513 friend VecLaneT<VecElem, !Const>; 514 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>; 522 523 /** My type alias. */ 524 using MyClass = VecLaneT<VecElem, Const>; 525 526 private: 527 using Cont = typename std::conditional<Const, 528 const VecElem, 529 VecElem>::type; 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. */ 535 Cont& container; 536 537 /** Constructor */ 538 VecLaneT(Cont& cont) : container(cont) { } 539 540 public: 541 /** Assignment operators. 542 * Assignment operators are only enabled if the underlying container is 543 * non-constant. 544 */ 545 /** @{ */ 546 template <bool Assignable = !Const> 547 typename std::enable_if<Assignable, MyClass&>::type 548 operator=(const VecElem& that) { 549 container = that; 550 return *this; 551 } 552 /** 553 * Generic. 554 * Generic bitwise assignment. Narrowing and widening assignemnts are 555 * not allowed, pre-treatment of the rhs is required to conform. 556 */ 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); 565 return *this; 566 } 567 /** @} */ 568 /** Cast to vecElem. */ 569 operator VecElem() const { return container; } 570 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>() 574 { 575 return VecLaneT<VecElem, true>(container); 576 } 577}; 578 579namespace std { 580 template<typename T, bool Const> 581 struct add_const<VecLaneT<T, Const>> { typedef VecLaneT<T, true> type; }; 582} 583 584/** View as the Nth lane of type VecElem. */ 585template <size_t Sz> 586template <typename VecElem, int LaneIdx> 587VecLaneT<VecElem, false> 588VecRegContainer<Sz>::laneView() 589{ 590 return VecLaneT<VecElem, false>(as<VecElem>()[LaneIdx]); 591} 592 593/** View as the const Nth lane of type VecElem. */ 594template <size_t Sz> 595template <typename VecElem, int LaneIdx> 596VecLaneT<VecElem, true> 597VecRegContainer<Sz>::laneView() const 598{ 599 return VecLaneT<VecElem, true>(as<VecElem>()[LaneIdx]); 600} 601 602/** View as the Nth lane of type VecElem. */ 603template <size_t Sz> 604template <typename VecElem> 605VecLaneT<VecElem, false> 606VecRegContainer<Sz>::laneView(int laneIdx) 607{ 608 return VecLaneT<VecElem, false>(as<VecElem>()[laneIdx]); 609} 610 611/** View as the const Nth lane of type VecElem. */ 612template <size_t Sz> 613template <typename VecElem> 614VecLaneT<VecElem, true> 615VecRegContainer<Sz>::laneView(int laneIdx) const 616{ 617 return VecLaneT<VecElem, true>(as<VecElem>()[laneIdx]); 618} 619 620using VecLane8 = VecLaneT<uint8_t, false>; 621using VecLane16 = VecLaneT<uint16_t, false>; 622using VecLane32 = VecLaneT<uint32_t, false>; 623using VecLane64 = VecLaneT<uint64_t, false>; 624 625using ConstVecLane8 = VecLaneT<uint8_t, true>; 626using ConstVecLane16 = VecLaneT<uint16_t, true>; 627using ConstVecLane32 = VecLaneT<uint32_t, true>; 628using ConstVecLane64 = VecLaneT<uint64_t, true>; 629 630/** 631 * Calls required for serialization/deserialization 632 */ 633/** @{ */ 634template <size_t Sz> 635inline bool 636to_number(const std::string& value, VecRegContainer<Sz>& v) 637{ 638 fatal_if(value.size() > 2 * VecRegContainer<Sz>::SIZE, 639 "Vector register value overflow at unserialize"); 640 641 for (int i = 0; i < VecRegContainer<Sz>::SIZE; i++) { 642 uint8_t b = 0; 643 if (2 * i < value.size()) 644 b = stoul(value.substr(i * 2, 2), nullptr, 16); 645 v.template raw_ptr<uint8_t>()[i] = b; 646 } 647 return true; 648} 649/** @} */ 650 651/** 652 * Dummy type aliases and constants for architectures that do not implement 653 * vector registers. 654 */ 655/** @{ */ 656using DummyVecElem = uint32_t; 657constexpr unsigned DummyNumVecElemPerVecReg = 2; 658using DummyVecReg = VecRegT<DummyVecElem, DummyNumVecElemPerVecReg, false>; 659using DummyConstVecReg = VecRegT<DummyVecElem, DummyNumVecElemPerVecReg, true>; 660using DummyVecRegContainer = DummyVecReg::Container; 661constexpr size_t DummyVecRegSizeBytes = DummyNumVecElemPerVecReg * 662 sizeof(DummyVecElem); 663/** @} */ 664 665#endif /* __ARCH_GENERIC_VEC_REG_HH__ */ 666