// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #include "vm/compiler/assembler/assembler_base.h" #include "platform/utils.h" #include "vm/compiler/assembler/object_pool_builder.h" #include "vm/compiler/backend/slot.h" #include "vm/cpu.h" #include "vm/flags.h" #include "vm/heap/heap.h" #include "vm/memory_region.h" #include "vm/os.h" #include "vm/zone.h" namespace dart { DEFINE_FLAG(bool, check_code_pointer, false, "Verify instructions offset in code object." "NOTE: This breaks the profiler."); #if defined(TARGET_ARCH_ARM) DEFINE_FLAG(bool, use_far_branches, false, "Enable far branches for ARM."); #endif namespace compiler { AssemblerBase::~AssemblerBase() {} void AssemblerBase::LoadFromSlot(Register dst, Register base, const Slot& slot, MemoryOrder memory_order) { if (!slot.is_tagged()) { // The result cannot be a floating point or SIMD value. ASSERT(slot.representation() == kUntagged || RepresentationUtils::IsUnboxedInteger(slot.representation())); // Since we only have a single destination register, the result value must // fit into a register. ASSERT(RepresentationUtils::ValueSize(slot.representation()) <= compiler::target::kWordSize); auto const sz = RepresentationUtils::OperandSize(slot.representation()); if (slot.has_untagged_instance()) { LoadFromOffset(dst, base, slot.offset_in_bytes(), sz); } else { LoadFieldFromOffset(dst, base, slot.offset_in_bytes(), sz); } } else if (slot.has_untagged_instance()) { // Non-Dart objects do not contain compressed pointers. ASSERT(!slot.is_compressed()); LoadFromOffset(dst, base, slot.offset_in_bytes()); } else if (!slot.is_guarded_field() && slot.type().ToCid() == kSmiCid) { if (slot.is_compressed()) { LoadCompressedSmiFieldFromOffset(dst, base, slot.offset_in_bytes()); } else { LoadSmiFieldFromOffset(dst, base, slot.offset_in_bytes()); } } else { if (slot.is_compressed()) { if (memory_order == kAcquire) { LoadAcquireCompressedFieldFromOffset(dst, base, slot.offset_in_bytes()); } else { LoadCompressedFieldFromOffset(dst, base, slot.offset_in_bytes()); } } else { if (memory_order == kAcquire) { LoadAcquire(dst, FieldAddress(base, slot.offset_in_bytes())); } else { LoadFieldFromOffset(dst, base, slot.offset_in_bytes()); } } } } void AssemblerBase::StoreToSlot(Register src, Register base, const Slot& slot, MemoryOrder memory_order, Register scratch) { auto const can_be_smi = slot.type().CanBeSmi() ? kValueCanBeSmi : kValueIsNotSmi; StoreToSlot(src, base, slot, can_be_smi, memory_order, scratch); } void AssemblerBase::StoreToSlot(Register src, Register base, const Slot& slot, CanBeSmi can_be_smi, MemoryOrder memory_order, Register scratch) { if (!slot.is_tagged() || slot.has_untagged_instance()) { // Same as the no barrier case. StoreToSlotNoBarrier(src, base, slot, memory_order); } else if (slot.is_compressed()) { StoreCompressedIntoObjectOffset(base, slot.offset_in_bytes(), src, can_be_smi, memory_order, scratch); } else { StoreIntoObjectOffset(base, slot.offset_in_bytes(), src, can_be_smi, memory_order, scratch); } } void AssemblerBase::StoreToSlotNoBarrier(Register src, Register base, const Slot& slot, MemoryOrder memory_order) { if (!slot.is_tagged()) { // The stored value cannot be a SIMD value. ASSERT(slot.representation() == kUntagged || RepresentationUtils::IsUnboxedFloat(slot.representation()) || RepresentationUtils::IsUnboxedInteger(slot.representation())); // Since we only have a single source register, the stored value must // fit into a register. ASSERT(RepresentationUtils::ValueSize(slot.representation()) <= compiler::target::kWordSize); auto const sz = RepresentationUtils::OperandSize(slot.representation()); if (slot.has_untagged_instance()) { StoreToOffset(src, base, slot.offset_in_bytes(), sz); } else { StoreFieldToOffset(src, base, slot.offset_in_bytes(), sz); } } else if (slot.has_untagged_instance()) { // Non-Dart objects do not contain compressed pointers. ASSERT(!slot.is_compressed()); StoreToOffset(src, base, slot.offset_in_bytes()); } else if (slot.is_compressed()) { StoreCompressedIntoObjectOffsetNoBarrier(base, slot.offset_in_bytes(), src, memory_order); } else { StoreIntoObjectOffsetNoBarrier(base, slot.offset_in_bytes(), src, memory_order); } } void AssemblerBase::LoadFromOffset(Register dst, Register base, int32_t offset, OperandSize sz) { Load(dst, Address(base, offset), sz); } void AssemblerBase::StoreToOffset(Register src, Register base, int32_t offset, OperandSize sz) { Store(src, Address(base, offset), sz); } void AssemblerBase::LoadField(Register dst, const FieldAddress& address, OperandSize sz) { Load(dst, address, sz); } void AssemblerBase::LoadFieldFromOffset(Register dst, Register base, int32_t offset, OperandSize sz) { Load(dst, FieldAddress(base, offset), sz); } void AssemblerBase::StoreFieldToOffset(Register src, Register base, int32_t offset, OperandSize sz) { Store(src, FieldAddress(base, offset), sz); } void AssemblerBase::LoadSmiField(Register dst, const FieldAddress& address) { LoadSmi(dst, address); } void AssemblerBase::LoadSmiFromOffset(Register dst, Register base, int32_t offset) { LoadSmi(dst, Address(base, offset)); } void AssemblerBase::LoadSmiFieldFromOffset(Register dst, Register base, int32_t offset) { LoadSmi(dst, FieldAddress(base, offset)); } void AssemblerBase::LoadAcquireCompressedFromOffset(Register dst, Register base, int32_t offset) { LoadAcquireCompressed(dst, Address(base, offset)); } void AssemblerBase::LoadAcquireCompressedFieldFromOffset(Register dst, Register base, int32_t offset) { LoadAcquireCompressed(dst, FieldAddress(base, offset)); } void AssemblerBase::LoadCompressedField(Register dst, const FieldAddress& address) { LoadCompressed(dst, address); } void AssemblerBase::LoadCompressedFromOffset(Register dst, Register base, int32_t offset) { LoadCompressed(dst, Address(base, offset)); } void AssemblerBase::LoadCompressedFieldFromOffset(Register dst, Register base, int32_t offset) { LoadCompressed(dst, FieldAddress(base, offset)); } void AssemblerBase::LoadCompressedSmiField(Register dst, const FieldAddress& address) { LoadCompressedSmi(dst, address); } void AssemblerBase::LoadCompressedSmiFromOffset(Register dst, Register base, int32_t offset) { LoadCompressedSmi(dst, Address(base, offset)); } void AssemblerBase::LoadCompressedSmiFieldFromOffset(Register dst, Register base, int32_t offset) { LoadCompressedSmi(dst, FieldAddress(base, offset)); } void AssemblerBase::LoadAcquireFromOffset(Register dst, Register base, int32_t offset, OperandSize size) { LoadAcquire(dst, Address(base, offset), size); } void AssemblerBase::StoreReleaseToOffset(Register src, Register base, int32_t offset, OperandSize size) { StoreRelease(src, Address(base, offset), size); } void AssemblerBase::StoreIntoObject(Register object, const Address& address, Register value, CanBeSmi can_be_smi, MemoryOrder memory_order, Register scratch, OperandSize size) { // A write barrier should never be applied when writing a reference to an // object into itself. ASSERT(object != value); ASSERT(object != scratch); ASSERT(value != scratch); if (memory_order == kRelease) { StoreRelease(value, address, size); } else { Store(value, address, size); } StoreBarrier(object, value, can_be_smi, scratch); } void AssemblerBase::StoreIntoObjectNoBarrier(Register object, const Address& address, Register value, MemoryOrder memory_order, OperandSize size) { if (memory_order == kRelease) { StoreRelease(value, address, size); } else { Store(value, address, size); } DEBUG_ONLY(VerifyStoreNeedsNoWriteBarrier(object, value)); } void AssemblerBase::StoreIntoObjectOffset(Register object, int32_t offset, Register value, CanBeSmi can_be_smi, MemoryOrder memory_order, Register scratch, OperandSize size) { StoreIntoObject(object, FieldAddress(object, offset), value, can_be_smi, memory_order, scratch, size); } void AssemblerBase::StoreIntoObjectOffsetNoBarrier(Register object, int32_t offset, Register value, MemoryOrder memory_order, OperandSize size) { StoreIntoObjectNoBarrier(object, FieldAddress(object, offset), value, memory_order, size); } void AssemblerBase::StoreObjectIntoObjectOffsetNoBarrier( Register object, int32_t offset, const Object& value, MemoryOrder memory_order, OperandSize size) { StoreObjectIntoObjectNoBarrier(object, FieldAddress(object, offset), value, memory_order, size); } void AssemblerBase::StoreIntoArray(Register object, Register slot, Register value, CanBeSmi can_be_smi, Register scratch, OperandSize size) { ASSERT(object != scratch); ASSERT(value != object); ASSERT(value != scratch); ASSERT(slot != object); ASSERT(slot != value); ASSERT(slot != scratch); Store(value, Address(slot, 0), size); ArrayStoreBarrier(object, slot, value, can_be_smi, scratch); } void AssemblerBase::UnrolledMemCopy(Register dst_base, intptr_t dst_offset, Register src_base, intptr_t src_offset, intptr_t size, Register temp) { intptr_t offset = 0; if (target::kWordSize >= 8) { while (offset + 8 <= size) { LoadFromOffset(temp, src_base, src_offset + offset, kEightBytes); StoreToOffset(temp, dst_base, dst_offset + offset, kEightBytes); offset += 8; } } while (offset + 4 <= size) { LoadFromOffset(temp, src_base, src_offset + offset, kUnsignedFourBytes); StoreToOffset(temp, dst_base, dst_offset + offset, kUnsignedFourBytes); offset += 4; } while (offset + 2 <= size) { LoadFromOffset(temp, src_base, src_offset + offset, kUnsignedTwoBytes); StoreToOffset(temp, dst_base, dst_offset + offset, kUnsignedTwoBytes); offset += 2; } while (offset + 1 <= size) { LoadFromOffset(temp, src_base, src_offset + offset, kUnsignedByte); StoreToOffset(temp, dst_base, dst_offset + offset, kUnsignedByte); offset += 1; } ASSERT(offset == size); } void AssemblerBase::LoadTypeClassId(Register dst, Register src) { if (dst != src) { EnsureHasClassIdInDEBUG(kTypeCid, src, dst); } else { #if !defined(TARGET_ARCH_IA32) EnsureHasClassIdInDEBUG(kTypeCid, src, TMP); #else // Skip check on IA32 since we don't have TMP. #endif } LoadFromSlot(dst, src, Slot::AbstractType_flags()); LsrImmediate(dst, compiler::target::UntaggedType::kTypeClassIdShift); } void AssemblerBase::LoadAbstractTypeNullability(Register dst, Register type) { LoadFromSlot(dst, type, Slot::AbstractType_flags()); AndImmediate(dst, compiler::target::UntaggedAbstractType::kNullabilityMask); } void AssemblerBase::CompareAbstractTypeNullabilityWith(Register type, int8_t value, Register scratch) { LoadAbstractTypeNullability(scratch, type); CompareImmediate(scratch, value); } intptr_t AssemblerBase::InsertAlignedRelocation(BSS::Relocation reloc) { // We cannot put a relocation at the very start (it's not a valid // instruction)! ASSERT(CodeSize() != 0); // Align to a target word boundary. const intptr_t offset = Utils::RoundUp(CodeSize(), compiler::target::kWordSize); while (CodeSize() < offset) { Breakpoint(); } ASSERT(CodeSize() == offset); AssemblerBuffer::EnsureCapacity ensured(&buffer_); buffer_.Emit(BSS::RelocationIndex(reloc) * compiler::target::kWordSize); ASSERT(CodeSize() == (offset + compiler::target::kWordSize)); return offset; } void AssemblerBase::MsanUnpoison(Register base, intptr_t length_in_bytes) { Comment("MsanUnpoison base %s length_in_bytes %" Pd, RegisterNames::RegisterName(base), length_in_bytes); LeafRuntimeScope rt(static_cast(this), /*frame_size=*/0, /*preserve_registers=*/true); MoveRegister(CallingConventions::ArgumentRegisters[0], base); LoadImmediate(CallingConventions::ArgumentRegisters[1], length_in_bytes); rt.Call(kMsanUnpoisonRuntimeEntry, /*argument_count=*/2); } void AssemblerBase::MsanUnpoison(Register base, Register length_in_bytes) { Comment("MsanUnpoison base %s length_in_bytes %s", RegisterNames::RegisterName(base), RegisterNames::RegisterName(length_in_bytes)); LeafRuntimeScope rt(static_cast(this), /*frame_size=*/0, /*preserve_registers=*/true); const Register a0 = CallingConventions::ArgumentRegisters[0]; const Register a1 = CallingConventions::ArgumentRegisters[1]; if (length_in_bytes == a0) { if (base == a1) { MoveRegister(TMP, length_in_bytes); MoveRegister(a0, base); MoveRegister(a1, TMP); } else { MoveRegister(a1, length_in_bytes); MoveRegister(a0, base); } } else { MoveRegister(a0, base); MoveRegister(a1, length_in_bytes); } rt.Call(kMsanUnpoisonRuntimeEntry, /*argument_count=*/2); } #if defined(DEBUG) static void InitializeMemoryWithBreakpoints(uword data, intptr_t length) { #if defined(TARGET_ARCH_ARM) || defined(TARGET_ARCH_ARM64) ASSERT(Utils::IsAligned(data, 4)); ASSERT(Utils::IsAligned(length, 4)); const uword end = data + length; while (data < end) { *reinterpret_cast(data) = Instr::kBreakPointInstruction; data += 4; } #else memset(reinterpret_cast(data), Instr::kBreakPointInstruction, length); #endif } #endif static uword NewContents(intptr_t capacity) { Zone* zone = Thread::Current()->zone(); uword result = zone->AllocUnsafe(capacity); #if defined(DEBUG) // Initialize the buffer with kBreakPointInstruction to force a break // point if we ever execute an uninitialized part of the code buffer. InitializeMemoryWithBreakpoints(result, capacity); #endif return result; } #if defined(DEBUG) AssemblerBuffer::EnsureCapacity::EnsureCapacity(AssemblerBuffer* buffer) { if (buffer->cursor() >= buffer->limit()) buffer->ExtendCapacity(); // In debug mode, we save the assembler buffer along with the gap // size before we start emitting to the buffer. This allows us to // check that any single generated instruction doesn't overflow the // limit implied by the minimum gap size. buffer_ = buffer; gap_ = ComputeGap(); // Make sure that extending the capacity leaves a big enough gap // for any kind of instruction. ASSERT(gap_ >= kMinimumGap); // Mark the buffer as having ensured the capacity. ASSERT(!buffer->HasEnsuredCapacity()); // Cannot nest. buffer->has_ensured_capacity_ = true; } AssemblerBuffer::EnsureCapacity::~EnsureCapacity() { // Unmark the buffer, so we cannot emit after this. buffer_->has_ensured_capacity_ = false; // Make sure the generated instruction doesn't take up more // space than the minimum gap. intptr_t delta = gap_ - ComputeGap(); ASSERT(delta <= kMinimumGap); } #endif AssemblerBuffer::AssemblerBuffer() : pointer_offsets_(new ZoneGrowableArray(16)) { const intptr_t kInitialBufferCapacity = 4 * KB; contents_ = NewContents(kInitialBufferCapacity); cursor_ = contents_; limit_ = ComputeLimit(contents_, kInitialBufferCapacity); fixup_ = nullptr; #if defined(DEBUG) has_ensured_capacity_ = false; fixups_processed_ = false; #endif // Verify internal state. ASSERT(Capacity() == kInitialBufferCapacity); ASSERT(Size() == 0); } AssemblerBuffer::~AssemblerBuffer() {} void AssemblerBuffer::ProcessFixups(const MemoryRegion& region) { AssemblerFixup* fixup = fixup_; while (fixup != nullptr) { fixup->Process(region, fixup->position()); fixup = fixup->previous(); } } void AssemblerBuffer::FinalizeInstructions(const MemoryRegion& instructions) { // Copy the instructions from the buffer. MemoryRegion from(reinterpret_cast(contents()), Size()); instructions.CopyFrom(0, from); // Process fixups in the instructions. ProcessFixups(instructions); #if defined(DEBUG) fixups_processed_ = true; #endif } void AssemblerBuffer::ExtendCapacity() { intptr_t old_size = Size(); intptr_t old_capacity = Capacity(); intptr_t new_capacity = Utils::Minimum(old_capacity * 2, old_capacity + 1 * MB); if (new_capacity < old_capacity) { FATAL("Unexpected overflow in AssemblerBuffer::ExtendCapacity"); } // Allocate the new data area and copy contents of the old one to it. uword new_contents = NewContents(new_capacity); memmove(reinterpret_cast(new_contents), reinterpret_cast(contents_), old_size); // Compute the relocation delta and switch to the new contents area. intptr_t delta = new_contents - contents_; contents_ = new_contents; // Update the cursor and recompute the limit. cursor_ += delta; limit_ = ComputeLimit(new_contents, new_capacity); // Verify internal state. ASSERT(Capacity() == new_capacity); ASSERT(Size() == old_size); } class PatchCodeWithHandle : public AssemblerFixup { public: PatchCodeWithHandle(ZoneGrowableArray* pointer_offsets, const Object& object) : pointer_offsets_(pointer_offsets), object_(object) {} void Process(const MemoryRegion& region, intptr_t position) { // Patch the handle into the code. Once the instructions are installed into // a raw code object and the pointer offsets are setup, the handle is // resolved. region.StoreUnaligned(position, &object_); pointer_offsets_->Add(position); } virtual bool IsPointerOffset() const { return true; } private: ZoneGrowableArray* pointer_offsets_; const Object& object_; }; intptr_t AssemblerBuffer::CountPointerOffsets() const { intptr_t count = 0; AssemblerFixup* current = fixup_; while (current != nullptr) { if (current->IsPointerOffset()) ++count; current = current->previous_; } return count; } #if defined(TARGET_ARCH_IA32) void AssemblerBuffer::EmitObject(const Object& object) { // Since we are going to store the handle as part of the fixup information // the handle needs to be a zone handle. DEBUG_ASSERT(IsNotTemporaryScopedHandle(object)); ASSERT(IsInOldSpace(object)); EmitFixup(new PatchCodeWithHandle(pointer_offsets_, object)); cursor_ += target::kWordSize; // Reserve space for pointer. } #endif // Shared macros are implemented here. void AssemblerBase::Unimplemented(const char* message) { const char* format = "Unimplemented: %s"; const intptr_t len = Utils::SNPrint(nullptr, 0, format, message); char* buffer = reinterpret_cast(malloc(len + 1)); Utils::SNPrint(buffer, len + 1, format, message); Stop(buffer); } void AssemblerBase::Untested(const char* message) { const char* format = "Untested: %s"; const intptr_t len = Utils::SNPrint(nullptr, 0, format, message); char* buffer = reinterpret_cast(malloc(len + 1)); Utils::SNPrint(buffer, len + 1, format, message); Stop(buffer); } void AssemblerBase::Unreachable(const char* message) { const char* format = "Unreachable: %s"; const intptr_t len = Utils::SNPrint(nullptr, 0, format, message); char* buffer = reinterpret_cast(malloc(len + 1)); Utils::SNPrint(buffer, len + 1, format, message); Stop(buffer); } void AssemblerBase::Comment(const char* format, ...) { if (EmittingComments()) { char buffer[1024]; va_list args; va_start(args, format); Utils::VSNPrint(buffer, sizeof(buffer), format, args); va_end(args); comments_.Add( new CodeComment(buffer_.GetPosition(), AllocateString(buffer))); } } bool AssemblerBase::EmittingComments() { return FLAG_code_comments || FLAG_disassemble || FLAG_disassemble_optimized || FLAG_disassemble_stubs; } void AssemblerBase::Stop(const char* message) { Comment("Stop: %s", message); Breakpoint(); } uword ObjIndexPair::Hash(Key key) { switch (key.type()) { case ObjectPoolBuilderEntry::kImmediate128: return HashBytes(&key.imm128_, sizeof(key.imm128_)); #if defined(TARGET_ARCH_IS_32_BIT) case ObjectPoolBuilderEntry::kImmediate64: return HashBytes(&key.imm64_, sizeof(key.imm64_)); #endif case ObjectPoolBuilderEntry::kImmediate: case ObjectPoolBuilderEntry::kNativeFunction: return HashBytes(&key.imm_, sizeof(key.imm_)); case ObjectPoolBuilderEntry::kTaggedObject: return ObjectHash(*key.obj_); } UNREACHABLE(); } void ObjectPoolBuilder::Reset() { // Null out the handles we've accumulated. for (intptr_t i = 0; i < object_pool_.length(); ++i) { if (object_pool_[i].type() == ObjectPoolBuilderEntry::kTaggedObject) { SetToNull(const_cast(object_pool_[i].obj_)); SetToNull(const_cast(object_pool_[i].equivalence_)); } } object_pool_.Clear(); object_pool_index_table_.Clear(); } intptr_t ObjectPoolBuilder::AddObject( const Object& obj, ObjectPoolBuilderEntry::Patchability patchable, ObjectPoolBuilderEntry::SnapshotBehavior snapshot_behavior) { DEBUG_ASSERT(IsNotTemporaryScopedHandle(obj)); return AddObject(ObjectPoolBuilderEntry(&obj, patchable, snapshot_behavior)); } intptr_t ObjectPoolBuilder::AddImmediate( uword imm, ObjectPoolBuilderEntry::Patchability patchable, ObjectPoolBuilderEntry::SnapshotBehavior snapshotability) { return AddObject(ObjectPoolBuilderEntry( imm, ObjectPoolBuilderEntry::kImmediate, patchable, snapshotability)); } intptr_t ObjectPoolBuilder::AddImmediate64(uint64_t imm) { #if defined(TARGET_ARCH_IS_32_BIT) return AddObject( ObjectPoolBuilderEntry(imm, ObjectPoolBuilderEntry::kImmediate64, ObjectPoolBuilderEntry::kNotPatchable)); #else return AddImmediate(imm); #endif } intptr_t ObjectPoolBuilder::AddImmediate128(simd128_value_t imm) { return AddObject( ObjectPoolBuilderEntry(imm, ObjectPoolBuilderEntry::kImmediate128, ObjectPoolBuilderEntry::kNotPatchable)); } intptr_t ObjectPoolBuilder::AddObject(ObjectPoolBuilderEntry entry) { DEBUG_ASSERT((entry.type() != ObjectPoolBuilderEntry::kTaggedObject) || (IsNotTemporaryScopedHandle(*entry.obj_) && (entry.equivalence_ == nullptr || IsNotTemporaryScopedHandle(*entry.equivalence_)))); if (entry.type() == ObjectPoolBuilderEntry::kTaggedObject) { // If the owner of the object pool wrapper specified a specific zone we // should use we'll do so. if (zone_ != nullptr) { entry.obj_ = &NewZoneHandle(zone_, *entry.obj_); if (entry.equivalence_ != nullptr) { entry.equivalence_ = &NewZoneHandle(zone_, *entry.equivalence_); } } } #if defined(TARGET_ARCH_IS_32_BIT) if (entry.type() == ObjectPoolBuilderEntry::kImmediate64) { ASSERT(entry.patchable() == ObjectPoolBuilderEntry::kNotPatchable); uint64_t imm = entry.imm64_; intptr_t idx = AddImmediate(Utils::Low32Bits(imm)); AddImmediate(Utils::High32Bits(imm)); object_pool_index_table_.Insert(ObjIndexPair(entry, idx)); return idx; } if (entry.type() == ObjectPoolBuilderEntry::kImmediate128) { ASSERT(entry.patchable() == ObjectPoolBuilderEntry::kNotPatchable); intptr_t idx = AddImmediate(entry.imm128_.int_storage[0]); AddImmediate(entry.imm128_.int_storage[1]); AddImmediate(entry.imm128_.int_storage[2]); AddImmediate(entry.imm128_.int_storage[3]); object_pool_index_table_.Insert(ObjIndexPair(entry, idx)); return idx; } #else if (entry.type() == ObjectPoolBuilderEntry::kImmediate128) { ASSERT(entry.patchable() == ObjectPoolBuilderEntry::kNotPatchable); uword lo64 = (static_cast(entry.imm128_.int_storage[0]) & 0xffffffff) | (static_cast(entry.imm128_.int_storage[1]) << 32); uword hi64 = (static_cast(entry.imm128_.int_storage[2]) & 0xffffffff) | (static_cast(entry.imm128_.int_storage[3]) << 32); intptr_t idx = AddImmediate(lo64); AddImmediate(hi64); object_pool_index_table_.Insert(ObjIndexPair(entry, idx)); return idx; } #endif const intptr_t idx = base_index_ + object_pool_.length(); object_pool_.Add(entry); if (entry.patchable() == ObjectPoolBuilderEntry::kNotPatchable) { // The object isn't patchable. Record the index for fast lookup. object_pool_index_table_.Insert(ObjIndexPair(entry, idx)); } return idx; } intptr_t ObjectPoolBuilder::FindObject(ObjectPoolBuilderEntry entry) { // If the object is not patchable, check if we've already got it in the // object pool. if (entry.patchable() == ObjectPoolBuilderEntry::kNotPatchable) { // First check in the parent pool if we have one. if (parent_ != nullptr) { const intptr_t idx = parent_->object_pool_index_table_.LookupValue(entry); if (idx != ObjIndexPair::kNoIndex) { used_from_parent_.Add(idx); return idx; } } const intptr_t idx = object_pool_index_table_.LookupValue(entry); if (idx != ObjIndexPair::kNoIndex) { return idx; } } return AddObject(entry); } intptr_t ObjectPoolBuilder::FindObject( const Object& obj, ObjectPoolBuilderEntry::Patchability patchable, ObjectPoolBuilderEntry::SnapshotBehavior snapshot_behavior) { return FindObject(ObjectPoolBuilderEntry(&obj, patchable, snapshot_behavior)); } intptr_t ObjectPoolBuilder::FindObject(const Object& obj, const Object& equivalence) { return FindObject(ObjectPoolBuilderEntry( &obj, &equivalence, ObjectPoolBuilderEntry::kNotPatchable)); } intptr_t ObjectPoolBuilder::FindImmediate(uword imm) { return FindObject( ObjectPoolBuilderEntry(imm, ObjectPoolBuilderEntry::kImmediate, ObjectPoolBuilderEntry::kNotPatchable)); } intptr_t ObjectPoolBuilder::FindImmediate64(uint64_t imm) { #if defined(TARGET_ARCH_IS_32_BIT) return FindObject( ObjectPoolBuilderEntry(imm, ObjectPoolBuilderEntry::kImmediate64, ObjectPoolBuilderEntry::kNotPatchable)); #else return FindImmediate(imm); #endif } intptr_t ObjectPoolBuilder::FindImmediate128(simd128_value_t imm) { return FindObject( ObjectPoolBuilderEntry(imm, ObjectPoolBuilderEntry::kImmediate128, ObjectPoolBuilderEntry::kNotPatchable)); } intptr_t ObjectPoolBuilder::FindNativeFunction( const ExternalLabel* label, ObjectPoolBuilderEntry::Patchability patchable) { return FindObject(ObjectPoolBuilderEntry( label->address(), ObjectPoolBuilderEntry::kNativeFunction, patchable)); } bool ObjectPoolBuilder::TryCommitToParent() { ASSERT(parent_ != nullptr); if (parent_->CurrentLength() != base_index_) { return false; } for (intptr_t i = 0; i < object_pool_.length(); i++) { intptr_t idx = parent_->AddObject(object_pool_[i]); ASSERT(idx == (base_index_ + i)); } return true; } } // namespace compiler } // namespace dart