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