1/* 2 * Copyright (c) 2015-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 157constexpr unsigned MaxVecRegLenInBytes = 4096; 158 159template <size_t Sz> 160class VecRegContainer; 161 162/** Vector Register Abstraction 163 * This generic class is a view in a particularization of MVC, to vector 164 * registers. There is a VecRegContainer that implements the model, and 165 * contains the data. To that model we can interpose different instantiations 166 * of VecRegT to view the container as a vector of NumElems elems of type 167 * VecElem. 168 * @tparam VecElem Type of each element of the vector. 169 * @tparam NumElems Amount of components of the vector. 170 * @tparam Const Indicate if the underlying container can be modified through 171 * the view. 172 */ 173template <typename VecElem, size_t NumElems, bool Const> 174class VecRegT 175{ 176 /** Size of the register in bytes. */ 177 static constexpr size_t SIZE = sizeof(VecElem) * NumElems; 178 public: 179 /** Container type alias. */ 180 using Container = typename std::conditional<Const, 181 const VecRegContainer<SIZE>, 182 VecRegContainer<SIZE>>::type; 183 private: 184 /** My type alias. */ 185 using MyClass = VecRegT<VecElem, NumElems, Const>; 186 /** Reference to container. */ 187 Container& container; 188 189 public: 190 /** Constructor. */ 191 VecRegT(Container& cnt) : container(cnt) {}; 192 193 /** Zero the container. */ 194 template<bool Condition = !Const> 195 typename std::enable_if<Condition, void>::type 196 zero() { container.zero(); } 197 198 template<bool Condition = !Const> 199 typename std::enable_if<Condition, MyClass&>::type 200 operator=(const MyClass& that) 201 { 202 container = that.container; 203 return *this; 204 } 205 206 /** Index operator. */ 207 const VecElem& operator[](size_t idx) const 208 { 209 return container.template raw_ptr<VecElem>()[idx]; 210 } 211 212 /** Index operator. */ 213 template<bool Condition = !Const> 214 typename std::enable_if<Condition, VecElem&>::type 215 operator[](size_t idx) 216 { 217 return container.template raw_ptr<VecElem>()[idx]; 218 } 219 220 /** Equality operator. 221 * Required to compare thread contexts. 222 */ 223 template<typename VE2, size_t NE2, bool C2> 224 bool 225 operator==(const VecRegT<VE2, NE2, C2>& that) const 226 { 227 return container == that.container; 228 } 229 /** Inequality operator. 230 * Required to compare thread contexts. 231 */ 232 template<typename VE2, size_t NE2, bool C2> 233 bool 234 operator!=(const VecRegT<VE2, NE2, C2>& that) const 235 { 236 return !operator==(that); 237 } 238 239 /** Output stream operator. */ 240 friend std::ostream& 241 operator<<(std::ostream& os, const MyClass& vr) 242 { 243 /* 0-sized is not allowed */ 244 os << "[" << std::hex << (uint32_t)vr[0]; 245 for (uint32_t e = 1; e < vr.SIZE; e++) 246 os << " " << std::hex << (uint32_t)vr[e]; 247 os << ']'; 248 return os; 249 } 250 251 const std::string print() const { return csprintf("%s", *this); } 252 /** 253 * Cast to VecRegContainer& 254 * It is useful to get the reference to the container for ISA tricks, 255 * because casting to reference prevents unnecessary copies. 256 */ 257 operator Container&() { return container; } 258}; 259 260/* Forward declaration. */ 261template <typename VecElem, bool Const> 262class VecLaneT; 263 264/** 265 * Vector Register Abstraction 266 * This generic class is the model in a particularization of MVC, to vector 267 * registers. The model has functionality to create views of itself, or a 268 * portion through the method 'as 269 * @tparam Sz Size of the container in bytes. 270 */ 271template <size_t Sz> 272class VecRegContainer 273{ 274 static_assert(Sz > 0, 275 "Cannot create Vector Register Container of zero size"); 276 static_assert(Sz <= MaxVecRegLenInBytes, 277 "Vector Register size limit exceeded"); 278 public: 279 static constexpr size_t SIZE = Sz; 280 using Container = std::array<uint8_t,Sz>; 281 private: 282 Container container; 283 using MyClass = VecRegContainer<SIZE>; 284 285 public: 286 VecRegContainer() {} 287 /* This is required for de-serialisation. */ 288 VecRegContainer(const std::vector<uint8_t>& that) 289 { 290 assert(that.size() >= SIZE); 291 std::memcpy(container.data(), &that[0], SIZE); 292 } 293 294 /** Zero the container. */ 295 void zero() { memset(container.data(), 0, SIZE); } 296 297 /** Assignment operators. */ 298 /** @{ */ 299 /** From VecRegContainer */ 300 MyClass& operator=(const MyClass& that) 301 { 302 if (&that == this) 303 return *this; 304 memcpy(container.data(), that.container.data(), SIZE); 305 return *this; 306 } 307 308 /** From appropriately sized uint8_t[]. */ 309 MyClass& operator=(const Container& that) 310 { 311 std::memcpy(container.data(), that.data(), SIZE); 312 return *this; 313 } 314 315 /** From vector<uint8_t>. 316 * This is required for de-serialisation. 317 * */ 318 MyClass& operator=(const std::vector<uint8_t>& that) 319 { 320 assert(that.size() >= SIZE); 321 std::memcpy(container.data(), that.data(), SIZE); 322 return *this; 323 } 324 /** @} */ 325 326 /** Copy the contents into the input buffer. */ 327 /** @{ */ 328 /** To appropriately sized uint8_t[] */ 329 void copyTo(Container& dst) const 330 { 331 std::memcpy(dst.data(), container.data(), SIZE); 332 } 333 334 /** To vector<uint8_t> 335 * This is required for serialisation. 336 * */ 337 void copyTo(std::vector<uint8_t>& dst) const 338 { 339 dst.resize(SIZE); 340 std::memcpy(dst.data(), container.data(), SIZE); 341 } 342 /** @} */ 343 344 /** Equality operator. 345 * Required to compare thread contexts. 346 */ 347 template<size_t S2> 348 inline bool 349 operator==(const VecRegContainer<S2>& that) const 350 { 351 return SIZE == S2 && 352 !memcmp(container.data(), that.container.data(), SIZE); 353 } 354 /** Inequality operator. 355 * Required to compare thread contexts. 356 */ 357 template<size_t S2> 358 bool 359 operator!=(const VecRegContainer<S2>& that) const 360 { 361 return !operator==(that); 362 } 363 364 const std::string print() const { return csprintf("%s", *this); } 365 /** Get pointer to bytes. */ 366 template <typename Ret> 367 const Ret* raw_ptr() const { return (const Ret*)container.data(); } 368 369 template <typename Ret> 370 Ret* raw_ptr() { return (Ret*)container.data(); } 371 372 /** 373 * View interposers. 374 * Create a view of this container as a vector of VecElems with an 375 * optional amount of elements. If the amount of elements is provided, 376 * the size of the container is checked, to test bounds. If it is not 377 * provided, the length is inferred from the container size and the 378 * element size. 379 * @tparam VecElem Type of each element of the vector for the view. 380 * @tparam NumElem Amount of elements in the view. 381 */ 382 /** @{ */ 383 template <typename VecElem, size_t NumElems = SIZE/sizeof(VecElem)> 384 VecRegT<VecElem, NumElems, true> as() const 385 { 386 static_assert(SIZE % sizeof(VecElem) == 0, 387 "VecElem does not evenly divide the register size"); 388 static_assert(sizeof(VecElem) * NumElems <= SIZE, 389 "Viewing VecReg as something bigger than it is"); 390 return VecRegT<VecElem, NumElems, true>(*this); 391 } 392 393 template <typename VecElem, size_t NumElems = SIZE/sizeof(VecElem)> 394 VecRegT<VecElem, NumElems, false> as() 395 { 396 static_assert(SIZE % sizeof(VecElem) == 0, 397 "VecElem does not evenly divide the register size"); 398 static_assert(sizeof(VecElem) * NumElems <= SIZE, 399 "Viewing VecReg as something bigger than it is"); 400 return VecRegT<VecElem, NumElems, false>(*this); 401 } 402 403 template <typename VecElem, int LaneIdx> 404 VecLaneT<VecElem, false> laneView(); 405 template <typename VecElem, int LaneIdx> 406 VecLaneT<VecElem, true> laneView() const; 407 template <typename VecElem> 408 VecLaneT<VecElem, false> laneView(int laneIdx); 409 template <typename VecElem> 410 VecLaneT<VecElem, true> laneView(int laneIdx) const; 411 /** @} */ 412 /** 413 * Output operator. 414 * Used for serialization. 415 */ 416 friend std::ostream& operator<<(std::ostream& os, const MyClass& v) 417 { 418 for (auto& b: v.container) { 419 os << csprintf("%02x", b); 420 } 421 return os; 422 } 423}; 424 425/** We define an auxiliary abstraction for LaneData. The ISA should care 426 * about the semantics of a, e.g., 32bit element, treating it as a signed or 427 * unsigned int, or a float depending on the semantics of a particular 428 * instruction. On the other hand, the cpu model should only care about it 429 * being a 32-bit value. */ 430enum class LaneSize 431{ 432 Empty = 0, 433 Byte, 434 TwoByte, 435 FourByte, 436 EightByte, 437}; 438 439/** LaneSize is an abstraction of a LS byte value for the execution and thread 440 * contexts to handle values just depending on its width. That way, the ISA 441 * can request, for example, the second 4 byte lane of register 5 to the model. 442 * The model serves that value, agnostic of the semantics of those bits. Then, 443 * it is up to the ISA to interpret those bits as a float, or as an uint. 444 * To maximize the utility, this class implements the assignment operator and 445 * the casting to equal-size types. 446 * As opposed to a RegLaneT, LaneData is not 'backed' by a VecRegContainer. 447 * The idea is: 448 * When data is passed and is susceptible to being copied, use LaneData, as 449 * copying the primitive type is build on is cheap. 450 * When data is passed as references (const or not), use RegLaneT, as all 451 * operations happen 'in place', avoiding any copies (no copies is always 452 * cheaper than cheap copies), especially when things are inlined, and 453 * references are not explicitly passed. 454 */ 455template <LaneSize LS> 456class LaneData 457{ 458 public: 459 /** Alias to the native type of the appropriate size. */ 460 using UnderlyingType = 461 typename std::conditional<LS == LaneSize::EightByte, uint64_t, 462 typename std::conditional<LS == LaneSize::FourByte, uint32_t, 463 typename std::conditional<LS == LaneSize::TwoByte, uint16_t, 464 typename std::conditional<LS == LaneSize::Byte, uint8_t, 465 void>::type 466 >::type 467 >::type 468 >::type; 469 private: 470 static constexpr auto ByteSz = sizeof(UnderlyingType); 471 UnderlyingType _val; 472 using MyClass = LaneData<LS>; 473 474 public: 475 template <typename T> explicit 476 LaneData(typename std::enable_if<sizeof(T) == ByteSz, const T&>::type t) 477 : _val(t) {} 478 479 template <typename T> 480 typename std::enable_if<sizeof(T) == ByteSz, MyClass&>::type 481 operator=(const T& that) 482 { 483 _val = that; 484 return *this; 485 } 486 template<typename T, 487 typename std::enable_if<sizeof(T) == ByteSz, int>::type I = 0> 488 operator T() const { 489 return *static_cast<const T*>(&_val); 490 } 491}; 492 493/** Output operator overload for LaneData<Size>. */ 494template <LaneSize LS> 495inline std::ostream& 496operator<<(std::ostream& os, const LaneData<LS>& d) 497{ 498 return os << static_cast<typename LaneData<LS>::UnderlyingType>(d); 499} 500 501/** Vector Lane abstraction 502 * Another view of a container. This time only a partial part of it is exposed. 503 * @tparam VecElem Type of each element of the vector. 504 * @tparam Const Indicate if the underlying container can be modified through 505 * the view. 506 */ 507/** @{ */ 508/* General */ 509template <typename VecElem, bool Const> 510class VecLaneT 511{ 512 public: 513 /** VecRegContainer friendship to access private VecLaneT constructors. 514 * Only VecRegContainers can build VecLanes. 515 */ 516 /** @{ */ 517 friend VecLaneT<VecElem, !Const>; 518 519 /*template <size_t Sz> 520 friend class VecRegContainer;*/ 521 friend class VecRegContainer<8>; 522 friend class VecRegContainer<16>; 523 friend class VecRegContainer<32>; 524 friend class VecRegContainer<64>; 525 friend class VecRegContainer<128>; 526 friend class VecRegContainer<256>; 527 friend class VecRegContainer<MaxVecRegLenInBytes>; 528 529 /** My type alias. */ 530 using MyClass = VecLaneT<VecElem, Const>; 531 532 private: 533 using Cont = typename std::conditional<Const, 534 const VecElem, 535 VecElem>::type; 536 static_assert(!std::is_const<VecElem>::value || Const, 537 "Asked for non-const lane of const type!"); 538 static_assert(std::is_integral<VecElem>::value, 539 "VecElem type is not integral!"); 540 /** Reference to data. */ 541 Cont& container; 542 543 /** Constructor */ 544 VecLaneT(Cont& cont) : container(cont) { } 545 546 public: 547 /** Assignment operators. 548 * Assignment operators are only enabled if the underlying container is 549 * non-constant. 550 */ 551 /** @{ */ 552 template <bool Assignable = !Const> 553 typename std::enable_if<Assignable, MyClass&>::type 554 operator=(const VecElem& that) { 555 container = that; 556 return *this; 557 } 558 /** 559 * Generic. 560 * Generic bitwise assignment. Narrowing and widening assignemnts are 561 * not allowed, pre-treatment of the rhs is required to conform. 562 */ 563 template <bool Assignable = !Const, typename T> 564 typename std::enable_if<Assignable, MyClass&>::type 565 operator=(const T& that) { 566 static_assert(sizeof(T) >= sizeof(VecElem), 567 "Attempt to perform widening bitwise copy."); 568 static_assert(sizeof(T) <= sizeof(VecElem), 569 "Attempt to perform narrowing bitwise copy."); 570 container = static_cast<VecElem>(that); 571 return *this; 572 } 573 /** @} */ 574 /** Cast to vecElem. */ 575 operator VecElem() const { return container; } 576 577 /** Constification. */ 578 template <bool Cond = !Const, typename std::enable_if<Cond, int>::type = 0> 579 operator VecLaneT<typename std::enable_if<Cond, VecElem>::type, true>() 580 { 581 return VecLaneT<VecElem, true>(container); 582 } 583}; 584 585namespace std { 586 template<typename T, bool Const> 587 struct add_const<VecLaneT<T, Const>> { typedef VecLaneT<T, true> type; }; 588} 589 590/** View as the Nth lane of type VecElem. */ 591template <size_t Sz> 592template <typename VecElem, int LaneIdx> 593VecLaneT<VecElem, false> 594VecRegContainer<Sz>::laneView() 595{ 596 return VecLaneT<VecElem, false>(as<VecElem>()[LaneIdx]); 597} 598 599/** View as the const Nth lane of type VecElem. */ 600template <size_t Sz> 601template <typename VecElem, int LaneIdx> 602VecLaneT<VecElem, true> 603VecRegContainer<Sz>::laneView() const 604{ 605 return VecLaneT<VecElem, true>(as<VecElem>()[LaneIdx]); 606} 607 608/** View as the Nth lane of type VecElem. */ 609template <size_t Sz> 610template <typename VecElem> 611VecLaneT<VecElem, false> 612VecRegContainer<Sz>::laneView(int laneIdx) 613{ 614 return VecLaneT<VecElem, false>(as<VecElem>()[laneIdx]); 615} 616 617/** View as the const Nth lane of type VecElem. */ 618template <size_t Sz> 619template <typename VecElem> 620VecLaneT<VecElem, true> 621VecRegContainer<Sz>::laneView(int laneIdx) const 622{ 623 return VecLaneT<VecElem, true>(as<VecElem>()[laneIdx]); 624} 625 626using VecLane8 = VecLaneT<uint8_t, false>; 627using VecLane16 = VecLaneT<uint16_t, false>; 628using VecLane32 = VecLaneT<uint32_t, false>; 629using VecLane64 = VecLaneT<uint64_t, false>; 630 631using ConstVecLane8 = VecLaneT<uint8_t, true>; 632using ConstVecLane16 = VecLaneT<uint16_t, true>; 633using ConstVecLane32 = VecLaneT<uint32_t, true>; 634using ConstVecLane64 = VecLaneT<uint64_t, true>; 635 636/** 637 * Calls required for serialization/deserialization 638 */ 639/** @{ */ 640template <size_t Sz> 641inline bool 642to_number(const std::string& value, VecRegContainer<Sz>& v) 643{ 644 fatal_if(value.size() > 2 * VecRegContainer<Sz>::SIZE, 645 "Vector register value overflow at unserialize"); 646 647 for (int i = 0; i < VecRegContainer<Sz>::SIZE; i++) { 648 uint8_t b = 0; 649 if (2 * i < value.size()) 650 b = stoul(value.substr(i * 2, 2), nullptr, 16); 651 v.template raw_ptr<uint8_t>()[i] = b; 652 } 653 return true; 654} 655/** @} */ 656 657/** 658 * Dummy type aliases and constants for architectures that do not implement 659 * vector registers. 660 */ 661/** @{ */ 662using DummyVecElem = uint32_t; 663constexpr unsigned DummyNumVecElemPerVecReg = 2; 664using DummyVecReg = VecRegT<DummyVecElem, DummyNumVecElemPerVecReg, false>; 665using DummyConstVecReg = VecRegT<DummyVecElem, DummyNumVecElemPerVecReg, true>; 666using DummyVecRegContainer = DummyVecReg::Container; 667constexpr size_t DummyVecRegSizeBytes = DummyNumVecElemPerVecReg * 668 sizeof(DummyVecElem); 669/** @} */ 670 671#endif /* __ARCH_GENERIC_VEC_REG_HH__ */ 672