• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/objects/code.h"
6 
7 #include <iomanip>
8 
9 #include "src/codegen/assembler-inl.h"
10 #include "src/codegen/cpu-features.h"
11 #include "src/codegen/reloc-info.h"
12 #include "src/codegen/safepoint-table.h"
13 #include "src/codegen/source-position.h"
14 #include "src/deoptimizer/deoptimizer.h"
15 #include "src/execution/isolate-utils-inl.h"
16 #include "src/interpreter/bytecode-array-iterator.h"
17 #include "src/interpreter/bytecode-decoder.h"
18 #include "src/interpreter/interpreter.h"
19 #include "src/objects/allocation-site-inl.h"
20 #include "src/objects/code-kind.h"
21 #include "src/objects/fixed-array.h"
22 #include "src/roots/roots-inl.h"
23 #include "src/snapshot/embedded/embedded-data-inl.h"
24 #include "src/utils/ostreams.h"
25 
26 #ifdef ENABLE_DISASSEMBLER
27 #include "src/codegen/code-comments.h"
28 #include "src/diagnostics/disasm.h"
29 #include "src/diagnostics/disassembler.h"
30 #include "src/diagnostics/eh-frame.h"
31 #endif
32 
33 namespace v8 {
34 namespace internal {
35 
36 namespace {
37 
38 // Helper function for getting an EmbeddedData that can handle un-embedded
39 // builtins when short builtin calls are enabled.
EmbeddedDataWithMaybeRemappedEmbeddedBuiltins(HeapObject code)40 inline EmbeddedData EmbeddedDataWithMaybeRemappedEmbeddedBuiltins(
41     HeapObject code) {
42 #if defined(V8_COMPRESS_POINTERS_IN_ISOLATE_CAGE)
43   // GetIsolateFromWritableObject(*this) works for both read-only and writable
44   // objects when pointer compression is enabled with a per-Isolate cage.
45   return EmbeddedData::FromBlob(GetIsolateFromWritableObject(code));
46 #elif defined(V8_COMPRESS_POINTERS_IN_SHARED_CAGE)
47   // When pointer compression is enabled with a shared cage, there is also a
48   // shared CodeRange. When short builtin calls are enabled, there is a single
49   // copy of the re-embedded builtins in the shared CodeRange, so use that if
50   // it's present.
51   if (FLAG_jitless) return EmbeddedData::FromBlob();
52   CodeRange* code_range = CodeRange::GetProcessWideCodeRange().get();
53   return (code_range && code_range->embedded_blob_code_copy() != nullptr)
54              ? EmbeddedData::FromBlob(code_range)
55              : EmbeddedData::FromBlob();
56 #else
57   // Otherwise there is a single copy of the blob across all Isolates, use the
58   // global atomic variables.
59   return EmbeddedData::FromBlob();
60 #endif
61 }
62 
63 }  // namespace
64 
OffHeapInstructionStart(HeapObject code,Builtin builtin)65 Address OffHeapInstructionStart(HeapObject code, Builtin builtin) {
66   // TODO(11527): Here and below: pass Isolate as an argument for getting
67   // the EmbeddedData.
68   EmbeddedData d = EmbeddedDataWithMaybeRemappedEmbeddedBuiltins(code);
69   return d.InstructionStartOfBuiltin(builtin);
70 }
71 
OffHeapInstructionEnd(HeapObject code,Builtin builtin)72 Address OffHeapInstructionEnd(HeapObject code, Builtin builtin) {
73   EmbeddedData d = EmbeddedDataWithMaybeRemappedEmbeddedBuiltins(code);
74   return d.InstructionStartOfBuiltin(builtin) +
75          d.InstructionSizeOfBuiltin(builtin);
76 }
77 
OffHeapInstructionSize(HeapObject code,Builtin builtin)78 int OffHeapInstructionSize(HeapObject code, Builtin builtin) {
79   EmbeddedData d = EmbeddedDataWithMaybeRemappedEmbeddedBuiltins(code);
80   return d.InstructionSizeOfBuiltin(builtin);
81 }
82 
OffHeapMetadataStart(HeapObject code,Builtin builtin)83 Address OffHeapMetadataStart(HeapObject code, Builtin builtin) {
84   EmbeddedData d = EmbeddedDataWithMaybeRemappedEmbeddedBuiltins(code);
85   return d.MetadataStartOfBuiltin(builtin);
86 }
87 
OffHeapMetadataEnd(HeapObject code,Builtin builtin)88 Address OffHeapMetadataEnd(HeapObject code, Builtin builtin) {
89   EmbeddedData d = EmbeddedDataWithMaybeRemappedEmbeddedBuiltins(code);
90   return d.MetadataStartOfBuiltin(builtin) + d.MetadataSizeOfBuiltin(builtin);
91 }
92 
OffHeapMetadataSize(HeapObject code,Builtin builtin)93 int OffHeapMetadataSize(HeapObject code, Builtin builtin) {
94   EmbeddedData d = EmbeddedDataWithMaybeRemappedEmbeddedBuiltins(code);
95   return d.MetadataSizeOfBuiltin(builtin);
96 }
97 
OffHeapSafepointTableAddress(HeapObject code,Builtin builtin)98 Address OffHeapSafepointTableAddress(HeapObject code, Builtin builtin) {
99   EmbeddedData d = EmbeddedDataWithMaybeRemappedEmbeddedBuiltins(code);
100   return d.SafepointTableStartOf(builtin);
101 }
102 
OffHeapSafepointTableSize(HeapObject code,Builtin builtin)103 int OffHeapSafepointTableSize(HeapObject code, Builtin builtin) {
104   EmbeddedData d = EmbeddedDataWithMaybeRemappedEmbeddedBuiltins(code);
105   return d.SafepointTableSizeOf(builtin);
106 }
107 
OffHeapHandlerTableAddress(HeapObject code,Builtin builtin)108 Address OffHeapHandlerTableAddress(HeapObject code, Builtin builtin) {
109   EmbeddedData d = EmbeddedDataWithMaybeRemappedEmbeddedBuiltins(code);
110   return d.HandlerTableStartOf(builtin);
111 }
112 
OffHeapHandlerTableSize(HeapObject code,Builtin builtin)113 int OffHeapHandlerTableSize(HeapObject code, Builtin builtin) {
114   EmbeddedData d = EmbeddedDataWithMaybeRemappedEmbeddedBuiltins(code);
115   return d.HandlerTableSizeOf(builtin);
116 }
117 
OffHeapConstantPoolAddress(HeapObject code,Builtin builtin)118 Address OffHeapConstantPoolAddress(HeapObject code, Builtin builtin) {
119   EmbeddedData d = EmbeddedDataWithMaybeRemappedEmbeddedBuiltins(code);
120   return d.ConstantPoolStartOf(builtin);
121 }
122 
OffHeapConstantPoolSize(HeapObject code,Builtin builtin)123 int OffHeapConstantPoolSize(HeapObject code, Builtin builtin) {
124   EmbeddedData d = EmbeddedDataWithMaybeRemappedEmbeddedBuiltins(code);
125   return d.ConstantPoolSizeOf(builtin);
126 }
127 
OffHeapCodeCommentsAddress(HeapObject code,Builtin builtin)128 Address OffHeapCodeCommentsAddress(HeapObject code, Builtin builtin) {
129   EmbeddedData d = EmbeddedDataWithMaybeRemappedEmbeddedBuiltins(code);
130   return d.CodeCommentsStartOf(builtin);
131 }
132 
OffHeapCodeCommentsSize(HeapObject code,Builtin builtin)133 int OffHeapCodeCommentsSize(HeapObject code, Builtin builtin) {
134   EmbeddedData d = EmbeddedDataWithMaybeRemappedEmbeddedBuiltins(code);
135   return d.CodeCommentsSizeOf(builtin);
136 }
137 
OffHeapUnwindingInfoAddress(HeapObject code,Builtin builtin)138 Address OffHeapUnwindingInfoAddress(HeapObject code, Builtin builtin) {
139   EmbeddedData d = EmbeddedDataWithMaybeRemappedEmbeddedBuiltins(code);
140   return d.UnwindingInfoStartOf(builtin);
141 }
142 
OffHeapUnwindingInfoSize(HeapObject code,Builtin builtin)143 int OffHeapUnwindingInfoSize(HeapObject code, Builtin builtin) {
144   EmbeddedData d = EmbeddedDataWithMaybeRemappedEmbeddedBuiltins(code);
145   return d.UnwindingInfoSizeOf(builtin);
146 }
147 
ClearEmbeddedObjects(Heap * heap)148 void Code::ClearEmbeddedObjects(Heap* heap) {
149   HeapObject undefined = ReadOnlyRoots(heap).undefined_value();
150   int mode_mask = RelocInfo::EmbeddedObjectModeMask();
151   for (RelocIterator it(*this, mode_mask); !it.done(); it.next()) {
152     DCHECK(RelocInfo::IsEmbeddedObjectMode(it.rinfo()->rmode()));
153     it.rinfo()->set_target_object(heap, undefined, SKIP_WRITE_BARRIER);
154   }
155   set_embedded_objects_cleared(true);
156 }
157 
Relocate(intptr_t delta)158 void Code::Relocate(intptr_t delta) {
159   for (RelocIterator it(*this, RelocInfo::kApplyMask); !it.done(); it.next()) {
160     it.rinfo()->apply(delta);
161   }
162   FlushICache();
163 }
164 
FlushICache() const165 void Code::FlushICache() const {
166   FlushInstructionCache(raw_instruction_start(), raw_instruction_size());
167 }
168 
CopyFromNoFlush(ByteArray reloc_info,Heap * heap,const CodeDesc & desc)169 void Code::CopyFromNoFlush(ByteArray reloc_info, Heap* heap,
170                            const CodeDesc& desc) {
171   // Copy code.
172   STATIC_ASSERT(kOnHeapBodyIsContiguous);
173   CopyBytes(reinterpret_cast<byte*>(raw_instruction_start()), desc.buffer,
174             static_cast<size_t>(desc.instr_size));
175   // TODO(jgruber,v8:11036): Merge with the above.
176   CopyBytes(reinterpret_cast<byte*>(raw_instruction_start() + desc.instr_size),
177             desc.unwinding_info, static_cast<size_t>(desc.unwinding_info_size));
178 
179   // Copy reloc info.
180   CopyRelocInfoToByteArray(reloc_info, desc);
181 
182   // Unbox handles and relocate.
183   RelocateFromDesc(reloc_info, heap, desc);
184 }
185 
RelocateFromDesc(ByteArray reloc_info,Heap * heap,const CodeDesc & desc)186 void Code::RelocateFromDesc(ByteArray reloc_info, Heap* heap,
187                             const CodeDesc& desc) {
188   // Unbox handles and relocate.
189   Assembler* origin = desc.origin;
190   const int mode_mask = RelocInfo::PostCodegenRelocationMask();
191   for (RelocIterator it(*this, reloc_info, mode_mask); !it.done(); it.next()) {
192     RelocInfo::Mode mode = it.rinfo()->rmode();
193     if (RelocInfo::IsEmbeddedObjectMode(mode)) {
194       Handle<HeapObject> p = it.rinfo()->target_object_handle(origin);
195       it.rinfo()->set_target_object(heap, *p, UPDATE_WRITE_BARRIER,
196                                     SKIP_ICACHE_FLUSH);
197     } else if (RelocInfo::IsCodeTargetMode(mode)) {
198       // Rewrite code handles to direct pointers to the first instruction in the
199       // code object.
200       Handle<HeapObject> p = it.rinfo()->target_object_handle(origin);
201       DCHECK(p->IsCodeT(GetPtrComprCageBaseSlow(*p)));
202       Code code = FromCodeT(CodeT::cast(*p));
203       it.rinfo()->set_target_address(code.raw_instruction_start(),
204                                      UPDATE_WRITE_BARRIER, SKIP_ICACHE_FLUSH);
205     } else if (RelocInfo::IsRuntimeEntry(mode)) {
206       Address p = it.rinfo()->target_runtime_entry(origin);
207       it.rinfo()->set_target_runtime_entry(p, UPDATE_WRITE_BARRIER,
208                                            SKIP_ICACHE_FLUSH);
209     } else {
210       intptr_t delta =
211           raw_instruction_start() - reinterpret_cast<Address>(desc.buffer);
212       it.rinfo()->apply(delta);
213     }
214   }
215 }
216 
GetSafepointEntry(Isolate * isolate,Address pc)217 SafepointEntry Code::GetSafepointEntry(Isolate* isolate, Address pc) {
218   SafepointTable table(isolate, pc, *this);
219   return table.FindEntry(pc);
220 }
221 
OffHeapInstructionStart(Isolate * isolate,Address pc) const222 Address Code::OffHeapInstructionStart(Isolate* isolate, Address pc) const {
223   DCHECK(is_off_heap_trampoline());
224   EmbeddedData d = EmbeddedData::GetEmbeddedDataForPC(isolate, pc);
225   return d.InstructionStartOfBuiltin(builtin_id());
226 }
227 
OffHeapInstructionEnd(Isolate * isolate,Address pc) const228 Address Code::OffHeapInstructionEnd(Isolate* isolate, Address pc) const {
229   DCHECK(is_off_heap_trampoline());
230   EmbeddedData d = EmbeddedData::GetEmbeddedDataForPC(isolate, pc);
231   return d.InstructionStartOfBuiltin(builtin_id()) +
232          d.InstructionSizeOfBuiltin(builtin_id());
233 }
234 
235 // TODO(cbruni): Move to BytecodeArray
SourcePosition(int offset)236 int AbstractCode::SourcePosition(int offset) {
237   CHECK_NE(kind(), CodeKind::BASELINE);
238   Object maybe_table = SourcePositionTableInternal();
239   if (maybe_table.IsException()) return kNoSourcePosition;
240 
241   ByteArray source_position_table = ByteArray::cast(maybe_table);
242   // Subtract one because the current PC is one instruction after the call site.
243   if (IsCode()) offset--;
244   int position = 0;
245   for (SourcePositionTableIterator iterator(
246            source_position_table, SourcePositionTableIterator::kJavaScriptOnly,
247            SourcePositionTableIterator::kDontSkipFunctionEntry);
248        !iterator.done() && iterator.code_offset() <= offset;
249        iterator.Advance()) {
250     position = iterator.source_position().ScriptOffset();
251   }
252   return position;
253 }
254 
255 // TODO(cbruni): Move to BytecodeArray
SourceStatementPosition(int offset)256 int AbstractCode::SourceStatementPosition(int offset) {
257   CHECK_NE(kind(), CodeKind::BASELINE);
258   // First find the closest position.
259   int position = SourcePosition(offset);
260   // Now find the closest statement position before the position.
261   int statement_position = 0;
262   for (SourcePositionTableIterator it(SourcePositionTableInternal());
263        !it.done(); it.Advance()) {
264     if (it.is_statement()) {
265       int p = it.source_position().ScriptOffset();
266       if (statement_position < p && p <= position) {
267         statement_position = p;
268       }
269     }
270   }
271   return statement_position;
272 }
273 
CanDeoptAt(Isolate * isolate,Address pc)274 bool Code::CanDeoptAt(Isolate* isolate, Address pc) {
275   DeoptimizationData deopt_data =
276       DeoptimizationData::cast(deoptimization_data());
277   Address code_start_address = InstructionStart(isolate, pc);
278   for (int i = 0; i < deopt_data.DeoptCount(); i++) {
279     if (deopt_data.Pc(i).value() == -1) continue;
280     Address address = code_start_address + deopt_data.Pc(i).value();
281     if (address == pc &&
282         deopt_data.GetBytecodeOffset(i) != BytecodeOffset::None()) {
283       return true;
284     }
285   }
286   return false;
287 }
288 
IsIsolateIndependent(Isolate * isolate)289 bool Code::IsIsolateIndependent(Isolate* isolate) {
290   static constexpr int kModeMask =
291       RelocInfo::AllRealModesMask() &
292       ~RelocInfo::ModeMask(RelocInfo::CONST_POOL) &
293       ~RelocInfo::ModeMask(RelocInfo::OFF_HEAP_TARGET) &
294       ~RelocInfo::ModeMask(RelocInfo::VENEER_POOL);
295   STATIC_ASSERT(kModeMask ==
296                 (RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
297                  RelocInfo::ModeMask(RelocInfo::RELATIVE_CODE_TARGET) |
298                  RelocInfo::ModeMask(RelocInfo::COMPRESSED_EMBEDDED_OBJECT) |
299                  RelocInfo::ModeMask(RelocInfo::FULL_EMBEDDED_OBJECT) |
300                  RelocInfo::ModeMask(RelocInfo::DATA_EMBEDDED_OBJECT) |
301                  RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) |
302                  RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE) |
303                  RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE_ENCODED) |
304                  RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY) |
305                  RelocInfo::ModeMask(RelocInfo::WASM_CALL) |
306                  RelocInfo::ModeMask(RelocInfo::WASM_STUB_CALL)));
307 
308 #if defined(V8_TARGET_ARCH_PPC) || defined(V8_TARGET_ARCH_PPC64) || \
309     defined(V8_TARGET_ARCH_MIPS64)
310   return RelocIterator(*this, kModeMask).done();
311 #elif defined(V8_TARGET_ARCH_X64) || defined(V8_TARGET_ARCH_ARM64) || \
312     defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_MIPS) ||    \
313     defined(V8_TARGET_ARCH_S390) || defined(V8_TARGET_ARCH_IA32) ||   \
314     defined(V8_TARGET_ARCH_RISCV64) || defined(V8_TARGET_ARCH_LOONG64)
315   for (RelocIterator it(*this, kModeMask); !it.done(); it.next()) {
316     // On these platforms we emit relative builtin-to-builtin
317     // jumps for isolate independent builtins in the snapshot. They are later
318     // rewritten as pc-relative jumps to the off-heap instruction stream and are
319     // thus process-independent. See also: FinalizeEmbeddedCodeTargets.
320     if (RelocInfo::IsCodeTargetMode(it.rinfo()->rmode())) {
321       Address target_address = it.rinfo()->target_address();
322       if (OffHeapInstructionStream::PcIsOffHeap(isolate, target_address))
323         continue;
324 
325       Code target = Code::GetCodeFromTargetAddress(target_address);
326       CHECK(target.IsCode());
327       if (Builtins::IsIsolateIndependentBuiltin(target)) continue;
328     }
329     return false;
330   }
331   return true;
332 #else
333 #error Unsupported architecture.
334 #endif
335 }
336 
Inlines(SharedFunctionInfo sfi)337 bool Code::Inlines(SharedFunctionInfo sfi) {
338   // We can only check for inlining for optimized code.
339   DCHECK(is_optimized_code());
340   DisallowGarbageCollection no_gc;
341   DeoptimizationData const data =
342       DeoptimizationData::cast(deoptimization_data());
343   if (data.length() == 0) return false;
344   if (data.SharedFunctionInfo() == sfi) return true;
345   DeoptimizationLiteralArray const literals = data.LiteralArray();
346   int const inlined_count = data.InlinedFunctionCount().value();
347   for (int i = 0; i < inlined_count; ++i) {
348     if (SharedFunctionInfo::cast(literals.get(i)) == sfi) return true;
349   }
350   return false;
351 }
352 
OptimizedCodeIterator(Isolate * isolate)353 Code::OptimizedCodeIterator::OptimizedCodeIterator(Isolate* isolate) {
354   isolate_ = isolate;
355   Object list = isolate->heap()->native_contexts_list();
356   next_context_ =
357       list.IsUndefined(isolate_) ? NativeContext() : NativeContext::cast(list);
358 }
359 
Next()360 Code Code::OptimizedCodeIterator::Next() {
361   do {
362     Object next;
363     if (!current_code_.is_null()) {
364       // Get next code in the linked list.
365       next = current_code_.next_code_link();
366     } else if (!next_context_.is_null()) {
367       // Linked list of code exhausted. Get list of next context.
368       next = next_context_.OptimizedCodeListHead();
369       Object next_context = next_context_.next_context_link();
370       next_context_ = next_context.IsUndefined(isolate_)
371                           ? NativeContext()
372                           : NativeContext::cast(next_context);
373     } else {
374       // Exhausted contexts.
375       return Code();
376     }
377     current_code_ =
378         next.IsUndefined(isolate_) ? Code() : FromCodeT(CodeT::cast(next));
379   } while (current_code_.is_null());
380   DCHECK(CodeKindCanDeoptimize(current_code_.kind()));
381   return current_code_;
382 }
383 
New(Isolate * isolate,int deopt_entry_count,AllocationType allocation)384 Handle<DeoptimizationData> DeoptimizationData::New(Isolate* isolate,
385                                                    int deopt_entry_count,
386                                                    AllocationType allocation) {
387   return Handle<DeoptimizationData>::cast(isolate->factory()->NewFixedArray(
388       LengthFor(deopt_entry_count), allocation));
389 }
390 
Empty(Isolate * isolate)391 Handle<DeoptimizationData> DeoptimizationData::Empty(Isolate* isolate) {
392   return Handle<DeoptimizationData>::cast(
393       isolate->factory()->empty_fixed_array());
394 }
395 
GetInlinedFunction(int index)396 SharedFunctionInfo DeoptimizationData::GetInlinedFunction(int index) {
397   if (index == -1) {
398     return SharedFunctionInfo::cast(SharedFunctionInfo());
399   } else {
400     return SharedFunctionInfo::cast(LiteralArray().get(index));
401   }
402 }
403 
404 #ifdef ENABLE_DISASSEMBLER
405 
GetName(Isolate * isolate) const406 const char* Code::GetName(Isolate* isolate) const {
407   if (kind() == CodeKind::BYTECODE_HANDLER) {
408     return isolate->interpreter()->LookupNameOfBytecodeHandler(*this);
409   } else {
410     // There are some handlers and ICs that we can also find names for with
411     // Builtins::Lookup.
412     return isolate->builtins()->Lookup(raw_instruction_start());
413   }
414 }
415 
416 namespace {
print_pc(std::ostream & os,int pc)417 void print_pc(std::ostream& os, int pc) {
418   if (pc == -1) {
419     os << "NA";
420   } else {
421     os << std::hex << pc << std::dec;
422   }
423 }
424 }  // anonymous namespace
425 
DeoptimizationDataPrint(std::ostream & os)426 void DeoptimizationData::DeoptimizationDataPrint(std::ostream& os) {
427   if (length() == 0) {
428     os << "Deoptimization Input Data invalidated by lazy deoptimization\n";
429     return;
430   }
431 
432   int const inlined_function_count = InlinedFunctionCount().value();
433   os << "Inlined functions (count = " << inlined_function_count << ")\n";
434   for (int id = 0; id < inlined_function_count; ++id) {
435     Object info = LiteralArray().get(id);
436     os << " " << Brief(SharedFunctionInfo::cast(info)) << "\n";
437   }
438   os << "\n";
439   int deopt_count = DeoptCount();
440   os << "Deoptimization Input Data (deopt points = " << deopt_count << ")\n";
441   if (0 != deopt_count) {
442 #ifdef DEBUG
443     os << " index  bytecode-offset  node-id    pc";
444 #else   // DEBUG
445     os << " index  bytecode-offset    pc";
446 #endif  // DEBUG
447     if (FLAG_print_code_verbose) os << "  commands";
448     os << "\n";
449   }
450   for (int i = 0; i < deopt_count; i++) {
451     os << std::setw(6) << i << "  " << std::setw(15)
452        << GetBytecodeOffset(i).ToInt() << "  "
453 #ifdef DEBUG
454        << std::setw(7) << NodeId(i).value() << "  "
455 #endif  // DEBUG
456        << std::setw(4);
457     print_pc(os, Pc(i).value());
458     os << std::setw(2);
459 
460     if (!FLAG_print_code_verbose) {
461       os << "\n";
462       continue;
463     }
464 
465     TranslationArrayPrintSingleFrame(os, TranslationByteArray(),
466                                      TranslationIndex(i).value(),
467                                      LiteralArray());
468   }
469 }
470 
471 namespace {
472 
DisassembleCodeRange(Isolate * isolate,std::ostream & os,Code code,Address begin,size_t size,Address current_pc)473 inline void DisassembleCodeRange(Isolate* isolate, std::ostream& os, Code code,
474                                  Address begin, size_t size,
475                                  Address current_pc) {
476   Address end = begin + size;
477   AllowHandleAllocation allow_handles;
478   DisallowGarbageCollection no_gc;
479   HandleScope handle_scope(isolate);
480   Disassembler::Decode(isolate, os, reinterpret_cast<byte*>(begin),
481                        reinterpret_cast<byte*>(end),
482                        CodeReference(handle(code, isolate)), current_pc);
483 }
484 
485 }  // namespace
486 
Disassemble(const char * name,std::ostream & os,Isolate * isolate,Address current_pc)487 void Code::Disassemble(const char* name, std::ostream& os, Isolate* isolate,
488                        Address current_pc) {
489   os << "kind = " << CodeKindToString(kind()) << "\n";
490   if (name == nullptr) {
491     name = GetName(isolate);
492   }
493   if ((name != nullptr) && (name[0] != '\0')) {
494     os << "name = " << name << "\n";
495   }
496   if (CodeKindIsOptimizedJSFunction(kind()) && kind() != CodeKind::BASELINE) {
497     os << "stack_slots = " << stack_slots() << "\n";
498   }
499   os << "compiler = "
500      << (is_turbofanned()
501              ? "turbofan"
502              : is_maglevved()
503                    ? "turbofan"
504                    : kind() == CodeKind::BASELINE ? "baseline" : "unknown")
505      << "\n";
506   os << "address = " << reinterpret_cast<void*>(ptr()) << "\n\n";
507 
508   if (is_off_heap_trampoline()) {
509     int trampoline_size = raw_instruction_size();
510     os << "Trampoline (size = " << trampoline_size << ")\n";
511     DisassembleCodeRange(isolate, os, *this, raw_instruction_start(),
512                          trampoline_size, current_pc);
513     os << "\n";
514   }
515 
516   {
517     int code_size = InstructionSize();
518     os << "Instructions (size = " << code_size << ")\n";
519     DisassembleCodeRange(isolate, os, *this, InstructionStart(), code_size,
520                          current_pc);
521 
522     if (int pool_size = constant_pool_size()) {
523       DCHECK_EQ(pool_size & kPointerAlignmentMask, 0);
524       os << "\nConstant Pool (size = " << pool_size << ")\n";
525       base::Vector<char> buf = base::Vector<char>::New(50);
526       intptr_t* ptr = reinterpret_cast<intptr_t*>(constant_pool());
527       for (int i = 0; i < pool_size; i += kSystemPointerSize, ptr++) {
528         SNPrintF(buf, "%4d %08" V8PRIxPTR, i, *ptr);
529         os << static_cast<const void*>(ptr) << "  " << buf.begin() << "\n";
530       }
531     }
532   }
533   os << "\n";
534 
535   // TODO(cbruni): add support for baseline code.
536   if (kind() != CodeKind::BASELINE) {
537     {
538       SourcePositionTableIterator it(
539           source_position_table(),
540           SourcePositionTableIterator::kJavaScriptOnly);
541       if (!it.done()) {
542         os << "Source positions:\n pc offset  position\n";
543         for (; !it.done(); it.Advance()) {
544           os << std::setw(10) << std::hex << it.code_offset() << std::dec
545              << std::setw(10) << it.source_position().ScriptOffset()
546              << (it.is_statement() ? "  statement" : "") << "\n";
547         }
548         os << "\n";
549       }
550     }
551 
552     {
553       SourcePositionTableIterator it(
554           source_position_table(), SourcePositionTableIterator::kExternalOnly);
555       if (!it.done()) {
556         os << "External Source positions:\n pc offset  fileid  line\n";
557         for (; !it.done(); it.Advance()) {
558           DCHECK(it.source_position().IsExternal());
559           os << std::setw(10) << std::hex << it.code_offset() << std::dec
560              << std::setw(10) << it.source_position().ExternalFileId()
561              << std::setw(10) << it.source_position().ExternalLine() << "\n";
562         }
563         os << "\n";
564       }
565     }
566   }
567 
568   if (CodeKindCanDeoptimize(kind())) {
569     DeoptimizationData data =
570         DeoptimizationData::cast(this->deoptimization_data());
571     data.DeoptimizationDataPrint(os);
572   }
573   os << "\n";
574 
575   if (uses_safepoint_table()) {
576     SafepointTable table(isolate, current_pc, *this);
577     table.Print(os);
578     os << "\n";
579   }
580 
581   if (has_handler_table()) {
582     HandlerTable table(*this);
583     os << "Handler Table (size = " << table.NumberOfReturnEntries() << ")\n";
584     if (CodeKindIsOptimizedJSFunction(kind())) {
585       table.HandlerTableReturnPrint(os);
586     }
587     os << "\n";
588   }
589 
590   os << "RelocInfo (size = " << relocation_size() << ")\n";
591   for (RelocIterator it(*this); !it.done(); it.next()) {
592     it.rinfo()->Print(isolate, os);
593   }
594   os << "\n";
595 
596   if (has_unwinding_info()) {
597     os << "UnwindingInfo (size = " << unwinding_info_size() << ")\n";
598     EhFrameDisassembler eh_frame_disassembler(
599         reinterpret_cast<byte*>(unwinding_info_start()),
600         reinterpret_cast<byte*>(unwinding_info_end()));
601     eh_frame_disassembler.DisassembleToStream(os);
602     os << "\n";
603   }
604 }
605 #endif  // ENABLE_DISASSEMBLER
606 
Disassemble(std::ostream & os)607 void BytecodeArray::Disassemble(std::ostream& os) {
608   DisallowGarbageCollection no_gc;
609 
610   os << "Parameter count " << parameter_count() << "\n";
611   os << "Register count " << register_count() << "\n";
612   os << "Frame size " << frame_size() << "\n";
613   os << "OSR urgency: " << osr_urgency() << "\n";
614   os << "Bytecode age: " << bytecode_age() << "\n";
615 
616   Address base_address = GetFirstBytecodeAddress();
617   SourcePositionTableIterator source_positions(SourcePositionTable());
618 
619   // Storage for backing the handle passed to the iterator. This handle won't be
620   // updated by the gc, but that's ok because we've disallowed GCs anyway.
621   BytecodeArray handle_storage = *this;
622   Handle<BytecodeArray> handle(reinterpret_cast<Address*>(&handle_storage));
623   interpreter::BytecodeArrayIterator iterator(handle);
624   while (!iterator.done()) {
625     if (!source_positions.done() &&
626         iterator.current_offset() == source_positions.code_offset()) {
627       os << std::setw(5) << source_positions.source_position().ScriptOffset();
628       os << (source_positions.is_statement() ? " S> " : " E> ");
629       source_positions.Advance();
630     } else {
631       os << "         ";
632     }
633     Address current_address = base_address + iterator.current_offset();
634     os << reinterpret_cast<const void*>(current_address) << " @ "
635        << std::setw(4) << iterator.current_offset() << " : ";
636     interpreter::BytecodeDecoder::Decode(
637         os, reinterpret_cast<byte*>(current_address));
638     if (interpreter::Bytecodes::IsJump(iterator.current_bytecode())) {
639       Address jump_target = base_address + iterator.GetJumpTargetOffset();
640       os << " (" << reinterpret_cast<void*>(jump_target) << " @ "
641          << iterator.GetJumpTargetOffset() << ")";
642     }
643     if (interpreter::Bytecodes::IsSwitch(iterator.current_bytecode())) {
644       os << " {";
645       bool first_entry = true;
646       for (interpreter::JumpTableTargetOffset entry :
647            iterator.GetJumpTableTargetOffsets()) {
648         if (first_entry) {
649           first_entry = false;
650         } else {
651           os << ",";
652         }
653         os << " " << entry.case_value << ": @" << entry.target_offset;
654       }
655       os << " }";
656     }
657     os << std::endl;
658     iterator.Advance();
659   }
660 
661   os << "Constant pool (size = " << constant_pool().length() << ")\n";
662 #ifdef OBJECT_PRINT
663   if (constant_pool().length() > 0) {
664     constant_pool().Print(os);
665   }
666 #endif
667 
668   os << "Handler Table (size = " << handler_table().length() << ")\n";
669 #ifdef ENABLE_DISASSEMBLER
670   if (handler_table().length() > 0) {
671     HandlerTable table(*this);
672     table.HandlerTableRangePrint(os);
673   }
674 #endif
675 
676   ByteArray source_position_table = SourcePositionTable();
677   os << "Source Position Table (size = " << source_position_table.length()
678      << ")\n";
679 #ifdef OBJECT_PRINT
680   if (source_position_table.length() > 0) {
681     os << Brief(source_position_table) << std::endl;
682   }
683 #endif
684 }
685 
CopyBytecodesTo(BytecodeArray to)686 void BytecodeArray::CopyBytecodesTo(BytecodeArray to) {
687   BytecodeArray from = *this;
688   DCHECK_EQ(from.length(), to.length());
689   CopyBytes(reinterpret_cast<byte*>(to.GetFirstBytecodeAddress()),
690             reinterpret_cast<byte*>(from.GetFirstBytecodeAddress()),
691             from.length());
692 }
693 
MakeOlder()694 void BytecodeArray::MakeOlder() {
695   // BytecodeArray is aged in concurrent marker.
696   // The word must be completely within the byte code array.
697   Address age_addr = address() + kBytecodeAgeOffset;
698   DCHECK_LE(RoundDown(age_addr, kTaggedSize) + kTaggedSize, address() + Size());
699   Age age = bytecode_age();
700   if (age < kLastBytecodeAge) {
701     static_assert(kBytecodeAgeSize == kUInt16Size);
702     base::AsAtomic16::Relaxed_CompareAndSwap(
703         reinterpret_cast<base::Atomic16*>(age_addr), age, age + 1);
704   }
705 
706   DCHECK_GE(bytecode_age(), kFirstBytecodeAge);
707   DCHECK_LE(bytecode_age(), kLastBytecodeAge);
708 }
709 
IsOld() const710 bool BytecodeArray::IsOld() const {
711   return bytecode_age() >= kIsOldBytecodeAge;
712 }
713 
GetDependentCode(Handle<HeapObject> object)714 DependentCode DependentCode::GetDependentCode(Handle<HeapObject> object) {
715   if (object->IsMap()) {
716     return Handle<Map>::cast(object)->dependent_code();
717   } else if (object->IsPropertyCell()) {
718     return Handle<PropertyCell>::cast(object)->dependent_code();
719   } else if (object->IsAllocationSite()) {
720     return Handle<AllocationSite>::cast(object)->dependent_code();
721   }
722   UNREACHABLE();
723 }
724 
SetDependentCode(Handle<HeapObject> object,Handle<DependentCode> dep)725 void DependentCode::SetDependentCode(Handle<HeapObject> object,
726                                      Handle<DependentCode> dep) {
727   if (object->IsMap()) {
728     Handle<Map>::cast(object)->set_dependent_code(*dep);
729   } else if (object->IsPropertyCell()) {
730     Handle<PropertyCell>::cast(object)->set_dependent_code(*dep);
731   } else if (object->IsAllocationSite()) {
732     Handle<AllocationSite>::cast(object)->set_dependent_code(*dep);
733   } else {
734     UNREACHABLE();
735   }
736 }
737 
738 namespace {
739 
PrintDependencyGroups(DependentCode::DependencyGroups groups)740 void PrintDependencyGroups(DependentCode::DependencyGroups groups) {
741   while (groups != 0) {
742     auto group = static_cast<DependentCode::DependencyGroup>(
743         1 << base::bits::CountTrailingZeros(static_cast<uint32_t>(groups)));
744     StdoutStream{} << DependentCode::DependencyGroupName(group);
745     groups &= ~group;
746     if (groups != 0) StdoutStream{} << ",";
747   }
748 }
749 
750 }  // namespace
751 
InstallDependency(Isolate * isolate,Handle<Code> code,Handle<HeapObject> object,DependencyGroups groups)752 void DependentCode::InstallDependency(Isolate* isolate, Handle<Code> code,
753                                       Handle<HeapObject> object,
754                                       DependencyGroups groups) {
755   if (V8_UNLIKELY(FLAG_trace_compilation_dependencies)) {
756     StdoutStream{} << "Installing dependency of [" << code->GetHeapObject()
757                    << "] on [" << object << "] in groups [";
758     PrintDependencyGroups(groups);
759     StdoutStream{} << "]\n";
760   }
761   Handle<DependentCode> old_deps(DependentCode::GetDependentCode(object),
762                                  isolate);
763   Handle<DependentCode> new_deps =
764       InsertWeakCode(isolate, old_deps, groups, code);
765 
766   // Update the list head if necessary.
767   if (!new_deps.is_identical_to(old_deps)) {
768     DependentCode::SetDependentCode(object, new_deps);
769   }
770 }
771 
InsertWeakCode(Isolate * isolate,Handle<DependentCode> entries,DependencyGroups groups,Handle<Code> code)772 Handle<DependentCode> DependentCode::InsertWeakCode(
773     Isolate* isolate, Handle<DependentCode> entries, DependencyGroups groups,
774     Handle<Code> code) {
775   if (entries->length() == entries->capacity()) {
776     // We'd have to grow - try to compact first.
777     entries->IterateAndCompact([](CodeT, DependencyGroups) { return false; });
778   }
779 
780   MaybeObjectHandle code_slot(HeapObjectReference::Weak(ToCodeT(*code)),
781                               isolate);
782   MaybeObjectHandle group_slot(MaybeObject::FromSmi(Smi::FromInt(groups)),
783                                isolate);
784   entries = Handle<DependentCode>::cast(
785       WeakArrayList::AddToEnd(isolate, entries, code_slot, group_slot));
786   return entries;
787 }
788 
New(Isolate * isolate,DependencyGroups groups,Handle<Code> code)789 Handle<DependentCode> DependentCode::New(Isolate* isolate,
790                                          DependencyGroups groups,
791                                          Handle<Code> code) {
792   Handle<DependentCode> result = Handle<DependentCode>::cast(
793       isolate->factory()->NewWeakArrayList(LengthFor(1), AllocationType::kOld));
794   result->Set(0, HeapObjectReference::Weak(ToCodeT(*code)));
795   result->Set(1, Smi::FromInt(groups));
796   return result;
797 }
798 
IterateAndCompact(const IterateAndCompactFn & fn)799 void DependentCode::IterateAndCompact(const IterateAndCompactFn& fn) {
800   DisallowGarbageCollection no_gc;
801 
802   int len = length();
803   if (len == 0) return;
804 
805   // We compact during traversal, thus use a somewhat custom loop construct:
806   //
807   // - Loop back-to-front s.t. trailing cleared entries can simply drop off
808   //   the back of the list.
809   // - Any cleared slots are filled from the back of the list.
810   int i = len - kSlotsPerEntry;
811   while (i >= 0) {
812     MaybeObject obj = Get(i + kCodeSlotOffset);
813     if (obj->IsCleared()) {
814       len = FillEntryFromBack(i, len);
815       i -= kSlotsPerEntry;
816       continue;
817     }
818 
819     if (fn(CodeT::cast(obj->GetHeapObjectAssumeWeak()),
820            static_cast<DependencyGroups>(
821                Get(i + kGroupsSlotOffset).ToSmi().value()))) {
822       len = FillEntryFromBack(i, len);
823     }
824 
825     i -= kSlotsPerEntry;
826   }
827 
828   set_length(len);
829 }
830 
MarkCodeForDeoptimization(DependentCode::DependencyGroups deopt_groups)831 bool DependentCode::MarkCodeForDeoptimization(
832     DependentCode::DependencyGroups deopt_groups) {
833   DisallowGarbageCollection no_gc;
834 
835   bool marked_something = false;
836   IterateAndCompact([&](CodeT codet, DependencyGroups groups) {
837     if ((groups & deopt_groups) == 0) return false;
838 
839     // TODO(v8:11880): avoid roundtrips between cdc and code.
840     Code code = FromCodeT(codet);
841     if (!code.marked_for_deoptimization()) {
842       code.SetMarkedForDeoptimization("code dependencies");
843       marked_something = true;
844     }
845 
846     return true;
847   });
848 
849   return marked_something;
850 }
851 
FillEntryFromBack(int index,int length)852 int DependentCode::FillEntryFromBack(int index, int length) {
853   DCHECK_EQ(index % 2, 0);
854   DCHECK_EQ(length % 2, 0);
855   for (int i = length - kSlotsPerEntry; i > index; i -= kSlotsPerEntry) {
856     MaybeObject obj = Get(i + kCodeSlotOffset);
857     if (obj->IsCleared()) continue;
858 
859     Set(index + kCodeSlotOffset, obj);
860     Set(index + kGroupsSlotOffset, Get(i + kGroupsSlotOffset),
861         SKIP_WRITE_BARRIER);
862     return i;
863   }
864   return index;  // No non-cleared entry found.
865 }
866 
DeoptimizeDependentCodeGroup(Isolate * isolate,DependentCode::DependencyGroups groups)867 void DependentCode::DeoptimizeDependentCodeGroup(
868     Isolate* isolate, DependentCode::DependencyGroups groups) {
869   DisallowGarbageCollection no_gc_scope;
870   bool marked_something = MarkCodeForDeoptimization(groups);
871   if (marked_something) {
872     DCHECK(AllowCodeDependencyChange::IsAllowed());
873     Deoptimizer::DeoptimizeMarkedCode(isolate);
874   }
875 }
876 
877 // static
empty_dependent_code(const ReadOnlyRoots & roots)878 DependentCode DependentCode::empty_dependent_code(const ReadOnlyRoots& roots) {
879   return DependentCode::cast(roots.empty_weak_array_list());
880 }
881 
SetMarkedForDeoptimization(const char * reason)882 void Code::SetMarkedForDeoptimization(const char* reason) {
883   set_marked_for_deoptimization(true);
884   Deoptimizer::TraceMarkForDeoptimization(*this, reason);
885 }
886 
DependencyGroupName(DependencyGroup group)887 const char* DependentCode::DependencyGroupName(DependencyGroup group) {
888   switch (group) {
889     case kTransitionGroup:
890       return "transition";
891     case kPrototypeCheckGroup:
892       return "prototype-check";
893     case kPropertyCellChangedGroup:
894       return "property-cell-changed";
895     case kFieldConstGroup:
896       return "field-const";
897     case kFieldTypeGroup:
898       return "field-type";
899     case kFieldRepresentationGroup:
900       return "field-representation";
901     case kInitialMapChangedGroup:
902       return "initial-map-changed";
903     case kAllocationSiteTenuringChangedGroup:
904       return "allocation-site-tenuring-changed";
905     case kAllocationSiteTransitionChangedGroup:
906       return "allocation-site-transition-changed";
907   }
908   UNREACHABLE();
909 }
910 
911 }  // namespace internal
912 }  // namespace v8
913