blob: aaed47620c08e21dc3cbedcb6b0119854a54d73b [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/opcode.h"
#include <functional>
#include <string>
#include <utility>
#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/format_name.h"
#include "mpact/sim/decoder/template_expression.h"
namespace mpact {
namespace sim {
namespace machine_description {
namespace instruction_set {
// There is not much to the Opcode class. Note that the AppendSourceOpName
// method use emplace since a string_view can be passed to a std::string
// constructor but cannot be passed directly to push_back().
Opcode::Opcode(absl::string_view name, int value)
: name_(name), pascal_name_(ToPascalCase(name)), value_(value) {}
Opcode::~Opcode() {
for (auto *dest_op : dest_op_vec_) {
delete dest_op;
}
dest_op_vec_.clear();
dest_op_map_.clear();
}
void Opcode::AppendSourceOp(absl::string_view op_name, bool is_array) {
source_op_vec_.emplace_back(std::string(op_name), is_array);
}
void Opcode::AppendDestOp(absl::string_view op_name, bool is_array) {
auto *op = new DestinationOperand(std::string(op_name), is_array);
dest_op_vec_.push_back(op);
dest_op_map_.insert(std::make_pair(std::string(op_name), op));
}
void Opcode::AppendDestOp(absl::string_view op_name, bool is_array,
TemplateExpression *expression) {
auto *op = new DestinationOperand(std::string(op_name), is_array, expression);
dest_op_vec_.push_back(op);
dest_op_map_.insert(std::make_pair(std::string(op_name), op));
}
DestinationOperand *Opcode::GetDestOp(absl::string_view op_name) {
auto iter = dest_op_map_.find(op_name);
if (iter != dest_op_map_.end()) return iter->second;
return nullptr;
}
bool Opcode::ValidateDestLatencies(
const std::function<bool(int)> &validator) const {
for (auto const *dest_op : dest_op_vec_) {
if (dest_op->expression() != nullptr) {
auto result = dest_op->GetLatency();
if (!result.ok()) return false;
if (!validator(result.value())) return false;
}
}
return true;
}
Opcode *OpcodeFactory::CreateDefaultOpcode() { return new Opcode("", -1); }
absl::StatusOr<Opcode *> OpcodeFactory::CreateOpcode(absl::string_view name) {
if (opcode_names_.contains(name)) {
return absl::InternalError(
absl::StrCat("Opcode '", name, "' already declared"));
}
// Using emplace since name is a string_view which insert doesn't accept.
opcode_names_.emplace(name);
auto opcode = new Opcode(name, opcode_value_++);
opcode_vec_.push_back(opcode);
return opcode;
}
Opcode *OpcodeFactory::CreateChildOpcode(Opcode *opcode) const {
if (opcode == nullptr) return nullptr;
auto *child = new Opcode(opcode->name(), -1);
return child;
}
absl::StatusOr<Opcode *> OpcodeFactory::CreateDerivedOpcode(
const Opcode *opcode, TemplateInstantiationArgs *args) {
// Allocate a new opcode. Copy the basic information.
auto new_opcode = new Opcode(opcode->name(), opcode->value());
new_opcode->set_instruction_size(opcode->instruction_size());
new_opcode->predicate_op_name_ = opcode->predicate_op_name();
new_opcode->op_locator_map_ = opcode->op_locator_map();
for (auto const &src_op : opcode->source_op_vec()) {
new_opcode->AppendSourceOp(src_op.name, src_op.is_array);
}
// Copy destination operands, but evaluate any latencies using the template
// instantiation arguments, in case those expressions use them.
for (auto const *dest_op : opcode->dest_op_vec()) {
if (dest_op->expression() == nullptr) {
new_opcode->AppendDestOp(dest_op->name(), dest_op->is_array());
} else {
// For each destination operand that has an expression, evaluate it in the
// context of the passed in TemplateInstantiationArgs. This creates a copy
// of the expression tree where any constant subexpressions are
// recursively folded into constant nodes.
auto result = dest_op->expression()->Evaluate(args);
if (result.ok()) {
new_opcode->AppendDestOp(dest_op->name(), dest_op->is_array(),
result.value());
} else {
delete new_opcode;
return absl::InternalError(absl::StrCat(
"Failed to create derived opcode for '", opcode->name(), "'"));
}
}
}
return new_opcode;
}
} // namespace instruction_set
} // namespace machine_description
} // namespace sim
} // namespace mpact