V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
reloc-info.cc
1 // Copyright 2018 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/reloc-info.h"
6 
7 #include "src/assembler-inl.h"
8 #include "src/code-reference.h"
9 #include "src/code-stubs.h"
10 #include "src/deoptimize-reason.h"
11 #include "src/deoptimizer.h"
12 #include "src/heap/heap-write-barrier-inl.h"
13 #include "src/objects/code-inl.h"
14 #include "src/snapshot/snapshot.h"
15 
16 namespace v8 {
17 namespace internal {
18 
19 const char* const RelocInfo::kFillerCommentString = "DEOPTIMIZATION PADDING";
20 
21 // -----------------------------------------------------------------------------
22 // Implementation of RelocInfoWriter and RelocIterator
23 //
24 // Relocation information is written backwards in memory, from high addresses
25 // towards low addresses, byte by byte. Therefore, in the encodings listed
26 // below, the first byte listed it at the highest address, and successive
27 // bytes in the record are at progressively lower addresses.
28 //
29 // Encoding
30 //
31 // The most common modes are given single-byte encodings. Also, it is
32 // easy to identify the type of reloc info and skip unwanted modes in
33 // an iteration.
34 //
35 // The encoding relies on the fact that there are fewer than 14
36 // different relocation modes using standard non-compact encoding.
37 //
38 // The first byte of a relocation record has a tag in its low 2 bits:
39 // Here are the record schemes, depending on the low tag and optional higher
40 // tags.
41 //
42 // Low tag:
43 // 00: embedded_object: [6-bit pc delta] 00
44 //
45 // 01: code_target: [6-bit pc delta] 01
46 //
47 // 10: wasm_stub_call: [6-bit pc delta] 10
48 //
49 // 11: long_record [6 bit reloc mode] 11
50 // followed by pc delta
51 // followed by optional data depending on type.
52 //
53 // If a pc delta exceeds 6 bits, it is split into a remainder that fits into
54 // 6 bits and a part that does not. The latter is encoded as a long record
55 // with PC_JUMP as pseudo reloc info mode. The former is encoded as part of
56 // the following record in the usual way. The long pc jump record has variable
57 // length:
58 // pc-jump: [PC_JUMP] 11
59 // [7 bits data] 0
60 // ...
61 // [7 bits data] 1
62 // (Bits 6..31 of pc delta, with leading zeroes
63 // dropped, and last non-zero chunk tagged with 1.)
64 
65 const int kTagBits = 2;
66 const int kTagMask = (1 << kTagBits) - 1;
67 const int kLongTagBits = 6;
68 
69 const int kEmbeddedObjectTag = 0;
70 const int kCodeTargetTag = 1;
71 const int kWasmStubCallTag = 2;
72 const int kDefaultTag = 3;
73 
74 const int kSmallPCDeltaBits = kBitsPerByte - kTagBits;
75 const int kSmallPCDeltaMask = (1 << kSmallPCDeltaBits) - 1;
76 const int RelocInfo::kMaxSmallPCDelta = kSmallPCDeltaMask;
77 
78 const int kChunkBits = 7;
79 const int kChunkMask = (1 << kChunkBits) - 1;
80 const int kLastChunkTagBits = 1;
81 const int kLastChunkTagMask = 1;
82 const int kLastChunkTag = 1;
83 
84 uint32_t RelocInfoWriter::WriteLongPCJump(uint32_t pc_delta) {
85  // Return if the pc_delta can fit in kSmallPCDeltaBits bits.
86  // Otherwise write a variable length PC jump for the bits that do
87  // not fit in the kSmallPCDeltaBits bits.
88  if (is_uintn(pc_delta, kSmallPCDeltaBits)) return pc_delta;
89  WriteMode(RelocInfo::PC_JUMP);
90  uint32_t pc_jump = pc_delta >> kSmallPCDeltaBits;
91  DCHECK_GT(pc_jump, 0);
92  // Write kChunkBits size chunks of the pc_jump.
93  for (; pc_jump > 0; pc_jump = pc_jump >> kChunkBits) {
94  byte b = pc_jump & kChunkMask;
95  *--pos_ = b << kLastChunkTagBits;
96  }
97  // Tag the last chunk so it can be identified.
98  *pos_ = *pos_ | kLastChunkTag;
99  // Return the remaining kSmallPCDeltaBits of the pc_delta.
100  return pc_delta & kSmallPCDeltaMask;
101 }
102 
103 void RelocInfoWriter::WriteShortTaggedPC(uint32_t pc_delta, int tag) {
104  // Write a byte of tagged pc-delta, possibly preceded by an explicit pc-jump.
105  pc_delta = WriteLongPCJump(pc_delta);
106  *--pos_ = pc_delta << kTagBits | tag;
107 }
108 
109 void RelocInfoWriter::WriteShortData(intptr_t data_delta) {
110  *--pos_ = static_cast<byte>(data_delta);
111 }
112 
113 void RelocInfoWriter::WriteMode(RelocInfo::Mode rmode) {
114  STATIC_ASSERT(RelocInfo::NUMBER_OF_MODES <= (1 << kLongTagBits));
115  *--pos_ = static_cast<int>((rmode << kTagBits) | kDefaultTag);
116 }
117 
118 void RelocInfoWriter::WriteModeAndPC(uint32_t pc_delta, RelocInfo::Mode rmode) {
119  // Write two-byte tagged pc-delta, possibly preceded by var. length pc-jump.
120  pc_delta = WriteLongPCJump(pc_delta);
121  WriteMode(rmode);
122  *--pos_ = pc_delta;
123 }
124 
125 void RelocInfoWriter::WriteIntData(int number) {
126  for (int i = 0; i < kIntSize; i++) {
127  *--pos_ = static_cast<byte>(number);
128  // Signed right shift is arithmetic shift. Tested in test-utils.cc.
129  number = number >> kBitsPerByte;
130  }
131 }
132 
133 void RelocInfoWriter::WriteData(intptr_t data_delta) {
134  for (int i = 0; i < kIntptrSize; i++) {
135  *--pos_ = static_cast<byte>(data_delta);
136  // Signed right shift is arithmetic shift. Tested in test-utils.cc.
137  data_delta = data_delta >> kBitsPerByte;
138  }
139 }
140 
141 void RelocInfoWriter::Write(const RelocInfo* rinfo) {
142  RelocInfo::Mode rmode = rinfo->rmode();
143 #ifdef DEBUG
144  byte* begin_pos = pos_;
145 #endif
146  DCHECK(rinfo->rmode() < RelocInfo::NUMBER_OF_MODES);
147  DCHECK_GE(rinfo->pc() - reinterpret_cast<Address>(last_pc_), 0);
148  // Use unsigned delta-encoding for pc.
149  uint32_t pc_delta =
150  static_cast<uint32_t>(rinfo->pc() - reinterpret_cast<Address>(last_pc_));
151 
152  // The two most common modes are given small tags, and usually fit in a byte.
153  if (rmode == RelocInfo::EMBEDDED_OBJECT) {
154  WriteShortTaggedPC(pc_delta, kEmbeddedObjectTag);
155  } else if (rmode == RelocInfo::CODE_TARGET) {
156  WriteShortTaggedPC(pc_delta, kCodeTargetTag);
157  DCHECK_LE(begin_pos - pos_, RelocInfo::kMaxCallSize);
158  } else if (rmode == RelocInfo::WASM_STUB_CALL) {
159  WriteShortTaggedPC(pc_delta, kWasmStubCallTag);
160  } else {
161  WriteModeAndPC(pc_delta, rmode);
162  if (RelocInfo::IsComment(rmode)) {
163  WriteData(rinfo->data());
164  } else if (RelocInfo::IsDeoptReason(rmode)) {
165  DCHECK_LT(rinfo->data(), 1 << kBitsPerByte);
166  WriteShortData(rinfo->data());
167  } else if (RelocInfo::IsConstPool(rmode) ||
168  RelocInfo::IsVeneerPool(rmode) || RelocInfo::IsDeoptId(rmode) ||
169  RelocInfo::IsDeoptPosition(rmode)) {
170  WriteIntData(static_cast<int>(rinfo->data()));
171  }
172  }
173  last_pc_ = reinterpret_cast<byte*>(rinfo->pc());
174 #ifdef DEBUG
175  DCHECK_LE(begin_pos - pos_, kMaxSize);
176 #endif
177 }
178 
179 inline int RelocIterator::AdvanceGetTag() { return *--pos_ & kTagMask; }
180 
181 inline RelocInfo::Mode RelocIterator::GetMode() {
182  return static_cast<RelocInfo::Mode>((*pos_ >> kTagBits) &
183  ((1 << kLongTagBits) - 1));
184 }
185 
186 inline void RelocIterator::ReadShortTaggedPC() {
187  rinfo_.pc_ += *pos_ >> kTagBits;
188 }
189 
190 inline void RelocIterator::AdvanceReadPC() { rinfo_.pc_ += *--pos_; }
191 
192 void RelocIterator::AdvanceReadInt() {
193  int x = 0;
194  for (int i = 0; i < kIntSize; i++) {
195  x |= static_cast<int>(*--pos_) << i * kBitsPerByte;
196  }
197  rinfo_.data_ = x;
198 }
199 
200 void RelocIterator::AdvanceReadData() {
201  intptr_t x = 0;
202  for (int i = 0; i < kIntptrSize; i++) {
203  x |= static_cast<intptr_t>(*--pos_) << i * kBitsPerByte;
204  }
205  rinfo_.data_ = x;
206 }
207 
208 void RelocIterator::AdvanceReadLongPCJump() {
209  // Read the 32-kSmallPCDeltaBits most significant bits of the
210  // pc jump in kChunkBits bit chunks and shift them into place.
211  // Stop when the last chunk is encountered.
212  uint32_t pc_jump = 0;
213  for (int i = 0; i < kIntSize; i++) {
214  byte pc_jump_part = *--pos_;
215  pc_jump |= (pc_jump_part >> kLastChunkTagBits) << i * kChunkBits;
216  if ((pc_jump_part & kLastChunkTagMask) == 1) break;
217  }
218  // The least significant kSmallPCDeltaBits bits will be added
219  // later.
220  rinfo_.pc_ += pc_jump << kSmallPCDeltaBits;
221 }
222 
223 inline void RelocIterator::ReadShortData() {
224  uint8_t unsigned_b = *pos_;
225  rinfo_.data_ = unsigned_b;
226 }
227 
228 void RelocIterator::next() {
229  DCHECK(!done());
230  // Basically, do the opposite of RelocInfoWriter::Write.
231  // Reading of data is as far as possible avoided for unwanted modes,
232  // but we must always update the pc.
233  //
234  // We exit this loop by returning when we find a mode we want.
235  while (pos_ > end_) {
236  int tag = AdvanceGetTag();
237  if (tag == kEmbeddedObjectTag) {
238  ReadShortTaggedPC();
239  if (SetMode(RelocInfo::EMBEDDED_OBJECT)) return;
240  } else if (tag == kCodeTargetTag) {
241  ReadShortTaggedPC();
242  if (SetMode(RelocInfo::CODE_TARGET)) return;
243  } else if (tag == kWasmStubCallTag) {
244  ReadShortTaggedPC();
245  if (SetMode(RelocInfo::WASM_STUB_CALL)) return;
246  } else {
247  DCHECK_EQ(tag, kDefaultTag);
248  RelocInfo::Mode rmode = GetMode();
249  if (rmode == RelocInfo::PC_JUMP) {
250  AdvanceReadLongPCJump();
251  } else {
252  AdvanceReadPC();
253  if (RelocInfo::IsComment(rmode)) {
254  if (SetMode(rmode)) {
255  AdvanceReadData();
256  return;
257  }
258  Advance(kIntptrSize);
259  } else if (RelocInfo::IsDeoptReason(rmode)) {
260  Advance();
261  if (SetMode(rmode)) {
262  ReadShortData();
263  return;
264  }
265  } else if (RelocInfo::IsConstPool(rmode) ||
266  RelocInfo::IsVeneerPool(rmode) ||
267  RelocInfo::IsDeoptId(rmode) ||
268  RelocInfo::IsDeoptPosition(rmode)) {
269  if (SetMode(rmode)) {
270  AdvanceReadInt();
271  return;
272  }
273  Advance(kIntSize);
274  } else if (SetMode(static_cast<RelocInfo::Mode>(rmode))) {
275  return;
276  }
277  }
278  }
279  }
280  done_ = true;
281 }
282 
283 RelocIterator::RelocIterator(Code code, int mode_mask)
284  : RelocIterator(code, code->unchecked_relocation_info(), mode_mask) {}
285 
286 RelocIterator::RelocIterator(Code code, ByteArray relocation_info,
287  int mode_mask)
288  : RelocIterator(code, code->raw_instruction_start(), code->constant_pool(),
289  relocation_info->GetDataEndAddress(),
290  relocation_info->GetDataStartAddress(), mode_mask) {}
291 
292 RelocIterator::RelocIterator(const CodeReference code_reference, int mode_mask)
293  : RelocIterator(Code(), code_reference.instruction_start(),
294  code_reference.constant_pool(),
295  code_reference.relocation_end(),
296  code_reference.relocation_start(), mode_mask) {}
297 
298 RelocIterator::RelocIterator(EmbeddedData* embedded_data, Code code,
299  int mode_mask)
300  : RelocIterator(
301  code, embedded_data->InstructionStartOfBuiltin(code->builtin_index()),
302  code->constant_pool(),
303  code->relocation_start() + code->relocation_size(),
304  code->relocation_start(), mode_mask) {}
305 
306 RelocIterator::RelocIterator(const CodeDesc& desc, int mode_mask)
307  : RelocIterator(Code(), reinterpret_cast<Address>(desc.buffer), 0,
308  desc.buffer + desc.buffer_size,
309  desc.buffer + desc.buffer_size - desc.reloc_size,
310  mode_mask) {}
311 
312 RelocIterator::RelocIterator(Vector<byte> instructions,
313  Vector<const byte> reloc_info, Address const_pool,
314  int mode_mask)
315  : RelocIterator(Code(), reinterpret_cast<Address>(instructions.start()),
316  const_pool, reloc_info.start() + reloc_info.size(),
317  reloc_info.start(), mode_mask) {}
318 
319 RelocIterator::RelocIterator(Code host, Address pc, Address constant_pool,
320  const byte* pos, const byte* end, int mode_mask)
321  : pos_(pos), end_(end), mode_mask_(mode_mask) {
322  // Relocation info is read backwards.
323  DCHECK_GE(pos_, end_);
324  rinfo_.host_ = host;
325  rinfo_.pc_ = pc;
326  rinfo_.constant_pool_ = constant_pool;
327  if (mode_mask_ == 0) pos_ = end_;
328  next();
329 }
330 
331 // -----------------------------------------------------------------------------
332 // Implementation of RelocInfo
333 
334 // static
335 bool RelocInfo::OffHeapTargetIsCodedSpecially() {
336 #if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_ARM64) || \
337  defined(V8_TARGET_ARCH_X64)
338  return false;
339 #elif defined(V8_TARGET_ARCH_IA32) || defined(V8_TARGET_ARCH_MIPS) || \
340  defined(V8_TARGET_ARCH_MIPS64) || defined(V8_TARGET_ARCH_PPC) || \
341  defined(V8_TARGET_ARCH_S390)
342  return true;
343 #endif
344 }
345 
346 Address RelocInfo::wasm_call_address() const {
347  DCHECK_EQ(rmode_, WASM_CALL);
348  return Assembler::target_address_at(pc_, constant_pool_);
349 }
350 
351 void RelocInfo::set_wasm_call_address(Address address,
352  ICacheFlushMode icache_flush_mode) {
353  DCHECK_EQ(rmode_, WASM_CALL);
354  Assembler::set_target_address_at(pc_, constant_pool_, address,
355  icache_flush_mode);
356 }
357 
358 Address RelocInfo::wasm_stub_call_address() const {
359  DCHECK_EQ(rmode_, WASM_STUB_CALL);
360  return Assembler::target_address_at(pc_, constant_pool_);
361 }
362 
363 void RelocInfo::set_wasm_stub_call_address(Address address,
364  ICacheFlushMode icache_flush_mode) {
365  DCHECK_EQ(rmode_, WASM_STUB_CALL);
366  Assembler::set_target_address_at(pc_, constant_pool_, address,
367  icache_flush_mode);
368 }
369 
370 void RelocInfo::set_target_address(Address target,
371  WriteBarrierMode write_barrier_mode,
372  ICacheFlushMode icache_flush_mode) {
373  DCHECK(IsCodeTargetMode(rmode_) || IsRuntimeEntry(rmode_) ||
374  IsWasmCall(rmode_));
375  Assembler::set_target_address_at(pc_, constant_pool_, target,
376  icache_flush_mode);
377  if (write_barrier_mode == UPDATE_WRITE_BARRIER && !host().is_null() &&
378  IsCodeTargetMode(rmode_)) {
379  Code target_code = Code::GetCodeFromTargetAddress(target);
380  MarkingBarrierForCode(host(), this, target_code);
381  }
382 }
383 
384 bool RelocInfo::RequiresRelocationAfterCodegen(const CodeDesc& desc) {
385  RelocIterator it(desc, RelocInfo::PostCodegenRelocationMask());
386  return !it.done();
387 }
388 
389 bool RelocInfo::RequiresRelocation(Code code) {
390  RelocIterator it(code, RelocInfo::kApplyMask);
391  return !it.done();
392 }
393 
394 #ifdef ENABLE_DISASSEMBLER
395 const char* RelocInfo::RelocModeName(RelocInfo::Mode rmode) {
396  switch (rmode) {
397  case NONE:
398  return "no reloc";
399  case EMBEDDED_OBJECT:
400  return "embedded object";
401  case CODE_TARGET:
402  return "code target";
403  case RELATIVE_CODE_TARGET:
404  return "relative code target";
405  case RUNTIME_ENTRY:
406  return "runtime entry";
407  case COMMENT:
408  return "comment";
409  case EXTERNAL_REFERENCE:
410  return "external reference";
411  case INTERNAL_REFERENCE:
412  return "internal reference";
413  case INTERNAL_REFERENCE_ENCODED:
414  return "encoded internal reference";
415  case OFF_HEAP_TARGET:
416  return "off heap target";
417  case DEOPT_SCRIPT_OFFSET:
418  return "deopt script offset";
419  case DEOPT_INLINING_ID:
420  return "deopt inlining id";
421  case DEOPT_REASON:
422  return "deopt reason";
423  case DEOPT_ID:
424  return "deopt index";
425  case CONST_POOL:
426  return "constant pool";
427  case VENEER_POOL:
428  return "veneer pool";
429  case WASM_CALL:
430  return "internal wasm call";
431  case WASM_STUB_CALL:
432  return "wasm stub call";
433  case NUMBER_OF_MODES:
434  case PC_JUMP:
435  UNREACHABLE();
436  }
437  return "unknown relocation type";
438 }
439 
440 void RelocInfo::Print(Isolate* isolate, std::ostream& os) { // NOLINT
441  os << reinterpret_cast<const void*>(pc_) << " " << RelocModeName(rmode_);
442  if (IsComment(rmode_)) {
443  os << " (" << reinterpret_cast<char*>(data_) << ")";
444  } else if (rmode_ == DEOPT_SCRIPT_OFFSET || rmode_ == DEOPT_INLINING_ID) {
445  os << " (" << data() << ")";
446  } else if (rmode_ == DEOPT_REASON) {
447  os << " ("
448  << DeoptimizeReasonToString(static_cast<DeoptimizeReason>(data_)) << ")";
449  } else if (rmode_ == EMBEDDED_OBJECT) {
450  os << " (" << Brief(target_object()) << ")";
451  } else if (rmode_ == EXTERNAL_REFERENCE) {
452  if (isolate) {
453  ExternalReferenceEncoder ref_encoder(isolate);
454  os << " ("
455  << ref_encoder.NameOfAddress(isolate, target_external_reference())
456  << ") ";
457  }
458  os << " (" << reinterpret_cast<const void*>(target_external_reference())
459  << ")";
460  } else if (IsCodeTargetMode(rmode_)) {
461  const Address code_target = target_address();
462  Code code = Code::GetCodeFromTargetAddress(code_target);
463  DCHECK(code->IsCode());
464  os << " (" << Code::Kind2String(code->kind());
465  if (Builtins::IsBuiltin(code)) {
466  os << " " << Builtins::name(code->builtin_index());
467  } else if (code->kind() == Code::STUB) {
468  os << " " << CodeStub::MajorName(CodeStub::GetMajorKey(code));
469  }
470  os << ") (" << reinterpret_cast<const void*>(target_address()) << ")";
471  } else if (IsRuntimeEntry(rmode_) && isolate->deoptimizer_data() != nullptr) {
472  // Deoptimization bailouts are stored as runtime entries.
473  DeoptimizeKind type;
474  if (Deoptimizer::IsDeoptimizationEntry(isolate, target_address(), &type)) {
475  int id = GetDeoptimizationId(isolate, type);
476  os << " (" << Deoptimizer::MessageFor(type) << " deoptimization bailout "
477  << id << ")";
478  }
479  } else if (IsConstPool(rmode_)) {
480  os << " (size " << static_cast<int>(data_) << ")";
481  }
482 
483  os << "\n";
484 }
485 #endif // ENABLE_DISASSEMBLER
486 
487 #ifdef VERIFY_HEAP
488 void RelocInfo::Verify(Isolate* isolate) {
489  switch (rmode_) {
490  case EMBEDDED_OBJECT:
491  Object::VerifyPointer(isolate, target_object());
492  break;
493  case CODE_TARGET:
494  case RELATIVE_CODE_TARGET: {
495  // convert inline target address to code object
496  Address addr = target_address();
497  CHECK_NE(addr, kNullAddress);
498  // Check that we can find the right code object.
499  Code code = Code::GetCodeFromTargetAddress(addr);
500  Object* found = isolate->FindCodeObject(addr);
501  CHECK(found->IsCode());
502  CHECK(code->address() == HeapObject::cast(found)->address());
503  break;
504  }
505  case INTERNAL_REFERENCE:
506  case INTERNAL_REFERENCE_ENCODED: {
507  Address target = target_internal_reference();
508  Address pc = target_internal_reference_address();
509  Code code = Code::cast(isolate->FindCodeObject(pc));
510  CHECK(target >= code->InstructionStart());
511  CHECK(target <= code->InstructionEnd());
512  break;
513  }
514  case OFF_HEAP_TARGET: {
515  Address addr = target_off_heap_target();
516  CHECK_NE(addr, kNullAddress);
517  CHECK(!InstructionStream::TryLookupCode(isolate, addr).is_null());
518  break;
519  }
520  case RUNTIME_ENTRY:
521  case COMMENT:
522  case EXTERNAL_REFERENCE:
523  case DEOPT_SCRIPT_OFFSET:
524  case DEOPT_INLINING_ID:
525  case DEOPT_REASON:
526  case DEOPT_ID:
527  case CONST_POOL:
528  case VENEER_POOL:
529  case WASM_CALL:
530  case WASM_STUB_CALL:
531  case NONE:
532  break;
533  case NUMBER_OF_MODES:
534  case PC_JUMP:
535  UNREACHABLE();
536  break;
537  }
538 }
539 #endif // VERIFY_HEAP
540 
541 } // namespace internal
542 } // namespace v8
Definition: libplatform.h:13