| // Copyright 2023 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "mpact/sim/generic/decode_cache.h" |
| |
| #include <cstdint> |
| |
| #include "absl/numeric/bits.h" |
| |
| namespace mpact { |
| namespace sim { |
| namespace generic { |
| |
| DecodeCache::DecodeCache(const DecodeCacheProperties &props, |
| DecoderInterface *decoder) |
| : decoder_(decoder), instruction_cache_(nullptr) { |
| num_entries_ = absl::bit_ceil(props.num_entries); |
| address_shift_ = |
| (absl::bit_width(static_cast<uint32_t>(props.minimum_pc_increment)) - 1); |
| address_inc_ = 1 << address_shift_; |
| if (address_inc_ != props.minimum_pc_increment) { |
| // minimum pc increment not a power of 2. |
| instruction_cache_ = nullptr; |
| return; |
| } |
| address_mask_ = (num_entries_ - 1) << address_shift_; |
| |
| instruction_cache_ = new Instruction *[num_entries_]; |
| if (nullptr == instruction_cache_) { |
| // memory allocation failed |
| return; |
| } |
| |
| for (int entry = 0; entry < num_entries_; ++entry) { |
| instruction_cache_[entry] = nullptr; |
| } |
| } |
| |
| DecodeCache::~DecodeCache() { |
| InvalidateAll(); |
| delete[] instruction_cache_; |
| instruction_cache_ = nullptr; |
| } |
| |
| DecodeCache *DecodeCache::Create(const DecodeCacheProperties &props, |
| DecoderInterface *decoder) { |
| DecodeCache *dc = new DecodeCache(props, decoder); |
| if (nullptr == dc->instruction_cache_) { |
| delete dc; |
| return nullptr; |
| } |
| return dc; |
| } |
| |
| Instruction *DecodeCache::GetDecodedInstruction(uint64_t address) { |
| // Verify that the instruction_cache_ was allocated |
| if (nullptr == instruction_cache_) { |
| return nullptr; |
| } |
| |
| uint64_t indx = (address & address_mask_) >> address_shift_; |
| Instruction *inst = instruction_cache_[indx]; |
| |
| if ((nullptr != inst) && (inst->address() == address)) { |
| return inst; |
| } |
| |
| Instruction *new_inst = decoder_->DecodeInstruction(address); |
| |
| if (nullptr == new_inst) { |
| return nullptr; |
| } |
| |
| if (nullptr != inst) { |
| inst->DecRef(); |
| } |
| |
| instruction_cache_[indx] = new_inst; |
| |
| return new_inst; |
| } |
| |
| void DecodeCache::Invalidate(uint64_t address) { |
| uint64_t entry = (address & address_mask_) >> address_shift_; |
| Instruction *inst = instruction_cache_[entry]; |
| if ((nullptr != inst) && (inst->address() == address)) { |
| instruction_cache_[entry]->DecRef(); |
| instruction_cache_[entry] = nullptr; |
| } |
| } |
| |
| void DecodeCache::InvalidateRange(uint64_t start_address, |
| uint64_t end_address) { |
| for (auto loc = start_address; loc < end_address; loc += address_inc_) { |
| Invalidate(loc); |
| } |
| } |
| |
| void DecodeCache::InvalidateAll() { |
| for (int entry = 0; entry < num_entries_; ++entry) { |
| if (instruction_cache_[entry] != nullptr) { |
| instruction_cache_[entry]->DecRef(); |
| instruction_cache_[entry] = nullptr; |
| } |
| } |
| } |
| |
| } // namespace generic |
| } // namespace sim |
| } // namespace mpact |