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