// OpenSTA, Static Timing Analyzer // Copyright (c) 2024, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #pragma once #include // memcpy #include #include "ObjectId.hh" #include "Error.hh" namespace sta { template class ArrayBlock; // Array tables allocate arrays of objects in blocks and use 32 bit IDs to // reference the array. Paging performance is improved by allocating // blocks instead of individual arrays, and object sizes are reduced // by using 32 bit references instead of 64 bit pointers. // They are similar to ObjectTables but do not support delete/destroy or // reclaiming deleted arrays. template class ArrayTable { public: ArrayTable(); ~ArrayTable(); void make(uint32_t count, TYPE *&array, ObjectId &id); void destroy(ObjectId id, uint32_t count); // Grow as necessary and return pointer for id. TYPE *ensureId(ObjectId id); TYPE *pointer(ObjectId id) const; TYPE &ref(ObjectId id) const; size_t size() const { return size_; } void clear(); static constexpr int idx_bits = 7; static constexpr int block_size = (1 << idx_bits); static constexpr int block_id_max = 1 << (object_id_bits - idx_bits); private: ArrayBlock *makeBlock(uint32_t size); void pushBlock(ArrayBlock *block); void deleteBlocks(); size_t size_; // Block index of free block (blocks_[size - 1]). BlockIdx free_block_idx_; // Index of next free object in free_block_idx_. ObjectIdx free_idx_; // Don't use std::vector so growing blocks_ can be thread safe. size_t blocks_size_; size_t blocks_capacity_; ArrayBlock* *blocks_; ArrayBlock* *prev_blocks_; // Linked list of free arrays indexed by array size. std::vector free_list_; static constexpr ObjectId idx_mask_ = block_size - 1; }; template ArrayTable::ArrayTable() : size_(0), free_block_idx_(block_idx_null), free_idx_(object_idx_null), blocks_size_(0), blocks_capacity_(1024), blocks_(new ArrayBlock*[blocks_capacity_]), prev_blocks_(nullptr) { } template ArrayTable::~ArrayTable() { deleteBlocks(); delete [] blocks_; delete [] prev_blocks_; } template void ArrayTable::deleteBlocks() { for (size_t i = 0; i < blocks_size_; i++) delete blocks_[i]; } template void ArrayTable::make(uint32_t count, TYPE *&array, ObjectId &id) { // Check the free list for a previously destroyed array with the right size. if (count < free_list_.size() && free_list_[count] != object_id_null) { id = free_list_[count]; array = pointer(id); ObjectId *head = reinterpret_cast(array); free_list_[count] = *head; } else { ArrayBlock *block = blocks_size_ ? blocks_[free_block_idx_] : nullptr; if ((free_idx_ == object_idx_null && free_block_idx_ == block_idx_null) || free_idx_ + count >= block->size()) { uint32_t size = block_size; if (blocks_size_ == 0 // First block starts at idx 1. && count > block_size - 1) size = count + 1; else if (count > block_size) size = count; block = makeBlock(size); } // makeId(free_block_idx_, idx_bits) id = (free_block_idx_ << idx_bits) + free_idx_; array = block->pointer(free_idx_); free_idx_ += count; } size_ += count; } template ArrayBlock * ArrayTable::makeBlock(uint32_t size) { BlockIdx block_idx = blocks_size_; ArrayBlock *block = new ArrayBlock(size); pushBlock(block); free_block_idx_ = block_idx; // ObjectId zero is reserved for object_id_null. free_idx_ = (block_idx > 0) ? 0 : 1; return block; } template void ArrayTable::pushBlock(ArrayBlock *block) { blocks_[blocks_size_++] = block; if (blocks_size_ >= block_id_max) criticalError(223, "max array table block count exceeded."); if (blocks_size_ == blocks_capacity_) { size_t new_capacity = blocks_capacity_ * 1.5; ArrayBlock** new_blocks = new ArrayBlock*[new_capacity]; memcpy(new_blocks, blocks_, blocks_capacity_ * sizeof(ArrayBlock*)); if (prev_blocks_) delete [] prev_blocks_; // Preserve block array for other threads to reference. prev_blocks_ = blocks_; blocks_ = new_blocks; blocks_capacity_ = new_capacity; } } template void ArrayTable::destroy(ObjectId id, uint32_t count) { if (count >= free_list_.size()) free_list_.resize(count + 1); TYPE *array = pointer(id); // Prepend id to the free list. ObjectId *head = reinterpret_cast(array); *head = free_list_[count]; free_list_[count] = id; size_ -= count; } template TYPE * ArrayTable::pointer(ObjectId id) const { if (id == object_id_null) return nullptr; else { BlockIdx blk_idx = id >> idx_bits; ObjectIdx obj_idx = id & idx_mask_; return blocks_[blk_idx]->pointer(obj_idx); } } template TYPE * ArrayTable::ensureId(ObjectId id) { BlockIdx blk_idx = id >> idx_bits; ObjectIdx obj_idx = id & idx_mask_; // Make enough blocks for blk_idx to be valid. for (BlockIdx i = blocks_size_; i <= blk_idx; i++) { ArrayBlock *block = new ArrayBlock(block_size); pushBlock(block); } return blocks_[blk_idx]->pointer(obj_idx); } template TYPE & ArrayTable::ref(ObjectId id) const { if (id == object_id_null) criticalError(222, "null ObjectId reference is undefined."); BlockIdx blk_idx = id >> idx_bits; ObjectIdx obj_idx = id & idx_mask_; return blocks_[blk_idx]->ref(obj_idx); } template void ArrayTable::clear() { deleteBlocks(); blocks_size_ = 0; size_ = 0; free_block_idx_ = block_idx_null; free_idx_ = object_idx_null; free_list_.clear(); } //////////////////////////////////////////////////////////////// template class ArrayBlock { public: ArrayBlock(uint32_t size); ~ArrayBlock(); uint32_t size() const { return size_; } TYPE &ref(ObjectIdx idx) { return objects_[idx]; } TYPE *pointer(ObjectIdx idx) { return &objects_[idx]; } private: uint32_t size_; TYPE *objects_; }; template ArrayBlock::ArrayBlock(uint32_t size) : size_(size), objects_(new TYPE[size]) { } template ArrayBlock::~ArrayBlock() { delete [] objects_; } } // Namespace