blob: 6232b11f0855595e06966d0d05b3521fd3483ee4 [file]
// 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) 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_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);
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