1 // Copyright (c) 1994-2006 Sun Microsystems Inc.
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
6 // met:
7 //
8 // - Redistributions of source code must retain the above copyright notice,
9 // this list of conditions and the following disclaimer.
10 //
11 // - Redistribution in binary form must reproduce the above copyright
12 // notice, this list of conditions and the following disclaimer in the
13 // documentation and/or other materials provided with the distribution.
14 //
15 // - Neither the name of Sun Microsystems or the names of contributors may
16 // be used to endorse or promote products derived from this software without
17 // specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20 // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31 // The original source code covered by the above license above has been
32 // modified significantly by Google Inc.
33 // Copyright 2012 the V8 project authors. All rights reserved.
34
35 #include "src/assembler.h"
36
37 #include "src/assembler-inl.h"
38 #include "src/code-stubs.h"
39 #include "src/deoptimizer.h"
40 #include "src/disassembler.h"
41 #include "src/instruction-stream.h"
42 #include "src/isolate.h"
43 #include "src/ostreams.h"
44 #include "src/simulator.h" // For flushing instruction cache.
45 #include "src/snapshot/serializer-common.h"
46 #include "src/snapshot/snapshot.h"
47
48 namespace v8 {
49 namespace internal {
50
Default(Isolate * isolate,bool explicitly_support_serialization)51 AssemblerOptions AssemblerOptions::Default(
52 Isolate* isolate, bool explicitly_support_serialization) {
53 AssemblerOptions options;
54 bool serializer =
55 isolate->serializer_enabled() || explicitly_support_serialization;
56 options.record_reloc_info_for_serialization = serializer;
57 options.enable_root_array_delta_access = !serializer;
58 #ifdef USE_SIMULATOR
59 // Don't generate simulator specific code if we are building a snapshot, which
60 // might be run on real hardware.
61 options.enable_simulator_code = !serializer;
62 #endif
63 options.inline_offheap_trampolines = !serializer;
64 #if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_ARM64
65 options.code_range_start =
66 isolate->heap()->memory_allocator()->code_range()->start();
67 #endif
68 return options;
69 }
70
71 // -----------------------------------------------------------------------------
72 // Implementation of AssemblerBase
73
AssemblerBase(const AssemblerOptions & options,void * buffer,int buffer_size)74 AssemblerBase::AssemblerBase(const AssemblerOptions& options, void* buffer,
75 int buffer_size)
76 : options_(options),
77 enabled_cpu_features_(0),
78 emit_debug_code_(FLAG_debug_code),
79 predictable_code_size_(false),
80 constant_pool_available_(false),
81 jump_optimization_info_(nullptr) {
82 own_buffer_ = buffer == nullptr;
83 if (buffer_size == 0) buffer_size = kMinimalBufferSize;
84 DCHECK_GT(buffer_size, 0);
85 if (own_buffer_) buffer = NewArray<byte>(buffer_size);
86 buffer_ = static_cast<byte*>(buffer);
87 buffer_size_ = buffer_size;
88 pc_ = buffer_;
89 }
90
~AssemblerBase()91 AssemblerBase::~AssemblerBase() {
92 if (own_buffer_) DeleteArray(buffer_);
93 }
94
FlushICache(void * start,size_t size)95 void AssemblerBase::FlushICache(void* start, size_t size) {
96 if (size == 0) return;
97
98 #if defined(USE_SIMULATOR)
99 base::LockGuard<base::Mutex> lock_guard(Simulator::i_cache_mutex());
100 Simulator::FlushICache(Simulator::i_cache(), start, size);
101 #else
102 CpuFeatures::FlushICache(start, size);
103 #endif // USE_SIMULATOR
104 }
105
Print(Isolate * isolate)106 void AssemblerBase::Print(Isolate* isolate) {
107 StdoutStream os;
108 v8::internal::Disassembler::Decode(isolate, &os, buffer_, pc_);
109 }
110
111 // -----------------------------------------------------------------------------
112 // Implementation of PredictableCodeSizeScope
113
PredictableCodeSizeScope(AssemblerBase * assembler,int expected_size)114 PredictableCodeSizeScope::PredictableCodeSizeScope(AssemblerBase* assembler,
115 int expected_size)
116 : assembler_(assembler),
117 expected_size_(expected_size),
118 start_offset_(assembler->pc_offset()),
119 old_value_(assembler->predictable_code_size()) {
120 assembler_->set_predictable_code_size(true);
121 }
122
~PredictableCodeSizeScope()123 PredictableCodeSizeScope::~PredictableCodeSizeScope() {
124 CHECK_EQ(expected_size_, assembler_->pc_offset() - start_offset_);
125 assembler_->set_predictable_code_size(old_value_);
126 }
127
128 // -----------------------------------------------------------------------------
129 // Implementation of CpuFeatureScope
130
131 #ifdef DEBUG
CpuFeatureScope(AssemblerBase * assembler,CpuFeature f,CheckPolicy check)132 CpuFeatureScope::CpuFeatureScope(AssemblerBase* assembler, CpuFeature f,
133 CheckPolicy check)
134 : assembler_(assembler) {
135 DCHECK_IMPLIES(check == kCheckSupported, CpuFeatures::IsSupported(f));
136 old_enabled_ = assembler_->enabled_cpu_features();
137 assembler_->EnableCpuFeature(f);
138 }
139
~CpuFeatureScope()140 CpuFeatureScope::~CpuFeatureScope() {
141 assembler_->set_enabled_cpu_features(old_enabled_);
142 }
143 #endif
144
145 bool CpuFeatures::initialized_ = false;
146 unsigned CpuFeatures::supported_ = 0;
147 unsigned CpuFeatures::icache_line_size_ = 0;
148 unsigned CpuFeatures::dcache_line_size_ = 0;
149
ConstantPoolBuilder(int ptr_reach_bits,int double_reach_bits)150 ConstantPoolBuilder::ConstantPoolBuilder(int ptr_reach_bits,
151 int double_reach_bits) {
152 info_[ConstantPoolEntry::INTPTR].entries.reserve(64);
153 info_[ConstantPoolEntry::INTPTR].regular_reach_bits = ptr_reach_bits;
154 info_[ConstantPoolEntry::DOUBLE].regular_reach_bits = double_reach_bits;
155 }
156
NextAccess(ConstantPoolEntry::Type type) const157 ConstantPoolEntry::Access ConstantPoolBuilder::NextAccess(
158 ConstantPoolEntry::Type type) const {
159 const PerTypeEntryInfo& info = info_[type];
160
161 if (info.overflow()) return ConstantPoolEntry::OVERFLOWED;
162
163 int dbl_count = info_[ConstantPoolEntry::DOUBLE].regular_count;
164 int dbl_offset = dbl_count * kDoubleSize;
165 int ptr_count = info_[ConstantPoolEntry::INTPTR].regular_count;
166 int ptr_offset = ptr_count * kPointerSize + dbl_offset;
167
168 if (type == ConstantPoolEntry::DOUBLE) {
169 // Double overflow detection must take into account the reach for both types
170 int ptr_reach_bits = info_[ConstantPoolEntry::INTPTR].regular_reach_bits;
171 if (!is_uintn(dbl_offset, info.regular_reach_bits) ||
172 (ptr_count > 0 &&
173 !is_uintn(ptr_offset + kDoubleSize - kPointerSize, ptr_reach_bits))) {
174 return ConstantPoolEntry::OVERFLOWED;
175 }
176 } else {
177 DCHECK(type == ConstantPoolEntry::INTPTR);
178 if (!is_uintn(ptr_offset, info.regular_reach_bits)) {
179 return ConstantPoolEntry::OVERFLOWED;
180 }
181 }
182
183 return ConstantPoolEntry::REGULAR;
184 }
185
AddEntry(ConstantPoolEntry & entry,ConstantPoolEntry::Type type)186 ConstantPoolEntry::Access ConstantPoolBuilder::AddEntry(
187 ConstantPoolEntry& entry, ConstantPoolEntry::Type type) {
188 DCHECK(!emitted_label_.is_bound());
189 PerTypeEntryInfo& info = info_[type];
190 const int entry_size = ConstantPoolEntry::size(type);
191 bool merged = false;
192
193 if (entry.sharing_ok()) {
194 // Try to merge entries
195 std::vector<ConstantPoolEntry>::iterator it = info.shared_entries.begin();
196 int end = static_cast<int>(info.shared_entries.size());
197 for (int i = 0; i < end; i++, it++) {
198 if ((entry_size == kPointerSize) ? entry.value() == it->value()
199 : entry.value64() == it->value64()) {
200 // Merge with found entry.
201 entry.set_merged_index(i);
202 merged = true;
203 break;
204 }
205 }
206 }
207
208 // By definition, merged entries have regular access.
209 DCHECK(!merged || entry.merged_index() < info.regular_count);
210 ConstantPoolEntry::Access access =
211 (merged ? ConstantPoolEntry::REGULAR : NextAccess(type));
212
213 // Enforce an upper bound on search time by limiting the search to
214 // unique sharable entries which fit in the regular section.
215 if (entry.sharing_ok() && !merged && access == ConstantPoolEntry::REGULAR) {
216 info.shared_entries.push_back(entry);
217 } else {
218 info.entries.push_back(entry);
219 }
220
221 // We're done if we found a match or have already triggered the
222 // overflow state.
223 if (merged || info.overflow()) return access;
224
225 if (access == ConstantPoolEntry::REGULAR) {
226 info.regular_count++;
227 } else {
228 info.overflow_start = static_cast<int>(info.entries.size()) - 1;
229 }
230
231 return access;
232 }
233
EmitSharedEntries(Assembler * assm,ConstantPoolEntry::Type type)234 void ConstantPoolBuilder::EmitSharedEntries(Assembler* assm,
235 ConstantPoolEntry::Type type) {
236 PerTypeEntryInfo& info = info_[type];
237 std::vector<ConstantPoolEntry>& shared_entries = info.shared_entries;
238 const int entry_size = ConstantPoolEntry::size(type);
239 int base = emitted_label_.pos();
240 DCHECK_GT(base, 0);
241 int shared_end = static_cast<int>(shared_entries.size());
242 std::vector<ConstantPoolEntry>::iterator shared_it = shared_entries.begin();
243 for (int i = 0; i < shared_end; i++, shared_it++) {
244 int offset = assm->pc_offset() - base;
245 shared_it->set_offset(offset); // Save offset for merged entries.
246 if (entry_size == kPointerSize) {
247 assm->dp(shared_it->value());
248 } else {
249 assm->dq(shared_it->value64());
250 }
251 DCHECK(is_uintn(offset, info.regular_reach_bits));
252
253 // Patch load sequence with correct offset.
254 assm->PatchConstantPoolAccessInstruction(shared_it->position(), offset,
255 ConstantPoolEntry::REGULAR, type);
256 }
257 }
258
EmitGroup(Assembler * assm,ConstantPoolEntry::Access access,ConstantPoolEntry::Type type)259 void ConstantPoolBuilder::EmitGroup(Assembler* assm,
260 ConstantPoolEntry::Access access,
261 ConstantPoolEntry::Type type) {
262 PerTypeEntryInfo& info = info_[type];
263 const bool overflow = info.overflow();
264 std::vector<ConstantPoolEntry>& entries = info.entries;
265 std::vector<ConstantPoolEntry>& shared_entries = info.shared_entries;
266 const int entry_size = ConstantPoolEntry::size(type);
267 int base = emitted_label_.pos();
268 DCHECK_GT(base, 0);
269 int begin;
270 int end;
271
272 if (access == ConstantPoolEntry::REGULAR) {
273 // Emit any shared entries first
274 EmitSharedEntries(assm, type);
275 }
276
277 if (access == ConstantPoolEntry::REGULAR) {
278 begin = 0;
279 end = overflow ? info.overflow_start : static_cast<int>(entries.size());
280 } else {
281 DCHECK(access == ConstantPoolEntry::OVERFLOWED);
282 if (!overflow) return;
283 begin = info.overflow_start;
284 end = static_cast<int>(entries.size());
285 }
286
287 std::vector<ConstantPoolEntry>::iterator it = entries.begin();
288 if (begin > 0) std::advance(it, begin);
289 for (int i = begin; i < end; i++, it++) {
290 // Update constant pool if necessary and get the entry's offset.
291 int offset;
292 ConstantPoolEntry::Access entry_access;
293 if (!it->is_merged()) {
294 // Emit new entry
295 offset = assm->pc_offset() - base;
296 entry_access = access;
297 if (entry_size == kPointerSize) {
298 assm->dp(it->value());
299 } else {
300 assm->dq(it->value64());
301 }
302 } else {
303 // Retrieve offset from shared entry.
304 offset = shared_entries[it->merged_index()].offset();
305 entry_access = ConstantPoolEntry::REGULAR;
306 }
307
308 DCHECK(entry_access == ConstantPoolEntry::OVERFLOWED ||
309 is_uintn(offset, info.regular_reach_bits));
310
311 // Patch load sequence with correct offset.
312 assm->PatchConstantPoolAccessInstruction(it->position(), offset,
313 entry_access, type);
314 }
315 }
316
317 // Emit and return position of pool. Zero implies no constant pool.
Emit(Assembler * assm)318 int ConstantPoolBuilder::Emit(Assembler* assm) {
319 bool emitted = emitted_label_.is_bound();
320 bool empty = IsEmpty();
321
322 if (!emitted) {
323 // Mark start of constant pool. Align if necessary.
324 if (!empty) assm->DataAlign(kDoubleSize);
325 assm->bind(&emitted_label_);
326 if (!empty) {
327 // Emit in groups based on access and type.
328 // Emit doubles first for alignment purposes.
329 EmitGroup(assm, ConstantPoolEntry::REGULAR, ConstantPoolEntry::DOUBLE);
330 EmitGroup(assm, ConstantPoolEntry::REGULAR, ConstantPoolEntry::INTPTR);
331 if (info_[ConstantPoolEntry::DOUBLE].overflow()) {
332 assm->DataAlign(kDoubleSize);
333 EmitGroup(assm, ConstantPoolEntry::OVERFLOWED,
334 ConstantPoolEntry::DOUBLE);
335 }
336 if (info_[ConstantPoolEntry::INTPTR].overflow()) {
337 EmitGroup(assm, ConstantPoolEntry::OVERFLOWED,
338 ConstantPoolEntry::INTPTR);
339 }
340 }
341 }
342
343 return !empty ? emitted_label_.pos() : 0;
344 }
345
HeapObjectRequest(double heap_number,int offset)346 HeapObjectRequest::HeapObjectRequest(double heap_number, int offset)
347 : kind_(kHeapNumber), offset_(offset) {
348 value_.heap_number = heap_number;
349 DCHECK(!IsSmiDouble(value_.heap_number));
350 }
351
HeapObjectRequest(CodeStub * code_stub,int offset)352 HeapObjectRequest::HeapObjectRequest(CodeStub* code_stub, int offset)
353 : kind_(kCodeStub), offset_(offset) {
354 value_.code_stub = code_stub;
355 DCHECK_NOT_NULL(value_.code_stub);
356 }
357
358 // Platform specific but identical code for all the platforms.
359
RecordDeoptReason(DeoptimizeReason reason,SourcePosition position,int id)360 void Assembler::RecordDeoptReason(DeoptimizeReason reason,
361 SourcePosition position, int id) {
362 EnsureSpace ensure_space(this);
363 RecordRelocInfo(RelocInfo::DEOPT_SCRIPT_OFFSET, position.ScriptOffset());
364 RecordRelocInfo(RelocInfo::DEOPT_INLINING_ID, position.InliningId());
365 RecordRelocInfo(RelocInfo::DEOPT_REASON, static_cast<int>(reason));
366 RecordRelocInfo(RelocInfo::DEOPT_ID, id);
367 }
368
RecordComment(const char * msg)369 void Assembler::RecordComment(const char* msg) {
370 if (FLAG_code_comments) {
371 EnsureSpace ensure_space(this);
372 RecordRelocInfo(RelocInfo::COMMENT, reinterpret_cast<intptr_t>(msg));
373 }
374 }
375
DataAlign(int m)376 void Assembler::DataAlign(int m) {
377 DCHECK(m >= 2 && base::bits::IsPowerOfTwo(m));
378 while ((pc_offset() & (m - 1)) != 0) {
379 db(0);
380 }
381 }
382
RequestHeapObject(HeapObjectRequest request)383 void AssemblerBase::RequestHeapObject(HeapObjectRequest request) {
384 request.set_offset(pc_offset());
385 heap_object_requests_.push_front(request);
386 }
387
AddCodeTarget(Handle<Code> target)388 int AssemblerBase::AddCodeTarget(Handle<Code> target) {
389 int current = static_cast<int>(code_targets_.size());
390 if (current > 0 && !target.is_null() &&
391 code_targets_.back().address() == target.address()) {
392 // Optimization if we keep jumping to the same code target.
393 return current - 1;
394 } else {
395 code_targets_.push_back(target);
396 return current;
397 }
398 }
399
GetCodeTarget(intptr_t code_target_index) const400 Handle<Code> AssemblerBase::GetCodeTarget(intptr_t code_target_index) const {
401 DCHECK_LE(0, code_target_index);
402 DCHECK_LT(code_target_index, code_targets_.size());
403 return code_targets_[code_target_index];
404 }
405
UpdateCodeTarget(intptr_t code_target_index,Handle<Code> code)406 void AssemblerBase::UpdateCodeTarget(intptr_t code_target_index,
407 Handle<Code> code) {
408 DCHECK_LE(0, code_target_index);
409 DCHECK_LT(code_target_index, code_targets_.size());
410 code_targets_[code_target_index] = code;
411 }
412
413 } // namespace internal
414 } // namespace v8
415