aboutsummaryrefslogtreecommitdiff
path: root/lib/Target/AArch64/AArch64StackOffset.h
blob: 13f12a6c9c30a488de40ba78254a6d1f50cacbf6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//==--AArch64StackOffset.h ---------------------------------------*- C++ -*-==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains the declaration of the StackOffset class, which is used to
// describe scalable and non-scalable offsets during frame lowering.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIB_TARGET_AARCH64_AARCH64STACKOFFSET_H
#define LLVM_LIB_TARGET_AARCH64_AARCH64STACKOFFSET_H

#include "llvm/Support/MachineValueType.h"

namespace llvm {

/// StackOffset is a wrapper around scalable and non-scalable offsets and is
/// used in several functions such as 'isAArch64FrameOffsetLegal' and
/// 'emitFrameOffset()'. StackOffsets are described by MVTs, e.g.
//
///   StackOffset(1, MVT::nxv16i8)
//
/// would describe an offset as being the size of a single SVE vector.
///
/// The class also implements simple arithmetic (addition/subtraction) on these
/// offsets, e.g.
//
///   StackOffset(1, MVT::nxv16i8) + StackOffset(1, MVT::i64)
//
/// describes an offset that spans the combined storage required for an SVE
/// vector and a 64bit GPR.
class StackOffset {
  int64_t Bytes;
  int64_t ScalableBytes;

  explicit operator int() const;

public:
  using Part = std::pair<int64_t, MVT>;

  StackOffset() : Bytes(0), ScalableBytes(0) {}

  StackOffset(int64_t Offset, MVT::SimpleValueType T) : StackOffset() {
    assert(MVT(T).getSizeInBits() % 8 == 0 &&
           "Offset type is not a multiple of bytes");
    *this += Part(Offset, T);
  }

  StackOffset(const StackOffset &Other)
      : Bytes(Other.Bytes), ScalableBytes(Other.ScalableBytes) {}

  StackOffset &operator=(const StackOffset &) = default;

  StackOffset &operator+=(const StackOffset::Part &Other) {
    int64_t OffsetInBytes = Other.first * (Other.second.getSizeInBits() / 8);
    if (Other.second.isScalableVector())
      ScalableBytes += OffsetInBytes;
    else
      Bytes += OffsetInBytes;
    return *this;
  }

  StackOffset &operator+=(const StackOffset &Other) {
    Bytes += Other.Bytes;
    ScalableBytes += Other.ScalableBytes;
    return *this;
  }

  StackOffset operator+(const StackOffset &Other) const {
    StackOffset Res(*this);
    Res += Other;
    return Res;
  }

  StackOffset &operator-=(const StackOffset &Other) {
    Bytes -= Other.Bytes;
    ScalableBytes -= Other.ScalableBytes;
    return *this;
  }

  StackOffset operator-(const StackOffset &Other) const {
    StackOffset Res(*this);
    Res -= Other;
    return Res;
  }

  StackOffset operator-() const {
    StackOffset Res = {};
    const StackOffset Other(*this);
    Res -= Other;
    return Res;
  }

  /// Returns the scalable part of the offset in bytes.
  int64_t getScalableBytes() const { return ScalableBytes; }

  /// Returns the non-scalable part of the offset in bytes.
  int64_t getBytes() const { return Bytes; }

  /// Returns the offset in parts to which this frame offset can be
  /// decomposed for the purpose of describing a frame offset.
  /// For non-scalable offsets this is simply its byte size.
  void getForFrameOffset(int64_t &NumBytes, int64_t &NumPredicateVectors,
                         int64_t &NumDataVectors) const {
    assert(isValid() && "Invalid frame offset");

    NumBytes = Bytes;
    NumDataVectors = 0;
    NumPredicateVectors = ScalableBytes / 2;
    // This method is used to get the offsets to adjust the frame offset.
    // If the function requires ADDPL to be used and needs more than two ADDPL
    // instructions, part of the offset is folded into NumDataVectors so that it
    // uses ADDVL for part of it, reducing the number of ADDPL instructions.
    if (NumPredicateVectors % 8 == 0 || NumPredicateVectors < -64 ||
        NumPredicateVectors > 62) {
      NumDataVectors = NumPredicateVectors / 8;
      NumPredicateVectors -= NumDataVectors * 8;
    }
  }

  /// Returns whether the offset is known zero.
  explicit operator bool() const { return Bytes || ScalableBytes; }

  bool isValid() const {
    // The smallest scalable element supported by scaled SVE addressing
    // modes are predicates, which are 2 scalable bytes in size. So the scalable
    // byte offset must always be a multiple of 2.
    return ScalableBytes % 2 == 0;
  }
};

} // end namespace llvm

#endif