| // 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. | |
| #ifndef RUNTIME_VM_COMPILER_ASSEMBLER_OBJECT_POOL_BUILDER_H_ | |
| #define RUNTIME_VM_COMPILER_ASSEMBLER_OBJECT_POOL_BUILDER_H_ | |
| #include "platform/assert.h" | |
| #include "platform/globals.h" | |
| #include "vm/bitfield.h" | |
| #include "vm/hash_map.h" | |
| namespace dart { | |
| class Object; | |
| namespace compiler { | |
| class ExternalLabel; | |
| bool IsSameObject(const Object& a, const Object& b); | |
| struct ObjectPoolBuilderEntry { | |
| enum Patchability { | |
| kPatchable = 0, | |
| kNotPatchable, | |
| }; | |
| enum SnapshotBehavior { | |
| kSnapshotable, | |
| // This should never be snapshot. Typically an memory address in the current | |
| // process. | |
| kNotSnapshotable, | |
| // Set the value to StubCode::CallBootstrapNative() on snapshot reading. | |
| kResetToBootstrapNative, | |
| // Only used in AOT. Every switchable call site will put (`ic_data`, | |
| // [kTaggedObject] `code`) into the object pool. The `code` is initialized | |
| // (at AOT compile-time) to be [StubCode::SwitchableCallMiss]. | |
| // | |
| // The extra indirection via the `code` object is removed by storing | |
| // (`ic_data`, [kImmediate] `entrypoint`) in the object pool instead on | |
| // deserialization. | |
| kResetToSwitchableCallMissEntryPoint, | |
| // Set the value to 0 on snapshot writing. | |
| kSetToZero, | |
| }; | |
| enum EntryType { | |
| kImmediate = 0, | |
| kTaggedObject, | |
| kNativeFunction, | |
| // Used only during object pool building to find duplicates. Become multiple | |
| // kImmediate in the final pool. | |
| #if defined(TARGET_ARCH_IS_32_BIT) | |
| kImmediate64, | |
| #endif | |
| kImmediate128, | |
| }; | |
| using TypeBits = BitField<uint8_t, EntryType, 0, 4>; | |
| using PatchableBit = BitField<uint8_t, Patchability, TypeBits::kNextBit, 1>; | |
| using SnapshotBehaviorBits = | |
| BitField<uint8_t, SnapshotBehavior, PatchableBit::kNextBit, 3>; | |
| static inline uint8_t EncodeTraits( | |
| EntryType type, | |
| Patchability patchable, | |
| SnapshotBehavior snapshot_behavior = SnapshotBehavior::kSnapshotable) { | |
| return TypeBits::encode(type) | PatchableBit::encode(patchable) | | |
| PatchableBit::encode(patchable) | | |
| SnapshotBehaviorBits::encode(snapshot_behavior); | |
| } | |
| ObjectPoolBuilderEntry() : imm128_(), entry_bits_(0), equivalence_() {} | |
| ObjectPoolBuilderEntry(const Object* obj, | |
| Patchability patchable, | |
| SnapshotBehavior snapshot_behavior = kSnapshotable) | |
| : ObjectPoolBuilderEntry(obj, obj, patchable, snapshot_behavior) {} | |
| ObjectPoolBuilderEntry(const Object* obj, | |
| const Object* eqv, | |
| Patchability patchable, | |
| SnapshotBehavior snapshot_behavior = kSnapshotable) | |
| : obj_(obj), | |
| entry_bits_(EncodeTraits(kTaggedObject, patchable, snapshot_behavior)), | |
| equivalence_(eqv) {} | |
| ObjectPoolBuilderEntry( | |
| uword value, | |
| EntryType info, | |
| Patchability patchable, | |
| SnapshotBehavior snapshot_behavior = SnapshotBehavior::kSnapshotable) | |
| : imm_(value), | |
| entry_bits_(EncodeTraits(info, patchable, snapshot_behavior)), | |
| equivalence_() {} | |
| #if defined(ARCH_IS_32_BIT) | |
| ObjectPoolBuilderEntry(uint64_t value, EntryType info, Patchability patchable) | |
| : imm64_(value), | |
| entry_bits_(EncodeTraits(info, patchable)), | |
| equivalence_() {} | |
| #endif | |
| ObjectPoolBuilderEntry(simd128_value_t value, | |
| EntryType info, | |
| Patchability patchable) | |
| : imm128_(value), | |
| entry_bits_(EncodeTraits(info, patchable)), | |
| equivalence_() {} | |
| EntryType type() const { return TypeBits::decode(entry_bits_); } | |
| Patchability patchable() const { return PatchableBit::decode(entry_bits_); } | |
| SnapshotBehavior snapshot_behavior() const { | |
| return SnapshotBehaviorBits::decode(entry_bits_); | |
| } | |
| union { | |
| const Object* obj_; | |
| uword imm_; | |
| uint64_t imm64_; | |
| simd128_value_t imm128_; | |
| }; | |
| uint8_t entry_bits_; | |
| const Object* equivalence_; | |
| }; | |
| // Pair type parameter for DirectChainedHashMap used for the constant pool. | |
| class ObjIndexPair { | |
| public: | |
| // Typedefs needed for the DirectChainedHashMap template. | |
| typedef ObjectPoolBuilderEntry Key; | |
| typedef intptr_t Value; | |
| typedef ObjIndexPair Pair; | |
| static constexpr intptr_t kNoIndex = -1; | |
| ObjIndexPair() | |
| : key_(reinterpret_cast<uword>(nullptr), | |
| ObjectPoolBuilderEntry::kTaggedObject, | |
| ObjectPoolBuilderEntry::kPatchable), | |
| value_(kNoIndex) {} | |
| ObjIndexPair(Key key, Value value) : value_(value) { | |
| key_.entry_bits_ = key.entry_bits_; | |
| if (key.type() == ObjectPoolBuilderEntry::kTaggedObject) { | |
| key_.obj_ = key.obj_; | |
| key_.equivalence_ = key.equivalence_; | |
| } else if (key.type() == ObjectPoolBuilderEntry::kImmediate128) { | |
| key_.imm128_ = key.imm128_; | |
| #if defined(TARGET_ARCH_IS_32_BIT) | |
| } else if (key.type() == ObjectPoolBuilderEntry::kImmediate64) { | |
| key_.imm64_ = key.imm64_; | |
| #endif | |
| } else { | |
| key_.imm_ = key.imm_; | |
| } | |
| } | |
| static Key KeyOf(Pair kv) { return kv.key_; } | |
| static Value ValueOf(Pair kv) { return kv.value_; } | |
| static uword Hash(Key key); | |
| static inline bool IsKeyEqual(Pair kv, Key key) { | |
| if (kv.key_.entry_bits_ != key.entry_bits_) return false; | |
| if (kv.key_.type() == ObjectPoolBuilderEntry::kTaggedObject) { | |
| return IsSameObject(*kv.key_.obj_, *key.obj_) && | |
| IsSameObject(*kv.key_.equivalence_, *key.equivalence_); | |
| } | |
| if (kv.key_.type() == ObjectPoolBuilderEntry::kImmediate128) { | |
| return (kv.key_.imm128_.int_storage[0] == key.imm128_.int_storage[0]) && | |
| (kv.key_.imm128_.int_storage[1] == key.imm128_.int_storage[1]) && | |
| (kv.key_.imm128_.int_storage[2] == key.imm128_.int_storage[2]) && | |
| (kv.key_.imm128_.int_storage[3] == key.imm128_.int_storage[3]); | |
| } | |
| #if defined(TARGET_ARCH_IS_32_BIT) | |
| if (kv.key_.type() == ObjectPoolBuilderEntry::kImmediate64) { | |
| return kv.key_.imm64_ == key.imm64_; | |
| } | |
| #endif | |
| return kv.key_.imm_ == key.imm_; | |
| } | |
| private: | |
| Key key_; | |
| Value value_; | |
| }; | |
| class ObjectPoolBuilder : public ValueObject { | |
| public: | |
| // When generating AOT code in the bare instructions mode we might use a two | |
| // stage process of forming the pool - first accumulate objects in the | |
| // intermediary pool and then commit them into the global pool at the | |
| // end of a successful compilation. Here [parent] is the pool into which | |
| // we are going to commit objects. | |
| // See PrecompileParsedFunctionHelper::Compile for more information. | |
| explicit ObjectPoolBuilder(ObjectPoolBuilder* parent = nullptr) | |
| : parent_(parent), | |
| base_index_(parent != nullptr ? parent->CurrentLength() : 0), | |
| zone_(nullptr) {} | |
| ~ObjectPoolBuilder() { | |
| if (zone_ != nullptr) { | |
| Reset(); | |
| zone_ = nullptr; | |
| } | |
| } | |
| // Clears all existing entries in this object pool builder. | |
| // | |
| // Note: Any code which has been compiled via this builder might use offsets | |
| // into the pool which are not correct anymore. | |
| void Reset(); | |
| // Initialize this object pool builder with a [zone]. | |
| // | |
| // Any objects added later on will be referenced using handles from [zone]. | |
| void InitializeWithZone(Zone* zone) { | |
| ASSERT(object_pool_.length() == 0); | |
| ASSERT(zone_ == nullptr && zone != nullptr); | |
| zone_ = zone; | |
| } | |
| intptr_t AddObject( | |
| const Object& obj, | |
| ObjectPoolBuilderEntry::Patchability patchable = | |
| ObjectPoolBuilderEntry::kNotPatchable, | |
| ObjectPoolBuilderEntry::SnapshotBehavior snapshot_behavior = | |
| ObjectPoolBuilderEntry::kSnapshotable); | |
| intptr_t AddImmediate( | |
| uword imm, | |
| ObjectPoolBuilderEntry::Patchability patchable = | |
| ObjectPoolBuilderEntry::kNotPatchable, | |
| ObjectPoolBuilderEntry::SnapshotBehavior snapshotability = | |
| ObjectPoolBuilderEntry::kSnapshotable); | |
| intptr_t AddImmediate64(uint64_t imm); | |
| intptr_t AddImmediate128(simd128_value_t imm); | |
| intptr_t FindObject( | |
| const Object& obj, | |
| ObjectPoolBuilderEntry::Patchability patchable = | |
| ObjectPoolBuilderEntry::kNotPatchable, | |
| ObjectPoolBuilderEntry::SnapshotBehavior snapshot_behavior = | |
| ObjectPoolBuilderEntry::kSnapshotable); | |
| intptr_t FindObject(const Object& obj, const Object& equivalence); | |
| intptr_t FindImmediate(uword imm); | |
| intptr_t FindImmediate64(uint64_t imm); | |
| intptr_t FindImmediate128(simd128_value_t imm); | |
| intptr_t FindNativeFunction(const ExternalLabel* label, | |
| ObjectPoolBuilderEntry::Patchability patchable); | |
| intptr_t CurrentLength() const { | |
| return object_pool_.length() + used_from_parent_.length(); | |
| } | |
| ObjectPoolBuilderEntry& EntryAt(intptr_t i) { | |
| if (i < used_from_parent_.length()) { | |
| return parent_->EntryAt(used_from_parent_[i]); | |
| } | |
| return object_pool_[i - used_from_parent_.length()]; | |
| } | |
| const ObjectPoolBuilderEntry& EntryAt(intptr_t i) const { | |
| if (i < used_from_parent_.length()) { | |
| return parent_->EntryAt(used_from_parent_[i]); | |
| } | |
| return object_pool_[i - used_from_parent_.length()]; | |
| } | |
| intptr_t AddObject(ObjectPoolBuilderEntry entry); | |
| // Try appending all entries from this pool into the parent pool. | |
| // This might fail if parent pool was modified invalidating indices which | |
| // we produced. In this case this function will return false. | |
| bool TryCommitToParent(); | |
| bool HasParent() const { return parent_ != nullptr; } | |
| private: | |
| intptr_t FindObject(ObjectPoolBuilderEntry entry); | |
| // Parent pool into which all entries from this pool will be added at | |
| // the end of the successful compilation. | |
| ObjectPoolBuilder* const parent_; | |
| // Base index at which entries will be inserted into the parent pool. | |
| // Should be equal to parent_->CurrentLength() - but is cached here | |
| // to detect cases when parent pool grows due to nested code generations. | |
| const intptr_t base_index_; | |
| GrowableArray<intptr_t> used_from_parent_; | |
| // Objects and jump targets. | |
| GrowableArray<ObjectPoolBuilderEntry> object_pool_; | |
| // Hashmap for fast lookup in object pool. | |
| DirectChainedHashMap<ObjIndexPair> object_pool_index_table_; | |
| // The zone used for allocating the handles we keep in the map and array (or | |
| // nullptr, in which case allocations happen using the zone active at the | |
| // point of insertion). | |
| Zone* zone_; | |
| }; | |
| } // namespace compiler | |
| } // namespace dart | |
| #endif // RUNTIME_VM_COMPILER_ASSEMBLER_OBJECT_POOL_BUILDER_H_ |