this repo has no description
at trunk 308 lines 8.6 kB view raw
1/* Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) */ 2// Copyright (c) 2013, the Dart project authors and Facebook, Inc. and its 3// affiliates. Please see the AUTHORS-Dart file for details. All rights 4// reserved. Use of this source code is governed by a BSD-style license that 5// can be found in the LICENSE-Dart file. 6 7#pragma once 8 9#include <string> 10 11#include "globals.h" 12#include "memory-region.h" 13#include "utils.h" 14#include "vector.h" 15 16namespace py { 17 18// Forward declarations. 19namespace x64 { 20class Assembler; 21} 22class AssemblerBuffer; 23 24class WARN_UNUSED Label { 25 public: 26 Label() : position_(0), unresolved_(0) { 27#ifndef NDEBUG 28 for (int i = 0; i < kMaxUnresolvedBranches; i++) { 29 unresolved_near_positions_[i] = -1; 30 } 31#endif 32 } 33 34 ~Label() { 35 // Assert if label is being destroyed with unresolved branches pending. 36 DCHECK(!isLinked(), "assert()"); 37 DCHECK(!hasNear(), "assert()"); 38 } 39 40 // Returns the position for bound and linked labels. Cannot be used 41 // for unused labels. 42 word position() const { 43 DCHECK(!isUnused(), "assert()"); 44 return isBound() ? -position_ - kBias : position_ - kBias; 45 } 46 47 word linkPosition() const { 48 DCHECK(isLinked(), "assert()"); 49 return position_ - kBias; 50 } 51 52 word nearPosition() { 53 DCHECK(hasNear(), "assert()"); 54 return unresolved_near_positions_[--unresolved_]; 55 } 56 57 bool isBound() const { return position_ < 0; } 58 bool isUnused() const { return position_ == 0 && unresolved_ == 0; } 59 bool isLinked() const { return position_ > 0; } 60 bool hasNear() const { return unresolved_ != 0; } 61 62 private: 63 static const int kMaxUnresolvedBranches = 20; 64 // Zero position_ means unused (neither bound nor linked to). 65 // Thus we offset actual positions by the given bias to prevent zero 66 // positions from occurring. 67 static const int kBias = 4; 68 69 word position_; 70 word unresolved_; 71 word unresolved_near_positions_[kMaxUnresolvedBranches]; 72 73 void reinitialize() { position_ = 0; } 74 75 void bindTo(word position) { 76 DCHECK(!isBound(), "assert()"); 77 DCHECK(!hasNear(), "assert()"); 78 position_ = -position - kBias; 79 DCHECK(isBound(), "assert()"); 80 } 81 82 void linkTo(word position) { 83 DCHECK(!isBound(), "assert()"); 84 position_ = position + kBias; 85 DCHECK(isLinked(), "assert()"); 86 } 87 88 void nearLinkTo(word position) { 89 DCHECK(!isBound(), "assert()"); 90 DCHECK(unresolved_ < kMaxUnresolvedBranches, "assert()"); 91 unresolved_near_positions_[unresolved_++] = position; 92 } 93 94 friend class x64::Assembler; 95 DISALLOW_COPY_AND_ASSIGN(Label); 96}; 97 98// Assembler fixups are positions in generated code that hold relocation 99// information that needs to be processed before finalizing the code 100// into executable memory. 101class AssemblerFixup { 102 public: 103 virtual void process(MemoryRegion region, word position) = 0; 104 105 // It would be ideal if the destructor method could be made private, 106 // but the g++ compiler complains when this is subclassed. 107 virtual ~AssemblerFixup() { UNREACHABLE("~AssemblerFixup"); } 108 109 private: 110 AssemblerFixup* previous_; 111 word position_; 112 113 AssemblerFixup* previous() const { return previous_; } 114 void setPrevious(AssemblerFixup* previous) { previous_ = previous; } 115 116 word position() const { return position_; } 117 void setPosition(word position) { position_ = position; } 118 119 friend class AssemblerBuffer; 120}; 121 122// Assembler buffers are used to emit binary code. They grow on demand. 123class AssemblerBuffer { 124 public: 125 AssemblerBuffer(); 126 ~AssemblerBuffer(); 127 128 // Basic support for emitting, loading, and storing. 129 template <typename T> 130 void emit(T value) { 131 DCHECK(hasEnsuredCapacity(), "assert()"); 132 std::memcpy(reinterpret_cast<char*>(cursor_), &value, sizeof value); 133 cursor_ += sizeof(T); 134 } 135 136 template <typename T> 137 void remit() { 138 DCHECK(size() >= static_cast<word>(sizeof(T)), "assert()"); 139 cursor_ -= sizeof(T); 140 } 141 142 // Return address to code at |position| bytes. 143 uword address(word position) { return contents_ + position; } 144 145 template <typename T> 146 T load(word position) { 147 DCHECK(position >= 0, "assert()"); 148 DCHECK(position <= (size() - static_cast<word>(sizeof(T))), "assert()"); 149 T result; 150 std::memcpy(&result, reinterpret_cast<char*>(contents_ + position), 151 sizeof result); 152 return result; 153 } 154 155 template <typename T> 156 void store(word position, T value) { 157 DCHECK(position >= 0, "assert()"); 158 DCHECK(position <= (size() - static_cast<word>(sizeof(T))), "assert()"); 159 std::memcpy(reinterpret_cast<char*>(contents_ + position), &value, 160 sizeof value); 161 } 162 163 const Vector<word>& pointerOffsets() const { 164#ifndef NDEBUG 165 DCHECK(fixups_processed_, "assert()"); 166#endif 167 return pointer_offsets_; 168 } 169 170 // Emit a fixup at the current location. 171 void emitFixup(AssemblerFixup* fixup) { 172 fixup->setPrevious(fixup_); 173 fixup->setPosition(size()); 174 fixup_ = fixup; 175 } 176 177 // Count the fixups that produce a pointer offset, without processing 178 // the fixups. 179 word countPointerOffsets() const; 180 181 // Get the size of the emitted code. 182 word size() const { return cursor_ - contents_; } 183 uword contents() const { return contents_; } 184 185 // Copy the assembled instructions into the specified memory block 186 // and apply all fixups. 187 void finalizeInstructions(MemoryRegion instructions); 188 189 // To emit an instruction to the assembler buffer, the EnsureCapacity helper 190 // must be used to guarantee that the underlying data area is big enough to 191 // hold the emitted instruction. Usage: 192 // 193 // AssemblerBuffer buffer; 194 // AssemblerBuffer::EnsureCapacity ensured(&buffer); 195 // ... emit bytes for single instruction ... 196 197#ifndef NDEBUG 198 class EnsureCapacity { 199 public: 200 explicit EnsureCapacity(AssemblerBuffer* buffer); 201 ~EnsureCapacity(); 202 203 private: 204 AssemblerBuffer* buffer_; 205 word gap_; 206 207 word computeGap() { return buffer_->capacity() - buffer_->size(); } 208 }; 209 210 bool has_ensured_capacity_; 211 bool hasEnsuredCapacity() const { return has_ensured_capacity_; } 212#else 213 class EnsureCapacity { 214 public: 215 explicit EnsureCapacity(AssemblerBuffer* buffer) { 216 if (buffer->cursor() >= buffer->limit()) buffer->extendCapacity(); 217 } 218 }; 219 220 // When building the C++ tests, assertion code is enabled. To allow 221 // asserting that the user of the assembler buffer has ensured the 222 // capacity needed for emitting, we add a dummy method in non-debug mode. 223 bool hasEnsuredCapacity() const { return true; } 224#endif 225 226 // Returns the position in the instruction stream. 227 word getPosition() const { return cursor_ - contents_; } 228 229 void reset() { cursor_ = contents_; } 230 231 private: 232 // The limit is set to kMinimumGap bytes before the end of the data area. 233 // This leaves enough space for the longest possible instruction and allows 234 // for a single, fast space check per instruction. 235 static const word kMinimumGap = 32; 236 237 uword contents_; 238 uword cursor_; 239 uword limit_; 240 AssemblerFixup* fixup_; 241 Vector<word> pointer_offsets_; 242#ifndef NDEBUG 243 bool fixups_processed_; 244#endif 245 246 uword cursor() const { return cursor_; } 247 uword limit() const { return limit_; } 248 word capacity() const { 249 DCHECK(limit_ >= contents_, "assert()"); 250 return (limit_ - contents_) + kMinimumGap; 251 } 252 253 // Process the fixup chain. 254 void processFixups(MemoryRegion region); 255 256 // Compute the limit based on the data area and the capacity. See 257 // description of kMinimumGap for the reasoning behind the value. 258 static uword computeLimit(uword data, word capacity) { 259 return data + capacity - kMinimumGap; 260 } 261 262 void extendCapacity(); 263 264 friend class AssemblerFixup; 265}; 266 267class CodeComment { 268 public: 269 CodeComment(word offset, const char* comment) 270 : offset_(offset), comment_(comment) {} 271 272 word offset() const { return offset_; } 273 const std::string& comment() const { return comment_; } 274 275 private: 276 word offset_ = -1; 277 std::string comment_; 278 279 DISALLOW_COPY_AND_ASSIGN(CodeComment); 280}; 281 282class CodeComments { 283 public: 284 CodeComments() = default; 285 286 ~CodeComments() { 287 for (word i = 0; i < comments_.size(); i++) { 288 delete comments_[i]; 289 } 290 } 291 292 void add(CodeComment* comment) { comments_.push_back(comment); } 293 294 const char* commentAt(word index) const { 295 return comments_[index]->comment().c_str(); 296 } 297 298 word length() const { return comments_.size(); } 299 300 word offsetAt(word index) const { return comments_[index]->offset(); } 301 302 private: 303 Vector<CodeComment*> comments_; 304 305 DISALLOW_COPY_AND_ASSIGN(CodeComments); 306}; 307 308} // namespace py