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