ARM64: updated literal pool implementation.

Currently the literal pool implemetation is inherited from the arm 32-bit port
and it shares the same limitations: 4k of range and 1000 entries max. In arm64
the load literal has a 1MB range giving us more flexibility.

Immutable entries are now shared.

BUG=
R=rmcilroy@chromium.org

Review URL: https://codereview.chromium.org/338523005

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@21924 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
rodolph.perfetta@arm.com 2014-06-23 09:30:45 +00:00
parent 16d5587c6d
commit ec22430733
2 changed files with 327 additions and 158 deletions

View File

@ -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);
}

View File

@ -7,6 +7,7 @@
#include <list>
#include <map>
#include <vector>
#include "src/arm64/instructions-arm64.h"
#include "src/assembler.h"
@ -740,6 +741,55 @@ class MemOperand {
};
class ConstPool {
public:
explicit ConstPool(Assembler* assm)
: assm_(assm),
first_use_(-1),
shared_entries_count(0) {}
void RecordEntry(intptr_t data, RelocInfo::Mode mode);
int EntryCount() const {
return shared_entries_count + unique_entries_.size();
}
bool IsEmpty() const {
return shared_entries_.empty() && unique_entries_.empty();
}
// Distance in bytes between the current pc and the first instruction
// using the pool. If there are no pending entries return kMaxInt.
int DistanceToFirstUse();
// Offset after which instructions using the pool will be out of range.
int MaxPcOffset();
// Maximum size the constant pool can be with current entries. It always
// includes alignment padding and branch over.
int WorstCaseSize();
// Size in bytes of the literal pool *if* it is emitted at the current
// pc. The size will include the branch over the pool if it was requested.
int SizeIfEmittedAtCurrentPc(bool require_jump);
// Emit the literal pool at the current pc with a branch over the pool if
// requested.
void Emit(bool require_jump);
// Discard any pending pool entries.
void Clear();
private:
bool CanBeShared(RelocInfo::Mode mode);
void EmitMarker();
void EmitGuard();
void EmitEntries();
Assembler* assm_;
// Keep track of the first instruction requiring a constant pool entry
// since the previous constant pool was emitted.
int first_use_;
// values, pc offset(s) of entries which can be shared.
std::multimap<uint64_t, int> shared_entries_;
// Number of distinct literal in shared entries.
int shared_entries_count;
// values, pc offset of entries which cannot be shared.
std::vector<std::pair<uint64_t, int> > unique_entries_;
};
// -----------------------------------------------------------------------------
// Assembler.
@ -763,7 +813,7 @@ class Assembler : public AssemblerBase {
virtual ~Assembler();
virtual void AbortedCodeGeneration() {
num_pending_reloc_info_ = 0;
constpool_.Clear();
}
// System functions ---------------------------------------------------------
@ -912,9 +962,7 @@ class Assembler : public AssemblerBase {
static bool IsConstantPoolAt(Instruction* instr);
static int ConstantPoolSizeAt(Instruction* instr);
// See Assembler::CheckConstPool for more info.
void ConstantPoolMarker(uint32_t size);
void EmitPoolGuard();
void ConstantPoolGuard();
// Prevent veneer pool emission until EndBlockVeneerPool is called.
// Call to this function can be nested but must be followed by an equal
@ -1695,7 +1743,9 @@ class Assembler : public AssemblerBase {
// Code generation helpers --------------------------------------------------
unsigned num_pending_reloc_info() const { return num_pending_reloc_info_; }
bool IsConstPoolEmpty() const { return constpool_.IsEmpty(); }
Instruction* pc() const { return Instruction::Cast(pc_); }
Instruction* InstructionAt(int offset) const {
return reinterpret_cast<Instruction*>(buffer_ + offset);
@ -2019,6 +2069,11 @@ class Assembler : public AssemblerBase {
// instructions.
void BlockConstPoolFor(int instructions);
// Set how far from current pc the next constant pool check will be.
void SetNextConstPoolCheckIn(int instructions) {
next_constant_pool_check_ = pc_offset() + instructions * kInstructionSize;
}
// Emit the instruction at pc_.
void Emit(Instr instruction) {
STATIC_ASSERT(sizeof(*pc_) == 1);
@ -2050,12 +2105,13 @@ class Assembler : public AssemblerBase {
int next_constant_pool_check_;
// Constant pool generation
// Pools are emitted in the instruction stream, preferably after unconditional
// jumps or after returns from functions (in dead code locations).
// If a long code sequence does not contain unconditional jumps, it is
// necessary to emit the constant pool before the pool gets too far from the
// location it is accessed from. In this case, we emit a jump over the emitted
// constant pool.
// Pools are emitted in the instruction stream. They are emitted when:
// * the distance to the first use is above a pre-defined distance or
// * the numbers of entries in the pool is above a pre-defined size or
// * code generation is finished
// If a pool needs to be emitted before code generation is finished a branch
// over the emitted pool will be inserted.
// Constants in the pool may be addresses of functions that gets relocated;
// if so, a relocation info entry is associated to the constant pool entry.
@ -2063,34 +2119,22 @@ class Assembler : public AssemblerBase {
// expensive. By default we only check again once a number of instructions
// has been generated. That also means that the sizing of the buffers is not
// an exact science, and that we rely on some slop to not overrun buffers.
static const int kCheckConstPoolIntervalInst = 128;
static const int kCheckConstPoolInterval =
kCheckConstPoolIntervalInst * kInstructionSize;
static const int kCheckConstPoolInterval = 128;
// Constants in pools are accessed via pc relative addressing, which can
// reach +/-4KB thereby defining a maximum distance between the instruction
// and the accessed constant.
static const int kMaxDistToConstPool = 4 * KB;
static const int kMaxNumPendingRelocInfo =
kMaxDistToConstPool / kInstructionSize;
// Distance to first use after a which a pool will be emitted. Pool entries
// are accessed with pc relative load therefore this cannot be more than
// 1 * MB. Since constant pool emission checks are interval based this value
// is an approximation.
static const int kApproxMaxDistToConstPool = 64 * KB;
// Average distance beetween a constant pool and the first instruction
// accessing the constant pool. Longer distance should result in less I-cache
// pollution.
// In practice the distance will be smaller since constant pool emission is
// forced after function return and sometimes after unconditional branches.
static const int kAvgDistToConstPool =
kMaxDistToConstPool - kCheckConstPoolInterval;
// Number of pool entries after which a pool will be emitted. Since constant
// pool emission checks are interval based this value is an approximation.
static const int kApproxMaxPoolEntryCount = 512;
// Emission of the constant pool may be blocked in some code sequences.
int const_pool_blocked_nesting_; // Block emission if this is not zero.
int no_const_pool_before_; // Block emission before this pc offset.
// Keep track of the first instruction requiring a constant pool entry
// since the previous constant pool was emitted.
int first_const_pool_use_;
// Emission of the veneer pools may be blocked in some code sequences.
int veneer_pool_blocked_nesting_; // Block emission if this is not zero.
@ -2106,10 +2150,8 @@ class Assembler : public AssemblerBase {
// If every instruction in a long sequence is accessing the pool, we need one
// pending relocation entry per instruction.
// the buffer of pending relocation info
RelocInfo pending_reloc_info_[kMaxNumPendingRelocInfo];
// number of pending reloc info entries in the buffer
int num_pending_reloc_info_;
// The pending constant pool.
ConstPool constpool_;
// Relocation for a type-recording IC has the AST id added to it. This
// member variable is a way to pass the information from the call site to
@ -2196,6 +2238,7 @@ class Assembler : public AssemblerBase {
PositionsRecorder positions_recorder_;
friend class PositionsRecorder;
friend class EnsureSpace;
friend class ConstPool;
};
class PatchingAssembler : public Assembler {
@ -2228,7 +2271,7 @@ class PatchingAssembler : public Assembler {
// Verify we have generated the number of instruction we expected.
ASSERT((pc_offset() + kGap) == buffer_size_);
// Verify no relocation information has been emitted.
ASSERT(num_pending_reloc_info() == 0);
ASSERT(IsConstPoolEmpty());
// Flush the Instruction cache.
size_t length = buffer_size_ - kGap;
CPU::FlushICache(buffer_, length);