| // 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/decoder/instruction.h" |
| |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/string_view.h" |
| #include "mpact/sim/decoder/instruction_set.h" |
| #include "mpact/sim/decoder/opcode.h" |
| #include "mpact/sim/decoder/slot.h" |
| #include "mpact/sim/decoder/template_expression.h" |
| |
| namespace mpact { |
| namespace sim { |
| namespace machine_description { |
| namespace instruction_set { |
| |
| Instruction::Instruction(Opcode* opcode, Slot* slot) |
| : Instruction(opcode, nullptr, slot) {} |
| Instruction::Instruction(Opcode* opcode, Instruction* child, Slot* slot) |
| : opcode_(opcode), child_(child), slot_(slot) {} |
| |
| Instruction::~Instruction() { |
| delete child_; |
| delete opcode_; |
| for (auto* ref : resource_use_vec_) { |
| delete ref; |
| } |
| resource_use_vec_.clear(); |
| for (auto* ref : resource_acquire_vec_) { |
| delete ref; |
| } |
| resource_acquire_vec_.clear(); |
| for (auto* disasm_format : disasm_format_vec_) { |
| delete disasm_format; |
| } |
| disasm_format_vec_.clear(); |
| for (auto& [ignored, expr] : attribute_map_) { |
| delete expr; |
| } |
| attribute_map_.clear(); |
| } |
| |
| // Append is implemented recursively. Few instructions have child instances, |
| // and when they do it's likely to be a very small number. Not concern for |
| // efficiency here. |
| void Instruction::AppendChild(Instruction* child) { |
| if (child_ == nullptr) { |
| child_ = child; |
| return; |
| } |
| child_->AppendChild(child); |
| } |
| |
| void Instruction::AppendResourceUse(const ResourceReference* resource_ref) { |
| resource_use_vec_.push_back(resource_ref); |
| } |
| |
| void Instruction::AppendResourceAcquire(const ResourceReference* resource_ref) { |
| resource_acquire_vec_.push_back(resource_ref); |
| } |
| |
| void Instruction::AddInstructionAttribute(absl::string_view attr_name, |
| TemplateExpression* expression) { |
| // See if the attribute is already defined. If not, create a new attribute |
| // in the map, otherwise update the expression. |
| auto iter = attribute_map_.find(attr_name); |
| if (iter == attribute_map_.end()) { |
| attribute_map_.emplace(attr_name, expression); |
| return; |
| } |
| delete iter->second; |
| iter->second = expression; |
| } |
| |
| // Add an attribute with the default value 1. |
| void Instruction::AddInstructionAttribute(absl::string_view attr_name) { |
| AddInstructionAttribute(attr_name, new TemplateConstant(1)); |
| } |
| |
| void Instruction::AppendDisasmFormat(DisasmFormat* disasm_format) { |
| disasm_format_vec_.push_back(disasm_format); |
| } |
| |
| // Creating a derived instruction involves copying attributes and re-evaluating |
| // any expressions that depend on any slot template instantiation values. |
| absl::StatusOr<Instruction*> Instruction::CreateDerivedInstruction( |
| TemplateInstantiationArgs* args, Slot* new_slot) const { |
| // First try to create a derived opcode object. Fail if it fails. |
| auto op_result = |
| slot_->instruction_set()->opcode_factory()->CreateDerivedOpcode(opcode(), |
| args); |
| if (!op_result.ok()) return op_result.status(); |
| |
| // Create a new instruction object with the derived opcode object. |
| auto* new_inst = new Instruction(op_result.value(), slot_); |
| |
| // Disassembly format. |
| for (auto const* disasm_fmt : disasm_format_vec()) { |
| new_inst->AppendDisasmFormat(new DisasmFormat(*disasm_fmt)); |
| } |
| // Semantic function string. |
| new_inst->set_semfunc_code_string(semfunc_code_string()); |
| |
| // Resource uses. |
| for (auto const& resource_use : resource_use_vec()) { |
| auto ref_result = CreateDerivedResourceRef(resource_use, args); |
| if (!ref_result.status().ok()) { |
| delete new_inst; |
| return ref_result.status(); |
| } |
| new_inst->AppendResourceUse(ref_result.value()); |
| } |
| |
| // Resource reservations. |
| for (auto const* resource_def : resource_acquire_vec()) { |
| auto ref_result = CreateDerivedResourceRef(resource_def, args); |
| if (!ref_result.status().ok()) { |
| delete new_inst; |
| return ref_result.status(); |
| } |
| new_inst->AppendResourceAcquire(ref_result.value()); |
| } |
| |
| // Instruction attributes. |
| for (auto const& [attr_name, expr_ptr] : attribute_map_) { |
| auto result = expr_ptr->Evaluate(args); |
| if (result.ok()) { |
| new_slot->AddAttributeName(attr_name); |
| new_inst->AddInstructionAttribute(attr_name, result.value()); |
| } else { |
| delete new_inst; |
| return absl::InternalError(absl::StrCat( |
| "Failed to create derived instruction for '", opcode()->name(), "'")); |
| } |
| } |
| |
| // Recursively hande child instructions. |
| if (child() == nullptr) return new_inst; |
| |
| auto result = child()->CreateDerivedInstruction(args, new_slot); |
| if (result.ok()) { |
| new_inst->AppendChild(result.value()); |
| return new_inst; |
| } |
| delete new_inst; |
| return result.status(); |
| } |
| |
| absl::StatusOr<ResourceReference*> Instruction::CreateDerivedResourceRef( |
| const ResourceReference* ref, TemplateInstantiationArgs* args) const { |
| TemplateExpression* begin_expr = nullptr; |
| TemplateExpression* end_expr = nullptr; |
| // Evaluate the begin expression in the context of any template instantiation |
| // arguments. |
| if (ref->begin_expression != nullptr) { |
| auto result = ref->begin_expression->Evaluate(args); |
| if (!result.ok()) { |
| return absl::InternalError(absl::StrCat( |
| "Failed to create derived instruction for '", opcode()->name(), |
| "': error evaluating begin expression")); |
| } |
| begin_expr = result.value(); |
| } |
| // Evaluate the end expression in the context of any template instantiation |
| // arguments. |
| if (ref->end_expression != nullptr) { |
| auto result = ref->end_expression->Evaluate(args); |
| if (!result.ok()) { |
| return absl::InternalError(absl::StrCat( |
| "Failed to create derived instruction for '", opcode()->name(), |
| "': error evaluating begin expression")); |
| } |
| end_expr = result.value(); |
| } |
| auto* new_ref = new ResourceReference(ref->resource, ref->is_array, |
| ref->dest_op, begin_expr, end_expr); |
| return new_ref; |
| } |
| |
| // The destination op is stored in the opcode object, however, the child pointer |
| // is in the instruction object, so traverse the instructions along the child |
| // chain to find the destination operand. |
| DestinationOperand* Instruction::GetDestOp(absl::string_view op_name) const { |
| auto dest_op = opcode()->GetDestOp(op_name); |
| if (dest_op != nullptr) return dest_op; |
| |
| if (child() != nullptr) { |
| return child()->GetDestOp(op_name); |
| } |
| return nullptr; |
| } |
| |
| // The following methods are used to clear the different instruction attributes. |
| // This is called prior to overriding the attribute value to clean up any |
| // allocated memory. |
| void Instruction::ClearDisasmFormat() { |
| for (auto* disasm_format : disasm_format_vec_) { |
| delete disasm_format; |
| } |
| disasm_format_vec_.clear(); |
| } |
| |
| void Instruction::ClearSemfuncCodeString() { semfunc_code_string_.clear(); } |
| |
| void Instruction::ClearResourceSpecs() { |
| for (auto* ref : resource_use_vec_) { |
| delete ref; |
| } |
| resource_use_vec_.clear(); |
| for (auto* ref : resource_acquire_vec_) { |
| delete ref; |
| } |
| resource_acquire_vec_.clear(); |
| } |
| |
| void Instruction::ClearAttributeSpecs() { |
| for (auto& [ignored, expr] : attribute_map_) { |
| delete expr; |
| } |
| attribute_map_.clear(); |
| } |
| |
| } // namespace instruction_set |
| } // namespace machine_description |
| } // namespace sim |
| } // namespace mpact |