| // 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 |