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/codegen/constant-pool.h" 6#include "src/codegen/assembler-arch.h" 7#include "src/codegen/assembler-inl.h" 8 9namespace v8 { 10namespace internal { 11 12#if defined(V8_TARGET_ARCH_PPC) || defined(V8_TARGET_ARCH_PPC64) 13 14ConstantPoolBuilder::ConstantPoolBuilder(int ptr_reach_bits, 15 int double_reach_bits) { 16 info_[ConstantPoolEntry::INTPTR].entries.reserve(64); 17 info_[ConstantPoolEntry::INTPTR].regular_reach_bits = ptr_reach_bits; 18 info_[ConstantPoolEntry::DOUBLE].regular_reach_bits = double_reach_bits; 19} 20 21ConstantPoolEntry::Access ConstantPoolBuilder::NextAccess( 22 ConstantPoolEntry::Type type) const { 23 const PerTypeEntryInfo& info = info_[type]; 24 25 if (info.overflow()) return ConstantPoolEntry::OVERFLOWED; 26 27 int dbl_count = info_[ConstantPoolEntry::DOUBLE].regular_count; 28 int dbl_offset = dbl_count * kDoubleSize; 29 int ptr_count = info_[ConstantPoolEntry::INTPTR].regular_count; 30 int ptr_offset = ptr_count * kSystemPointerSize + dbl_offset; 31 32 if (type == ConstantPoolEntry::DOUBLE) { 33 // Double overflow detection must take into account the reach for both types 34 int ptr_reach_bits = info_[ConstantPoolEntry::INTPTR].regular_reach_bits; 35 if (!is_uintn(dbl_offset, info.regular_reach_bits) || 36 (ptr_count > 0 && 37 !is_uintn(ptr_offset + kDoubleSize - kSystemPointerSize, 38 ptr_reach_bits))) { 39 return ConstantPoolEntry::OVERFLOWED; 40 } 41 } else { 42 DCHECK(type == ConstantPoolEntry::INTPTR); 43 if (!is_uintn(ptr_offset, info.regular_reach_bits)) { 44 return ConstantPoolEntry::OVERFLOWED; 45 } 46 } 47 48 return ConstantPoolEntry::REGULAR; 49} 50 51ConstantPoolEntry::Access ConstantPoolBuilder::AddEntry( 52 ConstantPoolEntry* entry, ConstantPoolEntry::Type type) { 53 DCHECK(!emitted_label_.is_bound()); 54 PerTypeEntryInfo& info = info_[type]; 55 const int entry_size = ConstantPoolEntry::size(type); 56 bool merged = false; 57 58 if (entry->sharing_ok()) { 59 // Try to merge entries 60 std::vector<ConstantPoolEntry>::iterator it = info.shared_entries.begin(); 61 int end = static_cast<int>(info.shared_entries.size()); 62 for (int i = 0; i < end; i++, it++) { 63 if ((entry_size == kSystemPointerSize) 64 ? entry->value() == it->value() 65 : entry->value64() == it->value64()) { 66 // Merge with found entry. 67 entry->set_merged_index(i); 68 merged = true; 69 break; 70 } 71 } 72 } 73 74 // By definition, merged entries have regular access. 75 DCHECK(!merged || entry->merged_index() < info.regular_count); 76 ConstantPoolEntry::Access access = 77 (merged ? ConstantPoolEntry::REGULAR : NextAccess(type)); 78 79 // Enforce an upper bound on search time by limiting the search to 80 // unique sharable entries which fit in the regular section. 81 if (entry->sharing_ok() && !merged && access == ConstantPoolEntry::REGULAR) { 82 info.shared_entries.push_back(*entry); 83 } else { 84 info.entries.push_back(*entry); 85 } 86 87 // We're done if we found a match or have already triggered the 88 // overflow state. 89 if (merged || info.overflow()) return access; 90 91 if (access == ConstantPoolEntry::REGULAR) { 92 info.regular_count++; 93 } else { 94 info.overflow_start = static_cast<int>(info.entries.size()) - 1; 95 } 96 97 return access; 98} 99 100void ConstantPoolBuilder::EmitSharedEntries(Assembler* assm, 101 ConstantPoolEntry::Type type) { 102 PerTypeEntryInfo& info = info_[type]; 103 std::vector<ConstantPoolEntry>& shared_entries = info.shared_entries; 104 const int entry_size = ConstantPoolEntry::size(type); 105 int base = emitted_label_.pos(); 106 DCHECK_GT(base, 0); 107 int shared_end = static_cast<int>(shared_entries.size()); 108 std::vector<ConstantPoolEntry>::iterator shared_it = shared_entries.begin(); 109 for (int i = 0; i < shared_end; i++, shared_it++) { 110 int offset = assm->pc_offset() - base; 111 shared_it->set_offset(offset); // Save offset for merged entries. 112 if (entry_size == kSystemPointerSize) { 113 assm->dp(shared_it->value()); 114 } else { 115 assm->dq(shared_it->value64()); 116 } 117 DCHECK(is_uintn(offset, info.regular_reach_bits)); 118 119 // Patch load sequence with correct offset. 120 assm->PatchConstantPoolAccessInstruction(shared_it->position(), offset, 121 ConstantPoolEntry::REGULAR, type); 122 } 123} 124 125void ConstantPoolBuilder::EmitGroup(Assembler* assm, 126 ConstantPoolEntry::Access access, 127 ConstantPoolEntry::Type type) { 128 PerTypeEntryInfo& info = info_[type]; 129 const bool overflow = info.overflow(); 130 std::vector<ConstantPoolEntry>& entries = info.entries; 131 std::vector<ConstantPoolEntry>& shared_entries = info.shared_entries; 132 const int entry_size = ConstantPoolEntry::size(type); 133 int base = emitted_label_.pos(); 134 DCHECK_GT(base, 0); 135 int begin; 136 int end; 137 138 if (access == ConstantPoolEntry::REGULAR) { 139 // Emit any shared entries first 140 EmitSharedEntries(assm, type); 141 } 142 143 if (access == ConstantPoolEntry::REGULAR) { 144 begin = 0; 145 end = overflow ? info.overflow_start : static_cast<int>(entries.size()); 146 } else { 147 DCHECK(access == ConstantPoolEntry::OVERFLOWED); 148 if (!overflow) return; 149 begin = info.overflow_start; 150 end = static_cast<int>(entries.size()); 151 } 152 153 std::vector<ConstantPoolEntry>::iterator it = entries.begin(); 154 if (begin > 0) std::advance(it, begin); 155 for (int i = begin; i < end; i++, it++) { 156 // Update constant pool if necessary and get the entry's offset. 157 int offset; 158 ConstantPoolEntry::Access entry_access; 159 if (!it->is_merged()) { 160 // Emit new entry 161 offset = assm->pc_offset() - base; 162 entry_access = access; 163 if (entry_size == kSystemPointerSize) { 164 assm->dp(it->value()); 165 } else { 166 assm->dq(it->value64()); 167 } 168 } else { 169 // Retrieve offset from shared entry. 170 offset = shared_entries[it->merged_index()].offset(); 171 entry_access = ConstantPoolEntry::REGULAR; 172 } 173 174 DCHECK(entry_access == ConstantPoolEntry::OVERFLOWED || 175 is_uintn(offset, info.regular_reach_bits)); 176 177 // Patch load sequence with correct offset. 178 assm->PatchConstantPoolAccessInstruction(it->position(), offset, 179 entry_access, type); 180 } 181} 182 183// Emit and return size of pool. 184int ConstantPoolBuilder::Emit(Assembler* assm) { 185 bool emitted = emitted_label_.is_bound(); 186 bool empty = IsEmpty(); 187 188 if (!emitted) { 189 // Mark start of constant pool. Align if necessary. 190 if (!empty) assm->DataAlign(kDoubleSize); 191 assm->bind(&emitted_label_); 192 if (!empty) { 193 // Emit in groups based on access and type. 194 // Emit doubles first for alignment purposes. 195 EmitGroup(assm, ConstantPoolEntry::REGULAR, ConstantPoolEntry::DOUBLE); 196 EmitGroup(assm, ConstantPoolEntry::REGULAR, ConstantPoolEntry::INTPTR); 197 if (info_[ConstantPoolEntry::DOUBLE].overflow()) { 198 assm->DataAlign(kDoubleSize); 199 EmitGroup(assm, ConstantPoolEntry::OVERFLOWED, 200 ConstantPoolEntry::DOUBLE); 201 } 202 if (info_[ConstantPoolEntry::INTPTR].overflow()) { 203 EmitGroup(assm, ConstantPoolEntry::OVERFLOWED, 204 ConstantPoolEntry::INTPTR); 205 } 206 } 207 } 208 209 return !empty ? (assm->pc_offset() - emitted_label_.pos()) : 0; 210} 211 212#endif // defined(V8_TARGET_ARCH_PPC) || defined(V8_TARGET_ARCH_PPC64) 213 214#if defined(V8_TARGET_ARCH_ARM64) 215 216// Constant Pool. 217 218ConstantPool::ConstantPool(Assembler* assm) : assm_(assm) {} 219ConstantPool::~ConstantPool() { DCHECK_EQ(blocked_nesting_, 0); } 220 221RelocInfoStatus ConstantPool::RecordEntry(uint32_t data, 222 RelocInfo::Mode rmode) { 223 ConstantPoolKey key(data, rmode); 224 CHECK(key.is_value32()); 225 return RecordKey(std::move(key), assm_->pc_offset()); 226} 227 228RelocInfoStatus ConstantPool::RecordEntry(uint64_t data, 229 RelocInfo::Mode rmode) { 230 ConstantPoolKey key(data, rmode); 231 CHECK(!key.is_value32()); 232 return RecordKey(std::move(key), assm_->pc_offset()); 233} 234 235RelocInfoStatus ConstantPool::RecordKey(ConstantPoolKey key, int offset) { 236 RelocInfoStatus write_reloc_info = GetRelocInfoStatusFor(key); 237 if (write_reloc_info == RelocInfoStatus::kMustRecord) { 238 if (key.is_value32()) { 239 if (entry32_count_ == 0) first_use_32_ = offset; 240 ++entry32_count_; 241 } else { 242 if (entry64_count_ == 0) first_use_64_ = offset; 243 ++entry64_count_; 244 } 245 } 246 entries_.insert(std::make_pair(key, offset)); 247 248 if (Entry32Count() + Entry64Count() > ConstantPool::kApproxMaxEntryCount) { 249 // Request constant pool emission after the next instruction. 250 SetNextCheckIn(1); 251 } 252 253 return write_reloc_info; 254} 255 256RelocInfoStatus ConstantPool::GetRelocInfoStatusFor( 257 const ConstantPoolKey& key) { 258 if (key.AllowsDeduplication()) { 259 auto existing = entries_.find(key); 260 if (existing != entries_.end()) { 261 return RelocInfoStatus::kMustOmitForDuplicate; 262 } 263 } 264 return RelocInfoStatus::kMustRecord; 265} 266 267void ConstantPool::EmitAndClear(Jump require_jump) { 268 DCHECK(!IsBlocked()); 269 // Prevent recursive pool emission. 270 Assembler::BlockPoolsScope block_pools(assm_, PoolEmissionCheck::kSkip); 271 Alignment require_alignment = 272 IsAlignmentRequiredIfEmittedAt(require_jump, assm_->pc_offset()); 273 int size = ComputeSize(require_jump, require_alignment); 274 Label size_check; 275 assm_->bind(&size_check); 276 assm_->RecordConstPool(size); 277 278 // Emit the constant pool. It is preceded by an optional branch if 279 // {require_jump} and a header which will: 280 // 1) Encode the size of the constant pool, for use by the disassembler. 281 // 2) Terminate the program, to try to prevent execution from accidentally 282 // flowing into the constant pool. 283 // 3) align the 64bit pool entries to 64-bit. 284 // TODO(all): Make the alignment part less fragile. Currently code is 285 // allocated as a byte array so there are no guarantees the alignment will 286 // be preserved on compaction. Currently it works as allocation seems to be 287 // 64-bit aligned. 288 289 Label after_pool; 290 if (require_jump == Jump::kRequired) assm_->b(&after_pool); 291 292 assm_->RecordComment("[ Constant Pool"); 293 EmitPrologue(require_alignment); 294 if (require_alignment == Alignment::kRequired) assm_->Align(kInt64Size); 295 EmitEntries(); 296 assm_->RecordComment("]"); 297 298 if (after_pool.is_linked()) assm_->bind(&after_pool); 299 300 DCHECK_EQ(assm_->SizeOfCodeGeneratedSince(&size_check), size); 301 Clear(); 302} 303 304void ConstantPool::Clear() { 305 entries_.clear(); 306 first_use_32_ = -1; 307 first_use_64_ = -1; 308 entry32_count_ = 0; 309 entry64_count_ = 0; 310 next_check_ = 0; 311} 312 313void ConstantPool::StartBlock() { 314 if (blocked_nesting_ == 0) { 315 // Prevent constant pool checks from happening by setting the next check to 316 // the biggest possible offset. 317 next_check_ = kMaxInt; 318 } 319 ++blocked_nesting_; 320} 321 322void ConstantPool::EndBlock() { 323 --blocked_nesting_; 324 if (blocked_nesting_ == 0) { 325 DCHECK(IsInImmRangeIfEmittedAt(assm_->pc_offset())); 326 // Make sure a check happens quickly after getting unblocked. 327 next_check_ = 0; 328 } 329} 330 331bool ConstantPool::IsBlocked() const { return blocked_nesting_ > 0; } 332 333void ConstantPool::SetNextCheckIn(size_t instructions) { 334 next_check_ = 335 assm_->pc_offset() + static_cast<int>(instructions * kInstrSize); 336} 337 338void ConstantPool::EmitEntries() { 339 for (auto iter = entries_.begin(); iter != entries_.end();) { 340 DCHECK(iter->first.is_value32() || IsAligned(assm_->pc_offset(), 8)); 341 auto range = entries_.equal_range(iter->first); 342 bool shared = iter->first.AllowsDeduplication(); 343 for (auto it = range.first; it != range.second; ++it) { 344 SetLoadOffsetToConstPoolEntry(it->second, assm_->pc(), it->first); 345 if (!shared) Emit(it->first); 346 } 347 if (shared) Emit(iter->first); 348 iter = range.second; 349 } 350} 351 352void ConstantPool::Emit(const ConstantPoolKey& key) { 353 if (key.is_value32()) { 354 assm_->dd(key.value32()); 355 } else { 356 assm_->dq(key.value64()); 357 } 358} 359 360bool ConstantPool::ShouldEmitNow(Jump require_jump, size_t margin) const { 361 if (IsEmpty()) return false; 362 if (Entry32Count() + Entry64Count() > ConstantPool::kApproxMaxEntryCount) { 363 return true; 364 } 365 // We compute {dist32/64}, i.e. the distance from the first instruction 366 // accessing a 32bit/64bit entry in the constant pool to any of the 367 // 32bit/64bit constant pool entries, respectively. This is required because 368 // we do not guarantee that entries are emitted in order of reference, i.e. it 369 // is possible that the entry with the earliest reference is emitted last. 370 // The constant pool should be emitted if either of the following is true: 371 // (A) {dist32/64} will be out of range at the next check in. 372 // (B) Emission can be done behind an unconditional branch and {dist32/64} 373 // exceeds {kOpportunityDist*}. 374 // (C) {dist32/64} exceeds the desired approximate distance to the pool. 375 int worst_case_size = ComputeSize(Jump::kRequired, Alignment::kRequired); 376 size_t pool_end_32 = assm_->pc_offset() + margin + worst_case_size; 377 size_t pool_end_64 = pool_end_32 - Entry32Count() * kInt32Size; 378 if (Entry64Count() != 0) { 379 // The 64-bit constants are always emitted before the 32-bit constants, so 380 // we subtract the size of the 32-bit constants from {size}. 381 size_t dist64 = pool_end_64 - first_use_64_; 382 bool next_check_too_late = dist64 + 2 * kCheckInterval >= kMaxDistToPool64; 383 bool opportune_emission_without_jump = 384 require_jump == Jump::kOmitted && (dist64 >= kOpportunityDistToPool64); 385 bool approximate_distance_exceeded = dist64 >= kApproxDistToPool64; 386 if (next_check_too_late || opportune_emission_without_jump || 387 approximate_distance_exceeded) { 388 return true; 389 } 390 } 391 if (Entry32Count() != 0) { 392 size_t dist32 = pool_end_32 - first_use_32_; 393 bool next_check_too_late = dist32 + 2 * kCheckInterval >= kMaxDistToPool32; 394 bool opportune_emission_without_jump = 395 require_jump == Jump::kOmitted && (dist32 >= kOpportunityDistToPool32); 396 bool approximate_distance_exceeded = dist32 >= kApproxDistToPool32; 397 if (next_check_too_late || opportune_emission_without_jump || 398 approximate_distance_exceeded) { 399 return true; 400 } 401 } 402 return false; 403} 404 405int ConstantPool::ComputeSize(Jump require_jump, 406 Alignment require_alignment) const { 407 int size_up_to_marker = PrologueSize(require_jump); 408 int alignment = require_alignment == Alignment::kRequired ? kInstrSize : 0; 409 size_t size_after_marker = 410 Entry32Count() * kInt32Size + alignment + Entry64Count() * kInt64Size; 411 return size_up_to_marker + static_cast<int>(size_after_marker); 412} 413 414Alignment ConstantPool::IsAlignmentRequiredIfEmittedAt(Jump require_jump, 415 int pc_offset) const { 416 int size_up_to_marker = PrologueSize(require_jump); 417 if (Entry64Count() != 0 && 418 !IsAligned(pc_offset + size_up_to_marker, kInt64Size)) { 419 return Alignment::kRequired; 420 } 421 return Alignment::kOmitted; 422} 423 424bool ConstantPool::IsInImmRangeIfEmittedAt(int pc_offset) { 425 // Check that all entries are in range if the pool is emitted at {pc_offset}. 426 // This ignores kPcLoadDelta (conservatively, since all offsets are positive), 427 // and over-estimates the last entry's address with the pool's end. 428 Alignment require_alignment = 429 IsAlignmentRequiredIfEmittedAt(Jump::kRequired, pc_offset); 430 size_t pool_end_32 = 431 pc_offset + ComputeSize(Jump::kRequired, require_alignment); 432 size_t pool_end_64 = pool_end_32 - Entry32Count() * kInt32Size; 433 bool entries_in_range_32 = 434 Entry32Count() == 0 || (pool_end_32 < first_use_32_ + kMaxDistToPool32); 435 bool entries_in_range_64 = 436 Entry64Count() == 0 || (pool_end_64 < first_use_64_ + kMaxDistToPool64); 437 return entries_in_range_32 && entries_in_range_64; 438} 439 440ConstantPool::BlockScope::BlockScope(Assembler* assm, size_t margin) 441 : pool_(&assm->constpool_) { 442 pool_->assm_->EmitConstPoolWithJumpIfNeeded(margin); 443 pool_->StartBlock(); 444} 445 446ConstantPool::BlockScope::BlockScope(Assembler* assm, PoolEmissionCheck check) 447 : pool_(&assm->constpool_) { 448 DCHECK_EQ(check, PoolEmissionCheck::kSkip); 449 pool_->StartBlock(); 450} 451 452ConstantPool::BlockScope::~BlockScope() { pool_->EndBlock(); } 453 454void ConstantPool::MaybeCheck() { 455 if (assm_->pc_offset() >= next_check_) { 456 Check(Emission::kIfNeeded, Jump::kRequired); 457 } 458} 459 460#endif // defined(V8_TARGET_ARCH_ARM64) 461 462#if defined(V8_TARGET_ARCH_RISCV64) 463 464// Constant Pool. 465 466ConstantPool::ConstantPool(Assembler* assm) : assm_(assm) {} 467ConstantPool::~ConstantPool() { DCHECK_EQ(blocked_nesting_, 0); } 468 469RelocInfoStatus ConstantPool::RecordEntry(uint32_t data, 470 RelocInfo::Mode rmode) { 471 ConstantPoolKey key(data, rmode); 472 CHECK(key.is_value32()); 473 return RecordKey(std::move(key), assm_->pc_offset()); 474} 475 476RelocInfoStatus ConstantPool::RecordEntry(uint64_t data, 477 RelocInfo::Mode rmode) { 478 ConstantPoolKey key(data, rmode); 479 CHECK(!key.is_value32()); 480 return RecordKey(std::move(key), assm_->pc_offset()); 481} 482 483RelocInfoStatus ConstantPool::RecordKey(ConstantPoolKey key, int offset) { 484 RelocInfoStatus write_reloc_info = GetRelocInfoStatusFor(key); 485 if (write_reloc_info == RelocInfoStatus::kMustRecord) { 486 if (key.is_value32()) { 487 if (entry32_count_ == 0) first_use_32_ = offset; 488 ++entry32_count_; 489 } else { 490 if (entry64_count_ == 0) first_use_64_ = offset; 491 ++entry64_count_; 492 } 493 } 494 entries_.insert(std::make_pair(key, offset)); 495 496 if (Entry32Count() + Entry64Count() > ConstantPool::kApproxMaxEntryCount) { 497 // Request constant pool emission after the next instruction. 498 SetNextCheckIn(1); 499 } 500 501 return write_reloc_info; 502} 503 504RelocInfoStatus ConstantPool::GetRelocInfoStatusFor( 505 const ConstantPoolKey& key) { 506 if (key.AllowsDeduplication()) { 507 auto existing = entries_.find(key); 508 if (existing != entries_.end()) { 509 return RelocInfoStatus::kMustOmitForDuplicate; 510 } 511 } 512 return RelocInfoStatus::kMustRecord; 513} 514 515void ConstantPool::EmitAndClear(Jump require_jump) { 516 DCHECK(!IsBlocked()); 517 // Prevent recursive pool emission. 518 Assembler::BlockPoolsScope block_pools(assm_, PoolEmissionCheck::kSkip); 519 Alignment require_alignment = 520 IsAlignmentRequiredIfEmittedAt(require_jump, assm_->pc_offset()); 521 int size = ComputeSize(require_jump, require_alignment); 522 Label size_check; 523 assm_->bind(&size_check); 524 assm_->RecordConstPool(size); 525 526 // Emit the constant pool. It is preceded by an optional branch if 527 // {require_jump} and a header which will: 528 // 1) Encode the size of the constant pool, for use by the disassembler. 529 // 2) Terminate the program, to try to prevent execution from accidentally 530 // flowing into the constant pool. 531 // 3) align the 64bit pool entries to 64-bit. 532 // TODO(all): Make the alignment part less fragile. Currently code is 533 // allocated as a byte array so there are no guarantees the alignment will 534 // be preserved on compaction. Currently it works as allocation seems to be 535 // 64-bit aligned. 536 DEBUG_PRINTF("\tConstant Pool start\n") 537 Label after_pool; 538 if (require_jump == Jump::kRequired) assm_->b(&after_pool); 539 540 assm_->RecordComment("[ Constant Pool"); 541 542 EmitPrologue(require_alignment); 543 if (require_alignment == Alignment::kRequired) assm_->DataAlign(kInt64Size); 544 EmitEntries(); 545 assm_->RecordComment("]"); 546 assm_->bind(&after_pool); 547 DEBUG_PRINTF("\tConstant Pool end\n") 548 549 DCHECK_LE(assm_->SizeOfCodeGeneratedSince(&size_check) - size, 3); 550 Clear(); 551} 552 553void ConstantPool::Clear() { 554 entries_.clear(); 555 first_use_32_ = -1; 556 first_use_64_ = -1; 557 entry32_count_ = 0; 558 entry64_count_ = 0; 559 next_check_ = 0; 560} 561 562void ConstantPool::StartBlock() { 563 if (blocked_nesting_ == 0) { 564 // Prevent constant pool checks from happening by setting the next check to 565 // the biggest possible offset. 566 next_check_ = kMaxInt; 567 } 568 ++blocked_nesting_; 569} 570 571void ConstantPool::EndBlock() { 572 --blocked_nesting_; 573 if (blocked_nesting_ == 0) { 574 DCHECK(IsInImmRangeIfEmittedAt(assm_->pc_offset())); 575 // Make sure a check happens quickly after getting unblocked. 576 next_check_ = 0; 577 } 578} 579 580bool ConstantPool::IsBlocked() const { return blocked_nesting_ > 0; } 581 582void ConstantPool::SetNextCheckIn(size_t instructions) { 583 next_check_ = 584 assm_->pc_offset() + static_cast<int>(instructions * kInstrSize); 585} 586 587void ConstantPool::EmitEntries() { 588 for (auto iter = entries_.begin(); iter != entries_.end();) { 589 DCHECK(iter->first.is_value32() || IsAligned(assm_->pc_offset(), 8)); 590 auto range = entries_.equal_range(iter->first); 591 bool shared = iter->first.AllowsDeduplication(); 592 for (auto it = range.first; it != range.second; ++it) { 593 SetLoadOffsetToConstPoolEntry(it->second, assm_->pc(), it->first); 594 if (!shared) Emit(it->first); 595 } 596 if (shared) Emit(iter->first); 597 iter = range.second; 598 } 599} 600 601void ConstantPool::Emit(const ConstantPoolKey& key) { 602 if (key.is_value32()) { 603 assm_->dd(key.value32()); 604 } else { 605 assm_->dq(key.value64()); 606 } 607} 608 609bool ConstantPool::ShouldEmitNow(Jump require_jump, size_t margin) const { 610 if (IsEmpty()) return false; 611 if (Entry32Count() + Entry64Count() > ConstantPool::kApproxMaxEntryCount) { 612 return true; 613 } 614 // We compute {dist32/64}, i.e. the distance from the first instruction 615 // accessing a 32bit/64bit entry in the constant pool to any of the 616 // 32bit/64bit constant pool entries, respectively. This is required because 617 // we do not guarantee that entries are emitted in order of reference, i.e. it 618 // is possible that the entry with the earliest reference is emitted last. 619 // The constant pool should be emitted if either of the following is true: 620 // (A) {dist32/64} will be out of range at the next check in. 621 // (B) Emission can be done behind an unconditional branch and {dist32/64} 622 // exceeds {kOpportunityDist*}. 623 // (C) {dist32/64} exceeds the desired approximate distance to the pool. 624 int worst_case_size = ComputeSize(Jump::kRequired, Alignment::kRequired); 625 size_t pool_end_32 = assm_->pc_offset() + margin + worst_case_size; 626 size_t pool_end_64 = pool_end_32 - Entry32Count() * kInt32Size; 627 if (Entry64Count() != 0) { 628 // The 64-bit constants are always emitted before the 32-bit constants, so 629 // we subtract the size of the 32-bit constants from {size}. 630 size_t dist64 = pool_end_64 - first_use_64_; 631 bool next_check_too_late = dist64 + 2 * kCheckInterval >= kMaxDistToPool64; 632 bool opportune_emission_without_jump = 633 require_jump == Jump::kOmitted && (dist64 >= kOpportunityDistToPool64); 634 bool approximate_distance_exceeded = dist64 >= kApproxDistToPool64; 635 if (next_check_too_late || opportune_emission_without_jump || 636 approximate_distance_exceeded) { 637 return true; 638 } 639 } 640 if (Entry32Count() != 0) { 641 size_t dist32 = pool_end_32 - first_use_32_; 642 bool next_check_too_late = dist32 + 2 * kCheckInterval >= kMaxDistToPool32; 643 bool opportune_emission_without_jump = 644 require_jump == Jump::kOmitted && (dist32 >= kOpportunityDistToPool32); 645 bool approximate_distance_exceeded = dist32 >= kApproxDistToPool32; 646 if (next_check_too_late || opportune_emission_without_jump || 647 approximate_distance_exceeded) { 648 return true; 649 } 650 } 651 return false; 652} 653 654int ConstantPool::ComputeSize(Jump require_jump, 655 Alignment require_alignment) const { 656 int size_up_to_marker = PrologueSize(require_jump); 657 int alignment = require_alignment == Alignment::kRequired ? kInstrSize : 0; 658 size_t size_after_marker = 659 Entry32Count() * kInt32Size + alignment + Entry64Count() * kInt64Size; 660 return size_up_to_marker + static_cast<int>(size_after_marker); 661} 662 663Alignment ConstantPool::IsAlignmentRequiredIfEmittedAt(Jump require_jump, 664 int pc_offset) const { 665 int size_up_to_marker = PrologueSize(require_jump); 666 if (Entry64Count() != 0 && 667 !IsAligned(pc_offset + size_up_to_marker, kInt64Size)) { 668 return Alignment::kRequired; 669 } 670 return Alignment::kOmitted; 671} 672 673bool ConstantPool::IsInImmRangeIfEmittedAt(int pc_offset) { 674 // Check that all entries are in range if the pool is emitted at {pc_offset}. 675 // This ignores kPcLoadDelta (conservatively, since all offsets are positive), 676 // and over-estimates the last entry's address with the pool's end. 677 Alignment require_alignment = 678 IsAlignmentRequiredIfEmittedAt(Jump::kRequired, pc_offset); 679 size_t pool_end_32 = 680 pc_offset + ComputeSize(Jump::kRequired, require_alignment); 681 size_t pool_end_64 = pool_end_32 - Entry32Count() * kInt32Size; 682 bool entries_in_range_32 = 683 Entry32Count() == 0 || (pool_end_32 < first_use_32_ + kMaxDistToPool32); 684 bool entries_in_range_64 = 685 Entry64Count() == 0 || (pool_end_64 < first_use_64_ + kMaxDistToPool64); 686 return entries_in_range_32 && entries_in_range_64; 687} 688 689ConstantPool::BlockScope::BlockScope(Assembler* assm, size_t margin) 690 : pool_(&assm->constpool_) { 691 pool_->assm_->EmitConstPoolWithJumpIfNeeded(margin); 692 pool_->StartBlock(); 693} 694 695ConstantPool::BlockScope::BlockScope(Assembler* assm, PoolEmissionCheck check) 696 : pool_(&assm->constpool_) { 697 DCHECK_EQ(check, PoolEmissionCheck::kSkip); 698 pool_->StartBlock(); 699} 700 701ConstantPool::BlockScope::~BlockScope() { pool_->EndBlock(); } 702 703void ConstantPool::MaybeCheck() { 704 if (assm_->pc_offset() >= next_check_) { 705 Check(Emission::kIfNeeded, Jump::kRequired); 706 } 707} 708 709#endif // defined(V8_TARGET_ARCH_RISCV64) 710 711} // namespace internal 712} // namespace v8 713