Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(565)

Unified Diff: src/arm64/assembler-arm64.cc

Issue 338523005: ARM64: updated literal pool implementation. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Addressed review comments Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/arm64/assembler-arm64.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/arm64/assembler-arm64.cc
diff --git a/src/arm64/assembler-arm64.cc b/src/arm64/assembler-arm64.cc
index 90cff59620e316061ad5513e176c5fd5cf8e2621..ff0ff52029b720d23096b8bc35ff94a1d2c0b6b7 100644
--- a/src/arm64/assembler-arm64.cc
+++ b/src/arm64/assembler-arm64.cc
@@ -296,10 +296,229 @@ bool Operand::NeedsRelocation(const Assembler* assembler) const {
}
-// Assembler
+// Constant Pool.
+void ConstPool::RecordEntry(intptr_t data,
+ RelocInfo::Mode mode) {
+ ASSERT(mode != RelocInfo::COMMENT &&
+ mode != RelocInfo::POSITION &&
+ mode != RelocInfo::STATEMENT_POSITION &&
+ mode != RelocInfo::CONST_POOL &&
+ mode != RelocInfo::VENEER_POOL &&
+ mode != RelocInfo::CODE_AGE_SEQUENCE);
+
+ uint64_t raw_data = static_cast<uint64_t>(data);
+ int offset = assm_->pc_offset();
+ if (IsEmpty()) {
+ first_use_ = offset;
+ }
+
+ std::pair<uint64_t, int> entry = std::make_pair(raw_data, offset);
+ if (CanBeShared(mode)) {
+ shared_entries_.insert(entry);
+ if (shared_entries_.count(entry.first) == 1) {
+ shared_entries_count++;
+ }
+ } else {
+ unique_entries_.push_back(entry);
+ }
+
+ if (EntryCount() > Assembler::kApproxMaxPoolEntryCount) {
+ // Request constant pool emission after the next instruction.
+ assm_->SetNextConstPoolCheckIn(1);
+ }
+}
+
+
+int ConstPool::DistanceToFirstUse() {
+ ASSERT(first_use_ >= 0);
+ return assm_->pc_offset() - first_use_;
+}
+
+
+int ConstPool::MaxPcOffset() {
+ // There are no pending entries in the pool so we can never get out of
+ // range.
+ if (IsEmpty()) return kMaxInt;
+
+ // Entries are not necessarily emitted in the order they are added so in the
+ // worst case the first constant pool use will be accessing the last entry.
+ return first_use_ + kMaxLoadLiteralRange - WorstCaseSize();
+}
+
+
+int ConstPool::WorstCaseSize() {
+ if (IsEmpty()) return 0;
+
+ // Max size prologue:
+ // b over
+ // ldr xzr, #pool_size
+ // blr xzr
+ // nop
+ // All entries are 64-bit for now.
+ return 4 * kInstructionSize + EntryCount() * kPointerSize;
+}
+
+
+int ConstPool::SizeIfEmittedAtCurrentPc(bool require_jump) {
+ if (IsEmpty()) return 0;
+
+ // Prologue is:
+ // b over ;; if require_jump
+ // ldr xzr, #pool_size
+ // blr xzr
+ // nop ;; if not 64-bit aligned
+ int prologue_size = require_jump ? kInstructionSize : 0;
+ prologue_size += 2 * kInstructionSize;
+ prologue_size += IsAligned(assm_->pc_offset() + prologue_size, 8) ?
+ 0 : kInstructionSize;
+
+ // All entries are 64-bit for now.
+ return prologue_size + EntryCount() * kPointerSize;
+}
+
+
+void ConstPool::Emit(bool require_jump) {
+ ASSERT(!assm_->is_const_pool_blocked());
+ // Prevent recursive pool emission and protect from veneer pools.
+ Assembler::BlockPoolsScope block_pools(assm_);
+
+ int size = SizeIfEmittedAtCurrentPc(require_jump);
+ Label size_check;
+ assm_->bind(&size_check);
+
+ assm_->RecordConstPool(size);
+ // Emit the constant pool. It is preceded by an optional branch if
+ // require_jump and a header which will:
+ // 1) Encode the size of the constant pool, for use by the disassembler.
+ // 2) Terminate the program, to try to prevent execution from accidentally
+ // flowing into the constant pool.
+ // 3) align the pool entries to 64-bit.
+ // The header is therefore made of up to three arm64 instructions:
+ // ldr xzr, #<size of the constant pool in 32-bit words>
+ // blr xzr
+ // nop
+ //
+ // If executed, the header will likely segfault and lr will point to the
+ // instruction following the offending blr.
+ // TODO(all): Make the alignment part less fragile. Currently code is
+ // allocated as a byte array so there are no guarantees the alignment will
+ // be preserved on compaction. Currently it works as allocation seems to be
+ // 64-bit aligned.
+
+ // Emit branch if required
+ Label after_pool;
+ if (require_jump) {
+ assm_->b(&after_pool);
+ }
+
+ // Emit the header.
+ assm_->RecordComment("[ Constant Pool");
+ EmitMarker();
+ EmitGuard();
+ assm_->Align(8);
+
+ // Emit constant pool entries.
+ // TODO(all): currently each relocated constant is 64 bits, consider adding
+ // support for 32-bit entries.
+ EmitEntries();
+ assm_->RecordComment("]");
+
+ if (after_pool.is_linked()) {
+ assm_->bind(&after_pool);
+ }
+
+ ASSERT(assm_->SizeOfCodeGeneratedSince(&size_check) ==
+ static_cast<unsigned>(size));
+}
+
+
+void ConstPool::Clear() {
+ shared_entries_.clear();
+ shared_entries_count = 0;
+ unique_entries_.clear();
+ first_use_ = -1;
+}
+
+
+bool ConstPool::CanBeShared(RelocInfo::Mode mode) {
+ // Constant pool currently does not support 32-bit entries.
+ ASSERT(mode != RelocInfo::NONE32);
+
+ return RelocInfo::IsNone(mode) ||
+ (!assm_->serializer_enabled() && (mode >= RelocInfo::CELL));
+}
+
+
+void ConstPool::EmitMarker() {
+ // A constant pool size is expressed in number of 32-bits words.
+ // Currently all entries are 64-bit.
+ // + 1 is for the crash guard.
+ // + 0/1 for alignment.
+ int word_count = EntryCount() * 2 + 1 +
+ (IsAligned(assm_->pc_offset(), 8)) ? 0 : 1;
+ assm_->Emit(LDR_x_lit |
+ Assembler::ImmLLiteral(word_count) |
+ Assembler::Rt(xzr));
+}
+
+void ConstPool::EmitGuard() {
+#ifdef DEBUG
+ Instruction* instr = reinterpret_cast<Instruction*>(assm_->pc());
+ ASSERT(instr->preceding()->IsLdrLiteralX() &&
+ instr->preceding()->Rt() == xzr.code());
+#endif
+ assm_->EmitPoolGuard();
+}
+
+
+void ConstPool::EmitEntries() {
+ ASSERT(IsAligned(assm_->pc_offset(), 8));
+
+ typedef std::multimap<uint64_t, int>::const_iterator SharedEntriesIterator;
+ SharedEntriesIterator value_it;
+ // Iterate through the keys (constant pool values).
+ for (value_it = shared_entries_.begin();
+ value_it != shared_entries_.end();
+ value_it = shared_entries_.upper_bound(value_it->first)) {
+ std::pair<SharedEntriesIterator, SharedEntriesIterator> range;
+ uint64_t data = value_it->first;
+ range = shared_entries_.equal_range(data);
+ SharedEntriesIterator offset_it;
+ // Iterate through the offsets of a given key.
+ for (offset_it = range.first; offset_it != range.second; offset_it++) {
+ Instruction* instr = assm_->InstructionAt(offset_it->second);
+
+ // Instruction to patch must be 'ldr rd, [pc, #offset]' with offset == 0.
+ ASSERT(instr->IsLdrLiteral() && instr->ImmLLiteral() == 0);
+ instr->SetImmPCOffsetTarget(assm_->pc());
+ }
+ assm_->dc64(data);
+ }
+ shared_entries_.clear();
+ shared_entries_count = 0;
+
+ // Emit unique entries.
+ std::vector<std::pair<uint64_t, int> >::const_iterator unique_it;
+ for (unique_it = unique_entries_.begin();
+ unique_it != unique_entries_.end();
+ unique_it++) {
+ Instruction* instr = assm_->InstructionAt(unique_it->second);
+
+ // Instruction to patch must be 'ldr rd, [pc, #offset]' with offset == 0.
+ ASSERT(instr->IsLdrLiteral() && instr->ImmLLiteral() == 0);
+ instr->SetImmPCOffsetTarget(assm_->pc());
+ assm_->dc64(unique_it->first);
+ }
+ unique_entries_.clear();
+ first_use_ = -1;
+}
+
+
+// Assembler
Assembler::Assembler(Isolate* isolate, void* buffer, int buffer_size)
: AssemblerBase(isolate, buffer, buffer_size),
+ constpool_(this),
recorded_ast_id_(TypeFeedbackId::None()),
unresolved_branches_(),
positions_recorder_(this) {
@@ -310,7 +529,7 @@ Assembler::Assembler(Isolate* isolate, void* buffer, int buffer_size)
Assembler::~Assembler() {
- ASSERT(num_pending_reloc_info_ == 0);
+ ASSERT(constpool_.IsEmpty());
ASSERT(const_pool_blocked_nesting_ == 0);
ASSERT(veneer_pool_blocked_nesting_ == 0);
}
@@ -327,11 +546,10 @@ void Assembler::Reset() {
pc_ = buffer_;
reloc_info_writer.Reposition(reinterpret_cast<byte*>(buffer_ + buffer_size_),
reinterpret_cast<byte*>(pc_));
- num_pending_reloc_info_ = 0;
+ constpool_.Clear();
next_constant_pool_check_ = 0;
next_veneer_pool_check_ = kMaxInt;
no_const_pool_before_ = 0;
- first_const_pool_use_ = -1;
ClearRecordedAstId();
}
@@ -339,7 +557,7 @@ void Assembler::Reset() {
void Assembler::GetCode(CodeDesc* desc) {
// Emit constant pool if necessary.
CheckConstPool(true, false);
- ASSERT(num_pending_reloc_info_ == 0);
+ ASSERT(constpool_.IsEmpty());
// Set up code descriptor.
if (desc) {
@@ -622,8 +840,7 @@ void Assembler::StartBlockConstPool() {
void Assembler::EndBlockConstPool() {
if (--const_pool_blocked_nesting_ == 0) {
// Check the constant pool hasn't been blocked for too long.
- ASSERT((num_pending_reloc_info_ == 0) ||
- (pc_offset() < (first_const_pool_use_ + kMaxDistToConstPool)));
+ ASSERT(pc_offset() < constpool_.MaxPcOffset());
// Two cases:
// * no_const_pool_before_ >= next_constant_pool_check_ and the emission is
// still blocked
@@ -682,13 +899,6 @@ int Assembler::ConstantPoolSizeAt(Instruction* instr) {
}
-void Assembler::ConstantPoolMarker(uint32_t size) {
- ASSERT(is_const_pool_blocked());
- // + 1 is for the crash guard.
- Emit(LDR_x_lit | ImmLLiteral(size + 1) | Rt(xzr));
-}
-
-
void Assembler::EmitPoolGuard() {
// We must generate only one instruction as this is used in scopes that
// control the size of the code generated.
@@ -696,18 +906,6 @@ void Assembler::EmitPoolGuard() {
}
-void Assembler::ConstantPoolGuard() {
-#ifdef DEBUG
- // Currently this is only used after a constant pool marker.
- ASSERT(is_const_pool_blocked());
- Instruction* instr = reinterpret_cast<Instruction*>(pc_);
- ASSERT(instr->preceding()->IsLdrLiteralX() &&
- instr->preceding()->Rt() == xzr.code());
-#endif
- EmitPoolGuard();
-}
-
-
void Assembler::StartBlockVeneerPool() {
++veneer_pool_blocked_nesting_;
}
@@ -2466,15 +2664,7 @@ void Assembler::GrowBuffer() {
// buffer nor pc absolute pointing inside the code buffer, so there is no need
// to relocate any emitted relocation entries.
- // Relocate pending relocation entries.
- for (int i = 0; i < num_pending_reloc_info_; i++) {
- RelocInfo& rinfo = pending_reloc_info_[i];
- ASSERT(rinfo.rmode() != RelocInfo::COMMENT &&
- rinfo.rmode() != RelocInfo::POSITION);
- if (rinfo.rmode() != RelocInfo::JS_RETURN) {
- rinfo.set_pc(rinfo.pc() + pc_delta);
- }
- }
+ // Pending relocation entries are also relative, no need to relocate.
}
@@ -2494,11 +2684,7 @@ void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) {
|| RelocInfo::IsVeneerPool(rmode));
// These modes do not need an entry in the constant pool.
} else {
- ASSERT(num_pending_reloc_info_ < kMaxNumPendingRelocInfo);
- if (num_pending_reloc_info_ == 0) {
- first_const_pool_use_ = pc_offset();
- }
- pending_reloc_info_[num_pending_reloc_info_++] = rinfo;
+ constpool_.RecordEntry(data, rmode);
// Make sure the constant pool is not emitted in place of the next
// instruction for which we just recorded relocation info.
BlockConstPoolFor(1);
@@ -2526,11 +2712,9 @@ void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) {
void Assembler::BlockConstPoolFor(int instructions) {
int pc_limit = pc_offset() + instructions * kInstructionSize;
if (no_const_pool_before_ < pc_limit) {
- // If there are some pending entries, the constant pool cannot be blocked
- // further than first_const_pool_use_ + kMaxDistToConstPool
- ASSERT((num_pending_reloc_info_ == 0) ||
- (pc_limit < (first_const_pool_use_ + kMaxDistToConstPool)));
no_const_pool_before_ = pc_limit;
+ // Make sure the pool won't be blocked for too long.
+ ASSERT(pc_limit < constpool_.MaxPcOffset());
}
if (next_constant_pool_check_ < no_const_pool_before_) {
@@ -2550,106 +2734,48 @@ void Assembler::CheckConstPool(bool force_emit, bool require_jump) {
}
// There is nothing to do if there are no pending constant pool entries.
- if (num_pending_reloc_info_ == 0) {
+ if (constpool_.IsEmpty()) {
// Calculate the offset of the next check.
- next_constant_pool_check_ = pc_offset() + kCheckConstPoolInterval;
+ SetNextConstPoolCheckIn(kCheckConstPoolInterval);
return;
}
// We emit a constant pool when:
// * requested to do so by parameter force_emit (e.g. after each function).
// * the distance to the first instruction accessing the constant pool is
- // kAvgDistToConstPool or more.
- // * no jump is required and the distance to the first instruction accessing
- // the constant pool is at least kMaxDistToPConstool / 2.
- ASSERT(first_const_pool_use_ >= 0);
- int dist = pc_offset() - first_const_pool_use_;
- if (!force_emit && dist < kAvgDistToConstPool &&
- (require_jump || (dist < (kMaxDistToConstPool / 2)))) {
+ // kApproxMaxDistToConstPool or more.
+ // * the number of entries in the pool is kApproxMaxPoolEntryCount or more.
+ int dist = constpool_.DistanceToFirstUse();
+ int count = constpool_.EntryCount();
+ if (!force_emit &&
+ (dist < kApproxMaxDistToConstPool) &&
+ (count < kApproxMaxPoolEntryCount)) {
return;
}
- int jump_instr = require_jump ? kInstructionSize : 0;
- int size_pool_marker = kInstructionSize;
- int size_pool_guard = kInstructionSize;
- int pool_size = jump_instr + size_pool_marker + size_pool_guard +
- num_pending_reloc_info_ * kPointerSize;
- int needed_space = pool_size + kGap;
// Emit veneers for branches that would go out of range during emission of the
// constant pool.
- CheckVeneerPool(false, require_jump, kVeneerDistanceMargin + pool_size);
-
- Label size_check;
- bind(&size_check);
+ int worst_case_size = constpool_.WorstCaseSize();
+ CheckVeneerPool(false, require_jump,
+ kVeneerDistanceMargin + worst_case_size);
// Check that the code buffer is large enough before emitting the constant
- // pool (include the jump over the pool, the constant pool marker, the
- // constant pool guard, and the gap to the relocation information).
+ // pool (this includes the gap to the relocation information).
+ int needed_space = worst_case_size + kGap + 1 * kInstructionSize;
while (buffer_space() <= needed_space) {
GrowBuffer();
}
- {
- // Block recursive calls to CheckConstPool and protect from veneer pools.
- BlockPoolsScope block_pools(this);
- RecordConstPool(pool_size);
-
- // Emit jump over constant pool if necessary.
- Label after_pool;
- if (require_jump) {
- b(&after_pool);
- }
-
- // Emit a constant pool header. The header has two goals:
- // 1) Encode the size of the constant pool, for use by the disassembler.
- // 2) Terminate the program, to try to prevent execution from accidentally
- // flowing into the constant pool.
- // The header is therefore made of two arm64 instructions:
- // ldr xzr, #<size of the constant pool in 32-bit words>
- // blr xzr
- // If executed the code will likely segfault and lr will point to the
- // beginning of the constant pool.
- // TODO(all): currently each relocated constant is 64 bits, consider adding
- // support for 32-bit entries.
- RecordComment("[ Constant Pool");
- ConstantPoolMarker(2 * num_pending_reloc_info_);
- ConstantPoolGuard();
-
- // Emit constant pool entries.
- for (int i = 0; i < num_pending_reloc_info_; i++) {
- RelocInfo& rinfo = pending_reloc_info_[i];
- ASSERT(rinfo.rmode() != RelocInfo::COMMENT &&
- rinfo.rmode() != RelocInfo::POSITION &&
- rinfo.rmode() != RelocInfo::STATEMENT_POSITION &&
- rinfo.rmode() != RelocInfo::CONST_POOL &&
- rinfo.rmode() != RelocInfo::VENEER_POOL);
-
- Instruction* instr = reinterpret_cast<Instruction*>(rinfo.pc());
- // Instruction to patch must be 'ldr rd, [pc, #offset]' with offset == 0.
- ASSERT(instr->IsLdrLiteral() &&
- instr->ImmLLiteral() == 0);
-
- instr->SetImmPCOffsetTarget(reinterpret_cast<Instruction*>(pc_));
- dc64(rinfo.data());
- }
-
- num_pending_reloc_info_ = 0;
- first_const_pool_use_ = -1;
-
- RecordComment("]");
-
- if (after_pool.is_linked()) {
- bind(&after_pool);
- }
- }
+ Label size_check;
+ bind(&size_check);
+ constpool_.Emit(require_jump);
+ ASSERT(SizeOfCodeGeneratedSince(&size_check) <=
+ static_cast<unsigned>(worst_case_size));
// Since a constant pool was just emitted, move the check offset forward by
// the standard interval.
- next_constant_pool_check_ = pc_offset() + kCheckConstPoolInterval;
-
- ASSERT(SizeOfCodeGeneratedSince(&size_check) ==
- static_cast<unsigned>(pool_size));
+ SetNextConstPoolCheckIn(kCheckConstPoolInterval);
}
« no previous file with comments | « src/arm64/assembler-arm64.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698