label-aarch32.h revision 2272afbda5f15c4bfb25c3c9bf95d960c9df39d6
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  void ClearForwardRef() { forward_.clear(); }
208
209  // Only used by the literal pool.
210  // Removes the last forward reference, in particular because of a rewind.
211  // TODO(all): This is hard to test as the checkpoint could be affected only
212  //   if the literal has multiple forward references. So, the literal has to be
213  //   shared between multiple instructions and part of the literal pool which
214  //   is not yet supperted.
215  void InvalidateLastForwardReference(
216      UpdateCheckpointOption update_checkpoint = kRecomputeCheckpoint) {
217    if (!IsBound()) {
218      VIXL_ASSERT(HasForwardReference());
219      forward_.pop_back();
220    }
221    VIXL_ASSERT((update_checkpoint == kNoUpdateNecessary) &&
222                ((checkpoint_ == GetNextCheckpoint()) ||
223                 ((checkpoint_ == Label::kMaxOffset) && forward_.empty())));
224    if (update_checkpoint == kRecomputeCheckpoint) {
225      checkpoint_ = GetNextCheckpoint();
226    }
227  }
228
229  // Only used by the literal pool.
230  // Update the checkpoint as the shorter distance from the last
231  // literal in the pool's reference location to the point
232  // where the forward reference will fail.
233  // The last forward reference is assumed to be the one freshly
234  // added regarding this literal.
235  void UpdateCheckpoint() {
236    if (HasForwardReference()) {
237      const ForwardReference& ref = forward_.back();
238      checkpoint_ = std::min(checkpoint_, ref.GetCheckpoint());
239    }
240    VIXL_ASSERT(GetNextCheckpoint() == checkpoint_);
241  }
242
243  static bool CompareLabels(Label* a, Label* b) {
244    return a->GetCheckpoint() < b->GetCheckpoint();
245  }
246
247 private:
248  // Once bound, location of this label in the code buffer.
249  Offset imm_offset_;
250  uint32_t pc_offset_;
251  // Is the label bound.
252  bool is_bound_;
253  // Special flag for 'pc - 0'.
254  bool minus_zero_;
255  // Is the label in T32 state.
256  bool is_t32_;
257  // True if the label has been used at least once.
258  bool referenced_;
259  // Not null if the label is currently inserted in the veneer pool.
260  VeneerPoolManager* veneer_pool_manager_;
261  // True if the label is inserted in the near_labels_ list.
262  bool is_near_;
263  // Contains the references to the unbound label
264  ForwardRefList forward_;
265  // Max offset in the code buffer. Must be emitted before this checkpoint.
266  Offset checkpoint_;
267};
268
269class VeneerPoolManager {
270 public:
271  explicit VeneerPoolManager(MacroAssembler* masm)
272      : masm_(masm),
273        near_checkpoint_(Label::kMaxOffset),
274        far_checkpoint_(Label::kMaxOffset),
275        max_near_checkpoint_(0),
276        near_checkpoint_margin_(0),
277        last_label_reference_offset_(0),
278        monitor_(0) {}
279  bool IsEmpty() const {
280    return (near_labels_.size() + far_labels_.size()) == 0;
281  }
282  Label::Offset GetCheckpoint() const {
283    // For the far labels, we subtract the veneer size. This way avoids problems
284    // when two label have the same checkpoint. In the usual case, we lose some
285    // range but, as the minimum range for far labels is 1 mega byte, it's not
286    // very important.
287    size_t veneer_max_size = GetMaxSize();
288    VIXL_ASSERT(IsInt32(veneer_max_size));
289    Label::Offset tmp =
290        far_checkpoint_ - static_cast<Label::Offset>(veneer_max_size);
291    // Make room for a branch over the pools.
292    return std::min(near_checkpoint_, tmp) - kMaxInstructionSizeInBytes -
293           near_checkpoint_margin_;
294  }
295  size_t GetMaxSize() const {
296    return (near_labels_.size() + far_labels_.size()) *
297           kMaxInstructionSizeInBytes;
298  }
299  void AddLabel(Label* label);
300  void RemoveLabel(Label* label);
301  void EmitLabel(Label* label, Label::Offset emitted_target);
302  void Emit(Label::Offset target);
303
304  void Block() { monitor_++; }
305  void Release();
306  bool IsBlocked() const { return monitor_ != 0; }
307
308 private:
309  MacroAssembler* masm_;
310  // Lists of all unbound labels which are used by a branch instruction.
311  std::list<Label*> near_labels_;
312  std::list<Label*> far_labels_;
313  // Offset in the code buffer after which the veneer needs to be emitted.
314  // It's the lowest checkpoint value in the associated list.
315  // A default value of Label::kMaxOffset means that the checkpoint is
316  // invalid (no entry in the list).
317  Label::Offset near_checkpoint_;
318  Label::Offset far_checkpoint_;
319  // Highest checkpoint value for the near list.
320  Label::Offset max_near_checkpoint_;
321  // Margin we have to take to ensure that 16 bit branch instructions will be
322  // able to generate 32 bit veneers.
323  uint32_t near_checkpoint_margin_;
324  // Offset where the last reference to a label has been added to the pool.
325  Label::Offset last_label_reference_offset_;
326  // Indicates whether the emission of this pool is blocked.
327  int monitor_;
328};
329
330}  // namespace aarch32
331}  // namespace vixl
332
333#endif  // VIXL_AARCH32_LABEL_AARCH32_H_
334