blob: 228612eb8a1fcb7db5805a982a6c0caf7bf17a37 [file] [log] [blame]
Carl Shapiroa5d5cfd2011-06-21 12:46:59 -07001// Copyright 2011 Google Inc. All Rights Reserved.
2
3#ifndef ART_SRC_ASSEMBLER_H_
4#define ART_SRC_ASSEMBLER_H_
5
6#include "src/logging.h"
7#include "src/macros.h"
8#include "src/memory_region.h"
9
Carl Shapiro6b6b5f02011-06-21 15:05:09 -070010namespace art {
Carl Shapiroa5d5cfd2011-06-21 12:46:59 -070011
12class Assembler;
13class AssemblerBuffer;
14class AssemblerFixup;
15
16
17class Label {
18 public:
19 Label() : position_(0) {}
20
21 ~Label() {
22 // Assert if label is being destroyed with unresolved branches pending.
23 CHECK(!IsLinked());
24 }
25
26 // Returns the position for bound and linked labels. Cannot be used
27 // for unused labels.
28 int Position() const {
29 CHECK(!IsUnused());
30 return IsBound() ? -position_ - kPointerSize : position_ - kPointerSize;
31 }
32
33 int LinkPosition() const {
34 CHECK(IsLinked());
35 return position_ - kWordSize;
36 }
37
38 bool IsBound() const { return position_ < 0; }
39 bool IsUnused() const { return position_ == 0; }
40 bool IsLinked() const { return position_ > 0; }
41
42 private:
43 int position_;
44
45 void Reinitialize() {
46 position_ = 0;
47 }
48
49 void BindTo(int position) {
50 CHECK(!IsBound());
51 position_ = -position - kPointerSize;
52 CHECK(IsBound());
53 }
54
55 void LinkTo(int position) {
56 CHECK(!IsBound());
57 position_ = position + kPointerSize;
58 CHECK(IsLinked());
59 }
60
61 friend class Assembler;
62 DISALLOW_COPY_AND_ASSIGN(Label);
63};
64
65
66// Assembler fixups are positions in generated code that require processing
67// after the code has been copied to executable memory. This includes building
68// relocation information.
69class AssemblerFixup {
70 public:
71 virtual void Process(const MemoryRegion& region, int position) = 0;
72 virtual ~AssemblerFixup() {}
73
74 private:
75 AssemblerFixup* previous_;
76 int position_;
77
78 AssemblerFixup* previous() const { return previous_; }
79 void set_previous(AssemblerFixup* previous) { previous_ = previous; }
80
81 int position() const { return position_; }
82 void set_position(int position) { position_ = position; }
83
84 friend class AssemblerBuffer;
85};
86
87
88class AssemblerBuffer {
89 public:
90 AssemblerBuffer();
91 ~AssemblerBuffer();
92
93 // Basic support for emitting, loading, and storing.
94 template<typename T> void Emit(T value) {
95 CHECK(HasEnsuredCapacity());
96 *reinterpret_cast<T*>(cursor_) = value;
97 cursor_ += sizeof(T);
98 }
99
100 template<typename T> T Load(size_t position) {
101 CHECK_LE(position, Size() - static_cast<int>(sizeof(T)));
102 return *reinterpret_cast<T*>(contents_ + position);
103 }
104
105 template<typename T> void Store(size_t position, T value) {
106 CHECK_LE(position, Size() - static_cast<int>(sizeof(T)));
107 *reinterpret_cast<T*>(contents_ + position) = value;
108 }
109
110 // Emit a fixup at the current location.
111 void EmitFixup(AssemblerFixup* fixup) {
112 fixup->set_previous(fixup_);
113 fixup->set_position(Size());
114 fixup_ = fixup;
115 }
116
117 // Get the size of the emitted code.
118 size_t Size() const {
119 CHECK_GE(cursor_, contents_);
120 return cursor_ - contents_;
121 }
122
123 byte* contents() const { return contents_; }
124
125 // Copy the assembled instructions into the specified memory block
126 // and apply all fixups.
127 void FinalizeInstructions(const MemoryRegion& region);
128
129 // To emit an instruction to the assembler buffer, the EnsureCapacity helper
130 // must be used to guarantee that the underlying data area is big enough to
131 // hold the emitted instruction. Usage:
132 //
133 // AssemblerBuffer buffer;
134 // AssemblerBuffer::EnsureCapacity ensured(&buffer);
135 // ... emit bytes for single instruction ...
136
137#ifdef DEBUG
138
139 class EnsureCapacity {
140 public:
141 explicit EnsureCapacity(AssemblerBuffer* buffer) {
142 if (buffer->cursor() >= buffer->limit()) buffer->ExtendCapacity();
143 // In debug mode, we save the assembler buffer along with the gap
144 // size before we start emitting to the buffer. This allows us to
145 // check that any single generated instruction doesn't overflow the
146 // limit implied by the minimum gap size.
147 buffer_ = buffer;
148 gap_ = ComputeGap();
149 // Make sure that extending the capacity leaves a big enough gap
150 // for any kind of instruction.
151 CHECK_GE(gap_, kMinimumGap);
152 // Mark the buffer as having ensured the capacity.
153 CHECK(!buffer->HasEnsuredCapacity()); // Cannot nest.
154 buffer->has_ensured_capacity_ = true;
155 }
156
157 ~EnsureCapacity() {
158 // Unmark the buffer, so we cannot emit after this.
159 buffer_->has_ensured_capacity_ = false;
160 // Make sure the generated instruction doesn't take up more
161 // space than the minimum gap.
162 int delta = gap_ - ComputeGap();
163 CHECK(delta <= kMinimumGap);
164 }
165
166 private:
167 AssemblerBuffer* buffer_;
168 int gap_;
169
170 int ComputeGap() { return buffer_->Capacity() - buffer_->Size(); }
171 };
172
173 bool has_ensured_capacity_;
174 bool HasEnsuredCapacity() const { return has_ensured_capacity_; }
175
176#else
177
178 class EnsureCapacity {
179 public:
180 explicit EnsureCapacity(AssemblerBuffer* buffer) {
181 if (buffer->cursor() >= buffer->limit()) buffer->ExtendCapacity();
182 }
183 };
184
185 // When building the C++ tests, assertion code is enabled. To allow
186 // asserting that the user of the assembler buffer has ensured the
187 // capacity needed for emitting, we add a dummy method in non-debug mode.
188 bool HasEnsuredCapacity() const { return true; }
189
190#endif
191
192 // Returns the position in the instruction stream.
193 int GetPosition() { return cursor_ - contents_; }
194
195 private:
196 // The limit is set to kMinimumGap bytes before the end of the data area.
197 // This leaves enough space for the longest possible instruction and allows
198 // for a single, fast space check per instruction.
199 static const int kMinimumGap = 32;
200
201 byte* contents_;
202 byte* cursor_;
203 byte* limit_;
204 AssemblerFixup* fixup_;
205 bool fixups_processed_;
206
207 byte* cursor() const { return cursor_; }
208 byte* limit() const { return limit_; }
209 size_t Capacity() const {
210 CHECK_GE(limit_, contents_);
211 return (limit_ - contents_) + kMinimumGap;
212 }
213
214 // Process the fixup chain starting at the given fixup. The offset is
215 // non-zero for fixups in the body if the preamble is non-empty.
216 void ProcessFixups(const MemoryRegion& region);
217
218 // Compute the limit based on the data area and the capacity. See
219 // description of kMinimumGap for the reasoning behind the value.
220 static byte* ComputeLimit(byte* data, size_t capacity) {
221 return data + capacity - kMinimumGap;
222 }
223
224 void ExtendCapacity();
225
226 friend class AssemblerFixup;
227};
228
Carl Shapiro6b6b5f02011-06-21 15:05:09 -0700229} // namespace art
Carl Shapiroa5d5cfd2011-06-21 12:46:59 -0700230
231#if defined(__i386__)
232#include "src/assembler_x86.h"
233#elif defined(__arm__)
234#include "src/assembler_arm.h"
235#endif
236
237#endif // ART_SRC_ASSEMBLER_H_