this repo has no description
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