1 //==--AArch64StackOffset.h ---------------------------------------*- C++ -*-==// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file contains the declaration of the StackOffset class, which is used to 10 // describe scalable and non-scalable offsets during frame lowering. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #ifndef LLVM_LIB_TARGET_AARCH64_AARCH64STACKOFFSET_H 15 #define LLVM_LIB_TARGET_AARCH64_AARCH64STACKOFFSET_H 16 17 #include "llvm/Support/MachineValueType.h" 18 #include "llvm/Support/TypeSize.h" 19 #include <cassert> 20 21 namespace llvm { 22 23 /// StackOffset is a wrapper around scalable and non-scalable offsets and is 24 /// used in several functions such as 'isAArch64FrameOffsetLegal' and 25 /// 'emitFrameOffset()'. StackOffsets are described by MVTs, e.g. 26 // 27 /// StackOffset(1, MVT::nxv16i8) 28 // 29 /// would describe an offset as being the size of a single SVE vector. 30 /// 31 /// The class also implements simple arithmetic (addition/subtraction) on these 32 /// offsets, e.g. 33 // 34 /// StackOffset(1, MVT::nxv16i8) + StackOffset(1, MVT::i64) 35 // 36 /// describes an offset that spans the combined storage required for an SVE 37 /// vector and a 64bit GPR. 38 class StackOffset { 39 int64_t Bytes; 40 int64_t ScalableBytes; 41 42 explicit operator int() const; 43 44 public: 45 using Part = std::pair<int64_t, MVT>; 46 StackOffset()47 StackOffset() : Bytes(0), ScalableBytes(0) {} 48 StackOffset(int64_t Offset,MVT::SimpleValueType T)49 StackOffset(int64_t Offset, MVT::SimpleValueType T) : StackOffset() { 50 assert(MVT(T).isByteSized() && "Offset type is not a multiple of bytes"); 51 *this += Part(Offset, T); 52 } 53 StackOffset(const StackOffset & Other)54 StackOffset(const StackOffset &Other) 55 : Bytes(Other.Bytes), ScalableBytes(Other.ScalableBytes) {} 56 57 StackOffset &operator=(const StackOffset &) = default; 58 59 StackOffset &operator+=(const StackOffset::Part &Other) { 60 const TypeSize Size = Other.second.getSizeInBits(); 61 if (Size.isScalable()) 62 ScalableBytes += Other.first * ((int64_t)Size.getKnownMinSize() / 8); 63 else 64 Bytes += Other.first * ((int64_t)Size.getFixedSize() / 8); 65 return *this; 66 } 67 68 StackOffset &operator+=(const StackOffset &Other) { 69 Bytes += Other.Bytes; 70 ScalableBytes += Other.ScalableBytes; 71 return *this; 72 } 73 74 StackOffset operator+(const StackOffset &Other) const { 75 StackOffset Res(*this); 76 Res += Other; 77 return Res; 78 } 79 80 StackOffset &operator-=(const StackOffset &Other) { 81 Bytes -= Other.Bytes; 82 ScalableBytes -= Other.ScalableBytes; 83 return *this; 84 } 85 86 StackOffset operator-(const StackOffset &Other) const { 87 StackOffset Res(*this); 88 Res -= Other; 89 return Res; 90 } 91 92 StackOffset operator-() const { 93 StackOffset Res = {}; 94 const StackOffset Other(*this); 95 Res -= Other; 96 return Res; 97 } 98 99 /// Returns the scalable part of the offset in bytes. getScalableBytes()100 int64_t getScalableBytes() const { return ScalableBytes; } 101 102 /// Returns the non-scalable part of the offset in bytes. getBytes()103 int64_t getBytes() const { return Bytes; } 104 105 /// Returns the offset in parts to which this frame offset can be 106 /// decomposed for the purpose of describing a frame offset. 107 /// For non-scalable offsets this is simply its byte size. getForFrameOffset(int64_t & NumBytes,int64_t & NumPredicateVectors,int64_t & NumDataVectors)108 void getForFrameOffset(int64_t &NumBytes, int64_t &NumPredicateVectors, 109 int64_t &NumDataVectors) const { 110 assert(isValid() && "Invalid frame offset"); 111 112 NumBytes = Bytes; 113 NumDataVectors = 0; 114 NumPredicateVectors = ScalableBytes / 2; 115 // This method is used to get the offsets to adjust the frame offset. 116 // If the function requires ADDPL to be used and needs more than two ADDPL 117 // instructions, part of the offset is folded into NumDataVectors so that it 118 // uses ADDVL for part of it, reducing the number of ADDPL instructions. 119 if (NumPredicateVectors % 8 == 0 || NumPredicateVectors < -64 || 120 NumPredicateVectors > 62) { 121 NumDataVectors = NumPredicateVectors / 8; 122 NumPredicateVectors -= NumDataVectors * 8; 123 } 124 } 125 getForDwarfOffset(int64_t & ByteSized,int64_t & VGSized)126 void getForDwarfOffset(int64_t &ByteSized, int64_t &VGSized) const { 127 assert(isValid() && "Invalid frame offset"); 128 129 // VGSized offsets are divided by '2', because the VG register is the 130 // the number of 64bit granules as opposed to 128bit vector chunks, 131 // which is how the 'n' in e.g. MVT::nxv1i8 is modelled. 132 // So, for a stack offset of 16 MVT::nxv1i8's, the size is n x 16 bytes. 133 // VG = n * 2 and the dwarf offset must be VG * 8 bytes. 134 ByteSized = Bytes; 135 VGSized = ScalableBytes / 2; 136 } 137 138 /// Returns whether the offset is known zero. 139 explicit operator bool() const { return Bytes || ScalableBytes; } 140 isValid()141 bool isValid() const { 142 // The smallest scalable element supported by scaled SVE addressing 143 // modes are predicates, which are 2 scalable bytes in size. So the scalable 144 // byte offset must always be a multiple of 2. 145 return ScalableBytes % 2 == 0; 146 } 147 }; 148 149 } // end namespace llvm 150 151 #endif 152