blob: d253d431a022c57f1c39a458fd5a2aa70af02ff4 [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/proto_instruction_group.h"
#include <string>
#include <utility>
#include <vector>
#include "absl/container/btree_map.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_replace.h"
#include "absl/strings/string_view.h"
#include "mpact/sim/decoder/decoder_error_listener.h"
#include "mpact/sim/decoder/format_name.h"
#include "mpact/sim/decoder/proto_encoding_group.h"
#include "mpact/sim/decoder/proto_encoding_info.h"
#include "mpact/sim/decoder/proto_format_contexts.h"
#include "mpact/sim/decoder/proto_instruction_encoding.h"
#include "src/google/protobuf/descriptor.h"
namespace mpact {
namespace sim {
namespace decoder {
namespace proto_fmt {
using ::mpact::sim::machine_description::instruction_set::ToPascalCase;
ProtoInstructionGroup::ProtoInstructionGroup(
std::string group_name, const google::protobuf::Descriptor* message_type,
std::string opcode_enum, ProtoEncodingInfo* encoding_info)
: name_(group_name),
message_type_(message_type),
opcode_enum_(opcode_enum),
encoding_info_(encoding_info) {}
ProtoInstructionGroup::~ProtoInstructionGroup() {
for (auto* encoding : encodings_) {
delete encoding;
}
encodings_.clear();
delete encoding_group_;
encoding_group_ = nullptr;
for (auto& [unused, setter_map] : setter_groups_) {
for (auto& [unused, setter_info] : setter_map) {
delete setter_info;
}
setter_map.clear();
}
setter_groups_.clear();
}
ProtoInstructionEncoding* ProtoInstructionGroup::AddInstructionEncoding(
std::string name) {
auto* encoding = new ProtoInstructionEncoding(name, this);
encodings_.push_back(encoding);
return encoding;
}
absl::StatusOr<
std::pair<absl::btree_map<std::string, SetterInfo*>::const_iterator,
absl::btree_map<std::string, SetterInfo*>::const_iterator>>
ProtoInstructionGroup::GetSetterGroup(absl::string_view group) const {
auto map_iter = setter_groups_.find(group);
if (map_iter == setter_groups_.end()) {
return absl::NotFoundError(absl::StrCat("No setter group '", group, "'."));
}
return std::pair<absl::btree_map<std::string, SetterInfo*>::const_iterator,
absl::btree_map<std::string, SetterInfo*>::const_iterator>{
map_iter->second.begin(), map_iter->second.end()};
}
// Add a group level setter.
absl::Status ProtoInstructionGroup::AddSetter(
const std::string& group_name, SetterDefCtx* ctx,
const std::string& setter_name,
const google::protobuf::FieldDescriptor* field_desc,
std::vector<const google::protobuf::FieldDescriptor*> one_of_fields,
IfNotCtx* if_not) {
auto map_iter = setter_groups_.find(group_name);
if (map_iter == setter_groups_.end()) {
auto [iter, unused] = setter_groups_.insert({group_name, {}});
map_iter = iter;
}
if (map_iter->second.contains(setter_name)) {
return absl::AlreadyExistsError(
absl::StrCat("Duplicate setter name '", setter_name,
"' in setter group '", group_name, "'."));
}
auto* setter_info =
new SetterInfo({ctx, setter_name, field_desc, one_of_fields, if_not});
map_iter->second.insert({setter_name, setter_info});
return absl::OkStatus();
}
void ProtoInstructionGroup::CopyInstructionEncoding(
ProtoInstructionEncoding* encoding) {
if (encoding_name_set_.contains(encoding->name())) {
encoding_info_->error_listener()->semanticWarning(
nullptr, absl::StrCat("Duplicate instruction opcode name '",
encoding->name(), "' in group '", name(), "'."));
}
encoding_name_set_.insert(encoding->name());
encodings_.push_back(encoding);
}
void ProtoInstructionGroup::ProcessEncodings(
DecoderErrorListener* error_listener) {
// Create a new encoding group for this instruction group and add all the
// encodings to it.
encoding_group_ = new ProtoEncodingGroup(this, 0, error_listener);
for (auto* encoding : encodings_) {
encoding_group_->AddEncoding(new ProtoInstructionEncoding(*encoding));
}
// Call the encoding group to break it into a proper decoding hierarchy.
encoding_group_->AddSubGroups();
}
std::string ProtoInstructionGroup::GenerateDecoder() const {
if (encoding_group_ == nullptr) {
return absl::StrCat("#error No decoder generated for instruction group '",
name(), "'.");
}
std::string output;
if (message_type_ == nullptr) {
absl::StrAppend(&output, "\n#error No message type for instruction group '",
name(), "'.\n");
return output;
}
std::string qualified_message_type =
absl::StrReplaceAll(message_type_->full_name(), {{".", "::"}});
std::string message_type = ToPascalCase(name()) + "MessageType";
absl::StrAppend(
&output, "\n// Decoding functions for instruction group: ", name(), "\n");
absl::StrAppend(&output, "namespace {\n\n");
absl::StrAppend(&output, encoding_info_->opcode_enum(), " ", "Decode",
ToPascalCase(name()), "_None(", message_type, ", ",
ToPascalCase(encoding_info_->decoder()->name()),
"Decoder *) {\n", " return ", encoding_info_->opcode_enum(),
"::kNone;\n}\n\n");
absl::StrAppend(&output, encoding_group_->EmitDecoders(
"Decode" + ToPascalCase(name()),
encoding_info_->opcode_enum(), message_type));
absl::StrAppend(&output, "} // namespace\n\n");
return output;
}
} // namespace proto_fmt
} // namespace decoder
} // namespace sim
} // namespace mpact