label-aarch32.h revision 7827144797ee5ebfa0b574f45ad8ff235f919304
1// Copyright 2015, VIXL authors
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are met:
6//
7//   * Redistributions of source code must retain the above copyright notice,
8//     this list of conditions and the following disclaimer.
9//   * Redistributions in binary form must reproduce the above copyright notice,
10//     this list of conditions and the following disclaimer in the documentation
11//     and/or other materials provided with the distribution.
12//   * Neither the name of ARM Limited nor the names of its contributors may be
13//     used to endorse or promote products derived from this software without
14//     specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
17// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27#ifndef VIXL_AARCH32_LABEL_AARCH32_H_
28#define VIXL_AARCH32_LABEL_AARCH32_H_
29
30extern "C" {
31#include <stdint.h>
32}
33
34#include <algorithm>
35#include <cstddef>
36#include <iomanip>
37#include <list>
38
39#include "utils-vixl.h"
40
41namespace vixl {
42namespace aarch32 {
43
44class VeneerPoolManager;
45class MacroAssembler;
46
47class Label {
48 public:
49  typedef int32_t Offset;
50  static const Offset kMaxOffset = 0x7fffffff;
51
52  class LabelEmitOperator {
53    Label::Offset max_backward_;
54    Label::Offset max_forward_;
55
56   public:
57    LabelEmitOperator(Label::Offset max_backward, Label::Offset max_forward)
58        : max_backward_(max_backward), max_forward_(max_forward) {}
59    virtual ~LabelEmitOperator() {}
60    virtual uint32_t Encode(uint32_t /*instr*/,
61                            Label::Offset /*pc*/,
62                            const Label* /*label*/) const {
63      return 0;
64    }
65    Label::Offset GetMaxForwardDistance() const { return max_forward_; }
66    Label::Offset GetMaxBackwardDistance() const { return max_backward_; }
67  };
68
69  class ForwardReference {
70   public:
71    ForwardReference(int32_t location, const LabelEmitOperator& op, bool t32)
72        : location_(location), op_(op), is_t32_(t32), is_branch_(false) {}
73    Offset GetMaxForwardDistance() const { return op_.GetMaxForwardDistance(); }
74    int32_t GetLocation() const { return location_; }
75    uint32_t GetStatePCOffset() const {
76      return is_t32_ ? kT32PcDelta : kA32PcDelta;
77    }
78    bool IsUsingT32() const { return is_t32_; }
79    bool IsBranch() const { return is_branch_; }
80    void SetIsBranch() { is_branch_ = true; }
81    const LabelEmitOperator& GetEmitOperator() const { return op_; }
82    Offset GetCheckpoint() const {
83      return GetMaxForwardDistance() + (GetLocation() + GetStatePCOffset());
84    }
85
86   private:
87    int32_t location_;
88    const LabelEmitOperator& op_;
89    bool is_t32_;
90    bool is_branch_;
91  };
92
93  typedef std::list<ForwardReference> ForwardRefList;
94
95  enum UpdateCheckpointOption { kNoUpdateNecessary, kRecomputeCheckpoint };
96
97  static bool CompareCheckpoints(const ForwardReference& a,
98                                 const ForwardReference& b) {
99    return a.GetCheckpoint() < b.GetCheckpoint();
100  }
101
102  Offset GetNextCheckpoint() {
103    if (IsReferenced()) {
104      ForwardRefList::iterator min_checkpoint =
105          std::min_element(forward_.begin(),
106                           forward_.end(),
107                           CompareCheckpoints);
108      return (*min_checkpoint).GetCheckpoint();
109    }
110    return kMaxOffset;
111  }
112
113 public:
114  Label()
115      : imm_offset_(kMaxOffset),
116        pc_offset_(0),
117        is_bound_(false),
118        minus_zero_(false),
119        is_t32_(false),
120        veneer_pool_manager_(NULL),
121        checkpoint_(kMaxOffset) {}
122  explicit Label(Offset offset, uint32_t pc_offset, bool minus_zero = false)
123      : imm_offset_(offset),
124        pc_offset_(pc_offset),
125        is_bound_(true),
126        minus_zero_(minus_zero),
127        is_t32_(false),
128        veneer_pool_manager_(NULL),
129        checkpoint_(kMaxOffset) {}
130  ~Label() {}
131  bool IsBound() const { return is_bound_; }
132  bool IsReferenced() const { return !forward_.empty(); }
133  void Bind(Offset offset, bool isT32) {
134    VIXL_ASSERT(!IsBound());
135    imm_offset_ = offset;
136    is_bound_ = true;
137    is_t32_ = isT32;
138  }
139  uint32_t GetPcOffset() const { return pc_offset_; }
140  Offset GetLocation() const {
141    VIXL_ASSERT(IsBound());
142    return imm_offset_ + static_cast<Offset>(pc_offset_);
143  }
144  bool IsUsingT32() const {
145    VIXL_ASSERT(IsBound());  // Must be bound to know if it's a T32 label
146    return is_t32_;
147  }
148  bool IsMinusZero() const {
149    VIXL_ASSERT(IsBound());
150    return minus_zero_;
151  }
152  bool IsInVeneerPool() const { return veneer_pool_manager_ != NULL; }
153  VeneerPoolManager* GetVeneerPoolManager() const {
154    return veneer_pool_manager_;
155  }
156  void SetVeneerPoolManager(VeneerPoolManager* veneer_pool_manager) {
157    veneer_pool_manager_ = veneer_pool_manager;
158  }
159  void ClearVeneerPoolManager() { veneer_pool_manager_ = NULL; }
160  void SetCheckpoint(Offset checkpoint) { checkpoint_ = checkpoint; }
161  Offset GetCheckpoint() const { return checkpoint_; }
162  Offset GetAlignedCheckpoint(int byte_align) const {
163    return AlignDown(GetCheckpoint(), byte_align);
164  }
165  void AddForwardRef(int32_t instr_location,
166                     bool isT32,
167                     const LabelEmitOperator& op) {
168    forward_.push_back(ForwardReference(instr_location, op, isT32));
169  }
170
171  ForwardRefList::iterator GetFirstForwardRef() { return forward_.begin(); }
172  ForwardRefList::iterator GetEndForwardRef() { return forward_.end(); }
173  const ForwardReference* GetForwardRefBack() const {
174    if (forward_.empty()) return NULL;
175    return &forward_.back();
176  }
177  // Erase an item in the list. We don't have to recompute the checkpoint as
178  // the caller does it.
179  ForwardRefList::iterator Erase(ForwardRefList::iterator ref) {
180    return forward_.erase(ref);
181  }
182  ForwardReference& GetBackForwardRef() { return forward_.back(); }
183
184  Offset GetLastInsertForwardDistance() const {
185    if (IsReferenced()) {
186      return forward_.back().GetMaxForwardDistance();
187    }
188    return kMaxOffset;
189  }
190
191  void ClearForwardRef() { forward_.clear(); }
192
193  // Only used by the literal pool.
194  // Removes the last forward reference, in particular because of a rewind.
195  // TODO(all): This is hard to test as the checkpoint could be affected only
196  //   if the literal has multiple forward references. So, the literal has to be
197  //   shared between multiple instructions and part of the literal pool which
198  //   is not yet supperted.
199  void InvalidateLastForwardReference(
200      UpdateCheckpointOption update_checkpoint = kRecomputeCheckpoint) {
201    if (!IsBound()) {
202      VIXL_ASSERT(IsReferenced());
203      forward_.pop_back();
204    }
205    VIXL_ASSERT((update_checkpoint == kNoUpdateNecessary) &&
206                ((checkpoint_ == GetNextCheckpoint()) ||
207                 ((checkpoint_ == Label::kMaxOffset) && forward_.empty())));
208    if (update_checkpoint == kRecomputeCheckpoint) {
209      checkpoint_ = GetNextCheckpoint();
210    }
211  }
212
213  // Only used by the literal pool.
214  // Update the checkpoint as the shorter distance from the last
215  // literal in the pool's reference location to the point
216  // where the forward reference will fail.
217  // The last forward reference is assumed to be the one freshly
218  // added regarding this literal.
219  void UpdateCheckpoint() {
220    if (IsReferenced()) {
221      const ForwardReference& ref = forward_.back();
222      checkpoint_ = std::min(checkpoint_, ref.GetCheckpoint());
223    }
224    VIXL_ASSERT(GetNextCheckpoint() == checkpoint_);
225  }
226
227  static bool CompareLabels(Label* a, Label* b) {
228    return a->GetCheckpoint() < b->GetCheckpoint();
229  }
230
231 private:
232  // Once bound, location of this label in the code buffer.
233  Offset imm_offset_;
234  uint32_t pc_offset_;
235  // Is the label bound.
236  bool is_bound_;
237  // Special flag for 'pc - 0'.
238  bool minus_zero_;
239  // Is the label in T32 state.
240  bool is_t32_;
241  // Not null if the label is currently inserted in the veneer pool.
242  VeneerPoolManager* veneer_pool_manager_;
243  // Contains the references to the unbound label
244  ForwardRefList forward_;
245  // Max offset in the code buffer. Must be emitted before this checkpoint.
246  Offset checkpoint_;
247};
248
249class VeneerPoolManager {
250 public:
251  explicit VeneerPoolManager(MacroAssembler* masm)
252      : masm_(masm), checkpoint_(Label::kMaxOffset) {}
253  bool IsEmpty() const { return checkpoint_ == Label::kMaxOffset; }
254  Label::Offset GetCheckpoint() const {
255    // Make room for a branch over the pools.
256    return checkpoint_ - kMaxInstructionSizeInBytes;
257  }
258  size_t GetMaxSize() const {
259    return labels_.size() * kMaxInstructionSizeInBytes;
260  }
261  void AddLabel(Label* label);
262  void RemoveLabel(Label* label);
263  void Emit(Label::Offset target);
264
265 private:
266  MacroAssembler* masm_;
267  // List of all unbound labels which are used by a branch instruction.
268  std::list<Label*> labels_;
269  // Max offset in the code buffer where the veneer needs to be emitted.
270  // A default value of Label::kMaxOffset means that the checkpoint is
271  // invalid.
272  Label::Offset checkpoint_;
273};
274
275}  // namespace aarch32
276}  // namespace vixl
277
278#endif  // VIXL_AARCH32_LABEL_AARCH32_H_
279