label-aarch32.h revision 15985a2fcc72ce0ec5e19c410b444ceec899c11f
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
41#include "constants-aarch32.h"
42
43namespace vixl {
44namespace aarch32 {
45
46class VeneerPoolManager;
47class MacroAssembler;
48
49class Label {
50 public:
51  typedef int32_t Offset;
52  static const Offset kMaxOffset = 0x7fffffff;
53
54  class LabelEmitOperator {
55    Label::Offset max_backward_;
56    Label::Offset max_forward_;
57
58   public:
59    LabelEmitOperator(Label::Offset max_backward, Label::Offset max_forward)
60        : max_backward_(max_backward), max_forward_(max_forward) {}
61    virtual ~LabelEmitOperator() {}
62    virtual uint32_t Encode(uint32_t /*instr*/,
63                            Label::Offset /*pc*/,
64                            const Label* /*label*/) const {
65      return 0;
66    }
67    Label::Offset GetMaxForwardDistance() const { return max_forward_; }
68    Label::Offset GetMaxBackwardDistance() const { return max_backward_; }
69  };
70
71  class ForwardReference {
72   public:
73    ForwardReference(int32_t location, const LabelEmitOperator& op, bool t32)
74        : location_(location), op_(op), is_t32_(t32), is_branch_(false) {}
75    Offset GetMaxForwardDistance() const { return op_.GetMaxForwardDistance(); }
76    int32_t GetLocation() const { return location_; }
77    uint32_t GetStatePCOffset() const {
78      return is_t32_ ? kT32PcDelta : kA32PcDelta;
79    }
80    bool IsUsingT32() const { return is_t32_; }
81    bool IsBranch() const { return is_branch_; }
82    void SetIsBranch() { is_branch_ = true; }
83    const LabelEmitOperator& GetEmitOperator() const { return op_; }
84    Offset GetCheckpoint() const {
85      // The load instructions align down PC before adding the offset.
86      // The alignment is only needed for T32 as A32 instructions are always
87      // 4 byte aligned.
88      int32_t pc = GetLocation() + GetStatePCOffset();
89      return GetMaxForwardDistance() +
90             ((IsUsingT32() && !IsBranch()) ? AlignDown(pc, 4) : pc);
91    }
92
93   private:
94    int32_t location_;
95    const LabelEmitOperator& op_;
96    bool is_t32_;
97    bool is_branch_;
98  };
99
100  typedef std::list<ForwardReference> ForwardRefList;
101
102  enum UpdateCheckpointOption { kNoUpdateNecessary, kRecomputeCheckpoint };
103
104  static bool CompareCheckpoints(const ForwardReference& a,
105                                 const ForwardReference& b) {
106    return a.GetCheckpoint() < b.GetCheckpoint();
107  }
108
109  Offset GetNextCheckpoint() {
110    if (HasForwardReference()) {
111      ForwardRefList::iterator min_checkpoint =
112          std::min_element(forward_.begin(),
113                           forward_.end(),
114                           CompareCheckpoints);
115      return (*min_checkpoint).GetCheckpoint();
116    }
117    return kMaxOffset;
118  }
119
120 public:
121  Label()
122      : imm_offset_(kMaxOffset),
123        pc_offset_(0),
124        is_bound_(false),
125        minus_zero_(false),
126        is_t32_(false),
127        referenced_(false),
128        veneer_pool_manager_(NULL),
129        is_near_(false),
130        checkpoint_(kMaxOffset) {}
131  explicit Label(Offset offset, uint32_t pc_offset, bool minus_zero = false)
132      : imm_offset_(offset),
133        pc_offset_(pc_offset),
134        is_bound_(true),
135        minus_zero_(minus_zero),
136        is_t32_(false),
137        referenced_(false),
138        veneer_pool_manager_(NULL),
139        is_near_(false),
140        checkpoint_(kMaxOffset) {}
141  ~Label() VIXL_THROW_IN_NEGATIVE_TESTING_MODE(std::runtime_error) {
142#ifdef VIXL_DEBUG
143    if (referenced_ && !is_bound_) {
144      VIXL_ABORT_WITH_MSG("Label used but not bound.\n");
145    }
146#endif
147  }
148  bool IsBound() const { return is_bound_; }
149  bool HasForwardReference() const { return !forward_.empty(); }
150  void Bind(Offset offset, bool isT32) {
151    VIXL_ASSERT(!IsBound());
152    imm_offset_ = offset;
153    is_bound_ = true;
154    is_t32_ = isT32;
155  }
156  uint32_t GetPcOffset() const { return pc_offset_; }
157  Offset GetLocation() const {
158    VIXL_ASSERT(IsBound());
159    return imm_offset_ + static_cast<Offset>(pc_offset_);
160  }
161  bool IsUsingT32() const {
162    VIXL_ASSERT(IsBound());  // Must be bound to know if it's a T32 label
163    return is_t32_;
164  }
165  bool IsMinusZero() const {
166    VIXL_ASSERT(IsBound());
167    return minus_zero_;
168  }
169  void SetReferenced() { referenced_ = true; }
170  bool IsReferenced() const { return referenced_; }
171  bool IsInVeneerPool() const { return veneer_pool_manager_ != NULL; }
172  VeneerPoolManager* GetVeneerPoolManager() const {
173    return veneer_pool_manager_;
174  }
175  void SetVeneerPoolManager(VeneerPoolManager* veneer_pool_manager,
176                            bool is_near) {
177    veneer_pool_manager_ = veneer_pool_manager;
178    is_near_ = is_near;
179  }
180  void ClearVeneerPoolManager() { veneer_pool_manager_ = NULL; }
181  bool IsNear() const { return is_near_; }
182  void SetCheckpoint(Offset checkpoint) { checkpoint_ = checkpoint; }
183  Offset GetCheckpoint() const { return checkpoint_; }
184  Offset GetAlignedCheckpoint(int byte_align) const {
185    return AlignDown(GetCheckpoint(), byte_align);
186  }
187  void AddForwardRef(int32_t instr_location,
188                     bool isT32,
189                     const LabelEmitOperator& op) {
190    VIXL_ASSERT(referenced_);
191    forward_.push_back(ForwardReference(instr_location, op, isT32));
192  }
193
194  ForwardRefList::iterator GetFirstForwardRef() { return forward_.begin(); }
195  ForwardRefList::iterator GetEndForwardRef() { return forward_.end(); }
196  const ForwardReference* GetForwardRefBack() const {
197    if (forward_.empty()) return NULL;
198    return &forward_.back();
199  }
200  // Erase an item in the list. We don't have to recompute the checkpoint as
201  // the caller does it.
202  ForwardRefList::iterator Erase(ForwardRefList::iterator ref) {
203    return forward_.erase(ref);
204  }
205  ForwardReference& GetBackForwardRef() { return forward_.back(); }
206
207  Offset GetLastInsertForwardDistance() const {
208    if (HasForwardReference()) {
209      return forward_.back().GetMaxForwardDistance();
210    }
211    return kMaxOffset;
212  }
213
214  void ClearForwardRef() { forward_.clear(); }
215
216  // Only used by the literal pool.
217  // Removes the last forward reference, in particular because of a rewind.
218  // TODO(all): This is hard to test as the checkpoint could be affected only
219  //   if the literal has multiple forward references. So, the literal has to be
220  //   shared between multiple instructions and part of the literal pool which
221  //   is not yet supperted.
222  void InvalidateLastForwardReference(
223      UpdateCheckpointOption update_checkpoint = kRecomputeCheckpoint) {
224    if (!IsBound()) {
225      VIXL_ASSERT(HasForwardReference());
226      forward_.pop_back();
227    }
228    VIXL_ASSERT((update_checkpoint == kNoUpdateNecessary) &&
229                ((checkpoint_ == GetNextCheckpoint()) ||
230                 ((checkpoint_ == Label::kMaxOffset) && forward_.empty())));
231    if (update_checkpoint == kRecomputeCheckpoint) {
232      checkpoint_ = GetNextCheckpoint();
233    }
234  }
235
236  // Only used by the literal pool.
237  // Update the checkpoint as the shorter distance from the last
238  // literal in the pool's reference location to the point
239  // where the forward reference will fail.
240  // The last forward reference is assumed to be the one freshly
241  // added regarding this literal.
242  void UpdateCheckpoint() {
243    if (HasForwardReference()) {
244      const ForwardReference& ref = forward_.back();
245      checkpoint_ = std::min(checkpoint_, ref.GetCheckpoint());
246    }
247    VIXL_ASSERT(GetNextCheckpoint() == checkpoint_);
248  }
249
250  static bool CompareLabels(Label* a, Label* b) {
251    return a->GetCheckpoint() < b->GetCheckpoint();
252  }
253
254 private:
255  // Once bound, location of this label in the code buffer.
256  Offset imm_offset_;
257  uint32_t pc_offset_;
258  // Is the label bound.
259  bool is_bound_;
260  // Special flag for 'pc - 0'.
261  bool minus_zero_;
262  // Is the label in T32 state.
263  bool is_t32_;
264  // True if the label has been used at least once.
265  bool referenced_;
266  // Not null if the label is currently inserted in the veneer pool.
267  VeneerPoolManager* veneer_pool_manager_;
268  // True if the label is inserted in the near_labels_ list.
269  bool is_near_;
270  // Contains the references to the unbound label
271  ForwardRefList forward_;
272  // Max offset in the code buffer. Must be emitted before this checkpoint.
273  Offset checkpoint_;
274};
275
276class VeneerPoolManager {
277 public:
278  explicit VeneerPoolManager(MacroAssembler* masm)
279      : masm_(masm),
280        near_checkpoint_(Label::kMaxOffset),
281        far_checkpoint_(Label::kMaxOffset),
282        last_label_reference_offset_(0),
283        monitor_(0) {}
284  bool IsEmpty() const {
285    return (near_labels_.size() + far_labels_.size()) == 0;
286  }
287  Label::Offset GetCheckpoint() const {
288    // For the far labels, we subtract the veneer size. This way avoids problems
289    // when two label have the same checkpoint. In the usual case, we lose some
290    // range but, as the minimum range for far labels is 1 mega byte, it's not
291    // very important.
292    size_t veneer_max_size = GetMaxSize();
293    VIXL_ASSERT(IsInt32(veneer_max_size));
294    Label::Offset tmp =
295        far_checkpoint_ - static_cast<Label::Offset>(veneer_max_size);
296    // Make room for a branch over the pools.
297    return std::min(near_checkpoint_, tmp) - kMaxInstructionSizeInBytes;
298  }
299  size_t GetMaxSize() const {
300    return (near_labels_.size() + far_labels_.size()) *
301           kMaxInstructionSizeInBytes;
302  }
303  void AddLabel(Label* label);
304  void RemoveLabel(Label* label);
305  void EmitLabel(Label* label, Label::Offset emitted_target);
306  void Emit(Label::Offset target);
307
308  void Block() { monitor_++; }
309  void Release();
310  bool IsBlocked() const { return monitor_ != 0; }
311
312 private:
313  MacroAssembler* masm_;
314  // Lists of all unbound labels which are used by a branch instruction.
315  std::list<Label*> near_labels_;
316  std::list<Label*> far_labels_;
317  // Max offset in the code buffer where the veneer needs to be emitted.
318  // A default value of Label::kMaxOffset means that the checkpoint is
319  // invalid.
320  Label::Offset near_checkpoint_;
321  Label::Offset far_checkpoint_;
322  // Offset where the last reference to a label has been added to the pool.
323  Label::Offset last_label_reference_offset_;
324  // Indicates whether the emission of this pool is blocked.
325  int monitor_;
326};
327
328}  // namespace aarch32
329}  // namespace vixl
330
331#endif  // VIXL_AARCH32_LABEL_AARCH32_H_
332