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